openmedialibrary_platform/Shared/lib/python3.7/site-packages/tornado/httpserver.py

331 lines
13 KiB
Python
Raw Permalink Normal View History

2013-10-11 17:28:32 +00:00
#
# Copyright 2009 Facebook
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""A non-blocking, single-threaded HTTP server.
Typical applications have little direct interaction with the `HTTPServer`
class except to start a server at the beginning of the process
(and even that is often done indirectly via `tornado.web.Application.listen`).
.. versionchanged:: 4.0
The ``HTTPRequest`` class that used to live in this module has been moved
to `tornado.httputil.HTTPServerRequest`. The old name remains as an alias.
2013-10-11 17:28:32 +00:00
"""
2019-01-13 08:01:53 +00:00
from __future__ import absolute_import, division, print_function
2013-10-11 17:28:32 +00:00
import socket
from tornado.escape import native_str
from tornado.http1connection import HTTP1ServerConnection, HTTP1ConnectionParameters
from tornado import gen
2013-10-11 17:28:32 +00:00
from tornado import httputil
from tornado import iostream
from tornado import netutil
from tornado.tcpserver import TCPServer
2016-02-23 06:06:55 +00:00
from tornado.util import Configurable
2013-10-11 17:28:32 +00:00
2016-02-23 06:06:55 +00:00
class HTTPServer(TCPServer, Configurable,
httputil.HTTPServerConnectionDelegate):
2013-10-11 17:28:32 +00:00
r"""A non-blocking, single-threaded HTTP server.
2016-02-23 06:06:55 +00:00
A server is defined by a subclass of `.HTTPServerConnectionDelegate`,
or, for backwards compatibility, a callback that takes an
`.HTTPServerRequest` as an argument. The delegate is usually a
`tornado.web.Application`.
2013-10-11 17:28:32 +00:00
`HTTPServer` supports keep-alive connections by default
(automatically for HTTP/1.1, or for HTTP/1.0 when the client
requests ``Connection: keep-alive``).
2013-10-11 17:28:32 +00:00
If ``xheaders`` is ``True``, we support the
``X-Real-Ip``/``X-Forwarded-For`` and
``X-Scheme``/``X-Forwarded-Proto`` headers, which override the
remote IP and URI scheme/protocol for all requests. These headers
are useful when running Tornado behind a reverse proxy or load
balancer. The ``protocol`` argument can also be set to ``https``
if Tornado is run behind an SSL-decoding proxy that does not set one of
the supported ``xheaders``.
2019-01-13 08:01:53 +00:00
By default, when parsing the ``X-Forwarded-For`` header, Tornado will
select the last (i.e., the closest) address on the list of hosts as the
remote host IP address. To select the next server in the chain, a list of
trusted downstream hosts may be passed as the ``trusted_downstream``
argument. These hosts will be skipped when parsing the ``X-Forwarded-For``
header.
2016-02-23 06:06:55 +00:00
To make this server serve SSL traffic, send the ``ssl_options`` keyword
argument with an `ssl.SSLContext` object. For compatibility with older
versions of Python ``ssl_options`` may also be a dictionary of keyword
arguments for the `ssl.wrap_socket` method.::
2013-10-11 17:28:32 +00:00
2016-02-23 06:06:55 +00:00
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_ctx.load_cert_chain(os.path.join(data_dir, "mydomain.crt"),
os.path.join(data_dir, "mydomain.key"))
2019-01-13 08:01:53 +00:00
HTTPServer(application, ssl_options=ssl_ctx)
2013-10-11 17:28:32 +00:00
`HTTPServer` initialization follows one of three patterns (the
initialization methods are defined on `tornado.tcpserver.TCPServer`):
1. `~tornado.tcpserver.TCPServer.listen`: simple single-process::
server = HTTPServer(app)
server.listen(8888)
2016-02-23 06:06:55 +00:00
IOLoop.current().start()
2013-10-11 17:28:32 +00:00
In many cases, `tornado.web.Application.listen` can be used to avoid
the need to explicitly create the `HTTPServer`.
2. `~tornado.tcpserver.TCPServer.bind`/`~tornado.tcpserver.TCPServer.start`:
simple multi-process::
server = HTTPServer(app)
server.bind(8888)
server.start(0) # Forks multiple sub-processes
2016-02-23 06:06:55 +00:00
IOLoop.current().start()
2013-10-11 17:28:32 +00:00
When using this interface, an `.IOLoop` must *not* be passed
to the `HTTPServer` constructor. `~.TCPServer.start` will always start
the server on the default singleton `.IOLoop`.
3. `~tornado.tcpserver.TCPServer.add_sockets`: advanced multi-process::
sockets = tornado.netutil.bind_sockets(8888)
tornado.process.fork_processes(0)
server = HTTPServer(app)
server.add_sockets(sockets)
2016-02-23 06:06:55 +00:00
IOLoop.current().start()
2013-10-11 17:28:32 +00:00
The `~.TCPServer.add_sockets` interface is more complicated,
but it can be used with `tornado.process.fork_processes` to
give you more flexibility in when the fork happens.
`~.TCPServer.add_sockets` can also be used in single-process
servers if you want to create your listening sockets in some
way other than `tornado.netutil.bind_sockets`.
.. versionchanged:: 4.0
Added ``decompress_request``, ``chunk_size``, ``max_header_size``,
``idle_connection_timeout``, ``body_timeout``, ``max_body_size``
arguments. Added support for `.HTTPServerConnectionDelegate`
instances as ``request_callback``.
2016-02-23 06:06:55 +00:00
.. versionchanged:: 4.1
`.HTTPServerConnectionDelegate.start_request` is now called with
two arguments ``(server_conn, request_conn)`` (in accordance with the
documentation) instead of one ``(request_conn)``.
.. versionchanged:: 4.2
`HTTPServer` is now a subclass of `tornado.util.Configurable`.
2019-01-13 08:01:53 +00:00
.. versionchanged:: 4.5
Added the ``trusted_downstream`` argument.
.. versionchanged:: 5.0
The ``io_loop`` argument has been removed.
2013-10-11 17:28:32 +00:00
"""
2016-02-23 06:06:55 +00:00
def __init__(self, *args, **kwargs):
# Ignore args to __init__; real initialization belongs in
# initialize since we're Configurable. (there's something
# weird in initialization order between this class,
# Configurable, and TCPServer so we can't leave __init__ out
# completely)
pass
2019-01-13 08:01:53 +00:00
def initialize(self, request_callback, no_keep_alive=False,
2016-02-23 06:06:55 +00:00
xheaders=False, ssl_options=None, protocol=None,
decompress_request=False,
chunk_size=None, max_header_size=None,
idle_connection_timeout=None, body_timeout=None,
2019-01-13 08:01:53 +00:00
max_body_size=None, max_buffer_size=None,
trusted_downstream=None):
2013-10-11 17:28:32 +00:00
self.request_callback = request_callback
self.xheaders = xheaders
self.protocol = protocol
self.conn_params = HTTP1ConnectionParameters(
decompress=decompress_request,
chunk_size=chunk_size,
max_header_size=max_header_size,
header_timeout=idle_connection_timeout or 3600,
max_body_size=max_body_size,
2019-01-13 08:01:53 +00:00
body_timeout=body_timeout,
no_keep_alive=no_keep_alive)
TCPServer.__init__(self, ssl_options=ssl_options,
max_buffer_size=max_buffer_size,
read_chunk_size=chunk_size)
self._connections = set()
2019-01-13 08:01:53 +00:00
self.trusted_downstream = trusted_downstream
2013-10-11 17:28:32 +00:00
2016-02-23 06:06:55 +00:00
@classmethod
def configurable_base(cls):
return HTTPServer
@classmethod
def configurable_default(cls):
return HTTPServer
@gen.coroutine
def close_all_connections(self):
while self._connections:
# Peek at an arbitrary element of the set
conn = next(iter(self._connections))
yield conn.close()
2013-10-11 17:28:32 +00:00
def handle_stream(self, stream, address):
context = _HTTPRequestContext(stream, address,
2019-01-13 08:01:53 +00:00
self.protocol,
self.trusted_downstream)
conn = HTTP1ServerConnection(
stream, self.conn_params, context)
self._connections.add(conn)
conn.start_serving(self)
2013-10-11 17:28:32 +00:00
def start_request(self, server_conn, request_conn):
2019-01-13 08:01:53 +00:00
if isinstance(self.request_callback, httputil.HTTPServerConnectionDelegate):
delegate = self.request_callback.start_request(server_conn, request_conn)
else:
delegate = _CallableAdapter(self.request_callback, request_conn)
if self.xheaders:
delegate = _ProxyAdapter(delegate, request_conn)
return delegate
2013-10-11 17:28:32 +00:00
def on_close(self, server_conn):
self._connections.remove(server_conn)
2013-10-11 17:28:32 +00:00
2019-01-13 08:01:53 +00:00
class _CallableAdapter(httputil.HTTPMessageDelegate):
def __init__(self, request_callback, request_conn):
self.connection = request_conn
self.request_callback = request_callback
self.request = None
self.delegate = None
self._chunks = []
def headers_received(self, start_line, headers):
self.request = httputil.HTTPServerRequest(
connection=self.connection, start_line=start_line,
headers=headers)
def data_received(self, chunk):
self._chunks.append(chunk)
def finish(self):
self.request.body = b''.join(self._chunks)
self.request._parse_body()
self.request_callback(self.request)
def on_connection_close(self):
self._chunks = None
class _HTTPRequestContext(object):
2019-01-13 08:01:53 +00:00
def __init__(self, stream, address, protocol, trusted_downstream=None):
2013-10-11 17:28:32 +00:00
self.address = address
# Save the socket's address family now so we know how to
# interpret self.address even after the stream is closed
# and its socket attribute replaced with None.
if stream.socket is not None:
self.address_family = stream.socket.family
2013-10-11 17:28:32 +00:00
else:
self.address_family = None
# In HTTPServerRequest we want an IP, not a full socket address.
if (self.address_family in (socket.AF_INET, socket.AF_INET6) and
address is not None):
self.remote_ip = address[0]
else:
# Unix (or other) socket; fake the remote address.
self.remote_ip = '0.0.0.0'
2013-10-11 17:28:32 +00:00
if protocol:
self.protocol = protocol
elif isinstance(stream, iostream.SSLIOStream):
2013-10-11 17:28:32 +00:00
self.protocol = "https"
else:
self.protocol = "http"
self._orig_remote_ip = self.remote_ip
self._orig_protocol = self.protocol
2019-01-13 08:01:53 +00:00
self.trusted_downstream = set(trusted_downstream or [])
def __str__(self):
if self.address_family in (socket.AF_INET, socket.AF_INET6):
return self.remote_ip
elif isinstance(self.address, bytes):
# Python 3 with the -bb option warns about str(bytes),
# so convert it explicitly.
# Unix socket addresses are str on mac but bytes on linux.
return native_str(self.address)
else:
return str(self.address)
def _apply_xheaders(self, headers):
"""Rewrite the ``remote_ip`` and ``protocol`` fields."""
# Squid uses X-Forwarded-For, others use X-Real-Ip
ip = headers.get("X-Forwarded-For", self.remote_ip)
2019-01-13 08:01:53 +00:00
# Skip trusted downstream hosts in X-Forwarded-For list
for ip in (cand.strip() for cand in reversed(ip.split(','))):
if ip not in self.trusted_downstream:
break
ip = headers.get("X-Real-Ip", ip)
if netutil.is_valid_ip(ip):
self.remote_ip = ip
# AWS uses X-Forwarded-Proto
proto_header = headers.get(
"X-Scheme", headers.get("X-Forwarded-Proto",
self.protocol))
2019-01-13 08:01:53 +00:00
if proto_header:
# use only the last proto entry if there is more than one
# TODO: support trusting mutiple layers of proxied protocol
proto_header = proto_header.split(',')[-1].strip()
if proto_header in ("http", "https"):
self.protocol = proto_header
def _unapply_xheaders(self):
"""Undo changes from `_apply_xheaders`.
Xheaders are per-request so they should not leak to the next
request on the same connection.
"""
self.remote_ip = self._orig_remote_ip
self.protocol = self._orig_protocol
2013-10-11 17:28:32 +00:00
2019-01-13 08:01:53 +00:00
class _ProxyAdapter(httputil.HTTPMessageDelegate):
def __init__(self, delegate, request_conn):
2016-02-23 06:06:55 +00:00
self.connection = request_conn
2019-01-13 08:01:53 +00:00
self.delegate = delegate
def headers_received(self, start_line, headers):
2019-01-13 08:01:53 +00:00
self.connection.context._apply_xheaders(headers)
return self.delegate.headers_received(start_line, headers)
def data_received(self, chunk):
2019-01-13 08:01:53 +00:00
return self.delegate.data_received(chunk)
2013-10-11 17:28:32 +00:00
def finish(self):
2019-01-13 08:01:53 +00:00
self.delegate.finish()
self._cleanup()
def on_connection_close(self):
2019-01-13 08:01:53 +00:00
self.delegate.on_connection_close()
self._cleanup()
def _cleanup(self):
2019-01-13 08:01:53 +00:00
self.connection.context._unapply_xheaders()
HTTPRequest = httputil.HTTPServerRequest