Skip to content

Commit 437c477

Browse files
committed
Move TCPServer to netutil, change handle_stream callback to overridden method hook, move IP address hack for unix domain sockets to HTTPConnection
1 parent dfda3a5 commit 437c477

File tree

2 files changed

+196
-185
lines changed

2 files changed

+196
-185
lines changed

tornado/httpserver.py

Lines changed: 6 additions & 185 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class except to start a server at the beginning of the process
3535
from tornado import httputil
3636
from tornado import ioloop
3737
from tornado import iostream
38-
from tornado import netutil
38+
from tornado.netutil import TCPServer
3939
from tornado import process
4040
from tornado import stack_context
4141
from tornado.util import b, bytes_type
@@ -45,188 +45,6 @@ class except to start a server at the beginning of the process
4545
except ImportError:
4646
ssl = None
4747

48-
49-
class TCPServer(object):
50-
r"""A non-blocking, single-threaded TCP server.
51-
52-
`TCPServer` can serve SSL traffic with Python 2.6+ and OpenSSL.
53-
To make this server serve SSL traffic, send the ssl_options dictionary
54-
argument with the arguments required for the `ssl.wrap_socket` method,
55-
including "certfile" and "keyfile"::
56-
57-
TCPServer(applicaton, ssl_options={
58-
"certfile": os.path.join(data_dir, "mydomain.crt"),
59-
"keyfile": os.path.join(data_dir, "mydomain.key"),
60-
})
61-
62-
`TCPServer` initialization follows one of three patterns:
63-
64-
1. `listen`: simple single-process::
65-
66-
server = TCPServer(app)
67-
server.listen(8888)
68-
IOLoop.instance().start()
69-
70-
2. `bind`/`start`: simple multi-process::
71-
72-
server = TCPServer(app)
73-
server.bind(8888)
74-
server.start(0) # Forks multiple sub-processes
75-
IOLoop.instance().start()
76-
77-
When using this interface, an `IOLoop` must *not* be passed
78-
to the `TCPServer` constructor. `start` will always start
79-
the server on the default singleton `IOLoop`.
80-
81-
3. `add_sockets`: advanced multi-process::
82-
83-
sockets = tornado.netutil.bind_sockets(8888)
84-
tornado.process.fork_processes(0)
85-
server = TCPServer(app)
86-
server.add_sockets(sockets)
87-
IOLoop.instance().start()
88-
89-
The `add_sockets` interface is more complicated, but it can be
90-
used with `tornado.process.fork_processes` to give you more
91-
flexibility in when the fork happens. `add_sockets` can
92-
also be used in single-process servers if you want to create
93-
your listening sockets in some way other than
94-
`tornado.netutil.bind_sockets`.
95-
"""
96-
def __init__(self, handle_stream, io_loop=None, ssl_options=None):
97-
self.handle_stream = handle_stream
98-
self.io_loop = io_loop
99-
self.ssl_options = ssl_options
100-
self._sockets = {} # fd -> socket object
101-
self._pending_sockets = []
102-
self._started = False
103-
104-
def listen(self, port, address=""):
105-
"""Starts accepting connections on the given port.
106-
107-
This method may be called more than once to listen on multiple ports.
108-
`listen` takes effect immediately; it is not necessary to call
109-
`TCPServer.start` afterwards. It is, however, necessary to start
110-
the `IOLoop`.
111-
"""
112-
sockets = netutil.bind_sockets(port, address=address)
113-
self.add_sockets(sockets)
114-
115-
def add_sockets(self, sockets):
116-
"""Makes this server start accepting connections on the given sockets.
117-
118-
The ``sockets`` parameter is a list of socket objects such as
119-
those returned by `tornado.netutil.bind_sockets`.
120-
`add_sockets` is typically used in combination with that
121-
method and `tornado.process.fork_processes` to provide greater
122-
control over the initialization of a multi-process server.
123-
"""
124-
if self.io_loop is None:
125-
self.io_loop = ioloop.IOLoop.instance()
126-
127-
for sock in sockets:
128-
self._sockets[sock.fileno()] = sock
129-
netutil.add_accept_handler(sock, self._handle_connection,
130-
io_loop=self.io_loop)
131-
132-
def add_socket(self, socket):
133-
"""Singular version of `add_sockets`. Takes a single socket object."""
134-
self.add_sockets([socket])
135-
136-
def bind(self, port, address=None, family=socket.AF_UNSPEC, backlog=128):
137-
"""Binds this server to the given port on the given address.
138-
139-
To start the server, call `start`. If you want to run this server
140-
in a single process, you can call `listen` as a shortcut to the
141-
sequence of `bind` and `start` calls.
142-
143-
Address may be either an IP address or hostname. If it's a hostname,
144-
the server will listen on all IP addresses associated with the
145-
name. Address may be an empty string or None to listen on all
146-
available interfaces. Family may be set to either ``socket.AF_INET``
147-
or ``socket.AF_INET6`` to restrict to ipv4 or ipv6 addresses, otherwise
148-
both will be used if available.
149-
150-
The ``backlog`` argument has the same meaning as for
151-
`socket.listen`.
152-
153-
This method may be called multiple times prior to `start` to listen
154-
on multiple ports or interfaces.
155-
"""
156-
sockets = netutil.bind_sockets(port, address=address,
157-
family=family, backlog=backlog)
158-
if self._started:
159-
self.add_sockets(sockets)
160-
else:
161-
self._pending_sockets.extend(sockets)
162-
163-
def start(self, num_processes=1):
164-
"""Starts this server in the IOLoop.
165-
166-
By default, we run the server in this process and do not fork any
167-
additional child process.
168-
169-
If num_processes is ``None`` or <= ``0``, we detect the number of cores
170-
available on this machine and fork that number of child
171-
processes. If num_processes is given and > ```1``, we fork that
172-
specific number of sub-processes.
173-
174-
Since we use processes and not threads, there is no shared memory
175-
between any server code.
176-
177-
Note that multiple processes are not compatible with the autoreload
178-
module (or the ``debug=True`` option to `tornado.web.Application`).
179-
When using multiple processes, no IOLoops can be created or
180-
referenced until after the call to ``TCPServer.start(n)``.
181-
"""
182-
assert not self._started
183-
self._started = True
184-
if num_processes != 1:
185-
process.fork_processes(num_processes)
186-
sockets = self._pending_sockets
187-
self._pending_sockets = []
188-
self.add_sockets(sockets)
189-
190-
def stop(self):
191-
"""Stops listening for new connections.
192-
193-
Requests currently in progress may still continue after the
194-
server is stopped.
195-
"""
196-
for fd, sock in self._sockets.iteritems():
197-
self.io_loop.remove_handler(fd)
198-
sock.close()
199-
200-
def _handle_connection(self, connection, address):
201-
if self.ssl_options is not None:
202-
assert ssl, "Python 2.6+ and OpenSSL required for SSL"
203-
try:
204-
connection = ssl.wrap_socket(connection,
205-
server_side=True,
206-
do_handshake_on_connect=False,
207-
**self.ssl_options)
208-
except ssl.SSLError, err:
209-
if err.args[0] == ssl.SSL_ERROR_EOF:
210-
return connection.close()
211-
else:
212-
raise
213-
except socket.error, err:
214-
if err.args[0] == errno.ECONNABORTED:
215-
return connection.close()
216-
else:
217-
raise
218-
try:
219-
if self.ssl_options is not None:
220-
stream = iostream.SSLIOStream(connection, io_loop=self.io_loop)
221-
else:
222-
stream = iostream.IOStream(connection, io_loop=self.io_loop)
223-
if connection.family not in (socket.AF_INET, socket.AF_INET6):
224-
# Unix (or other) socket; fake the remote address
225-
address = ('0.0.0.0', 0)
226-
self.handle_stream(stream, address)
227-
except Exception:
228-
logging.error("Error in connection callback", exc_info=True)
229-
23048
class HTTPServer(TCPServer):
23149
"""
23250
A server is defined by a request callback that takes an HTTPRequest
@@ -273,9 +91,9 @@ def __init__(self, request_callback, no_keep_alive=False, xheaders=False,
27391
self.request_callback = request_callback
27492
self.no_keep_alive = no_keep_alive
27593
self.xheaders = xheaders
276-
TCPServer.__init__(self, self._handle_stream, **kwargs)
94+
TCPServer.__init__(self, **kwargs)
27795

278-
def _handle_stream(self, stream, address):
96+
def handle_stream(self, stream, address):
27997
HTTPConnection(stream, address, self.request_callback,
28098
self.no_keep_alive, self.xheaders)
28199

@@ -292,6 +110,9 @@ class HTTPConnection(object):
292110
def __init__(self, stream, address, request_callback, no_keep_alive=False,
293111
xheaders=False):
294112
self.stream = stream
113+
if self.stream.socket.family not in (socket.AF_INET, socket.AF_INET6):
114+
# Unix (or other) socket; fake the remote address
115+
address = ('0.0.0.0', 0)
295116
self.address = address
296117
self.request_callback = request_callback
297118
self.no_keep_alive = no_keep_alive

0 commit comments

Comments
 (0)