Chapter 10 ■ http ServerS
180
In the absence of a framework, your code must do all of the negative work of determining which hostnames,
paths, and methods do not match the services you intend to provide. To offer a GET of the path / at the hostname
127.0.0.1, you have to return an error for every deviation from that combination of request parameters that you are
able to detect. Of course, for a tiny service like this, it might seem silly not to simply accept any hostname. But we are
pretending that we might grow into a big service that offers different content at dozens of different hostnames, and so
we are being careful to pay attention to them.
Note that you are responsible for breaking apart the hostname and port in case the client provides a Host header
like 127.0.0.1:8000. Further, you have to split the path on the character? in case the URL has a query string like
/?name=value dangling off of the end. (The listing assumes that, per common practice, you want to ignore extraneous
query strings instead of returning 404 Not Found.)
The next two listings demonstrate how these raw WSGI patterns can be made easier through third-party libraries,
which can be installed with the standard “pip” installation tool (see Chapter 1).
$ pip install WebOb
$ pip install Werkzeug
The WebOb “Web Object” library, , initially written by Ian Bicking, is a lightweight object interface that wraps a
standard WSGI dictionary to provide more convenient access to its information. Listing 10-3 shows how it eliminates
several common patterns from the previous example.
Listing 10-3. WSGI Callable Written with WebOb for Returning the Current Time
#!/usr/bin/env python3
Foundations of Python Network Programming, Third Edition
https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter10/timeapp_webob.py
A WSGI callable built using webob.
import time, webob
def app(environ, start_response):
request = webob.Request(environ)
if environ['REQUEST_METHOD'] != 'GET':
response = webob.Response('501 Not Implemented', status=501)
elif request.domain != '127.0.0.1' or request.path != '/':
response = webob.Response('404 Not Found', status=404)
else:
response = webob.Response(time.ctime())
return response(environ, start_response)
WebOb already implements the two common patterns of wanting to examine the hostname from the Host header
separately from any optional port number that might be attached and of looking at the path without its trailing query
string. It also provides a Response object that knows all about content types and encodings—it defaults to plain
text—so that you need only to provide a string for the response body, and WebOb will take care of everything else.
■ Note WebOb has a feature that makes it stand almost alone among the many python http response object
implementations. the WebOb Response class lets you treat the two pieces of a Content-type header like text/plain;
charset=utf-8 as two separate values, which it exposes as the separate attributes content_type and charset.