您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

134 行
4.6 KiB

  1. # Code to read HTTP data
  2. #
  3. # Strategy: each writer takes an event + a write-some-bytes function, which is
  4. # calls.
  5. #
  6. # WRITERS is a dict describing how to pick a reader. It maps states to either:
  7. # - a writer
  8. # - or, for body writers, a dict of framin-dependent writer factories
  9. import sys
  10. from ._util import LocalProtocolError
  11. from ._events import Data, EndOfMessage
  12. from ._state import CLIENT, SERVER, IDLE, SEND_RESPONSE, SEND_BODY
  13. __all__ = ["WRITERS"]
  14. # Equivalent of bstr % values, that works on python 3.x for x < 5
  15. if (3, 0) <= sys.version_info < (3, 5):
  16. def bytesmod(bstr, values):
  17. decoded_values = []
  18. for value in values:
  19. if isinstance(value, bytes):
  20. decoded_values.append(value.decode("ascii"))
  21. else:
  22. decoded_values.append(value)
  23. return (bstr.decode("ascii") % tuple(decoded_values)).encode("ascii")
  24. else:
  25. def bytesmod(bstr, values):
  26. return bstr % values
  27. def write_headers(headers, write):
  28. # "Since the Host field-value is critical information for handling a
  29. # request, a user agent SHOULD generate Host as the first header field
  30. # following the request-line." - RFC 7230
  31. for name, value in headers:
  32. if name == b"host":
  33. write(bytesmod(b"%s: %s\r\n", (name, value)))
  34. for name, value in headers:
  35. if name != b"host":
  36. write(bytesmod(b"%s: %s\r\n", (name, value)))
  37. write(b"\r\n")
  38. def write_request(request, write):
  39. if request.http_version != b"1.1":
  40. raise LocalProtocolError("I only send HTTP/1.1")
  41. write(bytesmod(b"%s %s HTTP/1.1\r\n", (request.method, request.target)))
  42. write_headers(request.headers, write)
  43. # Shared between InformationalResponse and Response
  44. def write_any_response(response, write):
  45. if response.http_version != b"1.1":
  46. raise LocalProtocolError("I only send HTTP/1.1")
  47. status_bytes = str(response.status_code).encode("ascii")
  48. # We don't bother sending ascii status messages like "OK"; they're
  49. # optional and ignored by the protocol. (But the space after the numeric
  50. # status code is mandatory.)
  51. #
  52. # XX FIXME: could at least make an effort to pull out the status message
  53. # from stdlib's http.HTTPStatus table. Or maybe just steal their enums
  54. # (either by import or copy/paste). We already accept them as status codes
  55. # since they're of type IntEnum < int.
  56. write(bytesmod(b"HTTP/1.1 %s %s\r\n", (status_bytes, response.reason)))
  57. write_headers(response.headers, write)
  58. class BodyWriter(object):
  59. def __call__(self, event, write):
  60. if type(event) is Data:
  61. self.send_data(event.data, write)
  62. elif type(event) is EndOfMessage:
  63. self.send_eom(event.headers, write)
  64. else: # pragma: no cover
  65. assert False
  66. #
  67. # These are all careful not to do anything to 'data' except call len(data) and
  68. # write(data). This allows us to transparently pass-through funny objects,
  69. # like placeholder objects referring to files on disk that will be sent via
  70. # sendfile(2).
  71. #
  72. class ContentLengthWriter(BodyWriter):
  73. def __init__(self, length):
  74. self._length = length
  75. def send_data(self, data, write):
  76. self._length -= len(data)
  77. if self._length < 0:
  78. raise LocalProtocolError(
  79. "Too much data for declared Content-Length")
  80. write(data)
  81. def send_eom(self, headers, write):
  82. if self._length != 0:
  83. raise LocalProtocolError(
  84. "Too little data for declared Content-Length")
  85. if headers:
  86. raise LocalProtocolError("Content-Length and trailers don't mix")
  87. class ChunkedWriter(BodyWriter):
  88. def send_data(self, data, write):
  89. # if we encoded 0-length data in the naive way, it would look like an
  90. # end-of-message.
  91. if not data:
  92. return
  93. write(bytesmod(b"%x\r\n", (len(data),)))
  94. write(data)
  95. write(b"\r\n")
  96. def send_eom(self, headers, write):
  97. write(b"0\r\n")
  98. write_headers(headers, write)
  99. class Http10Writer(BodyWriter):
  100. def send_data(self, data, write):
  101. write(data)
  102. def send_eom(self, headers, write):
  103. if headers:
  104. raise LocalProtocolError(
  105. "can't send trailers to HTTP/1.0 client")
  106. # no need to close the socket ourselves, that will be taken care of by
  107. # Connection: close machinery
  108. WRITERS = {
  109. (CLIENT, IDLE): write_request,
  110. (SERVER, IDLE): write_any_response,
  111. (SERVER, SEND_RESPONSE): write_any_response,
  112. SEND_BODY: {
  113. "chunked": ChunkedWriter,
  114. "content-length": ContentLengthWriter,
  115. "http/1.0": Http10Writer,
  116. },
  117. }