[Python-checkins] CVS: python/dist/src/Lib SocketServer.py,1.20,1.21

Guido van Rossum gvanrossum@users.sourceforge.net
Thu, 18 Jan 2001 16:44:43 -0800


Update of /cvsroot/python/python/dist/src/Lib
In directory usw-pr-cvs1:/tmp/cvs-serv409

Modified Files:
	SocketServer.py 
Log Message:
SF Patch #102980, by Luke Kenneth Casson Leighton: BaseServer class
for SocketServer.py (inherited by TCPServer)

Luke wrote:

  The socketserver code, with a little bit of tweaking, can be made
  sufficiently general to service "requests" of any kind, not just by sockets.
  The BaseServer class was created, for example, to poll a table in a MYSQL
  database every 2 seconds. each entry in the table can be allocated a
  Handler which deals with the entry. 

  With this patch, using BaseServer and ThreadedServer classes, the creation
  of the server that reads and handles MySQL table entries instead of a
  socket was utterly trivial: about 50 lines of python code. 

  You may consider this code to be utterly useless [why would anyone else
  want to do anything like this???] - you are entitled to your opinion. if you
  think so, then think of this: have you considered how to cleanly add SSL to
  the TCPSocketServer? What about using shared memory as the
  communications mechanism for a server, instead of sockets? What about
  communication using files? 

  The SocketServer code is extremely good every useful. it's just that as it
  stands, it is tied to sockets, which is not as useful.

I heartily approve of this idea.



Index: SocketServer.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/SocketServer.py,v
retrieving revision 1.20
retrieving revision 1.21
diff -C2 -r1.20 -r1.21
*** SocketServer.py	2000/12/13 20:39:17	1.20
--- SocketServer.py	2001/01/19 00:44:41	1.21
***************
*** 3,6 ****
--- 3,8 ----
  This module tries to capture the various aspects of defining a server:
  
+ For socket-based servers:
+ 
  - address family:
          - AF_INET: IP (Internet Protocol) sockets (default)
***************
*** 10,13 ****
--- 12,18 ----
          - SOCK_STREAM (reliable stream, e.g. TCP)
          - SOCK_DGRAM (datagrams, e.g. UDP)
+ 
+ For request-based servers (including socket-based):
+ 
  - client address verification before further looking at the request
          (This is actually a hook for any processing that needs to look
***************
*** 23,29 ****
  slows down method lookups.)
  
! There are four classes in an inheritance diagram that represent
  synchronous servers of four types:
  
          +-----------+        +------------------+
          | TCPServer |------->| UnixStreamServer |
--- 28,39 ----
  slows down method lookups.)
  
! There are five classes in an inheritance diagram, four of which represent
  synchronous servers of four types:
  
+         +------------+
+         | BaseServer |
+         +------------+
+               |
+               v
          +-----------+        +------------------+
          | TCPServer |------->| UnixStreamServer |
***************
*** 78,82 ****
  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.
  
--- 88,92 ----
  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
  handle() method.
  
***************
*** 88,92 ****
  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:
--- 98,102 ----
  incoming request).  This is particularly important for stream services
  where each client can potentially be connected for a long time (if
! threads or subprocesses cannot be used).
  
  Future work:
***************
*** 99,102 ****
--- 109,120 ----
  - What to do with out-of-band data?
  
+ BaseServer:
+ - split generic "request" functionality out into BaseServer class.
+   Copyright (C) 2000  Luke Kenneth Casson Leighton <lkcl@samba.org>
+ 
+   example: read entries from a SQL database (requires overriding
+   get_request() to return a table entry from the database).
+   entry is processed by a RequestHandlerClass.
+ 
  """
  
***************
*** 110,124 ****
  
  
! 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()
  
--- 128,140 ----
  
  
! class BaseServer:
  
!     """Base class for server classes.
  
      Methods for the caller:
  
      - __init__(server_address, RequestHandlerClass)
      - serve_forever()
!     - handle_request()  # if you do not use serve_forever()
      - fileno() -> int   # for select()
  
***************
*** 129,132 ****
--- 145,149 ----
      - get_request() -> request, client_address
      - verify_request(request, client_address)
+     - server_close()
      - process_request(request, client_address)
      - handle_error()
***************
*** 141,150 ****
      - address_family
      - socket_type
-     - request_queue_size (only for stream sockets)
      - reuse_address
  
      Instance variables:
  
-     - server_address
      - RequestHandlerClass
      - socket
--- 158,165 ----
***************
*** 152,182 ****
      """
  
-     address_family = socket.AF_INET
- 
-     socket_type = socket.SOCK_STREAM
- 
-     request_queue_size = 5
- 
-     allow_reuse_address = 1
- 
      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.
  
-         """
-         if self.allow_reuse_address:
-             self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-         self.socket.bind(self.server_address)
- 
      def server_activate(self):
          """Called by constructor to activate the server.
--- 167,175 ----
***************
*** 185,197 ****
  
          """
!         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):
--- 178,182 ----
  
          """
!         pass
  
      def serve_forever(self):
***************
*** 223,234 ****
                  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.
--- 208,211 ----
***************
*** 247,250 ****
--- 224,235 ----
          self.finish_request(request, client_address)
  
+     def server_close(self):
+         """Called to clean-up the server.
+ 
+         May be overridden.
+ 
+         """
+         pass
+ 
      def finish_request(self, request, client_address):
          """Finish one request by instantiating RequestHandlerClass."""
***************
*** 261,272 ****
          print client_address
          import traceback
!         traceback.print_exc()
          print '-'*40
  
  
  class UDPServer(TCPServer):
  
      """UDP server class."""
  
      socket_type = socket.SOCK_DGRAM
  
--- 246,360 ----
          print client_address
          import traceback
!         traceback.print_exc() # XXX But this goes to stderr!
          print '-'*40
  
  
+ class TCPServer(BaseServer):
+ 
+     """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)
+     - reuse_address
+ 
+     Instance variables:
+ 
+     - server_address
+     - RequestHandlerClass
+     - socket
+ 
+     """
+ 
+     address_family = socket.AF_INET
+ 
+     socket_type = socket.SOCK_STREAM
+ 
+     request_queue_size = 5
+ 
+     allow_reuse_address = 0
+ 
+     def __init__(self, server_address, RequestHandlerClass):
+         """Constructor.  May be extended, do not override."""
+         BaseServer.__init__(self, server_address, 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.
+ 
+         """
+         if self.allow_reuse_address:
+             self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+         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 server_close(self):
+         """Called to clean-up the server.
+ 
+         May be overridden.
+ 
+         """
+         self.socket.close()
+ 
+     def fileno(self):
+         """Return socket file number.
+ 
+         Interface required by select().
+ 
+         """
+         return self.socket.fileno()
+ 
+     def get_request(self):
+         """Get the request and client address from the socket.
+ 
+         May be overridden.
+ 
+         """
+         return self.socket.accept()
+ 
+ 
  class UDPServer(TCPServer):
  
      """UDP server class."""
  
+     allow_reuse_address = 0
+ 
      socket_type = socket.SOCK_DGRAM
  
***************
*** 319,323 ****
              # This must never return, hence os._exit()!
              try:
!                 self.socket.close()
                  self.finish_request(request, client_address)
                  os._exit(0)
--- 407,411 ----
              # This must never return, hence os._exit()!
              try:
!                 self.server_close()
                  self.finish_request(request, client_address)
                  os._exit(0)