httpserver.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # Copyright 2009 Facebook
00004 #
00005 # Licensed under the Apache License, Version 2.0 (the "License"); you may
00006 # not use this file except in compliance with the License. You may obtain
00007 # a copy of the License at
00008 #
00009 #     http://www.apache.org/licenses/LICENSE-2.0
00010 #
00011 # Unless required by applicable law or agreed to in writing, software
00012 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00013 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
00014 # License for the specific language governing permissions and limitations
00015 # under the License.
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             # Peek at an arbitrary element of the set
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         # Save the socket's address family now so we know how to
00186         # interpret self.address even after the stream is closed
00187         # and its socket attribute replaced with None.
00188         if stream.socket is not None:
00189             self.address_family = stream.socket.family
00190         else:
00191             self.address_family = None
00192         # In HTTPServerRequest we want an IP, not a full socket address.
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             # Unix (or other) socket; fake the remote address.
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             # Python 3 with the -bb option warns about str(bytes),
00213             # so convert it explicitly.
00214             # Unix socket addresses are str on mac but bytes on linux.
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         # Squid uses X-Forwarded-For, others use X-Real-Ip
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         # AWS uses X-Forwarded-Proto
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


rosbridge_server
Author(s): Jonathan Mace
autogenerated on Thu Jun 6 2019 21:51:50