Responding to HTTP Requests
HTTP is, on its surface, a simple protocol. A client sends a request, the server sends a response, the connection closes. You can experiment with HTTP by writing your own Protocol that accepts a connection, reads the request, and sends back an HTTP-formatted response.
4.1.1. How Do I Do That?
Every HTTP request starts with a single line containing the HTTP method, a partial Uniform Resource Identifier (URI), and the HTTP version. Following this line are an arbitrary number of header lines. A blank line indicates the end of the headers. The header section is optionally followed by additional data called the body of the request, such as data being posted from an HTML form.
Here's an example of a minimal HTTP request. This request asks the server to perform the method GET on the resource www.example.com/index.html, preferably using HTTP version 1.1:
GET /index.html HTTP/1.1 Host: www.example.com
The first line of the server's response tells the client the HTTP version being used for the response and the HTTP status code. Like the request, the response also contains header lines followed by a blank line and the message body. Here's a minimal HTTP response:
HTTP/1.1 200 OK Content-Type: text/plain Content-Length: 17 Connection: Close Hello HTTP world!
To set up a very basic HTTP server, write a Protocol that accepts input from the client. Look for the blank line that identifies the end of the headers. Then send an HTTP response. Example 4-1 shows a simple HTTP implementation that echoes each request back to the client.
Example 4-1. webecho.py
from twisted.protocols import basic from twisted.internet import protocol, reactor class HttpEchoProtocol(basic.LineReceiver): def _ _init_ _(self): self.lines = [] self.gotRequest = False def lineReceived(self, line): self.lines.append(line) if not line and not self.gotRequest: self.sendResponse( ) self.gotRequest = True def sendResponse(self): responseBody = "You said: " + " ".join(self.lines) self.sendLine("HTTP /1.0 200 OK") self.sendLine("Content-Type: text/plain") self.sendLine("Content-Length: %i" % len(responseBody)) self.sendLine("") self.transport.write(responseBody) self.transport.loseConnection( ) f = protocol.ServerFactory( ) f.protocol = HttpEchoProtocol reactor.listenTCP(8000, f) reactor.run( )
Run webecho.py to start the server. You can see the server in action by pointing your web browser to http://localhost:8000. You'll get a response echoing the request your browser sends to the server, as shown in Figure 4-1.
Figure 4-1. Viewing the response from webecho.py
4.1.2. How Does That Work?
HTTPEchoProtocol understands just enough about HTTP to return a response to each request. As data is received from the client, it appends each incoming line to self.lines. When it sees a blank line, it knows that it has come to the end of the headers. It then sends back an HTTP response. The first line contains the HTTP version and status code; in this case, 200 for OK (the string "OK" is a human-readable version of the status code; it could just as easily be another phrase with the same meaning, like "No problem!"). The next couple of lines are the Content-Type and Content-Length headers, which tell the client the format and length of the body. HTTPEchoProtocol sends a blank line to indicate the end of the headers, and then the body itself, which in this case is just an echo of the request sent by the client.