Previous Section Next Section

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.

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, 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.

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 8192 bytes) and sock is the server socket. Your handle method typically calls method sendto on sock to send a reply to the client.

server

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.

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; you must subclass it to 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 optional 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 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.

send_error

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.

send_response

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.

wfile

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( ).

register_function

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.

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'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']]
    Previous Section Next Section