[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)