/circuits/net/protocols/http.py

https://bitbucket.org/prologic/circuits/ · Python · 78 lines · 54 code · 22 blank · 2 comment · 6 complexity · b02d75f5286292e494eed7dc158a791a MD5 · raw file

  1. from io import BytesIO
  2. from circuits.web.headers import parse_headers
  3. from circuits.core import handler, BaseComponent, Event
  4. class Request(Event):
  5. """Request Event"""
  6. success = "request_success",
  7. failure = "request_failure",
  8. class Response(Event):
  9. """Response Event"""
  10. class ResponseObject(object):
  11. def __init__(self, status, message, protocol=None):
  12. self.status = status
  13. self.message = message
  14. self.protocol = protocol
  15. self._headers = None
  16. self._body = BytesIO()
  17. @property
  18. def headers(self):
  19. return self._headers
  20. def read(self):
  21. return self._body.read()
  22. class HTTP(BaseComponent):
  23. channel = "web"
  24. def __init__(self, encoding="utf-8", channel=channel):
  25. super(HTTP, self).__init__(channel=channel)
  26. self._encoding = encoding
  27. self._response = None
  28. self._buffer = BytesIO()
  29. @handler("read", target="client")
  30. def _on_client_read(self, data):
  31. if self._response is not None:
  32. self._response._body.write(data)
  33. cLen = int(self._response.headers.get("Content-Length", "0"))
  34. if cLen and self._response._body.tell() == cLen:
  35. self._response._body.seek(0)
  36. self.fire(Response(self._response))
  37. self._response = None
  38. else:
  39. statusline, data = data.split(b"\r\n", 1)
  40. statusline = statusline.strip().decode(
  41. self._encoding, "replace")
  42. protocol, status, message = statusline.split(" ", 2)
  43. status = int(status)
  44. protocol = tuple(map(int, protocol[5:].split(".")))
  45. response = ResponseObject(status, message, protocol)
  46. end_of_headers = data.find(b"\r\n\r\n")
  47. header_data = data[:end_of_headers].decode(
  48. self._encoding, "replace")
  49. headers = response._headers = parse_headers(header_data)
  50. response._body.write(data[(end_of_headers + 4):])
  51. cLen = int(headers.get("Content-Length", "0"))
  52. if cLen and response._body.tell() < cLen:
  53. self._response = response
  54. return
  55. response._body.seek(0)
  56. self.fire(Response(response))