25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 
 

437 satır
15 KiB

  1. #cython: language_level=3
  2. from __future__ import print_function
  3. from typing import Optional
  4. from cpython.mem cimport PyMem_Malloc, PyMem_Free
  5. from cpython cimport PyObject_GetBuffer, PyBuffer_Release, PyBUF_SIMPLE, \
  6. Py_buffer, PyBytes_AsString
  7. from .python cimport PyMemoryView_Check, PyMemoryView_GET_BUFFER
  8. from .errors import (HttpParserError,
  9. HttpParserCallbackError,
  10. HttpParserInvalidStatusError,
  11. HttpParserInvalidMethodError,
  12. HttpParserInvalidURLError,
  13. HttpParserUpgrade)
  14. cimport cython
  15. from . cimport cparser
  16. __all__ = ('HttpRequestParser', 'HttpResponseParser')
  17. @cython.internal
  18. cdef class HttpParser:
  19. cdef:
  20. cparser.llhttp_t* _cparser
  21. cparser.llhttp_settings_t* _csettings
  22. bytes _current_header_name
  23. bytes _current_header_value
  24. _proto_on_url, _proto_on_status, _proto_on_body, \
  25. _proto_on_header, _proto_on_headers_complete, \
  26. _proto_on_message_complete, _proto_on_chunk_header, \
  27. _proto_on_chunk_complete, _proto_on_message_begin
  28. object _last_error
  29. Py_buffer py_buf
  30. def __cinit__(self):
  31. self._cparser = <cparser.llhttp_t*> \
  32. PyMem_Malloc(sizeof(cparser.llhttp_t))
  33. if self._cparser is NULL:
  34. raise MemoryError()
  35. self._csettings = <cparser.llhttp_settings_t*> \
  36. PyMem_Malloc(sizeof(cparser.llhttp_settings_t))
  37. if self._csettings is NULL:
  38. raise MemoryError()
  39. def __dealloc__(self):
  40. PyMem_Free(self._cparser)
  41. PyMem_Free(self._csettings)
  42. cdef _init(self, protocol, cparser.llhttp_type_t mode):
  43. cparser.llhttp_settings_init(self._csettings)
  44. cparser.llhttp_init(self._cparser, mode, self._csettings)
  45. self._cparser.data = <void*>self
  46. self._current_header_name = None
  47. self._current_header_value = None
  48. self._proto_on_header = getattr(protocol, 'on_header', None)
  49. if self._proto_on_header is not None:
  50. self._csettings.on_header_field = cb_on_header_field
  51. self._csettings.on_header_value = cb_on_header_value
  52. self._proto_on_headers_complete = getattr(
  53. protocol, 'on_headers_complete', None)
  54. self._csettings.on_headers_complete = cb_on_headers_complete
  55. self._proto_on_body = getattr(protocol, 'on_body', None)
  56. if self._proto_on_body is not None:
  57. self._csettings.on_body = cb_on_body
  58. self._proto_on_message_begin = getattr(
  59. protocol, 'on_message_begin', None)
  60. if self._proto_on_message_begin is not None:
  61. self._csettings.on_message_begin = cb_on_message_begin
  62. self._proto_on_message_complete = getattr(
  63. protocol, 'on_message_complete', None)
  64. if self._proto_on_message_complete is not None:
  65. self._csettings.on_message_complete = cb_on_message_complete
  66. self._proto_on_chunk_header = getattr(
  67. protocol, 'on_chunk_header', None)
  68. self._csettings.on_chunk_header = cb_on_chunk_header
  69. self._proto_on_chunk_complete = getattr(
  70. protocol, 'on_chunk_complete', None)
  71. self._csettings.on_chunk_complete = cb_on_chunk_complete
  72. self._last_error = None
  73. cdef _maybe_call_on_header(self):
  74. if self._current_header_value is not None:
  75. current_header_name = self._current_header_name
  76. current_header_value = self._current_header_value
  77. self._current_header_name = self._current_header_value = None
  78. if self._proto_on_header is not None:
  79. self._proto_on_header(current_header_name,
  80. current_header_value)
  81. cdef _on_header_field(self, bytes field):
  82. self._maybe_call_on_header()
  83. if self._current_header_name is None:
  84. self._current_header_name = field
  85. else:
  86. self._current_header_name += field
  87. cdef _on_header_value(self, bytes val):
  88. if self._current_header_value is None:
  89. self._current_header_value = val
  90. else:
  91. # This is unlikely, as mostly HTTP headers are one-line
  92. self._current_header_value += val
  93. cdef _on_headers_complete(self):
  94. self._maybe_call_on_header()
  95. if self._proto_on_headers_complete is not None:
  96. self._proto_on_headers_complete()
  97. cdef _on_chunk_header(self):
  98. if (self._current_header_value is not None or
  99. self._current_header_name is not None):
  100. raise HttpParserError('invalid headers state')
  101. if self._proto_on_chunk_header is not None:
  102. self._proto_on_chunk_header()
  103. cdef _on_chunk_complete(self):
  104. self._maybe_call_on_header()
  105. if self._proto_on_chunk_complete is not None:
  106. self._proto_on_chunk_complete()
  107. ### Public API ###
  108. def set_dangerous_leniencies(
  109. self,
  110. lenient_headers: Optional[bool] = None,
  111. lenient_chunked_length: Optional[bool] = None,
  112. lenient_keep_alive: Optional[bool] = None,
  113. lenient_transfer_encoding: Optional[bool] = None,
  114. lenient_version: Optional[bool] = None,
  115. lenient_data_after_close: Optional[bool] = None,
  116. lenient_optional_lf_after_cr: Optional[bool] = None,
  117. lenient_optional_cr_before_lf: Optional[bool] = None,
  118. lenient_optional_crlf_after_chunk: Optional[bool] = None,
  119. lenient_spaces_after_chunk_size: Optional[bool] = None,
  120. ):
  121. cdef cparser.llhttp_t* parser = self._cparser
  122. if lenient_headers is not None:
  123. cparser.llhttp_set_lenient_headers(
  124. parser, lenient_headers)
  125. if lenient_chunked_length is not None:
  126. cparser.llhttp_set_lenient_chunked_length(
  127. parser, lenient_chunked_length)
  128. if lenient_keep_alive is not None:
  129. cparser.llhttp_set_lenient_keep_alive(
  130. parser, lenient_keep_alive)
  131. if lenient_transfer_encoding is not None:
  132. cparser.llhttp_set_lenient_transfer_encoding(
  133. parser, lenient_transfer_encoding)
  134. if lenient_version is not None:
  135. cparser.llhttp_set_lenient_version(
  136. parser, lenient_version)
  137. if lenient_data_after_close is not None:
  138. cparser.llhttp_set_lenient_data_after_close(
  139. parser, lenient_data_after_close)
  140. if lenient_optional_lf_after_cr is not None:
  141. cparser.llhttp_set_lenient_optional_lf_after_cr(
  142. parser, lenient_optional_lf_after_cr)
  143. if lenient_optional_cr_before_lf is not None:
  144. cparser.llhttp_set_lenient_optional_cr_before_lf(
  145. parser, lenient_optional_cr_before_lf)
  146. if lenient_optional_crlf_after_chunk is not None:
  147. cparser.llhttp_set_lenient_optional_crlf_after_chunk(
  148. parser, lenient_optional_crlf_after_chunk)
  149. if lenient_spaces_after_chunk_size is not None:
  150. cparser.llhttp_set_lenient_spaces_after_chunk_size(
  151. parser, lenient_spaces_after_chunk_size)
  152. def get_http_version(self):
  153. cdef cparser.llhttp_t* parser = self._cparser
  154. return '{}.{}'.format(parser.http_major, parser.http_minor)
  155. def should_keep_alive(self):
  156. return bool(cparser.llhttp_should_keep_alive(self._cparser))
  157. def should_upgrade(self):
  158. cdef cparser.llhttp_t* parser = self._cparser
  159. return bool(parser.upgrade)
  160. def feed_data(self, data):
  161. cdef:
  162. size_t data_len
  163. cparser.llhttp_errno_t err
  164. Py_buffer *buf
  165. bint owning_buf = False
  166. const char* err_pos
  167. if PyMemoryView_Check(data):
  168. buf = PyMemoryView_GET_BUFFER(data)
  169. data_len = <size_t>buf.len
  170. err = cparser.llhttp_execute(
  171. self._cparser,
  172. <char*>buf.buf,
  173. data_len)
  174. else:
  175. buf = &self.py_buf
  176. PyObject_GetBuffer(data, buf, PyBUF_SIMPLE)
  177. owning_buf = True
  178. data_len = <size_t>buf.len
  179. err = cparser.llhttp_execute(
  180. self._cparser,
  181. <char*>buf.buf,
  182. data_len)
  183. try:
  184. if self._cparser.upgrade == 1 and err == cparser.HPE_PAUSED_UPGRADE:
  185. err_pos = cparser.llhttp_get_error_pos(self._cparser)
  186. # Immediately free the parser from "error" state, simulating
  187. # http-parser behavior here because 1) we never had the API to
  188. # allow users manually "resume after upgrade", and 2) the use
  189. # case for resuming parsing is very rare.
  190. cparser.llhttp_resume_after_upgrade(self._cparser)
  191. # The err_pos here is specific for the input buf. So if we ever
  192. # switch to the llhttp behavior (re-raise HttpParserUpgrade for
  193. # successive calls to feed_data() until resume_after_upgrade is
  194. # called), we have to store the result and keep our own state.
  195. raise HttpParserUpgrade(err_pos - <char*>buf.buf)
  196. finally:
  197. if owning_buf:
  198. PyBuffer_Release(buf)
  199. if err != cparser.HPE_OK:
  200. ex = parser_error_from_errno(
  201. self._cparser,
  202. <cparser.llhttp_errno_t> self._cparser.error)
  203. if isinstance(ex, HttpParserCallbackError):
  204. if self._last_error is not None:
  205. ex.__context__ = self._last_error
  206. self._last_error = None
  207. raise ex
  208. cdef class HttpRequestParser(HttpParser):
  209. def __init__(self, protocol):
  210. self._init(protocol, cparser.HTTP_REQUEST)
  211. self._proto_on_url = getattr(protocol, 'on_url', None)
  212. if self._proto_on_url is not None:
  213. self._csettings.on_url = cb_on_url
  214. def get_method(self):
  215. cdef cparser.llhttp_t* parser = self._cparser
  216. return cparser.llhttp_method_name(<cparser.llhttp_method_t> parser.method)
  217. cdef class HttpResponseParser(HttpParser):
  218. def __init__(self, protocol):
  219. self._init(protocol, cparser.HTTP_RESPONSE)
  220. self._proto_on_status = getattr(protocol, 'on_status', None)
  221. if self._proto_on_status is not None:
  222. self._csettings.on_status = cb_on_status
  223. def get_status_code(self):
  224. cdef cparser.llhttp_t* parser = self._cparser
  225. return parser.status_code
  226. cdef int cb_on_message_begin(cparser.llhttp_t* parser) except -1:
  227. cdef HttpParser pyparser = <HttpParser>parser.data
  228. try:
  229. pyparser._proto_on_message_begin()
  230. except BaseException as ex:
  231. pyparser._last_error = ex
  232. return -1
  233. else:
  234. return 0
  235. cdef int cb_on_url(cparser.llhttp_t* parser,
  236. const char *at, size_t length) except -1:
  237. cdef HttpParser pyparser = <HttpParser>parser.data
  238. try:
  239. pyparser._proto_on_url(at[:length])
  240. except BaseException as ex:
  241. cparser.llhttp_set_error_reason(parser, "`on_url` callback error")
  242. pyparser._last_error = ex
  243. return cparser.HPE_USER
  244. else:
  245. return 0
  246. cdef int cb_on_status(cparser.llhttp_t* parser,
  247. const char *at, size_t length) except -1:
  248. cdef HttpParser pyparser = <HttpParser>parser.data
  249. try:
  250. pyparser._proto_on_status(at[:length])
  251. except BaseException as ex:
  252. cparser.llhttp_set_error_reason(parser, "`on_status` callback error")
  253. pyparser._last_error = ex
  254. return cparser.HPE_USER
  255. else:
  256. return 0
  257. cdef int cb_on_header_field(cparser.llhttp_t* parser,
  258. const char *at, size_t length) except -1:
  259. cdef HttpParser pyparser = <HttpParser>parser.data
  260. try:
  261. pyparser._on_header_field(at[:length])
  262. except BaseException as ex:
  263. cparser.llhttp_set_error_reason(parser, "`on_header_field` callback error")
  264. pyparser._last_error = ex
  265. return cparser.HPE_USER
  266. else:
  267. return 0
  268. cdef int cb_on_header_value(cparser.llhttp_t* parser,
  269. const char *at, size_t length) except -1:
  270. cdef HttpParser pyparser = <HttpParser>parser.data
  271. try:
  272. pyparser._on_header_value(at[:length])
  273. except BaseException as ex:
  274. cparser.llhttp_set_error_reason(parser, "`on_header_value` callback error")
  275. pyparser._last_error = ex
  276. return cparser.HPE_USER
  277. else:
  278. return 0
  279. cdef int cb_on_headers_complete(cparser.llhttp_t* parser) except -1:
  280. cdef HttpParser pyparser = <HttpParser>parser.data
  281. try:
  282. pyparser._on_headers_complete()
  283. except BaseException as ex:
  284. pyparser._last_error = ex
  285. return -1
  286. else:
  287. if pyparser._cparser.upgrade:
  288. return 1
  289. else:
  290. return 0
  291. cdef int cb_on_body(cparser.llhttp_t* parser,
  292. const char *at, size_t length) except -1:
  293. cdef HttpParser pyparser = <HttpParser>parser.data
  294. try:
  295. pyparser._proto_on_body(at[:length])
  296. except BaseException as ex:
  297. cparser.llhttp_set_error_reason(parser, "`on_body` callback error")
  298. pyparser._last_error = ex
  299. return cparser.HPE_USER
  300. else:
  301. return 0
  302. cdef int cb_on_message_complete(cparser.llhttp_t* parser) except -1:
  303. cdef HttpParser pyparser = <HttpParser>parser.data
  304. try:
  305. pyparser._proto_on_message_complete()
  306. except BaseException as ex:
  307. pyparser._last_error = ex
  308. return -1
  309. else:
  310. return 0
  311. cdef int cb_on_chunk_header(cparser.llhttp_t* parser) except -1:
  312. cdef HttpParser pyparser = <HttpParser>parser.data
  313. try:
  314. pyparser._on_chunk_header()
  315. except BaseException as ex:
  316. pyparser._last_error = ex
  317. return -1
  318. else:
  319. return 0
  320. cdef int cb_on_chunk_complete(cparser.llhttp_t* parser) except -1:
  321. cdef HttpParser pyparser = <HttpParser>parser.data
  322. try:
  323. pyparser._on_chunk_complete()
  324. except BaseException as ex:
  325. pyparser._last_error = ex
  326. return -1
  327. else:
  328. return 0
  329. cdef parser_error_from_errno(cparser.llhttp_t* parser, cparser.llhttp_errno_t errno):
  330. cdef bytes reason = cparser.llhttp_get_error_reason(parser)
  331. if errno in (cparser.HPE_CB_MESSAGE_BEGIN,
  332. cparser.HPE_CB_HEADERS_COMPLETE,
  333. cparser.HPE_CB_MESSAGE_COMPLETE,
  334. cparser.HPE_CB_CHUNK_HEADER,
  335. cparser.HPE_CB_CHUNK_COMPLETE,
  336. cparser.HPE_USER):
  337. cls = HttpParserCallbackError
  338. elif errno == cparser.HPE_INVALID_STATUS:
  339. cls = HttpParserInvalidStatusError
  340. elif errno == cparser.HPE_INVALID_METHOD:
  341. cls = HttpParserInvalidMethodError
  342. elif errno == cparser.HPE_INVALID_URL:
  343. cls = HttpParserInvalidURLError
  344. else:
  345. cls = HttpParserError
  346. return cls(reason.decode('latin-1'))