00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 """A non-blocking, single-threaded HTTP server.
00018
00019 Typical applications have little direct interaction with the `HTTPServer`
00020 class except to start a server at the beginning of the process
00021 (and even that is often done indirectly via `tornado.web.Application.listen`).
00022
00023 .. versionchanged:: 4.0
00024
00025 The ``HTTPRequest`` class that used to live in this module has been moved
00026 to `tornado.httputil.HTTPServerRequest`. The old name remains as an alias.
00027 """
00028
00029 from __future__ import absolute_import, division, print_function, with_statement
00030
00031 import socket
00032
00033 from tornado.escape import native_str
00034 from tornado.http1connection import HTTP1ServerConnection, HTTP1ConnectionParameters
00035 from tornado import gen
00036 from tornado import httputil
00037 from tornado import iostream
00038 from tornado import netutil
00039 from tornado.tcpserver import TCPServer
00040
00041
00042 class HTTPServer(TCPServer, httputil.HTTPServerConnectionDelegate):
00043 r"""A non-blocking, single-threaded HTTP server.
00044
00045 A server is defined by either a request callback that takes a
00046 `.HTTPServerRequest` as an argument or a `.HTTPServerConnectionDelegate`
00047 instance.
00048
00049 A simple example server that echoes back the URI you requested::
00050
00051 import tornado.httpserver
00052 import tornado.ioloop
00053
00054 def handle_request(request):
00055 message = "You requested %s\n" % request.uri
00056 request.connection.write_headers(
00057 httputil.ResponseStartLine('HTTP/1.1', 200, 'OK'),
00058 {"Content-Length": str(len(message))})
00059 request.connection.write(message)
00060 request.connection.finish()
00061
00062 http_server = tornado.httpserver.HTTPServer(handle_request)
00063 http_server.listen(8888)
00064 tornado.ioloop.IOLoop.instance().start()
00065
00066 Applications should use the methods of `.HTTPConnection` to write
00067 their response.
00068
00069 `HTTPServer` supports keep-alive connections by default
00070 (automatically for HTTP/1.1, or for HTTP/1.0 when the client
00071 requests ``Connection: keep-alive``).
00072
00073 If ``xheaders`` is ``True``, we support the
00074 ``X-Real-Ip``/``X-Forwarded-For`` and
00075 ``X-Scheme``/``X-Forwarded-Proto`` headers, which override the
00076 remote IP and URI scheme/protocol for all requests. These headers
00077 are useful when running Tornado behind a reverse proxy or load
00078 balancer. The ``protocol`` argument can also be set to ``https``
00079 if Tornado is run behind an SSL-decoding proxy that does not set one of
00080 the supported ``xheaders``.
00081
00082 To make this server serve SSL traffic, send the ``ssl_options`` dictionary
00083 argument with the arguments required for the `ssl.wrap_socket` method,
00084 including ``certfile`` and ``keyfile``. (In Python 3.2+ you can pass
00085 an `ssl.SSLContext` object instead of a dict)::
00086
00087 HTTPServer(applicaton, ssl_options={
00088 "certfile": os.path.join(data_dir, "mydomain.crt"),
00089 "keyfile": os.path.join(data_dir, "mydomain.key"),
00090 })
00091
00092 `HTTPServer` initialization follows one of three patterns (the
00093 initialization methods are defined on `tornado.tcpserver.TCPServer`):
00094
00095 1. `~tornado.tcpserver.TCPServer.listen`: simple single-process::
00096
00097 server = HTTPServer(app)
00098 server.listen(8888)
00099 IOLoop.instance().start()
00100
00101 In many cases, `tornado.web.Application.listen` can be used to avoid
00102 the need to explicitly create the `HTTPServer`.
00103
00104 2. `~tornado.tcpserver.TCPServer.bind`/`~tornado.tcpserver.TCPServer.start`:
00105 simple multi-process::
00106
00107 server = HTTPServer(app)
00108 server.bind(8888)
00109 server.start(0) # Forks multiple sub-processes
00110 IOLoop.instance().start()
00111
00112 When using this interface, an `.IOLoop` must *not* be passed
00113 to the `HTTPServer` constructor. `~.TCPServer.start` will always start
00114 the server on the default singleton `.IOLoop`.
00115
00116 3. `~tornado.tcpserver.TCPServer.add_sockets`: advanced multi-process::
00117
00118 sockets = tornado.netutil.bind_sockets(8888)
00119 tornado.process.fork_processes(0)
00120 server = HTTPServer(app)
00121 server.add_sockets(sockets)
00122 IOLoop.instance().start()
00123
00124 The `~.TCPServer.add_sockets` interface is more complicated,
00125 but it can be used with `tornado.process.fork_processes` to
00126 give you more flexibility in when the fork happens.
00127 `~.TCPServer.add_sockets` can also be used in single-process
00128 servers if you want to create your listening sockets in some
00129 way other than `tornado.netutil.bind_sockets`.
00130
00131 .. versionchanged:: 4.0
00132 Added ``decompress_request``, ``chunk_size``, ``max_header_size``,
00133 ``idle_connection_timeout``, ``body_timeout``, ``max_body_size``
00134 arguments. Added support for `.HTTPServerConnectionDelegate`
00135 instances as ``request_callback``.
00136 """
00137 def __init__(self, request_callback, no_keep_alive=False, io_loop=None,
00138 xheaders=False, ssl_options=None, protocol=None,
00139 decompress_request=False,
00140 chunk_size=None, max_header_size=None,
00141 idle_connection_timeout=None, body_timeout=None,
00142 max_body_size=None, max_buffer_size=None):
00143 self.request_callback = request_callback
00144 self.no_keep_alive = no_keep_alive
00145 self.xheaders = xheaders
00146 self.protocol = protocol
00147 self.conn_params = HTTP1ConnectionParameters(
00148 decompress=decompress_request,
00149 chunk_size=chunk_size,
00150 max_header_size=max_header_size,
00151 header_timeout=idle_connection_timeout or 3600,
00152 max_body_size=max_body_size,
00153 body_timeout=body_timeout)
00154 TCPServer.__init__(self, io_loop=io_loop, ssl_options=ssl_options,
00155 max_buffer_size=max_buffer_size,
00156 read_chunk_size=chunk_size)
00157 self._connections = set()
00158
00159 @gen.coroutine
00160 def close_all_connections(self):
00161 while self._connections:
00162
00163 conn = next(iter(self._connections))
00164 yield conn.close()
00165
00166 def handle_stream(self, stream, address):
00167 context = _HTTPRequestContext(stream, address,
00168 self.protocol)
00169 conn = HTTP1ServerConnection(
00170 stream, self.conn_params, context)
00171 self._connections.add(conn)
00172 conn.start_serving(self)
00173
00174 def start_request(self, server_conn, request_conn):
00175 return _ServerRequestAdapter(self, request_conn)
00176
00177 def on_close(self, server_conn):
00178 self._connections.remove(server_conn)
00179
00180
00181 class _HTTPRequestContext(object):
00182 def __init__(self, stream, address, protocol):
00183 self.address = address
00184 self.protocol = protocol
00185
00186
00187
00188 if stream.socket is not None:
00189 self.address_family = stream.socket.family
00190 else:
00191 self.address_family = None
00192
00193 if (self.address_family in (socket.AF_INET, socket.AF_INET6) and
00194 address is not None):
00195 self.remote_ip = address[0]
00196 else:
00197
00198 self.remote_ip = '0.0.0.0'
00199 if protocol:
00200 self.protocol = protocol
00201 elif isinstance(stream, iostream.SSLIOStream):
00202 self.protocol = "https"
00203 else:
00204 self.protocol = "http"
00205 self._orig_remote_ip = self.remote_ip
00206 self._orig_protocol = self.protocol
00207
00208 def __str__(self):
00209 if self.address_family in (socket.AF_INET, socket.AF_INET6):
00210 return self.remote_ip
00211 elif isinstance(self.address, bytes):
00212
00213
00214
00215 return native_str(self.address)
00216 else:
00217 return str(self.address)
00218
00219 def _apply_xheaders(self, headers):
00220 """Rewrite the ``remote_ip`` and ``protocol`` fields."""
00221
00222 ip = headers.get("X-Forwarded-For", self.remote_ip)
00223 ip = ip.split(',')[-1].strip()
00224 ip = headers.get("X-Real-Ip", ip)
00225 if netutil.is_valid_ip(ip):
00226 self.remote_ip = ip
00227
00228 proto_header = headers.get(
00229 "X-Scheme", headers.get("X-Forwarded-Proto",
00230 self.protocol))
00231 if proto_header in ("http", "https"):
00232 self.protocol = proto_header
00233
00234 def _unapply_xheaders(self):
00235 """Undo changes from `_apply_xheaders`.
00236
00237 Xheaders are per-request so they should not leak to the next
00238 request on the same connection.
00239 """
00240 self.remote_ip = self._orig_remote_ip
00241 self.protocol = self._orig_protocol
00242
00243
00244 class _ServerRequestAdapter(httputil.HTTPMessageDelegate):
00245 """Adapts the `HTTPMessageDelegate` interface to the interface expected
00246 by our clients.
00247 """
00248 def __init__(self, server, connection):
00249 self.server = server
00250 self.connection = connection
00251 self.request = None
00252 if isinstance(server.request_callback,
00253 httputil.HTTPServerConnectionDelegate):
00254 self.delegate = server.request_callback.start_request(connection)
00255 self._chunks = None
00256 else:
00257 self.delegate = None
00258 self._chunks = []
00259
00260 def headers_received(self, start_line, headers):
00261 if self.server.xheaders:
00262 self.connection.context._apply_xheaders(headers)
00263 if self.delegate is None:
00264 self.request = httputil.HTTPServerRequest(
00265 connection=self.connection, start_line=start_line,
00266 headers=headers)
00267 else:
00268 return self.delegate.headers_received(start_line, headers)
00269
00270 def data_received(self, chunk):
00271 if self.delegate is None:
00272 self._chunks.append(chunk)
00273 else:
00274 return self.delegate.data_received(chunk)
00275
00276 def finish(self):
00277 if self.delegate is None:
00278 self.request.body = b''.join(self._chunks)
00279 self.request._parse_body()
00280 self.server.request_callback(self.request)
00281 else:
00282 self.delegate.finish()
00283 self._cleanup()
00284
00285 def on_connection_close(self):
00286 if self.delegate is None:
00287 self._chunks = None
00288 else:
00289 self.delegate.on_connection_close()
00290 self._cleanup()
00291
00292 def _cleanup(self):
00293 if self.server.xheaders:
00294 self.connection.context._unapply_xheaders()
00295
00296
00297 HTTPRequest = httputil.HTTPServerRequest