Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/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)))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement