codekingpro/portable-devtools
114k
1from __future__ import annotations2 3import http.client as httplib4from email.errors import MultipartInvariantViolationDefect, StartBoundaryNotFoundDefect5 6from ..exceptions import HeaderParsingError7 8 9def is_fp_closed(obj: object) -> bool:10 """11 Checks whether a given file-like object is closed.12 13 :param obj:14 The file-like object to check.15 """16 17 try:18 # Check `isclosed()` first, in case Python3 doesn't set `closed`.19 # GH Issue #92820 return obj.isclosed() # type: ignore[no-any-return, attr-defined]21 except AttributeError:22 pass23 24 try:25 # Check via the official file-like-object way.26 return obj.closed # type: ignore[no-any-return, attr-defined]27 except AttributeError:28 pass29 30 try:31 # Check if the object is a container for another file-like object that32 # gets released on exhaustion (e.g. HTTPResponse).33 return obj.fp is None # type: ignore[attr-defined]34 except AttributeError:35 pass36 37 raise ValueError("Unable to determine whether fp is closed.")38 39 40def assert_header_parsing(headers: httplib.HTTPMessage) -> None:41 """42 Asserts whether all headers have been successfully parsed.43 Extracts encountered errors from the result of parsing headers.44 45 Only works on Python 3.46 47 :param http.client.HTTPMessage headers: Headers to verify.48 49 :raises urllib3.exceptions.HeaderParsingError:50 If parsing errors are found.51 """52 53 # This will fail silently if we pass in the wrong kind of parameter.54 # To make debugging easier add an explicit check.55 if not isinstance(headers, httplib.HTTPMessage):56 raise TypeError(f"expected httplib.Message, got {type(headers)}.")57 58 unparsed_data = None59 60 # get_payload is actually email.message.Message.get_payload;61 # we're only interested in the result if it's not a multipart message62 if not headers.is_multipart():63 payload = headers.get_payload()64 65 if isinstance(payload, (bytes, str)):66 unparsed_data = payload67 68 # httplib is assuming a response body is available69 # when parsing headers even when httplib only sends70 # header data to parse_headers() This results in71 # defects on multipart responses in particular.72 # See: https://github.com/urllib3/urllib3/issues/80073 74 # So we ignore the following defects:75 # - StartBoundaryNotFoundDefect:76 # The claimed start boundary was never found.77 # - MultipartInvariantViolationDefect:78 # A message claimed to be a multipart but no subparts were found.79 defects = [80 defect81 for defect in headers.defects82 if not isinstance(83 defect, (StartBoundaryNotFoundDefect, MultipartInvariantViolationDefect)84 )85 ]86 87 if defects or unparsed_data:88 raise HeaderParsingError(defects=defects, unparsed_data=unparsed_data)89 90 91def is_response_to_head(response: httplib.HTTPResponse) -> bool:92 """93 Checks whether the request of a response has been a HEAD-request.94 95 :param http.client.HTTPResponse response:96 Response to check if the originating request97 used 'HEAD' as a method.98 """99 # FIXME: Can we do this somehow without accessing private httplib _method?100 method_str = response._method # type: str # type: ignore[attr-defined]101 return method_str.upper() == "HEAD"102 