summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/SocketServer.py190
1 files changed, 139 insertions, 51 deletions
diff --git a/Lib/SocketServer.py b/Lib/SocketServer.py
index c7439dd..170696f 100644
--- a/Lib/SocketServer.py
+++ b/Lib/SocketServer.py
@@ -2,6 +2,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)
- AF_UNIX: Unix domain sockets
@@ -9,6 +11,9 @@ This module tries to capture the various aspects of defining a server:
- socket type:
- 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
at the request before anything else, e.g. logging)
@@ -22,9 +27,14 @@ 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
+There are five classes in an inheritance diagram, four of which represent
synchronous servers of four types:
+ +------------+
+ | BaseServer |
+ +------------+
+ |
+ v
+-----------+ +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+ +------------------+
@@ -77,7 +87,7 @@ 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
+server and doing an explicit fork in the request handler class
handle() method.
Another approach to handling multiple simultaneous requests in an
@@ -87,7 +97,7 @@ 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).
+threads or subprocesses cannot be used).
Future work:
- Standard classes for Sun RPC (which uses either UDP or TCP)
@@ -98,6 +108,14 @@ Future work:
XXX Open problems:
- 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.
+
"""
@@ -109,17 +127,15 @@ import sys
import os
-class TCPServer:
+class BaseServer:
- """Base class for various socket-based server classes.
-
- Defaults to synchronous IP stream (i.e., TCP).
+ """Base class for server classes.
Methods for the caller:
- __init__(server_address, RequestHandlerClass)
- serve_forever()
- - handle_request() # if you don't use serve_forever()
+ - handle_request() # if you do not use serve_forever()
- fileno() -> int # for select()
Methods that may be overridden:
@@ -128,6 +144,7 @@ class TCPServer:
- server_activate()
- get_request() -> request, client_address
- verify_request(request, client_address)
+ - server_close()
- process_request(request, client_address)
- handle_error()
@@ -140,43 +157,19 @@ class TCPServer:
- 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 = 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.
@@ -184,15 +177,7 @@ class TCPServer:
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()
+ pass
def serve_forever(self):
"""Handle one request at a time until doomsday."""
@@ -222,14 +207,6 @@ class TCPServer:
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.
@@ -246,6 +223,14 @@ class TCPServer:
"""
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."""
self.RequestHandlerClass(request, client_address, self)
@@ -260,14 +245,117 @@ class TCPServer:
print 'Exception happened during processing of request from',
print client_address
import traceback
- traceback.print_exc()
+ 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
max_packet_size = 8192
@@ -318,7 +406,7 @@ class ForkingMixIn:
# Child process.
# This must never return, hence os._exit()!
try:
- self.socket.close()
+ self.server_close()
self.finish_request(request, client_address)
os._exit(0)
except: