19.2 The SocketServer Module
The Python library supplies a framework
module, SocketServer, to help you implement
Internet servers. SocketServer supplies server
classes TCPServer, for connection-oriented servers
using TCP, and UDPServer, for datagram-oriented
servers using UDP, with the same
interface.
An instance s of either
TCPServer or UDPServer supplies
many attributes and methods, and you can subclass either class and
override some methods to architect your own specialized server
framework. However, I do not cover such advanced and rarely used
possibilities in this book.
Classes TCPServer and
UDPServer implement synchronous servers, able to
serve one request at a time. Classes
ThreadingTCPServer and
ThreadingUDPServer implement threaded servers,
spawning a new thread per request. You are responsible for
synchronizing the resulting threads as needed. Threading is covered
in Chapter 14.
19.2.1 The BaseRequestHandler Class
For normal use of
SocketServer, subclass the
BaseRequestHandler class provided by
SocketServer and override the
handle method. Then, instantiate a server class,
passing the address pair on which to serve and your subclass of
BaseRequestHandler. Finally, call method
serve_forever on the server class instance.
An instance h of
BaseRequestHandler supplies the following methods
and attributes.
The
h.client_address
attribute is the pair
(host,port)
of the client, set by the base class at connection.
Your subclass overrides this method, called by the server, on a new
instance of your subclass for each new incoming request. Typically,
for a TCP server, your implementation of handle
conducts a conversation with the client on socket
h.request to service
the request. For a UDP server, your implementation of
handle examines the datagram in
h.request[0] and sends
a reply string with
h.request[1].sendto.
For a TCP server, the
h.request attribute is
the socket connected to the client. For a UDP server, the
h.request attribute is
a pair
(data,sock),
where data is the string of data the
client sent as a request (up to 8192 bytes) and
sock is the server socket. Your
handle method typically calls method
sendto on sock to send
a reply to the client.
The
h.server attribute is
the instance of the server class that instantiated this handler
object.
Example 19-5 uses module
SocketServer to reimplement the server of Example 19-1 with the added ability to serve multiple
clients simultaneously by threading.
Example 19-5. Threaded TCP echo server using SocketServer
import SocketServer
class EchoHandler(SocketServer.BaseRequestHandler):
def handle(self):
print "Connected from", self.client_address
while True:
receivedData = self.request.recv(8192)
if not receivedData: break
self.request.sendall(receivedData)
self.request.close( )
print "Disconnected from", self.client_address
srv = SocketServer.ThreadingTCPServer(('',8881),EchoHandler)
srv.serve_forever( )
Run the server of Example 19-5 on a terminal window,
and try a few runs of Example 19-2 while the server
is running. Try also telnet localhost 8881 on
other terminal windows (or other platform-dependent Telnet-like
programs) to verify the behavior of longer-term connections.
19.2.2 HTTP Servers
The BaseHTTPServer,
SimpleHTTPServer,
CGIHTTPServer, and
SimpleXMLRPCServer modules implement HTTP servers
of different completeness and sophistication on top of module
SocketServer.
19.2.2.1 The BaseHTTPServer module
The
BaseHTTPServer module supplies a server class
HTTPServer that subclasses
SocketServer.TCPServer and is used in the same
way. It also provides a request handler class
BaseHTTPRequestHandler, which subclasses
SocketServer.BaseRequestHandler and adds
attributes and methods useful for HTTP servers, of which the most
commonly used are as follows.
The
h.command attribute is
the HTTP verb of the client's request, such as
'get', 'head', or
'post'.
Overrides the superclass's method
handle and delegates request handling to methods
whose names start with 'do_', such as
do_get, do_head, and
do_post. Class
BaseHTTPRequestHandler supplies no
do_ methods; you must subclass it to supply the
methods you want to implement.
Terminates the response's MIME headers by sending a
blank line.
The
h.path attribute is the
HTTP path of the client's request, such as
'/index.html'.
The
h.rfile attribute is a
file-like object open for reading, from which you can read optional
data sent as the body of the client's request (e.g.,
URL-encoded form data for a POST).
h.send_header(keyword,value)
|
|
Adds to the response a MIME header with the given
keyword and
value. Each time
send_header is called, another header is added to
the response. Even when send_header is called
repeatedly with the same keyword, multiple
headers with that keyword are added, one
per call to send_header, in the same order as the
calls to send_header.
h.send_error(code,message=None)
|
|
Sends a complete error reply with HTTP code
code and, optionally, more specific text
from string message, when
message is not None.
h.send_response(code,message=None)
|
|
Sends a response header with HTTP code
code and, optionally, more specific text
from string message, when
message is not None. The
headers sent automatically are Server and
Date.
The
h.wfile attribute is a
file-like object open for writing, to which you can write the
response body after calling send_response,
optionally send_header, and
end_headers.
As an example, here's a trivial HTTP server that
just answers every request with the 404 error code
and the corresponding message 'File not found'.
import BaseHTTPServer
class TrivialHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
"""Trivial HTTP request handler, answers not found to every request"""
server_version = "TrivialHTTP/1.0"
def do_GET(self):
"""Serve a GET request."""
self.send_error(404, "File not found")
do_HEAD = do_POST = do_GET
19.2.2.2 The SimpleHTTPServer module
The SimpleHTTPServer
module builds on top of BaseHTTPServer, supplying
what's needed to serve GET HTTP requests for files
in a given directory. It is most useful as an example of how to use
BaseHTTPServer for a real, although simple, HTTP
serving task.
19.2.2.3 The CGIHTTPServer module
The CGIHTTPServer
module builds on top of SimpleHTTPServer,
supplying the ability to serve GET and POST HTTP requests via CGI
scripts, covered in Chapter 20. You can use it to
debug CGI scripts on your local machine.
19.2.2.4 The SimpleXMLRPCServer module
XML-RPC is a higher-level protocol that
runs on top of HTTP. Python supports XML-RPC clients with module
xmlrpclib, covered in Chapter 18. The SimpleXMLRPCServer
module, introduced in Python 2.2, supplies class
SimpleXMLRPCServer to instantiate with the address
pair on which to serve.
In Python 2.2 and 2.2.1,
SimpleXMLRPCServer as supplied in the standard
Python library has a defect: when a method called via XML-RPC raises
an exception, the server does not correctly communicate exception
details to the XML-RPC client. The defect is fixed in Python 2.3 and
later. To get a fixed version for Python 2.2, download
SimpleXMLRPCServer.py from URL
http://www.sweetapp.com/xmlrpc to replace the
file of the same name in the Python library directory (e.g.,
c:\python22\Lib for a standard Python 2.2
installation on Windows).
An instance x of class
SimpleXMLRPCServer supplies two methods to call
before x.serve_forever(
).
x.register_function(callable,name=None)
|
|
Registers
callable, callable with a single argument,
to respond to XML-RPC requests for name.
name can be an identifier or a sequence of
identifiers joined by dots. When name is
None, uses name
callable._ _name_ _.
The argument to callable is the result of
xmlrpclib.loads(payload)
where payload is the
request's payload.
x.register_instance(inst)
|
|
Registers inst to respond to XML-RPC
requests with names not registered via
register_function. When
inst supplies a method
_dispatch,
inst._dispatch
is called with the request's name and parameters as
arguments. When inst does not supply
_dispatch, the request's name is
used as an attribute name to search on
inst. When the request's
name contains dots, the search repeats recursively for each
component. The attribute found by this search is then called with the
request's parameters as arguments. Only one instance
at a time can be registered with
register_instance: if you call
x.register_instance
again, the instance passed in the previous call to
x.register_instance is
replaced by the one passed in the later call.
Simple examples of all typical usage patterns for
impleXMLRPCServer are given in the docstring of
module SimpleXMLRPCServer.py, which you can find
in the Lib directory of your Python installation
(Python 2.2 and later only). Here is a toy example of using the
_dispatch method. In one terminal window, run the
following tiny script:
import SimpleXMLRPCServer
class with_dispatch:
def _dispatch(self, *args):
print '_dispatch', args
return args
server = SimpleXMLRPCServer.SimpleXMLRPCServer(('localhost',8888))
server.register_instance(with_dispatch( ))
server.serve_forever( )
From a Python interactive session on another terminal window of the
same machine (or an IDLE interactive session on the same machine),
you can now run:
>>> import xmlrpclib
>>> proxy = xmlrpclib.ServerProxy('http://localhost:8888')
>>> print proxy.whatever.method('any', 'args')
['whatever.method', ['any', 'args']]
|