Skip to content

Commit 8531b1b

Browse files
committed
Send HTTP requests with a single send() call instead of many.
The implementation now stores all the lines of the request in a buffer and makes a single send() call when the request is finished, specifically when endheaders() is called. This appears to improve performance. The old code called send() for each line. The sends are all short, so they caused bad interactions with the Nagle algorithm and delayed acknowledgements. In simple tests, the second packet was delayed by 100s of ms. The second send was delayed by the Nagle algorithm, waiting for the ack. The delayed ack strategy delays the ack in hopes of piggybacking it on a data packet, but the server won't send any data until it receives the complete request. This change minimizes the problem that Nagle + delayed ack will cause a problem, although a request large enough to be broken into two packets will still suffer some delay. Luckily the MSS is large enough to accomodate most single packets. XXX Bug fix candidate?
1 parent ca5ed5b commit 8531b1b

File tree

1 file changed

+26
-15
lines changed

1 file changed

+26
-15
lines changed

Lib/httplib.py

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ class HTTPConnection:
479479

480480
def __init__(self, host, port=None, strict=None):
481481
self.sock = None
482+
self._buffer = []
482483
self.__response = None
483484
self.__state = _CS_IDLE
484485

@@ -543,7 +544,7 @@ def send(self, str):
543544
else:
544545
raise NotConnected()
545546

546-
# send the data to the server. if we get a broken pipe, then close
547+
# send the data to the server. if we get a broken pipe, then closesdwu
547548
# the socket. we want to reconnect when somebody tries to send again.
548549
#
549550
# NOTE: we DO propagate the error, though, because we cannot simply
@@ -557,6 +558,23 @@ def send(self, str):
557558
self.close()
558559
raise
559560

561+
def _output(self, s):
562+
"""Add a line of output to the current request buffer.
563+
564+
Aassumes that the line does *not* end with \r\n.
565+
"""
566+
self._buffer.append(s)
567+
568+
def _send_output(self):
569+
"""Send the currently buffered request and clear the buffer.
570+
571+
Appends an extra \r\n to the buffer.
572+
"""
573+
self._buffer.extend(("", ""))
574+
msg = "\r\n".join(self._buffer)
575+
del self._buffer[:]
576+
self.send(msg)
577+
560578
def putrequest(self, method, url, skip_host=0):
561579
"""Send a request to the server.
562580
@@ -565,6 +583,7 @@ def putrequest(self, method, url, skip_host=0):
565583
"""
566584

567585
# check if a prior response has been completed
586+
# XXX What if it hasn't?
568587
if self.__response and self.__response.isclosed():
569588
self.__response = None
570589

@@ -594,16 +613,9 @@ def putrequest(self, method, url, skip_host=0):
594613

595614
if not url:
596615
url = '/'
597-
str = '%s %s %s\r\n' % (method, url, self._http_vsn_str)
616+
str = '%s %s %s' % (method, url, self._http_vsn_str)
598617

599-
try:
600-
self.send(str)
601-
except socket.error, v:
602-
# trap 'Broken pipe' if we're allowed to automatically reconnect
603-
if v[0] != 32 or not self.auto_open:
604-
raise
605-
# try one more time (the socket was closed; this will reopen)
606-
self.send(str)
618+
self._output(str)
607619

608620
if self._http_vsn == 11:
609621
# Issue some standard headers for better HTTP/1.1 compliance
@@ -664,8 +676,8 @@ def putheader(self, header, value):
664676
if self.__state != _CS_REQ_STARTED:
665677
raise CannotSendHeader()
666678

667-
str = '%s: %s\r\n' % (header, value)
668-
self.send(str)
679+
str = '%s: %s' % (header, value)
680+
self._output(str)
669681

670682
def endheaders(self):
671683
"""Indicate that the last header line has been sent to the server."""
@@ -675,7 +687,7 @@ def endheaders(self):
675687
else:
676688
raise CannotSendHeader()
677689

678-
self.send('\r\n')
690+
self._send_output()
679691

680692
def request(self, method, url, body=None, headers={}):
681693
"""Send a complete request to the server."""
@@ -1202,6 +1214,7 @@ class HTTP11(HTTP):
12021214
):
12031215
print "https://%s%s" % (host, selector)
12041216
hs = HTTPS()
1217+
hs.set_debuglevel(dl)
12051218
hs.connect(host)
12061219
hs.putrequest('GET', selector)
12071220
hs.endheaders()
@@ -1214,8 +1227,6 @@ class HTTP11(HTTP):
12141227
for header in headers.headers: print header.strip()
12151228
print
12161229

1217-
return
1218-
12191230
# Test a buggy server -- returns garbled status line.
12201231
# http://www.yahoo.com/promotions/mom_com97/supermom.html
12211232
c = HTTPConnection("promotions.yahoo.com")

0 commit comments

Comments
 (0)