]>
jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/pip/_vendor/cachecontrol/serialize.py
1 # SPDX-FileCopyrightText: 2015 Eric Larson
3 # SPDX-License-Identifier: Apache-2.0
10 from pip
._vendor
import msgpack
11 from pip
._vendor
.requests
.structures
import CaseInsensitiveDict
13 from .compat
import HTTPResponse
, pickle
, text_type
16 def _b64_decode_bytes(b
):
17 return base64
.b64decode(b
.encode("ascii"))
20 def _b64_decode_str(s
):
21 return _b64_decode_bytes(s
).decode("utf8")
24 _default_body_read
= object()
27 class Serializer(object):
28 def dumps(self
, request
, response
, body
=None):
29 response_headers
= CaseInsensitiveDict(response
.headers
)
32 # When a body isn't passed in, we'll read the response. We
33 # also update the response with a new file handler to be
34 # sure it acts as though it was never read.
35 body
= response
.read(decode_content
=False)
36 response
._fp
= io
.BytesIO(body
)
38 # NOTE: This is all a bit weird, but it's really important that on
39 # Python 2.x these objects are unicode and not str, even when
40 # they contain only ascii. The problem here is that msgpack
41 # understands the difference between unicode and bytes and we
42 # have it set to differentiate between them, however Python 2
43 # doesn't know the difference. Forcing these to unicode will be
44 # enough to have msgpack know the difference.
47 u
"body": body
, # Empty bytestring if body is stored separately
49 (text_type(k
), text_type(v
)) for k
, v
in response
.headers
.items()
51 u
"status": response
.status
,
52 u
"version": response
.version
,
53 u
"reason": text_type(response
.reason
),
54 u
"strict": response
.strict
,
55 u
"decode_content": response
.decode_content
,
59 # Construct our vary headers
61 if u
"vary" in response_headers
:
62 varied_headers
= response_headers
[u
"vary"].split(",")
63 for header
in varied_headers
:
64 header
= text_type(header
).strip()
65 header_value
= request
.headers
.get(header
, None)
66 if header_value
is not None:
67 header_value
= text_type(header_value
)
68 data
[u
"vary"][header
] = header_value
70 return b
",".join([b
"cc=4", msgpack
.dumps(data
, use_bin_type
=True)])
72 def loads(self
, request
, data
, body_file
=None):
73 # Short circuit if we've been given an empty set of data
77 # Determine what version of the serializer the data was serialized
80 ver
, data
= data
.split(b
",", 1)
84 # Make sure that our "ver" is actually a version and isn't a false
85 # positive from a , being in the data stream.
90 # Get the version number out of the cc=N
91 ver
= ver
.split(b
"=", 1)[-1].decode("ascii")
93 # Dispatch to the actual load method for the given version
95 return getattr(self
, "_loads_v{}".format(ver
))(request
, data
, body_file
)
97 except AttributeError:
98 # This is a version we don't have a loads function for, so we'll
99 # just treat it as a miss and return None
102 def prepare_response(self
, request
, cached
, body_file
=None):
103 """Verify our vary headers match and construct a real urllib3
106 # Special case the '*' Vary value as it means we cannot actually
107 # determine if the cached response is suitable for this request.
108 # This case is also handled in the controller code when creating
109 # a cache entry, but is left here for backwards compatibility.
110 if "*" in cached
.get("vary", {}):
113 # Ensure that the Vary headers for the cached response match our
115 for header
, value
in cached
.get("vary", {}).items():
116 if request
.headers
.get(header
, None) != value
:
119 body_raw
= cached
["response"].pop("body")
121 headers
= CaseInsensitiveDict(data
=cached
["response"]["headers"])
122 if headers
.get("transfer-encoding", "") == "chunked":
123 headers
.pop("transfer-encoding")
125 cached
["response"]["headers"] = headers
128 if body_file
is None:
129 body
= io
.BytesIO(body_raw
)
133 # This can happen if cachecontrol serialized to v1 format (pickle)
134 # using Python 2. A Python 2 str(byte string) will be unpickled as
135 # a Python 3 str (unicode string), which will cause the above to
138 # TypeError: 'str' does not support the buffer interface
139 body
= io
.BytesIO(body_raw
.encode("utf8"))
141 return HTTPResponse(body
=body
, preload_content
=False, **cached
["response"])
143 def _loads_v0(self
, request
, data
, body_file
=None):
144 # The original legacy cache data. This doesn't contain enough
145 # information to construct everything we need, so we'll treat this as
149 def _loads_v1(self
, request
, data
, body_file
=None):
151 cached
= pickle
.loads(data
)
155 return self
.prepare_response(request
, cached
, body_file
)
157 def _loads_v2(self
, request
, data
, body_file
=None):
158 assert body_file
is None
160 cached
= json
.loads(zlib
.decompress(data
).decode("utf8"))
161 except (ValueError, zlib
.error
):
164 # We need to decode the items that we've base64 encoded
165 cached
["response"]["body"] = _b64_decode_bytes(cached
["response"]["body"])
166 cached
["response"]["headers"] = dict(
167 (_b64_decode_str(k
), _b64_decode_str(v
))
168 for k
, v
in cached
["response"]["headers"].items()
170 cached
["response"]["reason"] = _b64_decode_str(cached
["response"]["reason"])
171 cached
["vary"] = dict(
172 (_b64_decode_str(k
), _b64_decode_str(v
) if v
is not None else v
)
173 for k
, v
in cached
["vary"].items()
176 return self
.prepare_response(request
, cached
, body_file
)
178 def _loads_v3(self
, request
, data
, body_file
):
179 # Due to Python 2 encoding issues, it's impossible to know for sure
180 # exactly how to load v3 entries, thus we'll treat these as a miss so
181 # that they get rewritten out as v4 entries.
184 def _loads_v4(self
, request
, data
, body_file
=None):
186 cached
= msgpack
.loads(data
, raw
=False)
190 return self
.prepare_response(request
, cached
, body_file
)