Skip to content

Commit fb07b5c

Browse files
igorp-collaboranejch
authored andcommitted
feat(api): Add argument that appends extra HTTP headers to a request
Currently the only way to manipulate the headers for a request is to use `Gitlab.headers` attribute. However, this makes it very concurrently unsafe because the `Gitlab` object can be shared between multiple requests at the same time. Instead add a new keyword argument `extra_headers` which will update the headers dictionary with new values just before the request is sent. For example, this can be used to download a part of a artifacts file using the `Range` header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests Signed-off-by: Igor Ponomarev <igor.ponomarev@collabora.com>
1 parent e4673d8 commit fb07b5c

File tree

4 files changed

+69
-0
lines changed

4 files changed

+69
-0
lines changed

docs/api-usage-advanced.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,20 @@ on your own, such as for nested API responses and ``Union`` return types. For ex
211211
212212
if TYPE_CHECKING:
213213
assert isinstance(license["plan"], str)
214+
215+
Per request HTTP headers override
216+
---------------------------------
217+
218+
The ``extra_headers`` keyword argument can be used to add and override
219+
the HTTP headers for a specific request. For example, it can be used do add ``Range``
220+
header to download a part of artifacts archive:
221+
222+
.. code-block:: python
223+
224+
import gitlab
225+
226+
gl = gitlab.Gitlab(url, token)
227+
project = gl.projects.get(1)
228+
job = project.jobs.get(123)
229+
230+
artifacts = job.artifacts(extra_headers={"Range": "bytes=0-9"})

gitlab/client.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,7 @@ def http_request(
654654
obey_rate_limit: bool = True,
655655
retry_transient_errors: Optional[bool] = None,
656656
max_retries: int = 10,
657+
extra_headers: Optional[Dict[str, Any]] = None,
657658
**kwargs: Any,
658659
) -> requests.Response:
659660
"""Make an HTTP request to the Gitlab server.
@@ -675,6 +676,7 @@ def http_request(
675676
or 52x responses. Defaults to False.
676677
max_retries: Max retries after 429 or transient errors,
677678
set to -1 to retry forever. Defaults to 10.
679+
extra_headers: Add and override HTTP headers for the request.
678680
**kwargs: Extra options to send to the server (e.g. sudo)
679681
680682
Returns:
@@ -721,6 +723,9 @@ def http_request(
721723
send_data = self._backend.prepare_send_data(files, post_data, raw)
722724
opts["headers"]["Content-type"] = send_data.content_type
723725

726+
if extra_headers is not None:
727+
opts["headers"].update(extra_headers)
728+
724729
retry = utils.Retry(
725730
max_retries=max_retries,
726731
obey_rate_limit=obey_rate_limit,

tests/unit/objects/test_job_artifacts.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,20 @@ def resp_project_artifacts_delete():
3535
yield rsps
3636

3737

38+
@pytest.fixture
39+
def resp_job_artifact_bytes_range(binary_content):
40+
with responses.RequestsMock() as rsps:
41+
rsps.add(
42+
method=responses.GET,
43+
url="http://localhost/api/v4/projects/1/jobs/123/artifacts",
44+
body=binary_content[:10],
45+
content_type="application/octet-stream",
46+
status=206,
47+
match=[responses.matchers.header_matcher({"Range": "bytes=0-9"})],
48+
)
49+
yield rsps
50+
51+
3852
def test_project_artifacts_delete(gl, resp_project_artifacts_delete):
3953
project = gl.projects.get(1, lazy=True)
4054
project.artifacts.delete()
@@ -46,3 +60,13 @@ def test_project_artifacts_download_by_ref_name(
4660
project = gl.projects.get(1, lazy=True)
4761
artifacts = project.artifacts.download(ref_name=ref_name, job=job)
4862
assert artifacts == binary_content
63+
64+
65+
def test_job_artifact_download_bytes_range(
66+
gl, binary_content, resp_job_artifact_bytes_range
67+
):
68+
project = gl.projects.get(1, lazy=True)
69+
job = project.jobs.get(123, lazy=True)
70+
71+
artifacts = job.artifacts(extra_headers={"Range": "bytes=0-9"})
72+
assert len(artifacts) == 10

tests/unit/test_gitlab_http_methods.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,29 @@ def request_callback(request):
117117
assert len(responses.calls) == calls_before_success
118118

119119

120+
@responses.activate
121+
def test_http_request_extra_headers(gl):
122+
path = "/projects/123/jobs/123456"
123+
url = "http://localhost/api/v4" + path
124+
125+
range_headers = {"Range": "bytes=0-99"}
126+
127+
responses.add(
128+
method=responses.GET,
129+
url=url,
130+
body=b"a" * 100,
131+
status=206,
132+
content_type="application/octet-stream",
133+
match=helpers.MATCH_EMPTY_QUERY_PARAMS
134+
+ [responses.matchers.header_matcher(range_headers)],
135+
)
136+
137+
http_r = gl.http_request("get", path, extra_headers=range_headers)
138+
139+
assert http_r.status_code == 206
140+
assert len(http_r.content) == 100
141+
142+
120143
@responses.activate
121144
@pytest.mark.parametrize(
122145
"exception",

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy