Update Tornado
This commit is contained in:
+47
-41
@@ -40,9 +40,9 @@ from tornado import stack_context
|
||||
from tornado.util import bytes_type
|
||||
|
||||
try:
|
||||
import Cookie # py2
|
||||
import Cookie # py2
|
||||
except ImportError:
|
||||
import http.cookies as Cookie # py3
|
||||
import http.cookies as Cookie # py3
|
||||
|
||||
|
||||
class HTTPServer(TCPServer):
|
||||
@@ -143,13 +143,13 @@ class HTTPServer(TCPServer):
|
||||
way other than `tornado.netutil.bind_sockets`.
|
||||
|
||||
"""
|
||||
def __init__(self, request_callback, no_keep_alive = False, io_loop = None,
|
||||
xheaders = False, ssl_options = None, protocol = None, **kwargs):
|
||||
def __init__(self, request_callback, no_keep_alive=False, io_loop=None,
|
||||
xheaders=False, ssl_options=None, protocol=None, **kwargs):
|
||||
self.request_callback = request_callback
|
||||
self.no_keep_alive = no_keep_alive
|
||||
self.xheaders = xheaders
|
||||
self.protocol = protocol
|
||||
TCPServer.__init__(self, io_loop = io_loop, ssl_options = ssl_options,
|
||||
TCPServer.__init__(self, io_loop=io_loop, ssl_options=ssl_options,
|
||||
**kwargs)
|
||||
|
||||
def handle_stream(self, stream, address):
|
||||
@@ -168,8 +168,8 @@ class HTTPConnection(object):
|
||||
We parse HTTP headers and bodies, and execute the request callback
|
||||
until the HTTP conection is closed.
|
||||
"""
|
||||
def __init__(self, stream, address, request_callback, no_keep_alive = False,
|
||||
xheaders = False, protocol = None):
|
||||
def __init__(self, stream, address, request_callback, no_keep_alive=False,
|
||||
xheaders=False, protocol=None):
|
||||
self.stream = stream
|
||||
self.address = address
|
||||
# Save the socket's address family now so we know how to
|
||||
@@ -180,23 +180,23 @@ class HTTPConnection(object):
|
||||
self.no_keep_alive = no_keep_alive
|
||||
self.xheaders = xheaders
|
||||
self.protocol = protocol
|
||||
self._request = None
|
||||
self._request_finished = False
|
||||
self._write_callback = None
|
||||
self._close_callback = None
|
||||
self._clear_request_state()
|
||||
# Save stack context here, outside of any request. This keeps
|
||||
# contexts from one request from leaking into the next.
|
||||
self._header_callback = stack_context.wrap(self._on_headers)
|
||||
self.stream.set_close_callback(self._on_connection_close)
|
||||
self.stream.read_until(b"\r\n\r\n", self._header_callback)
|
||||
|
||||
def _clear_callbacks(self):
|
||||
"""Clears the per-request callbacks.
|
||||
def _clear_request_state(self):
|
||||
"""Clears the per-request state.
|
||||
|
||||
This is run in between requests to allow the previous handler
|
||||
to be garbage collected (and prevent spurious close callbacks),
|
||||
and when the connection is closed (to break up cycles and
|
||||
facilitate garbage collection in cpython).
|
||||
"""
|
||||
self._request = None
|
||||
self._request_finished = False
|
||||
self._write_callback = None
|
||||
self._close_callback = None
|
||||
|
||||
@@ -209,34 +209,35 @@ class HTTPConnection(object):
|
||||
recommended approach prior to Tornado 3.0).
|
||||
"""
|
||||
self._close_callback = stack_context.wrap(callback)
|
||||
self.stream.set_close_callback(self._on_connection_close)
|
||||
|
||||
def _on_connection_close(self):
|
||||
callback = self._close_callback
|
||||
self._close_callback = None
|
||||
if callback: callback()
|
||||
if self._close_callback is not None:
|
||||
callback = self._close_callback
|
||||
self._close_callback = None
|
||||
callback()
|
||||
# Delete any unfinished callbacks to break up reference cycles.
|
||||
self._header_callback = None
|
||||
self._clear_callbacks()
|
||||
self._clear_request_state()
|
||||
|
||||
def close(self):
|
||||
self.stream.close()
|
||||
# Remove this reference to self, which would otherwise cause a
|
||||
# cycle and delay garbage collection of this connection.
|
||||
self._header_callback = None
|
||||
self._clear_callbacks()
|
||||
self._clear_request_state()
|
||||
|
||||
def write(self, chunk, callback = None):
|
||||
def write(self, chunk, callback=None):
|
||||
"""Writes a chunk of output to the stream."""
|
||||
assert self._request, "Request closed"
|
||||
if not self.stream.closed():
|
||||
self._write_callback = stack_context.wrap(callback)
|
||||
self.stream.write(chunk, self._on_write_complete)
|
||||
|
||||
def finish(self):
|
||||
"""Finishes the request."""
|
||||
assert self._request, "Request closed"
|
||||
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()
|
||||
|
||||
@@ -256,7 +257,7 @@ class HTTPConnection(object):
|
||||
self._finish_request()
|
||||
|
||||
def _finish_request(self):
|
||||
if self.no_keep_alive:
|
||||
if self.no_keep_alive or self._request is None:
|
||||
disconnect = True
|
||||
else:
|
||||
connection_header = self._request.headers.get("Connection")
|
||||
@@ -269,9 +270,7 @@ class HTTPConnection(object):
|
||||
disconnect = connection_header != "keep-alive"
|
||||
else:
|
||||
disconnect = True
|
||||
self._request = None
|
||||
self._request_finished = False
|
||||
self._clear_callbacks()
|
||||
self._clear_request_state()
|
||||
if disconnect:
|
||||
self.close()
|
||||
return
|
||||
@@ -280,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()
|
||||
|
||||
@@ -308,8 +311,8 @@ class HTTPConnection(object):
|
||||
remote_ip = '0.0.0.0'
|
||||
|
||||
self._request = HTTPRequest(
|
||||
connection = self, method = method, uri = uri, version = version,
|
||||
headers = headers, remote_ip = remote_ip, protocol = self.protocol)
|
||||
connection=self, method=method, uri=uri, version=version,
|
||||
headers=headers, remote_ip=remote_ip, protocol=self.protocol)
|
||||
|
||||
content_length = headers.get("Content-Length")
|
||||
if content_length:
|
||||
@@ -376,7 +379,10 @@ class HTTPRequest(object):
|
||||
|
||||
Client's IP address as a string. If ``HTTPServer.xheaders`` is set,
|
||||
will pass along the real IP address provided by a load balancer
|
||||
in the ``X-Real-Ip`` header
|
||||
in the ``X-Real-Ip`` or ``X-Forwarded-For`` header.
|
||||
|
||||
.. versionchanged:: 3.1
|
||||
The list format of ``X-Forwarded-For`` is now supported.
|
||||
|
||||
.. attribute:: protocol
|
||||
|
||||
@@ -409,9 +415,9 @@ class HTTPRequest(object):
|
||||
are typically kept open in HTTP/1.1, multiple requests can be handled
|
||||
sequentially on a single connection.
|
||||
"""
|
||||
def __init__(self, method, uri, version = "HTTP/1.0", headers = None,
|
||||
body = None, remote_ip = None, protocol = None, host = None,
|
||||
files = None, connection = None):
|
||||
def __init__(self, method, uri, version="HTTP/1.0", headers=None,
|
||||
body=None, remote_ip=None, protocol=None, host=None,
|
||||
files=None, connection=None):
|
||||
self.method = method
|
||||
self.uri = uri
|
||||
self.version = version
|
||||
@@ -431,8 +437,10 @@ class HTTPRequest(object):
|
||||
# xheaders can override the defaults
|
||||
if connection and connection.xheaders:
|
||||
# Squid uses X-Forwarded-For, others use X-Real-Ip
|
||||
ip = self.headers.get("X-Forwarded-For", self.remote_ip)
|
||||
ip = ip.split(',')[-1].strip()
|
||||
ip = self.headers.get(
|
||||
"X-Real-Ip", self.headers.get("X-Forwarded-For", self.remote_ip))
|
||||
"X-Real-Ip", ip)
|
||||
if netutil.is_valid_ip(ip):
|
||||
self.remote_ip = ip
|
||||
# AWS uses X-Forwarded-Proto
|
||||
@@ -441,7 +449,6 @@ class HTTPRequest(object):
|
||||
if proto in ("http", "https"):
|
||||
self.protocol = proto
|
||||
|
||||
|
||||
self.host = host or self.headers.get("Host") or "127.0.0.1"
|
||||
self.files = files or {}
|
||||
self.connection = connection
|
||||
@@ -449,7 +456,7 @@ class HTTPRequest(object):
|
||||
self._finish_time = None
|
||||
|
||||
self.path, sep, self.query = uri.partition('?')
|
||||
self.arguments = parse_qs_bytes(self.query, keep_blank_values = True)
|
||||
self.arguments = parse_qs_bytes(self.query, keep_blank_values=True)
|
||||
|
||||
def supports_http_1_1(self):
|
||||
"""Returns True if this request supports HTTP/1.1 semantics"""
|
||||
@@ -468,10 +475,10 @@ class HTTPRequest(object):
|
||||
self._cookies = {}
|
||||
return self._cookies
|
||||
|
||||
def write(self, chunk, callback = None):
|
||||
def write(self, chunk, callback=None):
|
||||
"""Writes the given chunk to the response stream."""
|
||||
assert isinstance(chunk, bytes_type)
|
||||
self.connection.write(chunk, callback = callback)
|
||||
self.connection.write(chunk, callback=callback)
|
||||
|
||||
def finish(self):
|
||||
"""Finishes this HTTP request on the open connection."""
|
||||
@@ -489,7 +496,7 @@ class HTTPRequest(object):
|
||||
else:
|
||||
return self._finish_time - self._start_time
|
||||
|
||||
def get_ssl_certificate(self, binary_form = False):
|
||||
def get_ssl_certificate(self, binary_form=False):
|
||||
"""Returns the client's SSL certificate, if any.
|
||||
|
||||
To use client certificates, the HTTPServer must have been constructed
|
||||
@@ -511,13 +518,12 @@ class HTTPRequest(object):
|
||||
"""
|
||||
try:
|
||||
return self.connection.stream.socket.getpeercert(
|
||||
binary_form = binary_form)
|
||||
binary_form=binary_form)
|
||||
except ssl.SSLError:
|
||||
return None
|
||||
|
||||
def __repr__(self):
|
||||
attrs = ("protocol", "host", "method", "uri", "version", "remote_ip",
|
||||
"body")
|
||||
attrs = ("protocol", "host", "method", "uri", "version", "remote_ip")
|
||||
args = ", ".join(["%s=%r" % (n, getattr(self, n)) for n in attrs])
|
||||
return "%s(%s, headers=%s)" % (
|
||||
self.__class__.__name__, args, dict(self.headers))
|
||||
|
||||
Reference in New Issue
Block a user