From: Ben Darnell Date: Sun, 5 May 2013 01:50:31 +0000 (-0400) Subject: Add an interface to set the TCP_NODELAY flag on an IOStream. X-Git-Tag: v3.1.0~85 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0fe807b543c36cbd8557651600d4dcaeff59d4aa;p=thirdparty%2Ftornado.git Add an interface to set the TCP_NODELAY flag on an IOStream. Use this flag from simple_httpclient (always) and httpserver (when a complete response is available), and add an accessor for WebSocketHandler. Also make simple_httpclient send headers and body in a single IOStream.write call. --- diff --git a/tornado/httpserver.py b/tornado/httpserver.py index c6488bce5..11f083299 100644 --- a/tornado/httpserver.py +++ b/tornado/httpserver.py @@ -235,6 +235,9 @@ class HTTPConnection(object): def finish(self): """Finishes the request.""" self._request_finished = True + # No more data is coming, so instruct TCP to send any remaining + # data immediately instead of waiting for a full packet or ack. + self.stream.set_nodelay(True) if not self.stream.writing(): self._finish_request() @@ -276,6 +279,10 @@ class HTTPConnection(object): # directly, because in some cases the stream doesn't discover # that it's closed until you try to read from it. self.stream.read_until(b"\r\n\r\n", self._header_callback) + + # Turn Nagle's algorithm back on, leaving the stream in its + # default state for the next request. + self.stream.set_nodelay(False) except iostream.StreamClosedError: self.close() diff --git a/tornado/iostream.py b/tornado/iostream.py index ad395f0e2..064193dee 100644 --- a/tornado/iostream.py +++ b/tornado/iostream.py @@ -273,6 +273,19 @@ class BaseIOStream(object): """Returns true if the stream has been closed.""" return self._closed + def set_nodelay(self, value): + """Sets the no-delay flag for this stream. + + By default, data written to TCP streams may be held for a time + to make the most efficient use of bandwidth (according to + Nagle's algorithm). The no-delay flag requests that data be + written as soon as possible, even if doing so would consume + additional bandwidth. + + This flag is currently defined only for TCP-based ``IOStreams``. + """ + pass + def _handle_events(self, fd, events): if self.closed(): gen_log.warning("Got events for closed stream %d", fd) @@ -726,6 +739,12 @@ class IOStream(BaseIOStream): self._run_callback(callback) self._connecting = False + def set_nodelay(self, value): + if (self.socket is not None and + self.socket.family in (socket.AF_INET, socket.AF_INET6)): + self.socket.setsockopt(socket.IPPROTO_TCP, + socket.TCP_NODELAY, 1) + class SSLIOStream(IOStream): """A utility class to write to and read from a non-blocking SSL socket. diff --git a/tornado/simple_httpclient.py b/tornado/simple_httpclient.py index 117ce75b8..31b5a73d8 100644 --- a/tornado/simple_httpclient.py +++ b/tornado/simple_httpclient.py @@ -279,9 +279,11 @@ class _HTTPConnection(object): if b'\n' in line: raise ValueError('Newline in header: ' + repr(line)) request_lines.append(line) - self.stream.write(b"\r\n".join(request_lines) + b"\r\n\r\n") + request_str = b"\r\n".join(request_lines) + b"\r\n\r\n" if self.request.body is not None: - self.stream.write(self.request.body) + request_str += self.request.body + self.stream.set_nodelay(True) + self.stream.write(request_str) self.stream.read_until_regex(b"\r?\n\r?\n", self._on_headers) def _release(self): diff --git a/tornado/websocket.py b/tornado/websocket.py index 7bc651386..dd4e4b475 100644 --- a/tornado/websocket.py +++ b/tornado/websocket.py @@ -227,6 +227,20 @@ class WebSocketHandler(tornado.web.RequestHandler): """ return False + def set_nodelay(self, value): + """Set the no-delay flag for this stream. + + By default, small messages may be delayed and/or combined to minimize + the number of packets sent. This can sometimes cause 200-500ms delays + due to the interaction between Nagle's algorithm and TCP delayed + ACKs. To reduce this delay (at the expense of possibly increasing + bandwidth usage), call ``self.set_nodelay(True)`` once the websocket + connection is established. + + See `.IOStream.set_nodelay` for additional details. + """ + self.stream.set_nodelay(value) + def get_websocket_scheme(self): """Return the url scheme used for this request, either "ws" or "wss".