@@ -130,8 +130,13 @@ class will essentially render the service "deaf" while one request is
130130
131131
132132import socket
133+ import select
133134import sys
134135import os
136+ try :
137+ import threading
138+ except ImportError :
139+ import dummy_threading as threading
135140
136141__all__ = ["TCPServer" ,"UDPServer" ,"ForkingUDPServer" ,"ForkingTCPServer" ,
137142 "ThreadingUDPServer" ,"ThreadingTCPServer" ,"BaseRequestHandler" ,
@@ -149,7 +154,8 @@ class BaseServer:
149154 Methods for the caller:
150155
151156 - __init__(server_address, RequestHandlerClass)
152- - serve_forever()
157+ - serve_forever(poll_interval=0.5)
158+ - shutdown()
153159 - handle_request() # if you do not use serve_forever()
154160 - fileno() -> int # for select()
155161
@@ -190,6 +196,8 @@ def __init__(self, server_address, RequestHandlerClass):
190196 """Constructor. May be extended, do not override."""
191197 self .server_address = server_address
192198 self .RequestHandlerClass = RequestHandlerClass
199+ self .__is_shut_down = threading .Event ()
200+ self .__serving = False
193201
194202 def server_activate (self ):
195203 """Called by constructor to activate the server.
@@ -199,27 +207,73 @@ def server_activate(self):
199207 """
200208 pass
201209
202- def serve_forever (self ):
203- """Handle one request at a time until doomsday."""
204- while 1 :
205- self .handle_request ()
210+ def serve_forever (self , poll_interval = 0.5 ):
211+ """Handle one request at a time until shutdown.
212+
213+ Polls for shutdown every poll_interval seconds. Ignores
214+ self.timeout. If you need to do periodic tasks, do them in
215+ another thread.
216+ """
217+ self .__serving = True
218+ self .__is_shut_down .clear ()
219+ while self .__serving :
220+ # XXX: Consider using another file descriptor or
221+ # connecting to the socket to wake this up instead of
222+ # polling. Polling reduces our responsiveness to a
223+ # shutdown request and wastes cpu at all other times.
224+ r , w , e = select .select ([self ], [], [], poll_interval )
225+ if r :
226+ self ._handle_request_noblock ()
227+ self .__is_shut_down .set ()
228+
229+ def shutdown (self ):
230+ """Stops the serve_forever loop.
231+
232+ Blocks until the loop has finished. This must be called while
233+ serve_forever() is running in another thread, or it will
234+ deadlock.
235+ """
236+ self .__serving = False
237+ self .__is_shut_down .wait ()
206238
207239 # The distinction between handling, getting, processing and
208240 # finishing a request is fairly arbitrary. Remember:
209241 #
210242 # - handle_request() is the top-level call. It calls
211- # await_request(), verify_request() and process_request()
212- # - get_request(), called by await_request(), is different for
213- # stream or datagram sockets
243+ # select, get_request(), verify_request() and process_request()
244+ # - get_request() is different for stream or datagram sockets
214245 # - process_request() is the place that may fork a new process
215246 # or create a new thread to finish the request
216247 # - finish_request() instantiates the request handler class;
217248 # this constructor will handle the request all by itself
218249
219250 def handle_request (self ):
220- """Handle one request, possibly blocking."""
251+ """Handle one request, possibly blocking.
252+
253+ Respects self.timeout.
254+ """
255+ # Support people who used socket.settimeout() to escape
256+ # handle_request before self.timeout was available.
257+ timeout = self .socket .gettimeout ()
258+ if timeout is None :
259+ timeout = self .timeout
260+ elif self .timeout is not None :
261+ timeout = min (timeout , self .timeout )
262+ fd_sets = select .select ([self ], [], [], timeout )
263+ if not fd_sets [0 ]:
264+ self .handle_timeout ()
265+ return
266+ self ._handle_request_noblock ()
267+
268+ def _handle_request_noblock (self ):
269+ """Handle one request, without blocking.
270+
271+ I assume that select.select has returned that the socket is
272+ readable before this function was called, so there should be
273+ no risk of blocking in get_request().
274+ """
221275 try :
222- request , client_address = self .await_request ()
276+ request , client_address = self .get_request ()
223277 except socket .error :
224278 return
225279 if self .verify_request (request , client_address ):
@@ -229,21 +283,6 @@ def handle_request(self):
229283 self .handle_error (request , client_address )
230284 self .close_request (request )
231285
232- def await_request (self ):
233- """Call get_request or handle_timeout, observing self.timeout.
234-
235- Returns value from get_request() or raises socket.timeout exception if
236- timeout was exceeded.
237- """
238- if self .timeout is not None :
239- # If timeout == 0, you're responsible for your own fd magic.
240- import select
241- fd_sets = select .select ([self ], [], [], self .timeout )
242- if not fd_sets [0 ]:
243- self .handle_timeout ()
244- raise socket .timeout ("Listening timed out" )
245- return self .get_request ()
246-
247286 def handle_timeout (self ):
248287 """Called if no new request arrives within self.timeout.
249288
@@ -307,7 +346,8 @@ class TCPServer(BaseServer):
307346 Methods for the caller:
308347
309348 - __init__(server_address, RequestHandlerClass, bind_and_activate=True)
310- - serve_forever()
349+ - serve_forever(poll_interval=0.5)
350+ - shutdown()
311351 - handle_request() # if you don't use serve_forever()
312352 - fileno() -> int # for select()
313353
@@ -523,7 +563,6 @@ def process_request_thread(self, request, client_address):
523563
524564 def process_request (self , request , client_address ):
525565 """Start a new thread to process the request."""
526- import threading
527566 t = threading .Thread (target = self .process_request_thread ,
528567 args = (request , client_address ))
529568 if self .daemon_threads :
0 commit comments