1from __future__ import absolute_import
2
3from email.errors import MultipartInvariantViolationDefect, StartBoundaryNotFoundDefect
4
5from ..exceptions import HeaderParsingError
6from ..packages.six.moves import http_client as httplib
7
8
9def is_fp_closed(obj):
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 #928
20        return obj.isclosed()
21    except AttributeError:
22        pass
23
24    try:
25        # Check via the official file-like-object way.
26        return obj.closed
27    except AttributeError:
28        pass
29
30    try:
31        # Check if the object is a container for another file-like object that
32        # gets released on exhaustion (e.g. HTTPResponse).
33        return obj.fp is None
34    except AttributeError:
35        pass
36
37    raise ValueError("Unable to determine whether fp is closed.")
38
39
40def assert_header_parsing(headers):
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("expected httplib.Message, got {0}.".format(type(headers)))
57
58    defects = getattr(headers, "defects", None)
59    get_payload = getattr(headers, "get_payload", None)
60
61    unparsed_data = None
62    if get_payload:
63        # get_payload is actually email.message.Message.get_payload;
64        # we're only interested in the result if it's not a multipart message
65        if not headers.is_multipart():
66            payload = get_payload()
67
68            if isinstance(payload, (bytes, str)):
69                unparsed_data = payload
70    if defects:
71        # httplib is assuming a response body is available
72        # when parsing headers even when httplib only sends
73        # header data to parse_headers() This results in
74        # defects on multipart responses in particular.
75        # See: https://github.com/urllib3/urllib3/issues/800
76
77        # So we ignore the following defects:
78        # - StartBoundaryNotFoundDefect:
79        #     The claimed start boundary was never found.
80        # - MultipartInvariantViolationDefect:
81        #     A message claimed to be a multipart but no subparts were found.
82        defects = [
83            defect
84            for defect in defects
85            if not isinstance(
86                defect, (StartBoundaryNotFoundDefect, MultipartInvariantViolationDefect)
87            )
88        ]
89
90    if defects or unparsed_data:
91        raise HeaderParsingError(defects=defects, unparsed_data=unparsed_data)
92
93
94def is_response_to_head(response):
95    """
96    Checks whether the request of a response has been a HEAD-request.
97    Handles the quirks of AppEngine.
98
99    :param http.client.HTTPResponse response:
100        Response to check if the originating request
101        used 'HEAD' as a method.
102    """
103    # FIXME: Can we do this somehow without accessing private httplib _method?
104    method = response._method
105    if isinstance(method, int):  # Platform-specific: Appengine
106        return method == 3
107    return method.upper() == "HEAD"
108