Advertisement
Guest User

Untitled

a guest
Feb 18th, 2018
56
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.67 KB | None | 0 0
  1. #!/usr/bin/env python2.6
  2. """
  3. Task: implement a rudimentary HTTP server in Python. It needs to support the
  4. GET method to serve static HTML files. Requests to ``/`` shall be
  5. automatically resolved to ``/index.html``. All invalid requests shall be
  6. answered with a ``400 Bad Request``. Queries on a non-existing file shall
  7. terminate in a ``404 Not Found``. The serving port shall be passed through the
  8. command line. The server must be threaded. Use the socket APIs.
  9.  
  10. This implementation is *just a single Python expression,* and only 512 bytes.
  11.  
  12. My favorite hacks:
  13.  
  14. * ``iter(s.accept,s.listen(8))`` actually uses the less known form of the
  15. ``iter()`` builtin: ``iter(callable, sentinel)``, where *the callable is
  16. called until it returns the sentinel.* The ``socket.listen()`` method
  17. handily returns ``None``, which means we will loop forever. I consume this
  18. iterator with ``map()``.
  19. * ``hasattr(type(k,(),{k:property(lambda x:open(d))})(),k)`` is a very
  20. special beast of its own -- and already stripped down to only the relevant
  21. parts! ``k`` is some string (any string basically, we don't really care for
  22. this part of the code) and ``d`` is the file name we want to try to open.
  23.  
  24. The fun part about ``hasattr`` is that it was meant to do the following: try
  25. to access the requested attribute. If it does not exist the corresponding
  26. object will throw an ``AttributeError`` and the object does not have the
  27. attribute in question. There is a long-standing quirk with its
  28. implementation though: it catches *all* exceptions (which is basically fine
  29. because you don't expect ``hasattr`` to ever raise/propagate an exception).
  30. If the object now *computes a value* when accessed, eg. through a property,
  31. we can handily check with ``hasattr()`` if that succeeded.
  32.  
  33. In the actual implementation it goes a step further: it has a *side effect,*
  34. mutating another object *z* in case everything went fine.
  35. * ``(s + ' '*2).split(' ', 2)`` is a nifty bit of code to make ``str.split``
  36. always succeed. By default, ``s.split(' ', 2)`` would return *at most* three
  37. strings delimited by a space -- but a list of one or two strings is fine too.
  38. If we deliberately pad the string beforehand no such error can happen (but we
  39. have to deal with the last element containing bogus trailing spaces
  40. throughout the other parts of the code).
  41. * ``~0`` is a neat obfuscation for ``-1``.
  42. * Python's parser is actually quite forgiving in the way it requires
  43. whitespace. Word boundaries are usually okay for terminating keywords and
  44. identifiers, too, which allows squeezing the code quite a bit -- take
  45. ``d.strip("/")or"index.html"`` for example.
  46. * Oh yeah, ``thread.start_new`` is a deprecated and undocumented shorthand for
  47. ``thread.start_new_thread``.
  48. * ``socket.makefile()`` saves me some work when reading the first line from a
  49. socket.
  50. * A hack I did ultimately reject, in favor of spec conformance, is HTTP version
  51. number trimming: Firefox and Chrome do accept ``HTTP/1.`` as a valid number,
  52. saving another precious byte!
  53.  
  54. Okay, now for those variables:
  55.  
  56. ``s``
  57. The listening socket (bound through a leaking list comprehension).
  58. ``c``
  59. Oh man, this is a bit tricky. It is generally bound to the recently
  60. accepted (socket, addr) pair returned by ``socket.accept()``. In the
  61. accepting *thread* it is immediately rebound to only the *socket.*
  62. ``z``
  63. This hack I actually like very much. It is the response! Now by default, it
  64. contains only the empty string. If reading the file and all that jazz
  65. succeeds, the ``Content-Length`` header and the file contents are appended to
  66. that list (see ``z.extend()``, around byte 271). When finally writing out
  67. the response only the last element -- the contents or, if appending did not
  68. happen for any reason, the empty string -- is sent.
  69. ``q``
  70. The string ``"HTTP/1.`` to save some bytes when checking for ``HTTP/1.0`` and
  71. ``HTTP/1.1`` and writing out the response (``HTTP/1.1 200 OK``).
  72. ``y``
  73. The file's contents.
  74. ``k``
  75. A space. It is used as a dummy string and for ``str.split``ting the
  76. request line.
  77. ``v``, ``d``, and ``p``
  78. The parsed request: verb, queried document, and protocol version.
  79.  
  80. """
  81. [s.bind(('',int(__import__('sys').argv[1])))for(s)
  82. in[__import__('socket').socket()]],map(lambda c,k=
  83. ' ':__import__('thread').start_new(lambda c,z=[""]
  84. ,q="HTTP/1.":[[c.send(q+"1 "+["400 Bad Request\n",[
  85. "404 Not Found\n","200 OK\n"][hasattr(type(k,(),{k:
  86. property(lambda x:z.extend("Content-Length: %d\n\n"
  87. %len(y)+y for(y)in[open(d.strip("/")or"index.html")
  88. .read()]))})(),k)]+z[~0]][v=='GET'and p.strip()in(q
  89. +"1",q+"0")])for(v,d,p)in[(c.makefile().readline()+
  90. k*2).split(k,2)]]],(c[0],)),iter(s.accept,s.listen(
  91. 8)))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement