Index: Lib/httplib.py =================================================================== --- Lib/httplib.py (revision 67120) +++ Lib/httplib.py (working copy) @@ -325,8 +325,8 @@ # See RFC 2616 sec 19.6 and RFC 1945 sec 6 for details. - def __init__(self, sock, debuglevel=0, strict=0, method=None): - self.fp = sock.makefile('rb', 0) + def __init__(self, sock, debuglevel=0, strict=0, method=None, bufsize=0): + self.fp = sock.makefile('rb', bufsize) self.debuglevel = debuglevel self.strict = strict self._method = method @@ -693,6 +693,7 @@ self.__state = _CS_IDLE def send(self, str): + print "send", repr(str) """Send `str' to the server.""" if self.sock is None: if self.auto_open: @@ -721,6 +722,7 @@ if v[0] == 32: # Broken pipe self.close() raise + print "sent" def _output(self, s): """Add a line of output to the current request buffer. @@ -734,10 +736,17 @@ Appends an extra \\r\\n to the buffer. """ + self.send(self._get_output()) + + def _get_output(self): + """Get the currently buffered request and clear the buffer. + + Appends an extra \\r\\n to the buffer. + """ self._buffer.extend(("", "")) msg = "\r\n".join(self._buffer) del self._buffer[:] - self.send(msg) + return msg def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0): """Send a request to the server. @@ -857,16 +866,29 @@ str = '%s: %s' % (header, value) self._output(str) - def endheaders(self): - """Indicate that the last header line has been sent to the server.""" - + def endheaders(self, extra_data=None): + """Indicate that the last header line has been sent to the server. + The optional extra_data is data that is sent afther the headers, and + can be either a string or a file-like object. It will be appended + to the header data and all sent in one chunk if possible. + This helps avoid the dreaded Nagle/delayed-ACK TCP performance + problem that can be caused by two send operations in a row. + """ if self.__state == _CS_REQ_STARTED: self.__state = _CS_REQ_SENT else: raise CannotSendHeader() + if extra_data is not None: + if isinstance(extra_data, str): + self.send(self._get_output() + extra_data) + else: + #if extra_data is a file, we have to live with risking + #the Nagle problem + self._send_output() + self.send(extra_data) + else: + self._send_output() - self._send_output() - def request(self, method, url, body=None, headers={}): """Send a complete request to the server.""" @@ -908,12 +930,9 @@ self.putheader('Content-Length',thelen) for hdr, value in headers.iteritems(): self.putheader(hdr, value) - self.endheaders() + self.endheaders(body) - if body: - self.send(body) - - def getresponse(self): + def getresponse(self, bufsize=0): "Get the response from the server." # if a prior response has been completed, then forget about it. @@ -942,10 +961,12 @@ if self.debuglevel > 0: response = self.response_class(self.sock, self.debuglevel, strict=self.strict, - method=self._method) + method=self._method, + bufsize=bufsize) else: response = self.response_class(self.sock, strict=self.strict, - method=self._method) + method=self._method, + bufsize=bufsize) response.begin() assert response.will_close != _UNKNOWN @@ -1012,7 +1033,7 @@ "The superclass allows only one value argument." self._conn.putheader(header, '\r\n\t'.join(values)) - def getreply(self): + def getreply(self, bufsize=0): """Compat definition since superclass does not define it. Returns a tuple consisting of: @@ -1021,14 +1042,14 @@ - any RFC822 headers in the response from the server """ try: - response = self._conn.getresponse() + response = self._conn.getresponse(bufsize) except BadStatusLine, e: ### hmm. if getresponse() ever closes the socket on a bad request, ### then we are going to have problems with self.sock ### should we keep this behavior? do people use it? # keep the socket open (as a file), and return it - self.file = self._conn.sock.makefile('rb', 0) + self.file = self._conn.sock.makefile('rb', bufsize) # close our socket -- we want to restart after any protocol error self.close() Index: Lib/test/test_xmlrpc.py =================================================================== --- Lib/test/test_xmlrpc.py (revision 67120) +++ Lib/test/test_xmlrpc.py (working copy) @@ -405,6 +405,9 @@ # protocol error; provide additional information in test output self.fail("%s\n%s" % (e, getattr(e, "headers", ""))) + + + def test_introspection2(self): try: # test _methodHelp() @@ -647,7 +650,9 @@ # 10035 (WSAEWOULDBLOCK) in the server thread handle_request call when # run on Windows. This only happens on the first test to run, but it # fails every time and so these tests are skipped on win32 platforms. - if sys.platform != 'win32': + # Update, nov 2008: This appears no longer to be the case, so they + # are included regardless + if True or sys.platform != 'win32': xmlrpc_tests.append(SimpleServerTestCase) xmlrpc_tests.append(FailingServerTestCase) xmlrpc_tests.append(CGIHandlerTestCase) Index: Lib/xmlrpclib.py =================================================================== --- Lib/xmlrpclib.py (revision 67120) +++ Lib/xmlrpclib.py (working copy) @@ -1234,7 +1234,9 @@ self.send_user_agent(h) self.send_content(h, request_body) - errcode, errmsg, headers = h.getreply() + #we will perform readline() on the h.getfile() so we need + #default buffering + errcode, errmsg, headers = h.getreply(bufsize = -1) if errcode != 200: raise ProtocolError( @@ -1346,9 +1348,7 @@ def send_content(self, connection, request_body): connection.putheader("Content-Type", "text/xml") connection.putheader("Content-Length", str(len(request_body))) - connection.endheaders() - if request_body: - connection.send(request_body) + connection.endheaders(request_body) ## # Parse response.