http.py 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. """
  2. Hypertext Transfer Protocol.
  3. """
  4. from pypacker import pypacker, triggerlist
  5. import re
  6. import logging
  7. logger = logging.getLogger("pypacker")
  8. class HTTPStartLine(triggerlist.TriggerList):
  9. def _pack(self):
  10. # logger.debug("packing HTTP-header")
  11. # no header = no CRNL
  12. if len(self) == 0:
  13. # logger.debug("empty buf 2")
  14. return b""
  15. return b"".join(self) + b"\r\n"
  16. class HTTPHeader(triggerlist.TriggerList):
  17. def _pack(self):
  18. # logger.debug("packing HTTP-header")
  19. # no header = no CRNL
  20. if len(self) == 0:
  21. # logger.debug("empty buf 2")
  22. return b""
  23. return b"\r\n".join([b": ".join(keyval) for keyval in self]) + b"\r\n\r\n"
  24. # REQ_METHODS_BASIC = set([b"GET", b"POST", b"HEAD", b"PUT", b"OPTIONS", b"CONNECT", b"UPDATE", b"TRACE"])
  25. PROG_SPLIT_HEADBODY = re.compile(b"\r\n\r\n")
  26. PROG_SPLIT_HEADER = re.compile(b"\r\n")
  27. PROG_SPLIT_KEYVAL = re.compile(b": ")
  28. class HTTP(pypacker.Packet):
  29. __hdr__ = (
  30. # content: ["startline"]
  31. ("startline", None, HTTPStartLine),
  32. # content: [("name", "value"), ...]
  33. ("hdr", None, HTTPHeader),
  34. )
  35. def _dissect(self, buf):
  36. # requestline: [method] [uri] [version] eg GET / HTTP/1.1
  37. # responseline: [version] [status] [reason] eg HTTP/1.1 200 OK
  38. # TODO: raises exception on reader
  39. try:
  40. bts_header, bts_body = PROG_SPLIT_HEADBODY.split(buf, 1)
  41. except ValueError:
  42. # assume this is part of a bigger (splittet) HTTP-message (no header)
  43. return len(buf)
  44. # logger.debug("head: %s" % bts_header)
  45. # logger.debug("body: %s" % bts_body)
  46. startline, bts_header = PROG_SPLIT_HEADER.split(bts_header, 1)
  47. # logger.debug("startline: %s" % startline)
  48. # logger.debug("bts_header: %s" % bts_header)
  49. self._init_triggerlist("startline", startline + b"\r\n", lambda bts: bts.strip())
  50. self._init_triggerlist("hdr", bts_header + b"\r\n\r\n", self.__parse_header)
  51. # logger.debug(self.startline.bin())
  52. # logger.debug(self.header.bin())
  53. # logger.debug(len(startline+b"\r\n") + len(bts_header+b"\r\n\r\n"))
  54. # logger.debug("lengths head/body: %d %d" % (len(buf), len(bts_body)))
  55. # logger.debug(buf[:len(buf) - len(bts_body)])
  56. # HEADER + "\r\n\r\n" + BODY -> newline is part of the header
  57. return len(buf) - len(bts_body)
  58. @staticmethod
  59. def __parse_header(buf):
  60. # logger.debug("parsing: %s" % buf)
  61. header = []
  62. lines = PROG_SPLIT_HEADER.split(buf)
  63. for line in lines:
  64. # logger.debug("checking HTTP-header: %s" % line)
  65. if len(line) == 0:
  66. break
  67. key, val = PROG_SPLIT_KEYVAL.split(line, 1)
  68. header.append((key, val))
  69. return header