Previous Page
Next Page

20.2. The SocketServer Module

The Python library supplies a framework module, SocketServer, to help you implement simple 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 that can 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 "Threads in Python" on page 341.

20.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 serve_forever on the server instance.

An instance h of BaseRequestHandler supplies the following methods and attributes.

client_address

The h.client_address attribute is the pair (host,port) of the client, set by the base class at connection.

handle

h.handle( )

Your subclass overrides this method, and the server calls the method on a new instance of your subclass for each incoming request. 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.

request

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 8,192 bytes) and sock is the the server socket. Your handle method can call method sendto on sock to send a reply to the client.

server

The h.server attribute is the server instance that instantiated this handler object.


Example 20-5 uses module SocketServer to reimplement the server of Example 20-1 with the added ability to serve multiple clients simultaneously by threading.

Example 20-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 20-5 on a terminal window and try a few runs of Example 20-2. Try also telnet localhost 8881 on other terminal windows (or other platform-dependent Telnet-like programs) to verify the behavior of longer-lived connections.

20.2.2. HTTP Servers

The BaseHTTPServer, SimpleHTTPServer, CGIHTTPServer, and SimpleXMLRPCServer modules implement HTTP servers of different completeness and sophistication on top of module SocketServer.

20.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.BaseRe-questHandler and adds attributes and methods useful for HTTP servers, of which the most commonly used are as follows.

command

The h.command attribute is the HTTP verb of the client's request, such as 'get', 'head', or 'post'.

handle

h.handle( )

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: subclass it and supply the methods you want to implement.

end_headers

h.end_headers( )

Terminates the response's MIME headers by sending a blank line.

path

The h.path attribute is the HTTP path of the client's request, such as '/index.html'.

rfile

The h.rfile attribute is a file-like object open for reading, from which you can read data sent as the body of the client's request (e.g., URL-encoded form data for a POST).

send_header

h.send_header(keyword,value)

Adds to the response a MIME header with given keyword and value. At each call to send_header, another header is added to the response. When you call send_header repeatedly with the same keyword, multiple headers with that keyword get added, one per call to send_header, in the same order as the calls to send_header.

send_error

h.send_error(code,message=None)

Sends a complete error reply with HTTP code code, and text from string message, when message is not None.

send_response

h.send_response(code,message=None)

Sends a response header with HTTP code code, and text from string message, when message is not None. Headers Server and Date are always sent automatically.

wfile

The h.wfile attribute is a file-like object open for writing, to which you can write the response body after you call send_response, send_header, and end_headers.


As an example, here's a trivial HTTP server that 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

server = BaseHTTPServer.HTTPServer((",80), TrivialHTTPRequestHandler)
server.serve_forever( )

20.2.2.2. The SimpleHTTPServer module

The SimpleHTTPServer module builds on top of BaseHTTPServer, supplying what's needed to serve HTTP GET requests for files in a directory. It is most useful as an example of how to use BaseHTTPServer for a simple real-world HTTP serving task.

20.2.2.3. The CGIHTTPServer module

The CGIHTTPServer module builds on top of SimpleHTTPServer, supplying the ability to serve HTTP GET and POST requests via CGI scripts, covered in Chapter 19. You can use it, for example, to debug CGI scripts on your local machine.

20.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 "Distributed Computing" on page 516. The SimpleXMLRPCServer module supplies class SimpleXMLRPCServer to instantiate with the address pair on which to serve.

An instance x of class SimpleXMLRPCServer supplies two methods to call before x.serve_forever( ).

register_function

x.register_function(callable,name=None)

Registers callable, callable with a single argument, to respond to XML-RPC requests for string name. name is 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.

register_instance

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 name and parameters as arguments. When inst does not supply _dispatch, the request name is used as an attribute name to search in inst. When the request name contains dots, the search repeats for each component. The attribute found by this search is called with the request 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 SimpleXMLRPCServer are given in the docstring of module SimpleXMLRPCServer.py, which you can find in the Lib directory of your Python installation. 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']]


Previous Page
Next Page