Source code for tornado.httpserver

#!/usr/bin/env python## 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."""from__future__importabsolute_import,division,print_function,with_statementimportsocketfromtornado.escapeimportnative_strfromtornado.http1connectionimportHTTP1ServerConnection,HTTP1ConnectionParametersfromtornadoimportgenfromtornadoimporthttputilfromtornadoimportiostreamfromtornadoimportnetutilfromtornado.tcpserverimportTCPServerfromtornado.utilimportConfigurable

[docs]classHTTPServer(TCPServer,Configurable,httputil.HTTPServerConnectionDelegate):r"""A non-blocking, single-threaded HTTP server. 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`. `HTTPServer` supports keep-alive connections by default (automatically for HTTP/1.1, or for HTTP/1.0 when the client requests ``Connection: keep-alive``). 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``. 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.:: 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")) HTTPServer(applicaton, ssl_options=ssl_ctx) `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) IOLoop.current().start() 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 IOLoop.current().start() 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) IOLoop.current().start() 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``. .. 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`. """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)passdefinitialize(self,request_callback,no_keep_alive=False,io_loop=None,xheaders=False,ssl_options=None,protocol=None,decompress_request=False,chunk_size=None,max_header_size=None,idle_connection_timeout=None,body_timeout=None,max_body_size=None,max_buffer_size=None):self.request_callback=request_callbackself.no_keep_alive=no_keep_aliveself.xheaders=xheadersself.protocol=protocolself.conn_params=HTTP1ConnectionParameters(decompress=decompress_request,chunk_size=chunk_size,max_header_size=max_header_size,header_timeout=idle_connection_timeoutor3600,max_body_size=max_body_size,body_timeout=body_timeout)TCPServer.__init__(self,io_loop=io_loop,ssl_options=ssl_options,max_buffer_size=max_buffer_size,read_chunk_size=chunk_size)self._connections=set()@classmethoddefconfigurable_base(cls):returnHTTPServer@classmethoddefconfigurable_default(cls):returnHTTPServer@gen.coroutinedefclose_all_connections(self):whileself._connections:# Peek at an arbitrary element of the setconn=next(iter(self._connections))yieldconn.close()defhandle_stream(self,stream,address):context=_HTTPRequestContext(stream,address,self.protocol)conn=HTTP1ServerConnection(stream,self.conn_params,context)self._connections.add(conn)conn.start_serving(self)defstart_request(self,server_conn,request_conn):ifisinstance(self.request_callback,httputil.HTTPServerConnectionDelegate):delegate=self.request_callback.start_request(server_conn,request_conn)else:delegate=_CallableAdapter(self.request_callback,request_conn)ifself.xheaders:delegate=_ProxyAdapter(delegate,request_conn)returndelegatedefon_close(self,server_conn):self._connections.remove(server_conn)

class_CallableAdapter(httputil.HTTPMessageDelegate):def__init__(self,request_callback,request_conn):self.connection=request_connself.request_callback=request_callbackself.request=Noneself.delegate=Noneself._chunks=[]defheaders_received(self,start_line,headers):self.request=httputil.HTTPServerRequest(connection=self.connection,start_line=start_line,headers=headers)defdata_received(self,chunk):self._chunks.append(chunk)deffinish(self):self.request.body=b''.join(self._chunks)self.request._parse_body()self.request_callback(self.request)defon_connection_close(self):self._chunks=Noneclass_HTTPRequestContext(object):def__init__(self,stream,address,protocol):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.ifstream.socketisnotNone:self.address_family=stream.socket.familyelse:self.address_family=None# In HTTPServerRequest we want an IP, not a full socket address.if(self.address_familyin(socket.AF_INET,socket.AF_INET6)andaddressisnotNone):self.remote_ip=address[0]else:# Unix (or other) socket; fake the remote address.self.remote_ip='0.0.0.0'ifprotocol:self.protocol=protocolelifisinstance(stream,iostream.SSLIOStream):self.protocol="https"else:self.protocol="http"self._orig_remote_ip=self.remote_ipself._orig_protocol=self.protocoldef__str__(self):ifself.address_familyin(socket.AF_INET,socket.AF_INET6):returnself.remote_ipelifisinstance(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.returnnative_str(self.address)else:returnstr(self.address)def_apply_xheaders(self,headers):"""Rewrite the ``remote_ip`` and ``protocol`` fields."""# Squid uses X-Forwarded-For, others use X-Real-Ipip=headers.get("X-Forwarded-For",self.remote_ip)ip=ip.split(',')[-1].strip()ip=headers.get("X-Real-Ip",ip)ifnetutil.is_valid_ip(ip):self.remote_ip=ip# AWS uses X-Forwarded-Protoproto_header=headers.get("X-Scheme",headers.get("X-Forwarded-Proto",self.protocol))ifproto_headerin("http","https"):self.protocol=proto_headerdef_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_ipself.protocol=self._orig_protocolclass_ProxyAdapter(httputil.HTTPMessageDelegate):def__init__(self,delegate,request_conn):self.connection=request_connself.delegate=delegatedefheaders_received(self,start_line,headers):self.connection.context._apply_xheaders(headers)returnself.delegate.headers_received(start_line,headers)defdata_received(self,chunk):returnself.delegate.data_received(chunk)deffinish(self):self.delegate.finish()self._cleanup()defon_connection_close(self):self.delegate.on_connection_close()self._cleanup()def_cleanup(self):self.connection.context._unapply_xheaders()HTTPRequest=httputil.HTTPServerRequest