utilities.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. """
  2. h2/utilities
  3. ~~~~~~~~~~~~
  4. Utility functions that do not belong in a separate module.
  5. """
  6. from __future__ import annotations
  7. import collections
  8. from typing import TYPE_CHECKING, Any, NamedTuple
  9. from hpack.struct import HeaderTuple, NeverIndexedHeaderTuple
  10. from .exceptions import FlowControlError, ProtocolError
  11. if TYPE_CHECKING: # pragma: no cover
  12. from collections.abc import Generator, Iterable
  13. from hpack.struct import Header, HeaderWeaklyTyped
  14. SIGIL = ord(b":")
  15. INFORMATIONAL_START = ord(b"1")
  16. # A set of headers that are hop-by-hop or connection-specific and thus
  17. # forbidden in HTTP/2. This list comes from RFC 7540 § 8.1.2.2.
  18. CONNECTION_HEADERS = frozenset([
  19. b"connection",
  20. b"proxy-connection",
  21. b"keep-alive",
  22. b"transfer-encoding",
  23. b"upgrade",
  24. ])
  25. _ALLOWED_PSEUDO_HEADER_FIELDS = frozenset([
  26. b":method",
  27. b":scheme",
  28. b":authority",
  29. b":path",
  30. b":status",
  31. b":protocol",
  32. ])
  33. _SECURE_HEADERS = frozenset([
  34. # May have basic credentials which are vulnerable to dictionary attacks.
  35. b"authorization",
  36. b"proxy-authorization",
  37. ])
  38. _REQUEST_ONLY_HEADERS = frozenset([
  39. b":scheme",
  40. b":path",
  41. b":authority",
  42. b":method",
  43. b":protocol",
  44. ])
  45. _RESPONSE_ONLY_HEADERS = frozenset([b":status"])
  46. # A Set of pseudo headers that are only valid if the method is
  47. # CONNECT, see RFC 8441 § 5
  48. _CONNECT_REQUEST_ONLY_HEADERS = frozenset([b":protocol"])
  49. def _secure_headers(headers: Iterable[Header],
  50. hdr_validation_flags: HeaderValidationFlags | None) -> Generator[Header, None, None]:
  51. """
  52. Certain headers are at risk of being attacked during the header compression
  53. phase, and so need to be kept out of header compression contexts. This
  54. function automatically transforms certain specific headers into HPACK
  55. never-indexed fields to ensure they don't get added to header compression
  56. contexts.
  57. This function currently implements two rules:
  58. - 'authorization' and 'proxy-authorization' fields are automatically made
  59. never-indexed.
  60. - Any 'cookie' header field shorter than 20 bytes long is made
  61. never-indexed.
  62. These fields are the most at-risk. These rules are inspired by Firefox
  63. and nghttp2.
  64. """
  65. for header in headers:
  66. assert isinstance(header[0], bytes)
  67. if header[0] in _SECURE_HEADERS or (header[0] in b"cookie" and len(header[1]) < 20):
  68. yield NeverIndexedHeaderTuple(header[0], header[1])
  69. else:
  70. yield header
  71. def extract_method_header(headers: Iterable[Header]) -> bytes | None:
  72. """
  73. Extracts the request method from the headers list.
  74. """
  75. for k, v in headers:
  76. if isinstance(v, bytes) and k == b":method":
  77. return v
  78. if isinstance(v, str) and k == ":method":
  79. return v.encode("utf-8") # pragma: no cover
  80. return None
  81. def is_informational_response(headers: Iterable[Header]) -> bool:
  82. """
  83. Searches headers list for a :status header to confirm that a given
  84. collection of headers are an informational response. Assumes the header
  85. are well formed and encoded as bytes: that is, that the HTTP/2 special
  86. headers are first in the block, and so that it can stop looking when it
  87. finds the first header field whose name does not begin with a colon.
  88. :param headers: The HTTP/2 headers.
  89. :returns: A boolean indicating if this is an informational response.
  90. """
  91. for n, v in headers:
  92. if not n.startswith(b":"):
  93. return False
  94. if n != b":status":
  95. # If we find a non-special header, we're done here: stop looping.
  96. continue
  97. # If the first digit is a 1, we've got informational headers.
  98. return v.startswith(b"1")
  99. return False
  100. def guard_increment_window(current: int, increment: int) -> int:
  101. """
  102. Increments a flow control window, guarding against that window becoming too
  103. large.
  104. :param current: The current value of the flow control window.
  105. :param increment: The increment to apply to that window.
  106. :returns: The new value of the window.
  107. :raises: ``FlowControlError``
  108. """
  109. # The largest value the flow control window may take.
  110. LARGEST_FLOW_CONTROL_WINDOW = 2**31 - 1 # noqa: N806
  111. new_size = current + increment
  112. if new_size > LARGEST_FLOW_CONTROL_WINDOW:
  113. msg = f"May not increment flow control window past {LARGEST_FLOW_CONTROL_WINDOW}"
  114. raise FlowControlError(msg)
  115. return new_size
  116. def authority_from_headers(headers: Iterable[Header]) -> bytes | None:
  117. """
  118. Given a header set, searches for the authority header and returns the
  119. value.
  120. Note that this doesn't use indexing, so should only be called if the
  121. headers are for a client request. Otherwise, will loop over the entire
  122. header set, which is potentially unwise.
  123. :param headers: The HTTP header set.
  124. :returns: The value of the authority header, or ``None``.
  125. :rtype: ``bytes`` or ``None``.
  126. """
  127. for n, v in headers:
  128. if n == b":authority":
  129. return v
  130. return None
  131. # Flags used by the validate_headers pipeline to determine which checks
  132. # should be applied to a given set of headers.
  133. class HeaderValidationFlags(NamedTuple):
  134. is_client: bool
  135. is_trailer: bool
  136. is_response_header: bool
  137. is_push_promise: bool
  138. def validate_headers(headers: Iterable[Header], hdr_validation_flags: HeaderValidationFlags) -> Iterable[Header]:
  139. """
  140. Validates a header sequence against a set of constraints from RFC 7540.
  141. :param headers: The HTTP header set.
  142. :param hdr_validation_flags: An instance of HeaderValidationFlags.
  143. """
  144. # This validation logic is built on a sequence of generators that are
  145. # iterated over to provide the final header list. This reduces some of the
  146. # overhead of doing this checking. However, it's worth noting that this
  147. # checking remains somewhat expensive, and attempts should be made wherever
  148. # possible to reduce the time spent doing them.
  149. #
  150. # For example, we avoid tuple unpacking in loops because it represents a
  151. # fixed cost that we don't want to spend, instead indexing into the header
  152. # tuples.
  153. headers = _reject_illegal_characters(
  154. headers, hdr_validation_flags,
  155. )
  156. headers = _reject_empty_header_names(
  157. headers, hdr_validation_flags,
  158. )
  159. headers = _reject_te(
  160. headers, hdr_validation_flags,
  161. )
  162. headers = _reject_connection_header(
  163. headers, hdr_validation_flags,
  164. )
  165. headers = _reject_pseudo_header_fields(
  166. headers, hdr_validation_flags,
  167. )
  168. headers = _check_host_authority_header(
  169. headers, hdr_validation_flags,
  170. )
  171. return _check_path_header(headers, hdr_validation_flags)
  172. def _reject_illegal_characters(headers: Iterable[Header],
  173. hdr_validation_flags: HeaderValidationFlags) -> Generator[Header, None, None]:
  174. """
  175. Raises a ProtocolError if any header names or values contain illegal characters.
  176. See <https://www.rfc-editor.org/rfc/rfc9113.html#section-8.2.1>.
  177. """
  178. for header in headers:
  179. # > A field name MUST NOT contain characters in the ranges 0x00-0x20, 0x41-0x5a,
  180. # > or 0x7f-0xff (all ranges inclusive).
  181. for c in header[0]:
  182. if 0x41 <= c <= 0x5a:
  183. msg = f"Received uppercase header name {header[0]!r}."
  184. raise ProtocolError(msg)
  185. if c <= 0x20 or c >= 0x7f:
  186. msg = f"Illegal character '{chr(c)}' in header name: {header[0]!r}"
  187. raise ProtocolError(msg)
  188. # > With the exception of pseudo-header fields (Section 8.3), which have a name
  189. # > that starts with a single colon, field names MUST NOT include a colon (ASCII
  190. # > COLON, 0x3a).
  191. if header[0].find(b":", 1) != -1:
  192. msg = f"Illegal character ':' in header name: {header[0]!r}"
  193. raise ProtocolError(msg)
  194. # For compatibility with RFC 7230 header fields, we need to allow the field
  195. # value to be an empty string. This is ludicrous, but technically allowed.
  196. if field_value := header[1]:
  197. # > A field value MUST NOT contain the zero value (ASCII NUL, 0x00), line feed
  198. # > (ASCII LF, 0x0a), or carriage return (ASCII CR, 0x0d) at any position.
  199. for c in field_value:
  200. if c == 0 or c == 0x0a or c == 0x0d: # noqa: PLR1714
  201. msg = f"Illegal character '{chr(c)}' in header value: {field_value!r}"
  202. raise ProtocolError(msg)
  203. # > A field value MUST NOT start or end with an ASCII whitespace character
  204. # > (ASCII SP or HTAB, 0x20 or 0x09).
  205. if (
  206. field_value[0] == 0x20 or
  207. field_value[0] == 0x09 or
  208. field_value[-1] == 0x20 or
  209. field_value[-1] == 0x09
  210. ):
  211. msg = f"Received header value surrounded by whitespace {field_value!r}"
  212. raise ProtocolError(msg)
  213. yield header
  214. def _reject_empty_header_names(headers: Iterable[Header],
  215. hdr_validation_flags: HeaderValidationFlags) -> Generator[Header, None, None]:
  216. """
  217. Raises a ProtocolError if any header names are empty (length 0).
  218. While hpack decodes such headers without errors, they are semantically
  219. forbidden in HTTP, see RFC 7230, stating that they must be at least one
  220. character long.
  221. """
  222. for header in headers:
  223. if len(header[0]) == 0:
  224. msg = "Received header name with zero length."
  225. raise ProtocolError(msg)
  226. yield header
  227. def _reject_te(headers: Iterable[Header], hdr_validation_flags: HeaderValidationFlags) -> Generator[Header, None, None]:
  228. """
  229. Raises a ProtocolError if the TE header is present in a header block and
  230. its value is anything other than "trailers".
  231. """
  232. for header in headers:
  233. if header[0] == b"te" and header[1].lower() != b"trailers":
  234. msg = f"Invalid value for TE header: {header[1]!r}"
  235. raise ProtocolError(msg)
  236. yield header
  237. def _reject_connection_header(headers: Iterable[Header], hdr_validation_flags: HeaderValidationFlags) -> Generator[Header, None, None]:
  238. """
  239. Raises a ProtocolError if the Connection header is present in a header
  240. block.
  241. """
  242. for header in headers:
  243. if header[0] in CONNECTION_HEADERS:
  244. msg = f"Connection-specific header field present: {header[0]!r}."
  245. raise ProtocolError(msg)
  246. yield header
  247. def _assert_header_in_set(bytes_header: bytes,
  248. header_set: set[bytes | str] | set[bytes] | set[str]) -> None:
  249. """
  250. Given a set of header names, checks whether the string or byte version of
  251. the header name is present. Raises a Protocol error with the appropriate
  252. error if it's missing.
  253. """
  254. if bytes_header not in header_set:
  255. msg = f"Header block missing mandatory {bytes_header!r} header"
  256. raise ProtocolError(msg)
  257. def _reject_pseudo_header_fields(headers: Iterable[Header],
  258. hdr_validation_flags: HeaderValidationFlags) -> Generator[Header, None, None]:
  259. """
  260. Raises a ProtocolError if duplicate pseudo-header fields are found in a
  261. header block or if a pseudo-header field appears in a block after an
  262. ordinary header field.
  263. Raises a ProtocolError if pseudo-header fields are found in trailers.
  264. """
  265. seen_pseudo_header_fields = set()
  266. seen_regular_header = False
  267. method = None
  268. for header in headers:
  269. if header[0][0] == SIGIL:
  270. if header[0] in seen_pseudo_header_fields:
  271. msg = f"Received duplicate pseudo-header field {header[0]!r}"
  272. raise ProtocolError(msg)
  273. seen_pseudo_header_fields.add(header[0])
  274. if seen_regular_header:
  275. msg = f"Received pseudo-header field out of sequence: {header[0]!r}"
  276. raise ProtocolError(msg)
  277. if header[0] not in _ALLOWED_PSEUDO_HEADER_FIELDS:
  278. msg = f"Received custom pseudo-header field {header[0]!r}"
  279. raise ProtocolError(msg)
  280. if header[0] in b":method":
  281. method = header[1]
  282. else:
  283. seen_regular_header = True
  284. yield header
  285. # Check the pseudo-headers we got to confirm they're acceptable.
  286. _check_pseudo_header_field_acceptability(
  287. seen_pseudo_header_fields, method, hdr_validation_flags,
  288. )
  289. def _check_pseudo_header_field_acceptability(pseudo_headers: set[bytes | str] | set[bytes] | set[str],
  290. method: bytes | None,
  291. hdr_validation_flags: HeaderValidationFlags) -> None:
  292. """
  293. Given the set of pseudo-headers present in a header block and the
  294. validation flags, confirms that RFC 7540 allows them.
  295. """
  296. # Pseudo-header fields MUST NOT appear in trailers - RFC 7540 § 8.1.2.1
  297. if hdr_validation_flags.is_trailer and pseudo_headers:
  298. msg = f"Received pseudo-header in trailer {pseudo_headers}"
  299. raise ProtocolError(msg)
  300. # If ':status' pseudo-header is not there in a response header, reject it.
  301. # Similarly, if ':path', ':method', or ':scheme' are not there in a request
  302. # header, reject it. Additionally, if a response contains any request-only
  303. # headers or vice-versa, reject it.
  304. # Relevant RFC section: RFC 7540 § 8.1.2.4
  305. # https://tools.ietf.org/html/rfc7540#section-8.1.2.4
  306. if hdr_validation_flags.is_response_header:
  307. _assert_header_in_set(b":status", pseudo_headers)
  308. invalid_response_headers = pseudo_headers & _REQUEST_ONLY_HEADERS
  309. if invalid_response_headers:
  310. msg = f"Encountered request-only headers {invalid_response_headers}"
  311. raise ProtocolError(msg)
  312. elif (not hdr_validation_flags.is_response_header and
  313. not hdr_validation_flags.is_trailer):
  314. # This is a request, so we need to have seen :path, :method, and
  315. # :scheme.
  316. _assert_header_in_set(b":path", pseudo_headers)
  317. _assert_header_in_set(b":method", pseudo_headers)
  318. _assert_header_in_set(b":scheme", pseudo_headers)
  319. invalid_request_headers = pseudo_headers & _RESPONSE_ONLY_HEADERS
  320. if invalid_request_headers:
  321. msg = f"Encountered response-only headers {invalid_request_headers}"
  322. raise ProtocolError(msg)
  323. if method != b"CONNECT":
  324. invalid_headers = pseudo_headers & _CONNECT_REQUEST_ONLY_HEADERS
  325. if invalid_headers:
  326. msg = f"Encountered connect-request-only headers {invalid_headers!r}"
  327. raise ProtocolError(msg)
  328. def _validate_host_authority_header(headers: Iterable[Header]) -> Generator[Header, None, None]:
  329. """
  330. Given the :authority and Host headers from a request block that isn't
  331. a trailer, check that:
  332. 1. At least one of these headers is set.
  333. 2. If both headers are set, they match.
  334. :param headers: The HTTP header set.
  335. :raises: ``ProtocolError``
  336. """
  337. # We use None as a sentinel value. Iterate over the list of headers,
  338. # and record the value of these headers (if present). We don't need
  339. # to worry about receiving duplicate :authority headers, as this is
  340. # enforced by the _reject_pseudo_header_fields() pipeline.
  341. #
  342. # TODO: We should also guard against receiving duplicate Host headers,
  343. # and against sending duplicate headers.
  344. authority_header_val = None
  345. host_header_val = None
  346. for header in headers:
  347. if header[0] == b":authority":
  348. authority_header_val = header[1]
  349. elif header[0] == b"host":
  350. host_header_val = header[1]
  351. yield header
  352. # If we have not-None values for these variables, then we know we saw
  353. # the corresponding header.
  354. authority_present = (authority_header_val is not None)
  355. host_present = (host_header_val is not None)
  356. # It is an error for a request header block to contain neither
  357. # an :authority header nor a Host header.
  358. if not authority_present and not host_present:
  359. msg = "Request header block does not have an :authority or Host header."
  360. raise ProtocolError(msg)
  361. # If we receive both headers, they should definitely match.
  362. if authority_present and host_present and authority_header_val != host_header_val:
  363. msg = (
  364. "Request header block has mismatched :authority and "
  365. f"Host headers: {authority_header_val!r} / {host_header_val!r}"
  366. )
  367. raise ProtocolError(msg)
  368. def _check_host_authority_header(headers: Iterable[Header],
  369. hdr_validation_flags: HeaderValidationFlags) -> Generator[Header, None, None]:
  370. """
  371. Raises a ProtocolError if a header block arrives that does not contain an
  372. :authority or a Host header, or if a header block contains both fields,
  373. but their values do not match.
  374. """
  375. # We only expect to see :authority and Host headers on request header
  376. # blocks that aren't trailers, so skip this validation if this is a
  377. # response header or we're looking at trailer blocks.
  378. skip_validation = (
  379. hdr_validation_flags.is_response_header or
  380. hdr_validation_flags.is_trailer
  381. )
  382. if skip_validation:
  383. return (h for h in headers)
  384. return _validate_host_authority_header(headers)
  385. def _check_path_header(headers: Iterable[Header],
  386. hdr_validation_flags: HeaderValidationFlags) -> Generator[Header, None, None]:
  387. """
  388. Raise a ProtocolError if a header block arrives or is sent that contains an
  389. empty :path header.
  390. """
  391. def inner() -> Generator[Header, None, None]:
  392. for header in headers:
  393. if header[0] == b":path" and not header[1]:
  394. msg = "An empty :path header is forbidden"
  395. raise ProtocolError(msg)
  396. yield header
  397. # We only expect to see :authority and Host headers on request header
  398. # blocks that aren't trailers, so skip this validation if this is a
  399. # response header or we're looking at trailer blocks.
  400. skip_validation = (
  401. hdr_validation_flags.is_response_header or
  402. hdr_validation_flags.is_trailer
  403. )
  404. if skip_validation:
  405. return (h for h in headers)
  406. return inner()
  407. def _to_bytes(v: bytes | str) -> bytes:
  408. """
  409. Given an assumed `str` (or anything that supports `.encode()`),
  410. encodes it using utf-8 into bytes. Returns the unmodified object
  411. if it is already a `bytes` object.
  412. """
  413. return v if isinstance(v, bytes) else v.encode("utf-8")
  414. def utf8_encode_headers(headers: Iterable[HeaderWeaklyTyped]) -> list[Header]:
  415. """
  416. Given an iterable of header two-tuples, rebuilds that as a list with the
  417. header names and values encoded as utf-8 bytes. This function produces
  418. tuples that preserve the original type of the header tuple for tuple and
  419. any ``HeaderTuple``.
  420. """
  421. encoded_headers: list[Header] = []
  422. for header in headers:
  423. h = (_to_bytes(header[0]), _to_bytes(header[1]))
  424. if isinstance(header, HeaderTuple):
  425. encoded_headers.append(header.__class__(h[0], h[1]))
  426. else:
  427. encoded_headers.append(h)
  428. return encoded_headers
  429. def _lowercase_header_names(headers: Iterable[Header],
  430. hdr_validation_flags: HeaderValidationFlags | None) -> Generator[Header, None, None]:
  431. """
  432. Given an iterable of header two-tuples, rebuilds that iterable with the
  433. header names lowercased. This generator produces tuples that preserve the
  434. original type of the header tuple for tuple and any ``HeaderTuple``.
  435. """
  436. for header in headers:
  437. if isinstance(header, HeaderTuple):
  438. yield header.__class__(header[0].lower(), header[1])
  439. else:
  440. yield (header[0].lower(), header[1])
  441. def _strip_surrounding_whitespace(headers: Iterable[Header],
  442. hdr_validation_flags: HeaderValidationFlags | None) -> Generator[Header, None, None]:
  443. """
  444. Given an iterable of header two-tuples, strip both leading and trailing
  445. whitespace from both header names and header values. This generator
  446. produces tuples that preserve the original type of the header tuple for
  447. tuple and any ``HeaderTuple``.
  448. """
  449. for header in headers:
  450. if isinstance(header, HeaderTuple):
  451. yield header.__class__(header[0].strip(), header[1].strip())
  452. else:
  453. yield (header[0].strip(), header[1].strip())
  454. def _strip_connection_headers(headers: Iterable[Header],
  455. hdr_validation_flags: HeaderValidationFlags | None) -> Generator[Header, None, None]:
  456. """
  457. Strip any connection headers as per RFC7540 § 8.1.2.2.
  458. """
  459. for header in headers:
  460. if header[0] not in CONNECTION_HEADERS:
  461. yield header
  462. def _check_sent_host_authority_header(headers: Iterable[Header],
  463. hdr_validation_flags: HeaderValidationFlags) -> Generator[Header, None, None]:
  464. """
  465. Raises an InvalidHeaderBlockError if we try to send a header block
  466. that does not contain an :authority or a Host header, or if
  467. the header block contains both fields, but their values do not match.
  468. """
  469. # We only expect to see :authority and Host headers on request header
  470. # blocks that aren't trailers, so skip this validation if this is a
  471. # response header or we're looking at trailer blocks.
  472. skip_validation = (
  473. hdr_validation_flags.is_response_header or
  474. hdr_validation_flags.is_trailer
  475. )
  476. if skip_validation:
  477. return (h for h in headers)
  478. return _validate_host_authority_header(headers)
  479. def _combine_cookie_fields(headers: Iterable[Header], hdr_validation_flags: HeaderValidationFlags) -> Generator[Header, None, None]:
  480. """
  481. RFC 7540 § 8.1.2.5 allows HTTP/2 clients to split the Cookie header field,
  482. which must normally appear only once, into multiple fields for better
  483. compression. However, they MUST be joined back up again when received.
  484. This normalization step applies that transform. The side-effect is that
  485. all cookie fields now appear *last* in the header block.
  486. """
  487. # There is a problem here about header indexing. Specifically, it's
  488. # possible that all these cookies are sent with different header indexing
  489. # values. At this point it shouldn't matter too much, so we apply our own
  490. # logic and make them never-indexed.
  491. cookies: list[bytes] = []
  492. for header in headers:
  493. if header[0] == b"cookie":
  494. cookies.append(header[1])
  495. else:
  496. yield header
  497. if cookies:
  498. cookie_val = b"; ".join(cookies)
  499. yield NeverIndexedHeaderTuple(b"cookie", cookie_val)
  500. def _split_outbound_cookie_fields(headers: Iterable[Header],
  501. hdr_validation_flags: HeaderValidationFlags | None) -> Generator[Header, None, None]:
  502. """
  503. RFC 7540 § 8.1.2.5 allows for better compression efficiency,
  504. to split the Cookie header field into separate header fields
  505. We want to do it for outbound requests, as we are doing for
  506. inbound.
  507. """
  508. for header in headers:
  509. assert isinstance(header[0], bytes)
  510. assert isinstance(header[1], bytes)
  511. if header[0] == b"cookie":
  512. for cookie_val in header[1].split(b"; "):
  513. if isinstance(header, HeaderTuple):
  514. yield header.__class__(header[0], cookie_val)
  515. else:
  516. yield header[0], cookie_val
  517. else:
  518. yield header
  519. def normalize_outbound_headers(headers: Iterable[Header],
  520. hdr_validation_flags: HeaderValidationFlags | None,
  521. should_split_outbound_cookies: bool=False) -> Generator[Header, None, None]:
  522. """
  523. Normalizes a header sequence that we are about to send.
  524. :param headers: The HTTP header set.
  525. :param hdr_validation_flags: An instance of HeaderValidationFlags.
  526. :param should_split_outbound_cookies: boolean flag
  527. """
  528. headers = _lowercase_header_names(headers, hdr_validation_flags)
  529. if should_split_outbound_cookies:
  530. headers = _split_outbound_cookie_fields(headers, hdr_validation_flags)
  531. headers = _strip_surrounding_whitespace(headers, hdr_validation_flags)
  532. headers = _strip_connection_headers(headers, hdr_validation_flags)
  533. return _secure_headers(headers, hdr_validation_flags)
  534. def normalize_inbound_headers(headers: Iterable[Header],
  535. hdr_validation_flags: HeaderValidationFlags) -> Generator[Header, None, None]:
  536. """
  537. Normalizes a header sequence that we have received.
  538. :param headers: The HTTP header set.
  539. :param hdr_validation_flags: An instance of HeaderValidationFlags
  540. """
  541. return _combine_cookie_fields(headers, hdr_validation_flags)
  542. def validate_outbound_headers(headers: Iterable[Header],
  543. hdr_validation_flags: HeaderValidationFlags) -> Generator[Header, None, None]:
  544. """
  545. Validates and normalizes a header sequence that we are about to send.
  546. :param headers: The HTTP header set.
  547. :param hdr_validation_flags: An instance of HeaderValidationFlags.
  548. """
  549. headers = _reject_te(
  550. headers, hdr_validation_flags,
  551. )
  552. headers = _reject_connection_header(
  553. headers, hdr_validation_flags,
  554. )
  555. headers = _reject_pseudo_header_fields(
  556. headers, hdr_validation_flags,
  557. )
  558. headers = _check_sent_host_authority_header(
  559. headers, hdr_validation_flags,
  560. )
  561. return _check_path_header(headers, hdr_validation_flags)
  562. class SizeLimitDict(collections.OrderedDict[int, Any]):
  563. def __init__(self, *args: dict[int, int], **kwargs: Any) -> None:
  564. self._size_limit = kwargs.pop("size_limit", None)
  565. super().__init__(*args, **kwargs)
  566. self._check_size_limit()
  567. def __setitem__(self, key: int, value: Any | int) -> None:
  568. super().__setitem__(key, value)
  569. self._check_size_limit()
  570. def _check_size_limit(self) -> None:
  571. if self._size_limit is not None:
  572. while len(self) > self._size_limit:
  573. self.popitem(last=False)