1 | """Generic socket server classes.
|
---|
2 |
|
---|
3 | This module tries to capture the various aspects of defining a server:
|
---|
4 |
|
---|
5 | For socket-based servers:
|
---|
6 |
|
---|
7 | - address family:
|
---|
8 | - AF_INET{,6}: IP (Internet Protocol) sockets (default)
|
---|
9 | - AF_UNIX: Unix domain sockets
|
---|
10 | - others, e.g. AF_DECNET are conceivable (see <socket.h>
|
---|
11 | - socket type:
|
---|
12 | - SOCK_STREAM (reliable stream, e.g. TCP)
|
---|
13 | - SOCK_DGRAM (datagrams, e.g. UDP)
|
---|
14 |
|
---|
15 | For request-based servers (including socket-based):
|
---|
16 |
|
---|
17 | - client address verification before further looking at the request
|
---|
18 | (This is actually a hook for any processing that needs to look
|
---|
19 | at the request before anything else, e.g. logging)
|
---|
20 | - how to handle multiple requests:
|
---|
21 | - synchronous (one request is handled at a time)
|
---|
22 | - forking (each request is handled by a new process)
|
---|
23 | - threading (each request is handled by a new thread)
|
---|
24 |
|
---|
25 | The classes in this module favor the server type that is simplest to
|
---|
26 | write: a synchronous TCP/IP server. This is bad class design, but
|
---|
27 | save some typing. (There's also the issue that a deep class hierarchy
|
---|
28 | slows down method lookups.)
|
---|
29 |
|
---|
30 | There are five classes in an inheritance diagram, four of which represent
|
---|
31 | synchronous servers of four types:
|
---|
32 |
|
---|
33 | +------------+
|
---|
34 | | BaseServer |
|
---|
35 | +------------+
|
---|
36 | |
|
---|
37 | v
|
---|
38 | +-----------+ +------------------+
|
---|
39 | | TCPServer |------->| UnixStreamServer |
|
---|
40 | +-----------+ +------------------+
|
---|
41 | |
|
---|
42 | v
|
---|
43 | +-----------+ +--------------------+
|
---|
44 | | UDPServer |------->| UnixDatagramServer |
|
---|
45 | +-----------+ +--------------------+
|
---|
46 |
|
---|
47 | Note that UnixDatagramServer derives from UDPServer, not from
|
---|
48 | UnixStreamServer -- the only difference between an IP and a Unix
|
---|
49 | stream server is the address family, which is simply repeated in both
|
---|
50 | unix server classes.
|
---|
51 |
|
---|
52 | Forking and threading versions of each type of server can be created
|
---|
53 | using the ForkingMixIn and ThreadingMixIn mix-in classes. For
|
---|
54 | instance, a threading UDP server class is created as follows:
|
---|
55 |
|
---|
56 | class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
|
---|
57 |
|
---|
58 | The Mix-in class must come first, since it overrides a method defined
|
---|
59 | in UDPServer! Setting the various member variables also changes
|
---|
60 | the behavior of the underlying server mechanism.
|
---|
61 |
|
---|
62 | To implement a service, you must derive a class from
|
---|
63 | BaseRequestHandler and redefine its handle() method. You can then run
|
---|
64 | various versions of the service by combining one of the server classes
|
---|
65 | with your request handler class.
|
---|
66 |
|
---|
67 | The request handler class must be different for datagram or stream
|
---|
68 | services. This can be hidden by using the request handler
|
---|
69 | subclasses StreamRequestHandler or DatagramRequestHandler.
|
---|
70 |
|
---|
71 | Of course, you still have to use your head!
|
---|
72 |
|
---|
73 | For instance, it makes no sense to use a forking server if the service
|
---|
74 | contains state in memory that can be modified by requests (since the
|
---|
75 | modifications in the child process would never reach the initial state
|
---|
76 | kept in the parent process and passed to each child). In this case,
|
---|
77 | you can use a threading server, but you will probably have to use
|
---|
78 | locks to avoid two requests that come in nearly simultaneous to apply
|
---|
79 | conflicting changes to the server state.
|
---|
80 |
|
---|
81 | On the other hand, if you are building e.g. an HTTP server, where all
|
---|
82 | data is stored externally (e.g. in the file system), a synchronous
|
---|
83 | class will essentially render the service "deaf" while one request is
|
---|
84 | being handled -- which may be for a very long time if a client is slow
|
---|
85 | to read all the data it has requested. Here a threading or forking
|
---|
86 | server is appropriate.
|
---|
87 |
|
---|
88 | In some cases, it may be appropriate to process part of a request
|
---|
89 | synchronously, but to finish processing in a forked child depending on
|
---|
90 | the request data. This can be implemented by using a synchronous
|
---|
91 | server and doing an explicit fork in the request handler class
|
---|
92 | handle() method.
|
---|
93 |
|
---|
94 | Another approach to handling multiple simultaneous requests in an
|
---|
95 | environment that supports neither threads nor fork (or where these are
|
---|
96 | too expensive or inappropriate for the service) is to maintain an
|
---|
97 | explicit table of partially finished requests and to use select() to
|
---|
98 | decide which request to work on next (or whether to handle a new
|
---|
99 | incoming request). This is particularly important for stream services
|
---|
100 | where each client can potentially be connected for a long time (if
|
---|
101 | threads or subprocesses cannot be used).
|
---|
102 |
|
---|
103 | Future work:
|
---|
104 | - Standard classes for Sun RPC (which uses either UDP or TCP)
|
---|
105 | - Standard mix-in classes to implement various authentication
|
---|
106 | and encryption schemes
|
---|
107 | - Standard framework for select-based multiplexing
|
---|
108 |
|
---|
109 | XXX Open problems:
|
---|
110 | - What to do with out-of-band data?
|
---|
111 |
|
---|
112 | BaseServer:
|
---|
113 | - split generic "request" functionality out into BaseServer class.
|
---|
114 | Copyright (C) 2000 Luke Kenneth Casson Leighton <lkcl@samba.org>
|
---|
115 |
|
---|
116 | example: read entries from a SQL database (requires overriding
|
---|
117 | get_request() to return a table entry from the database).
|
---|
118 | entry is processed by a RequestHandlerClass.
|
---|
119 |
|
---|
120 | """
|
---|
121 |
|
---|
122 | # Author of the BaseServer patch: Luke Kenneth Casson Leighton
|
---|
123 |
|
---|
124 | # XXX Warning!
|
---|
125 | # There is a test suite for this module, but it cannot be run by the
|
---|
126 | # standard regression test.
|
---|
127 | # To run it manually, run Lib/test/test_socketserver.py.
|
---|
128 |
|
---|
129 | __version__ = "0.4"
|
---|
130 |
|
---|
131 |
|
---|
132 | import socket
|
---|
133 | import select
|
---|
134 | import sys
|
---|
135 | import os
|
---|
136 | import errno
|
---|
137 | try:
|
---|
138 | import threading
|
---|
139 | except ImportError:
|
---|
140 | import dummy_threading as threading
|
---|
141 |
|
---|
142 | __all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer",
|
---|
143 | "ThreadingUDPServer","ThreadingTCPServer","BaseRequestHandler",
|
---|
144 | "StreamRequestHandler","DatagramRequestHandler",
|
---|
145 | "ThreadingMixIn", "ForkingMixIn"]
|
---|
146 | if hasattr(socket, "AF_UNIX"):
|
---|
147 | __all__.extend(["UnixStreamServer","UnixDatagramServer",
|
---|
148 | "ThreadingUnixStreamServer",
|
---|
149 | "ThreadingUnixDatagramServer"])
|
---|
150 |
|
---|
151 | def _eintr_retry(func, *args):
|
---|
152 | """restart a system call interrupted by EINTR"""
|
---|
153 | while True:
|
---|
154 | try:
|
---|
155 | return func(*args)
|
---|
156 | except (OSError, select.error) as e:
|
---|
157 | if e.args[0] != errno.EINTR:
|
---|
158 | raise
|
---|
159 |
|
---|
160 | class BaseServer:
|
---|
161 |
|
---|
162 | """Base class for server classes.
|
---|
163 |
|
---|
164 | Methods for the caller:
|
---|
165 |
|
---|
166 | - __init__(server_address, RequestHandlerClass)
|
---|
167 | - serve_forever(poll_interval=0.5)
|
---|
168 | - shutdown()
|
---|
169 | - handle_request() # if you do not use serve_forever()
|
---|
170 | - fileno() -> int # for select()
|
---|
171 |
|
---|
172 | Methods that may be overridden:
|
---|
173 |
|
---|
174 | - server_bind()
|
---|
175 | - server_activate()
|
---|
176 | - get_request() -> request, client_address
|
---|
177 | - handle_timeout()
|
---|
178 | - verify_request(request, client_address)
|
---|
179 | - server_close()
|
---|
180 | - process_request(request, client_address)
|
---|
181 | - shutdown_request(request)
|
---|
182 | - close_request(request)
|
---|
183 | - handle_error()
|
---|
184 |
|
---|
185 | Methods for derived classes:
|
---|
186 |
|
---|
187 | - finish_request(request, client_address)
|
---|
188 |
|
---|
189 | Class variables that may be overridden by derived classes or
|
---|
190 | instances:
|
---|
191 |
|
---|
192 | - timeout
|
---|
193 | - address_family
|
---|
194 | - socket_type
|
---|
195 | - allow_reuse_address
|
---|
196 |
|
---|
197 | Instance variables:
|
---|
198 |
|
---|
199 | - RequestHandlerClass
|
---|
200 | - socket
|
---|
201 |
|
---|
202 | """
|
---|
203 |
|
---|
204 | timeout = None
|
---|
205 |
|
---|
206 | def __init__(self, server_address, RequestHandlerClass):
|
---|
207 | """Constructor. May be extended, do not override."""
|
---|
208 | self.server_address = server_address
|
---|
209 | self.RequestHandlerClass = RequestHandlerClass
|
---|
210 | self.__is_shut_down = threading.Event()
|
---|
211 | self.__shutdown_request = False
|
---|
212 |
|
---|
213 | def server_activate(self):
|
---|
214 | """Called by constructor to activate the server.
|
---|
215 |
|
---|
216 | May be overridden.
|
---|
217 |
|
---|
218 | """
|
---|
219 | pass
|
---|
220 |
|
---|
221 | def serve_forever(self, poll_interval=0.5):
|
---|
222 | """Handle one request at a time until shutdown.
|
---|
223 |
|
---|
224 | Polls for shutdown every poll_interval seconds. Ignores
|
---|
225 | self.timeout. If you need to do periodic tasks, do them in
|
---|
226 | another thread.
|
---|
227 | """
|
---|
228 | self.__is_shut_down.clear()
|
---|
229 | try:
|
---|
230 | while not self.__shutdown_request:
|
---|
231 | # XXX: Consider using another file descriptor or
|
---|
232 | # connecting to the socket to wake this up instead of
|
---|
233 | # polling. Polling reduces our responsiveness to a
|
---|
234 | # shutdown request and wastes cpu at all other times.
|
---|
235 | r, w, e = _eintr_retry(select.select, [self], [], [],
|
---|
236 | poll_interval)
|
---|
237 | if self in r:
|
---|
238 | self._handle_request_noblock()
|
---|
239 | finally:
|
---|
240 | self.__shutdown_request = False
|
---|
241 | self.__is_shut_down.set()
|
---|
242 |
|
---|
243 | def shutdown(self):
|
---|
244 | """Stops the serve_forever loop.
|
---|
245 |
|
---|
246 | Blocks until the loop has finished. This must be called while
|
---|
247 | serve_forever() is running in another thread, or it will
|
---|
248 | deadlock.
|
---|
249 | """
|
---|
250 | self.__shutdown_request = True
|
---|
251 | self.__is_shut_down.wait()
|
---|
252 |
|
---|
253 | # The distinction between handling, getting, processing and
|
---|
254 | # finishing a request is fairly arbitrary. Remember:
|
---|
255 | #
|
---|
256 | # - handle_request() is the top-level call. It calls
|
---|
257 | # select, get_request(), verify_request() and process_request()
|
---|
258 | # - get_request() is different for stream or datagram sockets
|
---|
259 | # - process_request() is the place that may fork a new process
|
---|
260 | # or create a new thread to finish the request
|
---|
261 | # - finish_request() instantiates the request handler class;
|
---|
262 | # this constructor will handle the request all by itself
|
---|
263 |
|
---|
264 | def handle_request(self):
|
---|
265 | """Handle one request, possibly blocking.
|
---|
266 |
|
---|
267 | Respects self.timeout.
|
---|
268 | """
|
---|
269 | # Support people who used socket.settimeout() to escape
|
---|
270 | # handle_request before self.timeout was available.
|
---|
271 | timeout = self.socket.gettimeout()
|
---|
272 | if timeout is None:
|
---|
273 | timeout = self.timeout
|
---|
274 | elif self.timeout is not None:
|
---|
275 | timeout = min(timeout, self.timeout)
|
---|
276 | fd_sets = _eintr_retry(select.select, [self], [], [], timeout)
|
---|
277 | if not fd_sets[0]:
|
---|
278 | self.handle_timeout()
|
---|
279 | return
|
---|
280 | self._handle_request_noblock()
|
---|
281 |
|
---|
282 | def _handle_request_noblock(self):
|
---|
283 | """Handle one request, without blocking.
|
---|
284 |
|
---|
285 | I assume that select.select has returned that the socket is
|
---|
286 | readable before this function was called, so there should be
|
---|
287 | no risk of blocking in get_request().
|
---|
288 | """
|
---|
289 | try:
|
---|
290 | request, client_address = self.get_request()
|
---|
291 | except socket.error:
|
---|
292 | return
|
---|
293 | if self.verify_request(request, client_address):
|
---|
294 | try:
|
---|
295 | self.process_request(request, client_address)
|
---|
296 | except:
|
---|
297 | self.handle_error(request, client_address)
|
---|
298 | self.shutdown_request(request)
|
---|
299 |
|
---|
300 | def handle_timeout(self):
|
---|
301 | """Called if no new request arrives within self.timeout.
|
---|
302 |
|
---|
303 | Overridden by ForkingMixIn.
|
---|
304 | """
|
---|
305 | pass
|
---|
306 |
|
---|
307 | def verify_request(self, request, client_address):
|
---|
308 | """Verify the request. May be overridden.
|
---|
309 |
|
---|
310 | Return True if we should proceed with this request.
|
---|
311 |
|
---|
312 | """
|
---|
313 | return True
|
---|
314 |
|
---|
315 | def process_request(self, request, client_address):
|
---|
316 | """Call finish_request.
|
---|
317 |
|
---|
318 | Overridden by ForkingMixIn and ThreadingMixIn.
|
---|
319 |
|
---|
320 | """
|
---|
321 | self.finish_request(request, client_address)
|
---|
322 | self.shutdown_request(request)
|
---|
323 |
|
---|
324 | def server_close(self):
|
---|
325 | """Called to clean-up the server.
|
---|
326 |
|
---|
327 | May be overridden.
|
---|
328 |
|
---|
329 | """
|
---|
330 | pass
|
---|
331 |
|
---|
332 | def finish_request(self, request, client_address):
|
---|
333 | """Finish one request by instantiating RequestHandlerClass."""
|
---|
334 | self.RequestHandlerClass(request, client_address, self)
|
---|
335 |
|
---|
336 | def shutdown_request(self, request):
|
---|
337 | """Called to shutdown and close an individual request."""
|
---|
338 | self.close_request(request)
|
---|
339 |
|
---|
340 | def close_request(self, request):
|
---|
341 | """Called to clean up an individual request."""
|
---|
342 | pass
|
---|
343 |
|
---|
344 | def handle_error(self, request, client_address):
|
---|
345 | """Handle an error gracefully. May be overridden.
|
---|
346 |
|
---|
347 | The default is to print a traceback and continue.
|
---|
348 |
|
---|
349 | """
|
---|
350 | print '-'*40
|
---|
351 | print 'Exception happened during processing of request from',
|
---|
352 | print client_address
|
---|
353 | import traceback
|
---|
354 | traceback.print_exc() # XXX But this goes to stderr!
|
---|
355 | print '-'*40
|
---|
356 |
|
---|
357 |
|
---|
358 | class TCPServer(BaseServer):
|
---|
359 |
|
---|
360 | """Base class for various socket-based server classes.
|
---|
361 |
|
---|
362 | Defaults to synchronous IP stream (i.e., TCP).
|
---|
363 |
|
---|
364 | Methods for the caller:
|
---|
365 |
|
---|
366 | - __init__(server_address, RequestHandlerClass, bind_and_activate=True)
|
---|
367 | - serve_forever(poll_interval=0.5)
|
---|
368 | - shutdown()
|
---|
369 | - handle_request() # if you don't use serve_forever()
|
---|
370 | - fileno() -> int # for select()
|
---|
371 |
|
---|
372 | Methods that may be overridden:
|
---|
373 |
|
---|
374 | - server_bind()
|
---|
375 | - server_activate()
|
---|
376 | - get_request() -> request, client_address
|
---|
377 | - handle_timeout()
|
---|
378 | - verify_request(request, client_address)
|
---|
379 | - process_request(request, client_address)
|
---|
380 | - shutdown_request(request)
|
---|
381 | - close_request(request)
|
---|
382 | - handle_error()
|
---|
383 |
|
---|
384 | Methods for derived classes:
|
---|
385 |
|
---|
386 | - finish_request(request, client_address)
|
---|
387 |
|
---|
388 | Class variables that may be overridden by derived classes or
|
---|
389 | instances:
|
---|
390 |
|
---|
391 | - timeout
|
---|
392 | - address_family
|
---|
393 | - socket_type
|
---|
394 | - request_queue_size (only for stream sockets)
|
---|
395 | - allow_reuse_address
|
---|
396 |
|
---|
397 | Instance variables:
|
---|
398 |
|
---|
399 | - server_address
|
---|
400 | - RequestHandlerClass
|
---|
401 | - socket
|
---|
402 |
|
---|
403 | """
|
---|
404 |
|
---|
405 | address_family = socket.AF_INET
|
---|
406 |
|
---|
407 | socket_type = socket.SOCK_STREAM
|
---|
408 |
|
---|
409 | request_queue_size = 5
|
---|
410 |
|
---|
411 | allow_reuse_address = False
|
---|
412 |
|
---|
413 | def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
|
---|
414 | """Constructor. May be extended, do not override."""
|
---|
415 | BaseServer.__init__(self, server_address, RequestHandlerClass)
|
---|
416 | self.socket = socket.socket(self.address_family,
|
---|
417 | self.socket_type)
|
---|
418 | if bind_and_activate:
|
---|
419 | self.server_bind()
|
---|
420 | self.server_activate()
|
---|
421 |
|
---|
422 | def server_bind(self):
|
---|
423 | """Called by constructor to bind the socket.
|
---|
424 |
|
---|
425 | May be overridden.
|
---|
426 |
|
---|
427 | """
|
---|
428 | if self.allow_reuse_address:
|
---|
429 | self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
---|
430 | self.socket.bind(self.server_address)
|
---|
431 | self.server_address = self.socket.getsockname()
|
---|
432 |
|
---|
433 | def server_activate(self):
|
---|
434 | """Called by constructor to activate the server.
|
---|
435 |
|
---|
436 | May be overridden.
|
---|
437 |
|
---|
438 | """
|
---|
439 | self.socket.listen(self.request_queue_size)
|
---|
440 |
|
---|
441 | def server_close(self):
|
---|
442 | """Called to clean-up the server.
|
---|
443 |
|
---|
444 | May be overridden.
|
---|
445 |
|
---|
446 | """
|
---|
447 | self.socket.close()
|
---|
448 |
|
---|
449 | def fileno(self):
|
---|
450 | """Return socket file number.
|
---|
451 |
|
---|
452 | Interface required by select().
|
---|
453 |
|
---|
454 | """
|
---|
455 | return self.socket.fileno()
|
---|
456 |
|
---|
457 | def get_request(self):
|
---|
458 | """Get the request and client address from the socket.
|
---|
459 |
|
---|
460 | May be overridden.
|
---|
461 |
|
---|
462 | """
|
---|
463 | return self.socket.accept()
|
---|
464 |
|
---|
465 | def shutdown_request(self, request):
|
---|
466 | """Called to shutdown and close an individual request."""
|
---|
467 | try:
|
---|
468 | #explicitly shutdown. socket.close() merely releases
|
---|
469 | #the socket and waits for GC to perform the actual close.
|
---|
470 | request.shutdown(socket.SHUT_WR)
|
---|
471 | except socket.error:
|
---|
472 | pass #some platforms may raise ENOTCONN here
|
---|
473 | self.close_request(request)
|
---|
474 |
|
---|
475 | def close_request(self, request):
|
---|
476 | """Called to clean up an individual request."""
|
---|
477 | request.close()
|
---|
478 |
|
---|
479 |
|
---|
480 | class UDPServer(TCPServer):
|
---|
481 |
|
---|
482 | """UDP server class."""
|
---|
483 |
|
---|
484 | allow_reuse_address = False
|
---|
485 |
|
---|
486 | socket_type = socket.SOCK_DGRAM
|
---|
487 |
|
---|
488 | max_packet_size = 8192
|
---|
489 |
|
---|
490 | def get_request(self):
|
---|
491 | data, client_addr = self.socket.recvfrom(self.max_packet_size)
|
---|
492 | return (data, self.socket), client_addr
|
---|
493 |
|
---|
494 | def server_activate(self):
|
---|
495 | # No need to call listen() for UDP.
|
---|
496 | pass
|
---|
497 |
|
---|
498 | def shutdown_request(self, request):
|
---|
499 | # No need to shutdown anything.
|
---|
500 | self.close_request(request)
|
---|
501 |
|
---|
502 | def close_request(self, request):
|
---|
503 | # No need to close anything.
|
---|
504 | pass
|
---|
505 |
|
---|
506 | class ForkingMixIn:
|
---|
507 |
|
---|
508 | """Mix-in class to handle each request in a new process."""
|
---|
509 |
|
---|
510 | timeout = 300
|
---|
511 | active_children = None
|
---|
512 | max_children = 40
|
---|
513 |
|
---|
514 | def collect_children(self):
|
---|
515 | """Internal routine to wait for children that have exited."""
|
---|
516 | if self.active_children is None: return
|
---|
517 | while len(self.active_children) >= self.max_children:
|
---|
518 | # XXX: This will wait for any child process, not just ones
|
---|
519 | # spawned by this library. This could confuse other
|
---|
520 | # libraries that expect to be able to wait for their own
|
---|
521 | # children.
|
---|
522 | try:
|
---|
523 | pid, status = os.waitpid(0, 0)
|
---|
524 | except os.error:
|
---|
525 | pid = None
|
---|
526 | if pid not in self.active_children: continue
|
---|
527 | self.active_children.remove(pid)
|
---|
528 |
|
---|
529 | # XXX: This loop runs more system calls than it ought
|
---|
530 | # to. There should be a way to put the active_children into a
|
---|
531 | # process group and then use os.waitpid(-pgid) to wait for any
|
---|
532 | # of that set, but I couldn't find a way to allocate pgids
|
---|
533 | # that couldn't collide.
|
---|
534 | for child in self.active_children:
|
---|
535 | try:
|
---|
536 | pid, status = os.waitpid(child, os.WNOHANG)
|
---|
537 | except os.error:
|
---|
538 | pid = None
|
---|
539 | if not pid: continue
|
---|
540 | try:
|
---|
541 | self.active_children.remove(pid)
|
---|
542 | except ValueError, e:
|
---|
543 | raise ValueError('%s. x=%d and list=%r' % (e.message, pid,
|
---|
544 | self.active_children))
|
---|
545 |
|
---|
546 | def handle_timeout(self):
|
---|
547 | """Wait for zombies after self.timeout seconds of inactivity.
|
---|
548 |
|
---|
549 | May be extended, do not override.
|
---|
550 | """
|
---|
551 | self.collect_children()
|
---|
552 |
|
---|
553 | def process_request(self, request, client_address):
|
---|
554 | """Fork a new subprocess to process the request."""
|
---|
555 | self.collect_children()
|
---|
556 | pid = os.fork()
|
---|
557 | if pid:
|
---|
558 | # Parent process
|
---|
559 | if self.active_children is None:
|
---|
560 | self.active_children = []
|
---|
561 | self.active_children.append(pid)
|
---|
562 | self.close_request(request) #close handle in parent process
|
---|
563 | return
|
---|
564 | else:
|
---|
565 | # Child process.
|
---|
566 | # This must never return, hence os._exit()!
|
---|
567 | try:
|
---|
568 | self.finish_request(request, client_address)
|
---|
569 | self.shutdown_request(request)
|
---|
570 | os._exit(0)
|
---|
571 | except:
|
---|
572 | try:
|
---|
573 | self.handle_error(request, client_address)
|
---|
574 | self.shutdown_request(request)
|
---|
575 | finally:
|
---|
576 | os._exit(1)
|
---|
577 |
|
---|
578 |
|
---|
579 | class ThreadingMixIn:
|
---|
580 | """Mix-in class to handle each request in a new thread."""
|
---|
581 |
|
---|
582 | # Decides how threads will act upon termination of the
|
---|
583 | # main process
|
---|
584 | daemon_threads = False
|
---|
585 |
|
---|
586 | def process_request_thread(self, request, client_address):
|
---|
587 | """Same as in BaseServer but as a thread.
|
---|
588 |
|
---|
589 | In addition, exception handling is done here.
|
---|
590 |
|
---|
591 | """
|
---|
592 | try:
|
---|
593 | self.finish_request(request, client_address)
|
---|
594 | self.shutdown_request(request)
|
---|
595 | except:
|
---|
596 | self.handle_error(request, client_address)
|
---|
597 | self.shutdown_request(request)
|
---|
598 |
|
---|
599 | def process_request(self, request, client_address):
|
---|
600 | """Start a new thread to process the request."""
|
---|
601 | t = threading.Thread(target = self.process_request_thread,
|
---|
602 | args = (request, client_address))
|
---|
603 | t.daemon = self.daemon_threads
|
---|
604 | t.start()
|
---|
605 |
|
---|
606 |
|
---|
607 | class ForkingUDPServer(ForkingMixIn, UDPServer): pass
|
---|
608 | class ForkingTCPServer(ForkingMixIn, TCPServer): pass
|
---|
609 |
|
---|
610 | class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
|
---|
611 | class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
|
---|
612 |
|
---|
613 | if hasattr(socket, 'AF_UNIX'):
|
---|
614 |
|
---|
615 | class UnixStreamServer(TCPServer):
|
---|
616 | address_family = socket.AF_UNIX
|
---|
617 |
|
---|
618 | class UnixDatagramServer(UDPServer):
|
---|
619 | address_family = socket.AF_UNIX
|
---|
620 |
|
---|
621 | class ThreadingUnixStreamServer(ThreadingMixIn, UnixStreamServer): pass
|
---|
622 |
|
---|
623 | class ThreadingUnixDatagramServer(ThreadingMixIn, UnixDatagramServer): pass
|
---|
624 |
|
---|
625 | class BaseRequestHandler:
|
---|
626 |
|
---|
627 | """Base class for request handler classes.
|
---|
628 |
|
---|
629 | This class is instantiated for each request to be handled. The
|
---|
630 | constructor sets the instance variables request, client_address
|
---|
631 | and server, and then calls the handle() method. To implement a
|
---|
632 | specific service, all you need to do is to derive a class which
|
---|
633 | defines a handle() method.
|
---|
634 |
|
---|
635 | The handle() method can find the request as self.request, the
|
---|
636 | client address as self.client_address, and the server (in case it
|
---|
637 | needs access to per-server information) as self.server. Since a
|
---|
638 | separate instance is created for each request, the handle() method
|
---|
639 | can define arbitrary other instance variariables.
|
---|
640 |
|
---|
641 | """
|
---|
642 |
|
---|
643 | def __init__(self, request, client_address, server):
|
---|
644 | self.request = request
|
---|
645 | self.client_address = client_address
|
---|
646 | self.server = server
|
---|
647 | self.setup()
|
---|
648 | try:
|
---|
649 | self.handle()
|
---|
650 | finally:
|
---|
651 | self.finish()
|
---|
652 |
|
---|
653 | def setup(self):
|
---|
654 | pass
|
---|
655 |
|
---|
656 | def handle(self):
|
---|
657 | pass
|
---|
658 |
|
---|
659 | def finish(self):
|
---|
660 | pass
|
---|
661 |
|
---|
662 |
|
---|
663 | # The following two classes make it possible to use the same service
|
---|
664 | # class for stream or datagram servers.
|
---|
665 | # Each class sets up these instance variables:
|
---|
666 | # - rfile: a file object from which receives the request is read
|
---|
667 | # - wfile: a file object to which the reply is written
|
---|
668 | # When the handle() method returns, wfile is flushed properly
|
---|
669 |
|
---|
670 |
|
---|
671 | class StreamRequestHandler(BaseRequestHandler):
|
---|
672 |
|
---|
673 | """Define self.rfile and self.wfile for stream sockets."""
|
---|
674 |
|
---|
675 | # Default buffer sizes for rfile, wfile.
|
---|
676 | # We default rfile to buffered because otherwise it could be
|
---|
677 | # really slow for large data (a getc() call per byte); we make
|
---|
678 | # wfile unbuffered because (a) often after a write() we want to
|
---|
679 | # read and we need to flush the line; (b) big writes to unbuffered
|
---|
680 | # files are typically optimized by stdio even when big reads
|
---|
681 | # aren't.
|
---|
682 | rbufsize = -1
|
---|
683 | wbufsize = 0
|
---|
684 |
|
---|
685 | # A timeout to apply to the request socket, if not None.
|
---|
686 | timeout = None
|
---|
687 |
|
---|
688 | # Disable nagle algorithm for this socket, if True.
|
---|
689 | # Use only when wbufsize != 0, to avoid small packets.
|
---|
690 | disable_nagle_algorithm = False
|
---|
691 |
|
---|
692 | def setup(self):
|
---|
693 | self.connection = self.request
|
---|
694 | if self.timeout is not None:
|
---|
695 | self.connection.settimeout(self.timeout)
|
---|
696 | if self.disable_nagle_algorithm:
|
---|
697 | self.connection.setsockopt(socket.IPPROTO_TCP,
|
---|
698 | socket.TCP_NODELAY, True)
|
---|
699 | self.rfile = self.connection.makefile('rb', self.rbufsize)
|
---|
700 | self.wfile = self.connection.makefile('wb', self.wbufsize)
|
---|
701 |
|
---|
702 | def finish(self):
|
---|
703 | if not self.wfile.closed:
|
---|
704 | try:
|
---|
705 | self.wfile.flush()
|
---|
706 | except socket.error:
|
---|
707 | # An final socket error may have occurred here, such as
|
---|
708 | # the local error ECONNABORTED.
|
---|
709 | pass
|
---|
710 | self.wfile.close()
|
---|
711 | self.rfile.close()
|
---|
712 |
|
---|
713 |
|
---|
714 | class DatagramRequestHandler(BaseRequestHandler):
|
---|
715 |
|
---|
716 | # XXX Regrettably, I cannot get this working on Linux;
|
---|
717 | # s.recvfrom() doesn't return a meaningful client address.
|
---|
718 |
|
---|
719 | """Define self.rfile and self.wfile for datagram sockets."""
|
---|
720 |
|
---|
721 | def setup(self):
|
---|
722 | try:
|
---|
723 | from cStringIO import StringIO
|
---|
724 | except ImportError:
|
---|
725 | from StringIO import StringIO
|
---|
726 | self.packet, self.socket = self.request
|
---|
727 | self.rfile = StringIO(self.packet)
|
---|
728 | self.wfile = StringIO()
|
---|
729 |
|
---|
730 | def finish(self):
|
---|
731 | self.socket.sendto(self.wfile.getvalue(), self.client_address)
|
---|