Part 2: Parameterization of Requests
Dynamic pages are the norm. The concept of static HTML files being served at high traffic web sites seems quite alien at this point in time. Dynamic HTTP responses are useful in a world where data is constantly being updated. They are also useful when the requests serve as a parameterized action request. These actions are as varied as our imaginations. Status updates, archive queries, feed refresh, purchase transactions, etc. What all of these have in common is that input data of some form is used. The problem is that there are quite a few channels that data can be passed to the HTTP request.
The URI
Let's start with the simple GET request. The fundamental input data of the HTTP request, GET and others, is the request URI. This can be considered the "path" of the resource.
GET /foo/bar/2008/10/3 HTTP/1.1
Host: www.example.com
The request URI is quite flexible in how it can be processed by the web application. The HTTP daemon could be dispatching this to a static resource off of /var/www/foo/bar/2008/10/3 or it could be proxying the request to a Tomcat application on another machine servicing for /foo/bar or there could be a Django foo.py script that is regular expression matching the pieces of information for the "bar" category posted on Oct 10, 2008.
The Query String
Already we see that the request URI can be a source of data for processing an HTTP request. Now there is another oft-used avenue of data passing for something as simple as the GET request. The query string.
GET /foo/bar/2008/10/3?offset=30&filter=-2 HTTP/1.1
Host: www.example.com
In this request the string "?offset=30&filter=-2" has been appended to the request URI. This is the query string that is used to represent a hash of value lists. In the example above the data is basically.
Key | Value
==============
offset | 30
filter | -2
I say the data is essentially a hash of "value lists" because the form of
?offset=30&filter=-2&filter=-5
is also valid.
Key | Value
=================
offset | [30]
filter | [-2,-5]
In the query string we have a standard predictable way passing data with the HTTP request that has a natural mapping to programming data structures. Although there seems to be a strange backlash against the venerable query string calling it "ugly" and preferring to use the URI path as the data vehicle.
There is a valid argument there. Take, for example, the two examples below.
/foo?category=bar&year=2008&month=10&day=3
vs
/foo/bar/2008/10/3
The second just has an aesthetic polish to it that would be preferable for deployment even though the first is easier to code for than the second. But the blurring of the lines between resource location, the path, and the request data, the query string, has added a layer of complexity and decision making when creating a web application.
The Cookie
Now on top of the the URI path and the query string there are also cookies that can be sent along with the request.
GET /foo/bar/2008/10/3?offset=30&filter=-2 HTTP/1.1
Host: www.example.com
Cookie: _SID_=n328vn3f2vhuwv437r
Cookies can store many useful things but in our applications it stores a useful little session ID that can reference sensitive server side information such as user ID, name, email, etc. So for the last example we may actually be processing all of this data.
handler: foo (from URI path)
category: bar (from URI path)
year: 2008 (from URI path)
month: 10 (from URI path)
day: 3 (from URI path)
offset: 30 (from query string)
filter: -2 (from query string)
session_id: n328vn3f2vhuwv437r (from cookie)
logged_in: true (from session storage)
user_id: 55 (from session storage)
We can see from the simple example above that the request is quite a multifaceted concept that cannot be summed up in simple native data structure.
Request Modeling
From that, one's natural inclinations would guide us to model an object that represents the request. And from that we see that we are divergent from the original concept of garter and much more isomorphic to the mod_python and Django methods of request modeling.
request.method # method of the request
request.host # destination host
request.path # Path portion of the URI
request.query_string # query string object from the URI query string
request.post # Post data object
request.files # multi-part upload attachments
request.cookies # cookie hash
request.session # convenience session object
request.referer # HTTP referer
request.start_response # function to send status line and headers back
request.env # original environment
request.headers # hash of original headers
In fact, what would be the Request object would be created even before it is sent to the dispatch layer of code. In the Garter.handleRequest() method the Request would be created from the environ before being sent off to the handler. The signature of the handler then changes from:
application(environ, start_response)
to
application(request)
Extra Credit
GET /foo/bar/2008/10/3?offset=30&filter=-2 HTTP/1.1
Host: baz.example.com
Cookie: _SID_=n328vn3f2vhuwv437r
In the request above baz is yet another piece of data that can affect the query results.