Patch #742598 from Michael Pomraning: add .timeout attribute to SocketServer that will call

.handle_timeout() method when no requests are received within the timeout period.
This commit is contained in:
Andrew M. Kuchling 2008-01-19 16:26:13 +00:00
parent 5e3745c886
commit e45a77adbe
4 changed files with 60 additions and 5 deletions

View File

@ -44,7 +44,7 @@ to behave autonomously; the default is :const:`False`, meaning that Python will
not exit until all threads created by :class:`ThreadingMixIn` have exited.
Server classes have the same external methods and attributes, no matter what
network protocol they use:
network protocol they use.
Server Creation Notes
@ -193,6 +193,13 @@ The server classes support the following class variables:
The type of socket used by the server; :const:`socket.SOCK_STREAM` and
:const:`socket.SOCK_DGRAM` are two possible values.
.. data:: timeout
Timeout duration, measured in seconds, or :const:`None` if no timeout is desired.
If no incoming requests are received within the timeout period,
the :meth:`handle_timeout` method is called and then the server resumes waiting for
requests.
There are various server methods that can be overridden by subclasses of base
server classes like :class:`TCPServer`; these methods aren't useful to external
users of the server object.
@ -220,6 +227,13 @@ users of the server object.
method raises an exception. The default action is to print the traceback to
standard output and continue handling further requests.
.. function:: handle_timeout()
This function is called when the :attr:`timeout` attribute has been set to a
value other than :const:`None` and the timeout period has passed with no
requests being received. The default action for forking servers is
to collect the status of any child processes that have exited, while
in threading servers this method does nothing.
.. function:: process_request(request, client_address)

View File

@ -158,6 +158,7 @@ class BaseServer:
- server_bind()
- server_activate()
- get_request() -> request, client_address
- handle_timeout()
- verify_request(request, client_address)
- server_close()
- process_request(request, client_address)
@ -171,6 +172,7 @@ class BaseServer:
Class variables that may be overridden by derived classes or
instances:
- timeout
- address_family
- socket_type
- allow_reuse_address
@ -182,6 +184,8 @@ class BaseServer:
"""
timeout = None
def __init__(self, server_address, RequestHandlerClass):
"""Constructor. May be extended, do not override."""
self.server_address = server_address
@ -204,8 +208,9 @@ class BaseServer:
# finishing a request is fairly arbitrary. Remember:
#
# - handle_request() is the top-level call. It calls
# get_request(), verify_request() and process_request()
# - get_request() is different for stream or datagram sockets
# await_request(), verify_request() and process_request()
# - get_request(), called by await_request(), is different for
# stream or datagram sockets
# - process_request() is the place that may fork a new process
# or create a new thread to finish the request
# - finish_request() instantiates the request handler class;
@ -214,7 +219,7 @@ class BaseServer:
def handle_request(self):
"""Handle one request, possibly blocking."""
try:
request, client_address = self.get_request()
request, client_address = self.await_request()
except socket.error:
return
if self.verify_request(request, client_address):
@ -224,6 +229,28 @@ class BaseServer:
self.handle_error(request, client_address)
self.close_request(request)
def await_request(self):
"""Call get_request or handle_timeout, observing self.timeout.
Returns value from get_request() or raises socket.timeout exception if
timeout was exceeded.
"""
if self.timeout is not None:
# If timeout == 0, you're responsible for your own fd magic.
import select
fd_sets = select.select([self], [], [], self.timeout)
if not fd_sets[0]:
self.handle_timeout()
raise socket.timeout("Listening timed out")
return self.get_request()
def handle_timeout(self):
"""Called if no new request arrives within self.timeout.
Overridden by ForkingMixIn.
"""
pass
def verify_request(self, request, client_address):
"""Verify the request. May be overridden.
@ -289,6 +316,7 @@ class TCPServer(BaseServer):
- server_bind()
- server_activate()
- get_request() -> request, client_address
- handle_timeout()
- verify_request(request, client_address)
- process_request(request, client_address)
- close_request(request)
@ -301,6 +329,7 @@ class TCPServer(BaseServer):
Class variables that may be overridden by derived classes or
instances:
- timeout
- address_family
- socket_type
- request_queue_size (only for stream sockets)
@ -405,11 +434,12 @@ class ForkingMixIn:
"""Mix-in class to handle each request in a new process."""
timeout = 300
active_children = None
max_children = 40
def collect_children(self):
"""Internal routine to wait for died children."""
"""Internal routine to wait for children that have exited."""
while self.active_children:
if len(self.active_children) < self.max_children:
options = os.WNOHANG
@ -424,6 +454,13 @@ class ForkingMixIn:
if not pid: break
self.active_children.remove(pid)
def handle_timeout(self):
"""Wait for zombies after self.timeout seconds of inactivity.
May be extended, do not override.
"""
self.collect_children()
def process_request(self, request, client_address):
"""Fork a new subprocess to process the request."""
self.collect_children()

View File

@ -521,6 +521,7 @@ Martijn Pieters
François Pinard
Zach Pincus
Michael Piotrowski
Michael Pomraning
Iustin Pop
John Popplewell
Amrit Prem

View File

@ -704,6 +704,9 @@ Library
be equal to calling getsockname() on the server's socket. Fixed by
patch #1545011.
- Patch #742598: Add .timeout attribute to SocketServer that calls
.handle_timeout() when no requests are received.
- Bug #1651235: When a tuple was passed to a ctypes function call,
Python would crash instead of raising an error.