Twisted.web2 is an asynchronous HTTP 1.1 server written for the Twisted internet framework. It provides a RFC 2616 compliant HTTP 1.1 protocol implementation, with pipelined and persistent request support, in a non-blocking threadless manner.
It also includes a simple web framework with request and response objects, static file support, error handling, form upload support, HTTP range support, pre-built parsers for all standard headers, and a bunch of other goodies.
It is deployable as a standard TCP or SSL web server, or as a CGI script invoked by another server. Other deployment mechanisms (such as SCGI) are planned.
In addition to running native twisted.web2 applications, it can also run any WSGI or CGI application, or, via compatibility wrappers, most applications written for the older twisted.web and nevow APIs.
Currently, twisted.web2 does not include a HTTP client or proxy, but will at a future date.
Twisted.web2 is not a templating framework. It provides mechanisms for locating and running code associated with a URL, but does not provide any means for separating code and data or to ease the task of generating HTML. Nevow (www.nevow.com) is a templating framework which is recommended for use with twisted.web2.
Twisted.web2 is in general fairly speedy. However, keep in mind that it is a python program, and, while it is empirically "fast enough", it cannot match Apache in static file serving speed. <insert actual measurements here>
This tutorial should be readable by people who do not have much Twisted experience yet, but, you should know Python, and HTML, before starting. While it is hopefully redundant to say this, you also ought to have installed twisted, and twisted.web2.
When you have finished this tutorial, you should be able to write some simple resources and publish them with twisted.web2.
from twisted.web2 import server, http, resource class Toplevel(resource.Resource): addSlash = True def render(self, ctx): return http.Response(stream="Hello monkey!") site = server.Site(Toplevel()) # Standard twisted application Boilerplate from twisted.application import service, strports application = service.Application("demoserver") s = strports.service('tcp:8080', http.HTTPFactory(site)) s.setServiceParent(application)
Save this program as example1.py, and then run it via twistd -ny example1.py. twistd is the Twisted runner; it knows how to execute applications by looking for the magic application variable declared at top-level. You can also run your server daemonized, via twistd -oy example1.py. You can then access your server via the url "http://localhost:8080/". For more deployment options, see the deployment chapter.
A resource is responsible for handling one segment of the URL. Here, we have created a resource to handle the top level of the url hierarchy. The addSlash = True setting tells twisted.web2 that this is a directory-like resource. This means that it will add a "/" to the end of the URL automatically, if needed, and respond under that name. Root resources should always have addSlash = True.
The defined class has just a single method: render. This method takes a single argument: ctx, which contains all the state related to the current rendering operation. This particular render method always returns the same data, so we won't use ctx. We'll get back to it later.
Here, the render method simply returns a http.Response object containing the output.
After defining this class, next we need to tell twisted.web2 to serve it up. This is accomplished by creating the server.Site object, with an instance of your top-level resource as its argument, and then some standard boilerplate to tell Twisted what services to start and what port to serve them on.
Of course, what good is a webserver that can only serve up a single page? So, you can also add child resources to your top-level resource, via child_<name> attributes.
import os.path, time from twisted.web2 import server, http, resource from twisted.web2 import static, http_headers, responsecode class Child(resource.Resource): creation_time = time.time() text = 'Yo Ho Ho and a bottle of rum.' content_type = http_headers.MimeType('text', 'plain') def render(self, ctx): return http.Response( responsecode.OK, {'last-modified': self.creation_time, 'etag': http_headers.ETag(str(hash(self.text))), 'content-type': self.content_type}, self.text) class Toplevel(resource.Resource): addSlash = True child_monkey = static.File(os.path.dirname(static.__file__)+'/static.py') child_elephant = Child() def render(self, ctx): return http.Response( 200, {'content-type': http_headers.MimeType('text', 'html')}, """<html><body> <a href="monkey">The source code of twisted.web2.static</a><br> <a href="elephant">A defined child</a></body></html>""") site = server.Site(Toplevel()) # Standard twisted application Boilerplate from twisted.application import service, strports application = service.Application("demoserver") s = strports.service('tcp:8080', http.HTTPFactory(site)) s.setServiceParent(application)
Here a few new concepts have been introduced:
As an aside for those who know a bit about HTTP, note that just by setting the Last-Modified and ETag response headers, you enable automatic precondition checks which support the If-Modified-Since, If-Unmodified-Since, If-Match, and If-None-Match input headers. This allows the client to request that the resource only be sent if it has changed since the last time the client downloaded it, saving bandwidth. Also, the Range and If-Range headers are supported on every resource, allowing partial downloads, and default values for the "Server" and "Date" headers are added to the output for you automatically.