lazy-wheel: be more robust with regard to Artifactory's incorrect han… · python-poetry/poetry@d8afecb (original) (raw)
`@@ -2,6 +2,7 @@
`
2
2
``
3
3
`import re
`
4
4
``
``
5
`+
from enum import IntEnum
`
5
6
`from pathlib import Path
`
6
7
`from typing import TYPE_CHECKING
`
7
8
`from typing import Any
`
`@@ -51,7 +52,10 @@ def call(
`
51
52
` ) -> None: ...
`
52
53
``
53
54
``
54
``
`-
NEGATIVE_OFFSET_AS_POSITIVE = -1
`
``
55
`+
class NegativeOffsetFailure(IntEnum):
`
``
56
`+
numbers must be negative to avoid conflicts with HTTP status codes
`
``
57
`+
as_positive = -1 # JFrog Artifactory bug (RTDEV-38572)
`
``
58
`+
one_more = -2 # JFrog Artifactory bug (one more byte than requested)
`
55
59
``
56
60
``
57
61
`def build_head_response(
`
`@@ -68,19 +72,26 @@ def build_partial_response(
`
68
72
`wheel_bytes: bytes,
`
69
73
`response_headers: dict[str, Any],
`
70
74
`*,
`
71
``
`-
negative_offset_as_positive: bool = False,
`
``
75
`+
negative_offset_failure: NegativeOffsetFailure | None = None,
`
72
76
`) -> HTTPrettyResponse:
`
73
77
`status_code = 206
`
74
78
`response_headers["Accept-Ranges"] = "bytes"
`
75
79
`total_length = len(wheel_bytes)
`
76
80
`if rng.startswith("-"):
`
77
81
`# negative offset
`
78
82
`offset = int(rng)
`
79
``
`-
if negative_offset_as_positive:
`
``
83
`+
if negative_offset_failure == NegativeOffsetFailure.as_positive:
`
80
84
`# some servers interpret a negative offset like "-10" as "0-10"
`
81
85
`start = 0
`
82
86
`end = min(-offset, total_length - 1)
`
83
87
`body = wheel_bytes[start : end + 1]
`
``
88
`+
elif negative_offset_failure == NegativeOffsetFailure.one_more:
`
``
89
`+
https://github.com/python-poetry/poetry/issues/9056#issuecomment-1973273721
`
``
90
`+
offset -= 1 # one more byte
`
``
91
`+
start = total_length + offset # negative start of content range possible!
`
``
92
`+
end = total_length - 1
`
``
93
`+
body = wheel_bytes[offset:]
`
``
94
`+
response_headers["Content-Length"] = -offset # just wrong...
`
84
95
`else:
`
85
96
`start = total_length + offset
`
86
97
`if start < 0:
`
`@@ -131,12 +142,14 @@ def handle_request(
`
131
142
``
132
143
`rng = request.headers.get("Range", "=").split("=")[1]
`
133
144
``
134
``
`-
negative_offset_as_positive = False
`
``
145
`+
negative_offset_failure = None
`
135
146
`if negative_offset_error and rng.startswith("-"):
`
136
147
`if negative_offset_error[0] == codes.requested_range_not_satisfiable:
`
137
148
`response_headers["Content-Range"] = f"bytes */{len(wheel_bytes)}"
`
138
``
`-
if negative_offset_error[0] == NEGATIVE_OFFSET_AS_POSITIVE:
`
139
``
`-
negative_offset_as_positive = True
`
``
149
`+
if negative_offset_error[0] == NegativeOffsetFailure.as_positive:
`
``
150
`+
negative_offset_failure = NegativeOffsetFailure.as_positive
`
``
151
`+
elif negative_offset_error[0] == NegativeOffsetFailure.one_more:
`
``
152
`+
negative_offset_failure = NegativeOffsetFailure.one_more
`
140
153
`else:
`
141
154
`return (
`
142
155
`negative_offset_error[0],
`
`@@ -148,7 +161,7 @@ def handle_request(
`
148
161
`rng,
`
149
162
`wheel_bytes,
`
150
163
`response_headers,
`
151
``
`-
negative_offset_as_positive=negative_offset_as_positive,
`
``
164
`+
negative_offset_failure=negative_offset_failure,
`
152
165
` )
`
153
166
``
154
167
`status_code = 200
`
`@@ -219,7 +232,8 @@ def _assertion(
`
219
232
` (codes.requested_range_not_satisfiable, b"Requested range not satisfiable"),
`
220
233
` (codes.internal_server_error, b"Internal server error"), # GAR
`
221
234
` (codes.not_implemented, b"Unsupported client range"), # PyPI
`
222
``
`-
(NEGATIVE_OFFSET_AS_POSITIVE, b"handle negative offset as positive"),
`
``
235
`+
(NegativeOffsetFailure.as_positive, b"handle negative offset as positive"),
`
``
236
`+
(NegativeOffsetFailure.one_more, b"one more byte than requested"),
`
223
237
` ],
`
224
238
`)
`
225
239
`def test_metadata_from_wheel_url(
`
`@@ -236,10 +250,11 @@ def test_metadata_from_wheel_url(
`
236
250
`# 3.-5. see negative offsets 1.-3.
`
237
251
`expected_requests = 3
`
238
252
`if negative_offset_error:
`
239
``
`-
if negative_offset_error[0] in (
`
``
253
`+
if negative_offset_error[0] in {
`
240
254
`codes.requested_range_not_satisfiable,
`
241
``
`-
NEGATIVE_OFFSET_AS_POSITIVE,
`
242
``
`-
):
`
``
255
`+
NegativeOffsetFailure.as_positive,
`
``
256
`+
NegativeOffsetFailure.one_more,
`
``
257
`+
}:
`
243
258
`expected_requests += 1
`
244
259
`else:
`
245
260
`expected_requests += 2
`
`@@ -299,17 +314,25 @@ def test_metadata_from_wheel_url_with_redirect(
`
299
314
` )
`
300
315
``
301
316
``
302
``
`-
@pytest.mark.parametrize("negative_offset_as_positive", [False, True])
`
``
317
`+
@pytest.mark.parametrize(
`
``
318
`+
("negative_offset_failure", "expected_requests"),
`
``
319
`+
[
`
``
320
`+
(None, 1),
`
``
321
`+
(NegativeOffsetFailure.as_positive, 1),
`
``
322
`+
(NegativeOffsetFailure.one_more, 2),
`
``
323
`+
],
`
``
324
`+
)
`
303
325
`def test_metadata_from_wheel_url_smaller_than_initial_chunk_size(
`
304
326
`http: type[httpretty.httpretty],
`
305
327
`handle_request_factory: RequestCallbackFactory,
`
306
``
`-
negative_offset_as_positive: bool,
`
``
328
`+
negative_offset_failure: NegativeOffsetFailure | None,
`
``
329
`+
expected_requests: int,
`
307
330
`) -> None:
`
308
``
`-
domain = f"tiny-wheel-{str(negative_offset_as_positive).casefold()}.com"
`
``
331
`+
domain = f"tiny-wheel-{str(negative_offset_failure).casefold()}.com"
`
309
332
`uri_regex = re.compile(f"^https://{domain}/.*$")
`
310
333
`request_callback = handle_request_factory(
`
311
334
`negative_offset_error=(
`
312
``
`-
(NEGATIVE_OFFSET_AS_POSITIVE, b"") if negative_offset_as_positive else None
`
``
335
`+
(negative_offset_failure, b"") if negative_offset_failure else None
`
313
336
` )
`
314
337
` )
`
315
338
`http.register_uri(http.GET, uri_regex, body=request_callback)
`
`@@ -324,10 +347,8 @@ def test_metadata_from_wheel_url_smaller_than_initial_chunk_size(
`
324
347
`assert metadata["author"] == "Jason R. Coombs"
`
325
348
`assert len(metadata["requires_dist"]) == 12
`
326
349
``
327
``
`-
only one request because server gives a normal response with the entire wheel
`
328
``
`-
except for when server interprets negative offset as positive
`
329
350
`latest_requests = http.latest_requests()
`
330
``
`-
assert len(latest_requests) == 1
`
``
351
`+
assert len(latest_requests) == expected_requests
`
331
352
``
332
353
``
333
354
`@pytest.mark.parametrize("accept_ranges", [None, "none"])
`