Update Tornado

This commit is contained in:
Ruud
2013-06-12 23:42:38 +02:00
parent 6e0857c6c1
commit 8aeea60888
24 changed files with 1364 additions and 372 deletions
+47 -41
View File
@@ -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))