Created
July 14, 2011 05:46
-
-
Save lehmannro/1082003 to your computer and use it in GitHub Desktop.
Simple HTTP Server in 512 bytes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2.6 | |
""" | |
Task: implement a rudimentary HTTP server in Python. It needs to support the | |
GET method to serve static HTML files. Requests to ``/`` shall be | |
automatically resolved to ``/index.html``. All invalid requests shall be | |
answered with a ``400 Bad Request``. Queries on a non-existing file shall | |
terminate in a ``404 Not Found``. The serving port shall be passed through the | |
command line. The server must be threaded. Use the socket APIs. | |
This implementation is *just a single Python expression,* and only 512 bytes. | |
My favorite hacks: | |
* ``iter(s.accept,s.listen(8))`` actually uses the less known form of the | |
``iter()`` builtin: ``iter(callable, sentinel)``, where *the callable is | |
called until it returns the sentinel.* The ``socket.listen()`` method | |
handily returns ``None``, which means we will loop forever. I consume this | |
iterator with ``map()``. | |
* ``hasattr(type(k,(),{k:property(lambda x:open(d))})(),k)`` is a very | |
special beast of its own -- and already stripped down to only the relevant | |
parts! ``k`` is some string (any string basically, we don't really care for | |
this part of the code) and ``d`` is the file name we want to try to open. | |
The fun part about ``hasattr`` is that it was meant to do the following: try | |
to access the requested attribute. If it does not exist the corresponding | |
object will throw an ``AttributeError`` and the object does not have the | |
attribute in question. There is a long-standing quirk with its | |
implementation though: it catches *all* exceptions (which is basically fine | |
because you don't expect ``hasattr`` to ever raise/propagate an exception). | |
If the object now *computes a value* when accessed, eg. through a property, | |
we can handily check with ``hasattr()`` if that succeeded. | |
In the actual implementation it goes a step further: it has a *side effect,* | |
mutating another object *z* in case everything went fine. | |
* ``(s + ' '*2).split(' ', 2)`` is a nifty bit of code to make ``str.split`` | |
always succeed. By default, ``s.split(' ', 2)`` would return *at most* three | |
strings delimited by a space -- but a list of one or two strings is fine too. | |
If we deliberately pad the string beforehand no such error can happen (but we | |
have to deal with the last element containing bogus trailing spaces | |
throughout the other parts of the code). | |
* ``~0`` is a neat obfuscation for ``-1``. | |
* Python's parser is actually quite forgiving in the way it requires | |
whitespace. Word boundaries are usually okay for terminating keywords and | |
identifiers, too, which allows squeezing the code quite a bit -- take | |
``d.strip("/")or"index.html"`` for example. | |
* Oh yeah, ``thread.start_new`` is a deprecated and undocumented shorthand for | |
``thread.start_new_thread``. | |
* ``socket.makefile()`` saves me some work when reading the first line from a | |
socket. | |
* A hack I did ultimately reject, in favor of spec conformance, is HTTP version | |
number trimming: Firefox and Chrome do accept ``HTTP/1.`` as a valid number, | |
saving another precious byte! | |
Okay, now for those variables: | |
``s`` | |
The listening socket (bound through a leaking list comprehension). | |
``c`` | |
Oh man, this is a bit tricky. It is generally bound to the recently | |
accepted (socket, addr) pair returned by ``socket.accept()``. In the | |
accepting *thread* it is immediately rebound to only the *socket.* | |
``z`` | |
This hack I actually like very much. It is the response! Now by default, it | |
contains only the empty string. If reading the file and all that jazz | |
succeeds, the ``Content-Length`` header and the file contents are appended to | |
that list (see ``z.extend()``, around byte 271). When finally writing out | |
the response only the last element -- the contents or, if appending did not | |
happen for any reason, the empty string -- is sent. | |
``q`` | |
The string ``"HTTP/1.`` to save some bytes when checking for ``HTTP/1.0`` and | |
``HTTP/1.1`` and writing out the response (``HTTP/1.1 200 OK``). | |
``y`` | |
The file's contents. | |
``k`` | |
A space. It is used as a dummy string and for ``str.split``ting the | |
request line. | |
``v``, ``d``, and ``p`` | |
The parsed request: verb, queried document, and protocol version. | |
""" | |
[s.bind(('',int(__import__('sys').argv[1])))for(s) | |
in[__import__('socket').socket()]],map(lambda c,k= | |
' ':__import__('thread').start_new(lambda c,z=[""] | |
,q="HTTP/1.":[[c.send(q+"1 "+["400 Bad Request\n",[ | |
"404 Not Found\n","200 OK\n"][hasattr(type(k,(),{k: | |
property(lambda x:z.extend("Content-Length: %d\n\n" | |
%len(y)+y for(y)in[open(d.strip("/")or"index.html") | |
.read()]))})(),k)]+z[~0]][v=='GET'and p.strip()in(q | |
+"1",q+"0")])for(v,d,p)in[(c.makefile().readline()+ | |
k*2).split(k,2)]]],(c[0],)),iter(s.accept,s.listen( | |
8))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment