5562: Add comment about UPLOAD
[arvados.git] / sdk / python / arvados / keep.py
index 5caa5721dd77eb7790a103f18b0bdbf55d2e7e1d..aca10dea0cfdd7af4a157e7759055052ce733d90 100644 (file)
@@ -340,7 +340,8 @@ class KeepClient(object):
             except:
                 ua.close()
 
-        def _socket_open(self, family, socktype, protocol, address):
+        @staticmethod
+        def _socket_open(family, socktype, protocol, address=None):
             """Because pycurl doesn't have CURLOPT_TCP_KEEPALIVE"""
             s = socket.socket(family, socktype, protocol)
             s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
@@ -419,12 +420,20 @@ class KeepClient(object):
             curl = self._get_user_agent()
             try:
                 self._headers = {}
+                body_reader = StringIO.StringIO(body)
                 response_body = StringIO.StringIO()
                 curl.setopt(pycurl.NOSIGNAL, 1)
                 curl.setopt(pycurl.OPENSOCKETFUNCTION, self._socket_open)
                 curl.setopt(pycurl.URL, url.encode('utf-8'))
-                curl.setopt(pycurl.POSTFIELDS, body)
-                curl.setopt(pycurl.CUSTOMREQUEST, 'PUT')
+                # Using UPLOAD tells cURL to wait for a "go ahead" from the
+                # Keep server (in the form of a HTTP/1.1 "100 Continue"
+                # response) instead of sending the request body immediately.
+                # This allows the server to reject the request if the request
+                # is invalid or the server is read-only, without waiting for
+                # the client to send the entire block.
+                curl.setopt(pycurl.UPLOAD, True)
+                curl.setopt(pycurl.INFILESIZE, len(body))
+                curl.setopt(pycurl.READFUNCTION, body_reader.read)
                 curl.setopt(pycurl.HTTPHEADER, [
                     '{}: {}'.format(k,v) for k,v in self.put_headers.iteritems()])
                 curl.setopt(pycurl.WRITEFUNCTION, response_body.write)