mirror of
https://github.com/python/cpython.git
synced 2025-07-24 11:44:31 +00:00
The usual.
This commit is contained in:
parent
65e5399081
commit
548703a1b8
43 changed files with 2463 additions and 2323 deletions
|
@ -3,19 +3,19 @@
|
|||
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>
|
||||
- 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)
|
||||
- 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)
|
||||
(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)
|
||||
- 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
|
||||
|
@ -25,14 +25,14 @@ slows down method lookups.)
|
|||
There are four classes in an inheritance diagram that represent
|
||||
synchronous servers of four types:
|
||||
|
||||
+-----------+ +------------------+
|
||||
| TCPServer |------->| UnixStreamServer |
|
||||
+-----------+ +------------------+
|
||||
|
|
||||
v
|
||||
+-----------+ +--------------------+
|
||||
| UDPServer |------->| UnixDatagramServer |
|
||||
+-----------+ +--------------------+
|
||||
+-----------+ +------------------+
|
||||
| TCPServer |------->| UnixStreamServer |
|
||||
+-----------+ +------------------+
|
||||
|
|
||||
v
|
||||
+-----------+ +--------------------+
|
||||
| UDPServer |------->| UnixDatagramServer |
|
||||
+-----------+ +--------------------+
|
||||
|
||||
Note that UnixDatagramServer derives from UDPServer, not from
|
||||
UnixStreamServer -- the only difference between an IP and a Unix
|
||||
|
@ -43,7 +43,7 @@ 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
|
||||
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
|
||||
|
||||
The Mix-in class must come first, since it overrides a method defined
|
||||
in UDPServer!
|
||||
|
@ -119,8 +119,8 @@ class TCPServer:
|
|||
|
||||
- __init__(server_address, RequestHandlerClass)
|
||||
- serve_forever()
|
||||
- handle_request() # if you don't use serve_forever()
|
||||
- fileno() -> int # for select()
|
||||
- handle_request() # if you don't use serve_forever()
|
||||
- fileno() -> int # for select()
|
||||
|
||||
Methods that may be overridden:
|
||||
|
||||
|
@ -157,42 +157,42 @@ class TCPServer:
|
|||
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()
|
||||
"""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.
|
||||
"""Called by constructor to bind the socket.
|
||||
|
||||
May be overridden.
|
||||
May be overridden.
|
||||
|
||||
"""
|
||||
self.socket.bind(self.server_address)
|
||||
"""
|
||||
self.socket.bind(self.server_address)
|
||||
|
||||
def server_activate(self):
|
||||
"""Called by constructor to activate the server.
|
||||
"""Called by constructor to activate the server.
|
||||
|
||||
May be overridden.
|
||||
May be overridden.
|
||||
|
||||
"""
|
||||
self.socket.listen(self.request_queue_size)
|
||||
"""
|
||||
self.socket.listen(self.request_queue_size)
|
||||
|
||||
def fileno(self):
|
||||
"""Return socket file number.
|
||||
"""Return socket file number.
|
||||
|
||||
Interface required by select().
|
||||
Interface required by select().
|
||||
|
||||
"""
|
||||
return self.socket.fileno()
|
||||
"""
|
||||
return self.socket.fileno()
|
||||
|
||||
def serve_forever(self):
|
||||
"""Handle one request at a time until doomsday."""
|
||||
while 1:
|
||||
self.handle_request()
|
||||
"""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:
|
||||
|
@ -206,54 +206,54 @@ class TCPServer:
|
|||
# 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)
|
||||
"""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.
|
||||
"""Get the request and client address from the socket.
|
||||
|
||||
May be overridden.
|
||||
May be overridden.
|
||||
|
||||
"""
|
||||
return self.socket.accept()
|
||||
"""
|
||||
return self.socket.accept()
|
||||
|
||||
def verify_request(self, request, client_address):
|
||||
"""Verify the request. May be overridden.
|
||||
"""Verify the request. May be overridden.
|
||||
|
||||
Return true if we should proceed with this request.
|
||||
Return true if we should proceed with this request.
|
||||
|
||||
"""
|
||||
return 1
|
||||
"""
|
||||
return 1
|
||||
|
||||
def process_request(self, request, client_address):
|
||||
"""Call finish_request.
|
||||
"""Call finish_request.
|
||||
|
||||
Overridden by ForkingMixIn and ThreadingMixIn.
|
||||
Overridden by ForkingMixIn and ThreadingMixIn.
|
||||
|
||||
"""
|
||||
self.finish_request(request, client_address)
|
||||
"""
|
||||
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)
|
||||
"""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.
|
||||
"""Handle an error gracefully. May be overridden.
|
||||
|
||||
The default is to print a traceback and continue.
|
||||
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
|
||||
"""
|
||||
print '-'*40
|
||||
print 'Exception happened during processing of request from',
|
||||
print client_address
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print '-'*40
|
||||
|
||||
|
||||
class UDPServer(TCPServer):
|
||||
|
@ -265,19 +265,19 @@ class UDPServer(TCPServer):
|
|||
max_packet_size = 8192
|
||||
|
||||
def get_request(self):
|
||||
return self.socket.recvfrom(self.max_packet_size)
|
||||
return self.socket.recvfrom(self.max_packet_size)
|
||||
|
||||
|
||||
if hasattr(socket, 'AF_UNIX'):
|
||||
|
||||
class UnixStreamServer(TCPServer):
|
||||
|
||||
address_family = socket.AF_UNIX
|
||||
address_family = socket.AF_UNIX
|
||||
|
||||
|
||||
class UnixDatagramServer(UDPServer):
|
||||
|
||||
address_family = socket.AF_UNIX
|
||||
address_family = socket.AF_UNIX
|
||||
|
||||
|
||||
class ForkingMixIn:
|
||||
|
@ -287,34 +287,34 @@ class ForkingMixIn:
|
|||
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)
|
||||
"""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)
|
||||
"""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:
|
||||
|
@ -322,10 +322,10 @@ 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))
|
||||
"""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
|
||||
|
@ -354,27 +354,27 @@ class BaseRequestHandler:
|
|||
"""
|
||||
|
||||
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
|
||||
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
|
||||
pass
|
||||
|
||||
def __del__(self):
|
||||
pass
|
||||
pass
|
||||
|
||||
def handle(self):
|
||||
pass
|
||||
pass
|
||||
|
||||
def finish(self):
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
# The following two classes make it possible to use the same service
|
||||
|
@ -390,12 +390,12 @@ 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)
|
||||
self.connection = self.request
|
||||
self.rfile = self.connection.makefile('rb', 0)
|
||||
self.wfile = self.connection.makefile('wb', 0)
|
||||
|
||||
def finish(self):
|
||||
self.wfile.flush()
|
||||
self.wfile.flush()
|
||||
|
||||
|
||||
class DatagramRequestHandler(BaseRequestHandler):
|
||||
|
@ -403,10 +403,10 @@ 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)
|
||||
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())
|
||||
self.socket.send(self.wfile.getvalue())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue