summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew M. Kuchling <amk@amk.ca>2008-01-19 16:26:13 (GMT)
committerAndrew M. Kuchling <amk@amk.ca>2008-01-19 16:26:13 (GMT)
commite45a77adbec3ceb2aefe7ae7e60e28e903d16172 (patch)
treedbb47d9de478721f04abc073d3d85bd466c18e95
parent5e3745c886929bc8db4b987b5eb178fbeede75da (diff)
downloadcpython-e45a77adbec3ceb2aefe7ae7e60e28e903d16172.zip
cpython-e45a77adbec3ceb2aefe7ae7e60e28e903d16172.tar.gz
cpython-e45a77adbec3ceb2aefe7ae7e60e28e903d16172.tar.bz2
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.
-rw-r--r--Doc/library/socketserver.rst16
-rw-r--r--Lib/SocketServer.py45
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS3
4 files changed, 60 insertions, 5 deletions
diff --git a/Doc/library/socketserver.rst b/Doc/library/socketserver.rst
index c900ea7..2c85c86 100644
--- a/Doc/library/socketserver.rst
+++ b/Doc/library/socketserver.rst
@@ -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)
diff --git a/Lib/SocketServer.py b/Lib/SocketServer.py
index 5506aa5..2eed914 100644
--- a/Lib/SocketServer.py
+++ b/Lib/SocketServer.py
@@ -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()
diff --git a/Misc/ACKS b/Misc/ACKS
index 2ae4528..cf65424 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -521,6 +521,7 @@ Martijn Pieters
François Pinard
Zach Pincus
Michael Piotrowski
+Michael Pomraning
Iustin Pop
John Popplewell
Amrit Prem
diff --git a/Misc/NEWS b/Misc/NEWS
index 35d9634..9e25b96 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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.