| 
									
										
										
										
											1995-08-04 04:00:20 +00:00
										 |  |  | """Generic socket server classes.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This module tries to capture the various aspects of defining a server: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - address family: | 
					
						
							|  |  |  | 	- AF_INET: IP (Internet Protocol) sockets (default) | 
					
						
							|  |  |  | 	- AF_UNIX: Unix domain sockets | 
					
						
							|  |  |  | 	- others, e.g. AF_DECNET are conceivable (see <socket.h> | 
					
						
							|  |  |  | - socket type: | 
					
						
							|  |  |  | 	- SOCK_STREAM (reliable stream, e.g. TCP) | 
					
						
							|  |  |  | 	- SOCK_DGRAM (datagrams, e.g. UDP) | 
					
						
							|  |  |  | - client address verification before further looking at the request | 
					
						
							|  |  |  | 	(This is actually a hook for any processing that needs to look | 
					
						
							|  |  |  | 	 at the request before anything else, e.g. logging) | 
					
						
							|  |  |  | - how to handle multiple requests: | 
					
						
							|  |  |  | 	- synchronous (one request is handled at a time) | 
					
						
							|  |  |  | 	- forking (each request is handled by a new process) | 
					
						
							|  |  |  | 	- threading (each request is handled by a new thread) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The classes in this module favor the server type that is simplest to | 
					
						
							|  |  |  | write: a synchronous TCP/IP server.  This is bad class design, but | 
					
						
							|  |  |  | save some typing.  (There's also the issue that a deep class hierarchy | 
					
						
							|  |  |  | slows down method lookups.) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | There are four classes in an inheritance diagram that represent | 
					
						
							|  |  |  | synchronous servers of four types: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	+-----------+        +------------------+ | 
					
						
							|  |  |  | 	| TCPServer |------->| UnixStreamServer | | 
					
						
							|  |  |  | 	+-----------+        +------------------+ | 
					
						
							|  |  |  | 	      | | 
					
						
							|  |  |  | 	      v | 
					
						
							|  |  |  | 	+-----------+        +--------------------+ | 
					
						
							|  |  |  | 	| UDPServer |------->| UnixDatagramServer | | 
					
						
							|  |  |  | 	+-----------+        +--------------------+ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | (Note that UnixDatagramServer derives from UDPServer, not from | 
					
						
							|  |  |  | UnixStreamServer -- the only difference between an IP and a Unix | 
					
						
							|  |  |  | stream server is the address family, which is simply repeated in both | 
					
						
							|  |  |  | unix server classes.) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Forking and threading versions of each type of server can be created | 
					
						
							|  |  |  | using the ForkingServer and ThreadingServer mix-in classes.  For | 
					
						
							|  |  |  | instance, a threading UDP server class is created as follows: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | (The Mix-in class must come first, since it overrides a method defined | 
					
						
							|  |  |  | in UDPServer!) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | To implement a service, you must derive a class from | 
					
						
							|  |  |  | BaseRequestHandler and redefine its handle() method.  You can then run | 
					
						
							|  |  |  | various versions of the service by combining one of the server classes | 
					
						
							|  |  |  | with your request handler class. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The request handler class must be different for datagram or stream | 
					
						
							|  |  |  | services.  This can be hidden by using the mix-in request handler | 
					
						
							|  |  |  | classes StreamRequestHandler or DatagramRequestHandler. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Of course, you still have to use your head! | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | For instance, it makes no sense to use a forking server if the service | 
					
						
							|  |  |  | contains state in memory that can be modified by requests (since the | 
					
						
							|  |  |  | modifications in the child process would never reach the initial state | 
					
						
							|  |  |  | kept in the parent process and passed to each child).  In this case, | 
					
						
							|  |  |  | you can use a threading server, but you will probably have to use | 
					
						
							|  |  |  | locks to avoid two requests that come in nearly simultaneous to apply | 
					
						
							|  |  |  | conflicting changes to the server state. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | On the other hand, if you are building e.g. an HTTP server, where all | 
					
						
							|  |  |  | data is stored externally (e.g. in the file system), a synchronous | 
					
						
							|  |  |  | class will essentially render the service "deaf" while one request is | 
					
						
							|  |  |  | being handled -- which may be for a very long time if a client is slow | 
					
						
							|  |  |  | to reqd all the data it has requested.  Here a threading or forking | 
					
						
							|  |  |  | server is appropriate. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | In some cases, it may be appropriate to process part of a request | 
					
						
							|  |  |  | synchronously, but to finish processing in a forked child depending on | 
					
						
							|  |  |  | the request data.  This can be implemented by using a synchronous | 
					
						
							|  |  |  | server and doing an explicit fork in the request handler class's | 
					
						
							|  |  |  | handle() method. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Another approach to handling multiple simultaneous requests in an | 
					
						
							|  |  |  | environment that supports neither threads nor fork (or where these are | 
					
						
							|  |  |  | too expensive or inappropriate for the service) is to maintain an | 
					
						
							|  |  |  | explicit table of partially finished requests and to use select() to | 
					
						
							|  |  |  | decide which request to work on next (or whether to handle a new | 
					
						
							|  |  |  | incoming request).  This is particularly important for stream services | 
					
						
							|  |  |  | where each client can potentially be connected for a long time (if | 
					
						
							|  |  |  | threads or subprocesses can't be used). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Future work: | 
					
						
							|  |  |  | - Standard classes for Sun RPC (which uses either UDP or TCP) | 
					
						
							|  |  |  | - Standard mix-in classes to implement various authentication | 
					
						
							|  |  |  |   and encryption schemes | 
					
						
							|  |  |  | - Standard framework for select-based multiplexing | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | XXX Open problems: | 
					
						
							|  |  |  | - What to do with out-of-band data? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __version__ = "0.2" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import socket | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TCPServer: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Base class for various socket-based server classes.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Defaults to synchronous IP stream (i.e., TCP). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Methods for the caller: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     - __init__(server_address, RequestHandlerClass) | 
					
						
							|  |  |  |     - serve_forever() | 
					
						
							|  |  |  |     - handle_request()	# if you don't use serve_forever() | 
					
						
							|  |  |  |     - fileno() -> int	# for select() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Methods that may be overridden: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     - server_bind() | 
					
						
							|  |  |  |     - server_activate() | 
					
						
							|  |  |  |     - get_request() -> request, client_address | 
					
						
							|  |  |  |     - verify_request(request, client_address) | 
					
						
							|  |  |  |     - process_request(request, client_address) | 
					
						
							|  |  |  |     - handle_error() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Methods for derived classes: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     - finish_request(request, client_address) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Class variables that may be overridden by derived classes or | 
					
						
							|  |  |  |     instances: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     - address_family | 
					
						
							|  |  |  |     - socket_type | 
					
						
							|  |  |  |     - request_queue_size (only for stream sockets) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Instance variables: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     - server_address | 
					
						
							|  |  |  |     - RequestHandlerClass | 
					
						
							|  |  |  |     - socket | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     address_family = socket.AF_INET | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     socket_type = socket.SOCK_STREAM | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     request_queue_size = 5 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, server_address, RequestHandlerClass): | 
					
						
							|  |  |  | 	"""Constructor.  May be extended, do not override.""" | 
					
						
							|  |  |  | 	self.server_address = server_address | 
					
						
							|  |  |  | 	self.RequestHandlerClass = RequestHandlerClass | 
					
						
							|  |  |  | 	self.socket = socket.socket(self.address_family, | 
					
						
							|  |  |  | 				    self.socket_type) | 
					
						
							|  |  |  | 	self.server_bind() | 
					
						
							|  |  |  | 	self.server_activate() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def server_bind(self): | 
					
						
							|  |  |  | 	"""Called by constructor to bind the socket.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	May be overridden. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"""
 | 
					
						
							|  |  |  | 	self.socket.bind(self.server_address) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def server_activate(self): | 
					
						
							|  |  |  | 	"""Called by constructor to activate the server.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	May be overridden. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"""
 | 
					
						
							|  |  |  | 	self.socket.listen(self.request_queue_size) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def fileno(self): | 
					
						
							|  |  |  | 	"""Return socket file number.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Interface required by select(). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"""
 | 
					
						
							|  |  |  | 	return self.socket.fileno() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def serve_forever(self): | 
					
						
							|  |  |  | 	"""Handle one request at a time until doomsday.""" | 
					
						
							|  |  |  | 	while 1: | 
					
						
							|  |  |  | 	    self.handle_request() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # The distinction between handling, getting, processing and | 
					
						
							|  |  |  |     # 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 | 
					
						
							|  |  |  |     # - 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; | 
					
						
							|  |  |  |     #   this constructor will handle the request all by itself | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def handle_request(self): | 
					
						
							|  |  |  | 	"""Handle one request, possibly blocking.""" | 
					
						
							|  |  |  | 	request, client_address = self.get_request() | 
					
						
							|  |  |  | 	if self.verify_request(request, client_address): | 
					
						
							|  |  |  | 	    try: | 
					
						
							|  |  |  | 		self.process_request(request, client_address) | 
					
						
							|  |  |  | 	    except: | 
					
						
							|  |  |  | 		self.handle_error(request, client_address) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_request(self): | 
					
						
							|  |  |  | 	"""Get the request and client address from the socket.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	May be overridden. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"""
 | 
					
						
							|  |  |  | 	return self.socket.accept() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def verify_request(self, request, client_address): | 
					
						
							|  |  |  | 	"""Verify the request.  May be overridden.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Return true if we should proceed with this request. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"""
 | 
					
						
							|  |  |  | 	return 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def process_request(self, request, client_address): | 
					
						
							|  |  |  | 	"""Call finish_request.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Overridden by ForkingMixIn and ThreadingMixIn. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"""
 | 
					
						
							|  |  |  | 	self.finish_request(request, client_address) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def finish_request(self, request, client_address): | 
					
						
							|  |  |  | 	"""Finish one request by instantiating RequestHandlerClass.""" | 
					
						
							|  |  |  | 	self.RequestHandlerClass(request, client_address, self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def handle_error(self, request, client_address): | 
					
						
							|  |  |  | 	"""Handle an error gracefully.  May be overridden.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	The default is to print a traceback and continue. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"""
 | 
					
						
							|  |  |  | 	exc, value, tb = sys.exc_type, sys.exc_value, sys.exc_traceback | 
					
						
							|  |  |  | 	print '-'*40 | 
					
						
							|  |  |  | 	print 'Exception happened during processing of request from', | 
					
						
							|  |  |  | 	print client_address | 
					
						
							|  |  |  | 	import traceback | 
					
						
							|  |  |  | 	traceback.print_exception(exc, value, tb) | 
					
						
							|  |  |  | 	print '-'*40 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class UDPServer(TCPServer): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """UDP server class.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     socket_type = socket.SOCK_DGRAM | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     max_packet_size = 8192 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_request(self): | 
					
						
							|  |  |  | 	return self.socket.recvfrom(max_packet_size) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if hasattr(socket, 'AF_UNIX'): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class UnixStreamServer(TCPServer): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	address_family = socket.AF_UNIX | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class UnixDatagramServer(UDPServer): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	address_family = socket.AF_UNIX | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ForkingMixIn: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Mix-in class to handle each request in a new process.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     active_children = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def collect_children(self): | 
					
						
							|  |  |  | 	"""Internal routine to wait for died children.""" | 
					
						
							|  |  |  | 	while self.active_children: | 
					
						
							| 
									
										
										
										
											1996-01-25 18:13:30 +00:00
										 |  |  | 	    pid, status = os.waitpid(0, os.WNOHANG) | 
					
						
							| 
									
										
										
										
											1995-08-04 04:00:20 +00:00
										 |  |  | 	    if not pid: break | 
					
						
							|  |  |  | 	    self.active_children.remove(pid) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def process_request(self, request, client_address): | 
					
						
							|  |  |  | 	"""Fork a new subprocess to process the request.""" | 
					
						
							|  |  |  | 	self.collect_children() | 
					
						
							|  |  |  | 	pid = os.fork() | 
					
						
							|  |  |  | 	if pid: | 
					
						
							|  |  |  | 	    # Parent process | 
					
						
							|  |  |  | 	    if self.active_children is None: | 
					
						
							|  |  |  | 		self.active_children = [] | 
					
						
							|  |  |  | 	    self.active_children.append(pid) | 
					
						
							|  |  |  | 	    return | 
					
						
							|  |  |  | 	else: | 
					
						
							|  |  |  | 	    # Child process. | 
					
						
							|  |  |  | 	    # This must never return, hence os._exit()! | 
					
						
							|  |  |  | 	    try: | 
					
						
							|  |  |  | 		self.finish_request(request, client_address) | 
					
						
							|  |  |  | 		os._exit(0) | 
					
						
							|  |  |  | 	    except: | 
					
						
							|  |  |  | 		try: | 
					
						
							|  |  |  | 		    self.handle_error(request, | 
					
						
							|  |  |  | 				      client_address) | 
					
						
							|  |  |  | 		finally: | 
					
						
							|  |  |  | 		    os._exit(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ThreadingMixIn: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Mix-in class to handle each request in a new thread.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def process_request(self, request, client_address): | 
					
						
							|  |  |  | 	"""Start a new thread to process the request.""" | 
					
						
							|  |  |  | 	import thread | 
					
						
							|  |  |  | 	thread.start_new_thread(self.finish_request, | 
					
						
							|  |  |  | 				(request, client_address)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ForkingUDPServer(ForkingMixIn, UDPServer): pass | 
					
						
							|  |  |  | class ForkingTCPServer(ForkingMixIn, TCPServer): pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass | 
					
						
							|  |  |  | class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class BaseRequestHandler: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Base class for request handler classes.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This class is instantiated for each request to be handled.  The | 
					
						
							|  |  |  |     constructor sets the instance variables request, client_address | 
					
						
							|  |  |  |     and server, and then calls the handle() method.  To implement a | 
					
						
							|  |  |  |     specific service, all you need to do is to derive a class which | 
					
						
							|  |  |  |     defines a handle() method. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The handle() method can find the request as self.request, the | 
					
						
							|  |  |  |     client address as self.client_request, and the server (in case it | 
					
						
							|  |  |  |     needs access to per-server information) as self.server.  Since a | 
					
						
							|  |  |  |     separate instance is created for each request, the handle() method | 
					
						
							|  |  |  |     can define arbitrary other instance variariables. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, request, client_address, server): | 
					
						
							|  |  |  | 	self.request = request | 
					
						
							|  |  |  | 	self.client_address = client_address | 
					
						
							|  |  |  | 	self.server = server | 
					
						
							|  |  |  | 	try: | 
					
						
							|  |  |  | 	    self.setup() | 
					
						
							|  |  |  | 	    self.handle() | 
					
						
							|  |  |  | 	    self.finish() | 
					
						
							|  |  |  | 	finally: | 
					
						
							|  |  |  | 	    sys.exc_traceback = None	# Help garbage collection | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setup(self): | 
					
						
							|  |  |  | 	pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __del__(self): | 
					
						
							|  |  |  | 	pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def handle(self): | 
					
						
							|  |  |  | 	pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def finish(self): | 
					
						
							|  |  |  | 	pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # The following two classes make it possible to use the same service | 
					
						
							|  |  |  | # class for stream or datagram servers. | 
					
						
							|  |  |  | # Each class sets up these instance variables: | 
					
						
							|  |  |  | # - rfile: a file object from which receives the request is read | 
					
						
							|  |  |  | # - wfile: a file object to which the reply is written | 
					
						
							|  |  |  | # When the handle() method returns, wfile is flushed properly | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class StreamRequestHandler(BaseRequestHandler): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Define self.rfile and self.wfile for stream sockets.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setup(self): | 
					
						
							|  |  |  | 	self.connection = self.request | 
					
						
							| 
									
										
										
										
											1996-02-14 16:06:24 +00:00
										 |  |  | 	self.rfile = self.connection.makefile('rb') | 
					
						
							|  |  |  | 	self.wfile = self.connection.makefile('wb', 0) | 
					
						
							| 
									
										
										
										
											1995-08-04 04:00:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def finish(self): | 
					
						
							|  |  |  | 	self.wfile.flush() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DatagramRequestHandler(BaseRequestHandler): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Define self.rfile and self.wfile for datagram sockets.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setup(self): | 
					
						
							|  |  |  | 	import StringIO | 
					
						
							|  |  |  | 	self.packet, self.socket = self.request | 
					
						
							|  |  |  | 	self.rfile = StringIO.StringIO(self.packet) | 
					
						
							|  |  |  | 	self.wfile = StringIO.StringIO(self.packet) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def finish(self): | 
					
						
							|  |  |  | 	self.socket.send(self.wfile.getvalue()) |