| """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. | 
 |  | 
 | 	""" | 
 | 	print '-'*40 | 
 | 	print 'Exception happened during processing of request from', | 
 | 	print client_address | 
 | 	import traceback | 
 | 	traceback.print_exc() | 
 | 	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(self.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: | 
 | 	    pid, status = os.waitpid(0, os.WNOHANG) | 
 | 	    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 | 
 | 	self.rfile = self.connection.makefile('rb', 0) | 
 | 	self.wfile = self.connection.makefile('wb', 0) | 
 |  | 
 |     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()) |