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