Skip to content

Commit 58c1ffe

Browse files
committed
Added headers to response to specify file size for file download endpoints
1 parent 258d825 commit 58c1ffe

File tree

1 file changed

+84
-39
lines changed

1 file changed

+84
-39
lines changed

src/murfey/server/api/bootstrap.py

Lines changed: 84 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
from __future__ import annotations
1616

1717
import functools
18-
import io
1918
import json
2019
import logging
2120
import random
2221
import re
22+
from io import BytesIO
2323
from urllib.parse import quote
2424

2525
import packaging.version
@@ -170,11 +170,19 @@ def get_cygwin_setup():
170170
"""
171171
filename = "setup-x86_64.exe"
172172
response = http_session.get(f"https://www.cygwin.com/{filename}")
173+
174+
# Construct headers to return with response
175+
headers: dict[str, str] = {
176+
"Content-Disposition": f"attachment; filename=cygwin-{filename}"
177+
}
178+
if response.headers.get("Content-Length"):
179+
headers["Content-Length"] = response.headers["Content-Length"]
180+
173181
return StreamingResponse(
174182
content=response.iter_content(chunk_size=8192),
175-
media_type=response.headers.get("Content-Type"),
176-
headers={"Content-Disposition": f"attachment; filename=cygwin-{filename}"},
177183
status_code=response.status_code,
184+
headers=headers,
185+
media_type=response.headers.get("Content-Type"),
178186
)
179187

180188

@@ -251,10 +259,16 @@ def parse_cygwin_request(
251259

252260
logger.info(f"Forwarding Cygwin download request to {_sanitise_str(url)}")
253261
response = http_session.get(url)
262+
263+
headers: dict[str, str] = {}
264+
if response.headers.get("Content-Length"):
265+
headers["Content-Length"] = response.headers["Content-Length"]
266+
254267
return StreamingResponse(
255268
content=response.iter_content(chunk_size=8192),
256-
media_type=response.headers.get("Content-Type"),
257269
status_code=response.status_code,
270+
headers=headers,
271+
media_type=response.headers.get("Content-Type"),
258272
)
259273

260274

@@ -324,10 +338,16 @@ def get_msys2_setup(
324338
raise ValueError(f"{setup_file!r} is not a valid executable")
325339

326340
response = http_session.get(f"{msys2_url}/distrib/{setup_file}")
341+
342+
headers: dict[str, str] = {}
343+
if response.headers.get("Content-Length"):
344+
headers["Content-Length"] = response.headers["Content-Length"]
345+
327346
return StreamingResponse(
328347
content=response.iter_content(chunk_size=8192),
329-
media_type=response.headers.get("Content-Type"),
330348
status_code=response.status_code,
349+
headers=headers,
350+
media_type=response.headers.get("Content-Type"),
331351
)
332352

333353

@@ -476,10 +496,16 @@ def get_msys2_package_file(
476496
response = http_session.get(package_url)
477497
if response.status_code != 200:
478498
raise HTTPException(status_code=response.status_code)
499+
500+
headers: dict[str, str] = {}
501+
if response.headers.get("Content-Length"):
502+
headers["Content-Length"] = response.headers["Content-Length"]
503+
479504
return StreamingResponse(
480505
content=response.iter_content(chunk_size=8192),
481-
media_type=response.headers.get("Content-Type"),
482506
status_code=response.status_code,
507+
headers=headers,
508+
media_type=response.headers.get("Content-Type"),
483509
)
484510

485511

@@ -540,12 +566,18 @@ def get_cargo_config(request: Request):
540566
"",
541567
]
542568
)
543-
config_bytes = io.BytesIO(config_data.encode("utf-8"))
569+
config_bytes = config_data.encode("utf-8")
570+
571+
headers: dict[str, str] = {
572+
"Content-Disposition": "attachment; filename=config.toml",
573+
"Content-Length": str(len(config_bytes)),
574+
}
544575

545576
return StreamingResponse(
546-
config_bytes,
577+
BytesIO(config_bytes),
578+
status_code=200,
579+
headers=headers,
547580
media_type="application/toml+json",
548-
headers={"Content-Disposition": "attachment; filename=config.toml"},
549581
)
550582

551583

@@ -565,8 +597,8 @@ def get_index_page():
565597
raise HTTPException(status_code=response.status_code)
566598
return Response(
567599
content=response.content,
568-
media_type=response.headers.get("Content-Type"),
569600
status_code=response.status_code,
601+
media_type=response.headers.get("Content-Type"),
570602
)
571603

572604

@@ -604,12 +636,18 @@ def get_index_config(request: Request):
604636

605637
# Save it as a JSON and return it as part of the response
606638
json_data = json.dumps(config, indent=4)
607-
json_bytes = io.BytesIO(json_data.encode("utf-8"))
639+
json_bytes = json_data.encode("utf-8")
640+
641+
headers: dict[str, str] = {
642+
"Content-Disposition": "attachment; filename=config.json",
643+
"Content-Length": str(len(json_bytes)),
644+
}
608645

609646
return StreamingResponse(
610-
json_bytes,
647+
BytesIO(json_bytes),
648+
status_code=200,
649+
headers=headers,
611650
media_type="application/json",
612-
headers={"Content-Disposition": "attachment; filename=config.json"},
613651
)
614652

615653

@@ -651,7 +689,8 @@ def get_index_package_metadata(
651689
raise HTTPException(status_code=response.status_code)
652690
return StreamingResponse(
653691
response.iter_content(chunk_size=8192),
654-
media_type="application/json",
692+
status_code=response.status_code,
693+
media_type=response.headers.get("Content-Type"),
655694
)
656695

657696

@@ -682,7 +721,8 @@ def get_index_package_metadata_for_short_package_names(
682721
raise HTTPException(status_code=response.status_code)
683722
return StreamingResponse(
684723
response.iter_content(chunk_size=8192),
685-
media_type="application/json",
724+
status_code=response.status_code,
725+
media_type=response.headers.get("Content-Type"),
686726
)
687727

688728

@@ -711,16 +751,16 @@ def get_rust_package_download(
711751
file_name = f"{package}-{version}.crate" # Construct file name to save package as
712752
if response.status_code != 200:
713753
raise HTTPException(status_code=response.status_code)
754+
755+
headers = {"Content-Disposition": f'attachment; filename="{file_name}"'}
756+
if response.headers.get("Content-Length"):
757+
headers["Content-Length"] = response.headers["Content-Length"]
758+
714759
return StreamingResponse(
715760
content=response.iter_content(chunk_size=8192),
716-
headers={
717-
"Content-Disposition": f'attachment; filename="{file_name}"',
718-
"Content-Type": response.headers.get(
719-
"Content-Type", "application/octet-stream"
720-
),
721-
"Content-Length": response.headers.get("Content-Length"),
722-
},
723761
status_code=response.status_code,
762+
headers=headers,
763+
media_type=response.headers.get("Content-Type", "application/octet-stream"),
724764
)
725765

726766

@@ -844,16 +884,16 @@ def get_rust_api_package_download(
844884
file_name = f"{package}-{version}.crate" # Construct crate name to save as
845885
if response.status_code != 200:
846886
raise HTTPException(status_code=response.status_code)
887+
888+
headers = {"Content-Disposition": f'attachment; filename="{file_name}"'}
889+
if response.headers.get("Content-Length"):
890+
headers["Content-Length"] = response.headers["Content-Length"]
891+
847892
return StreamingResponse(
848893
content=response.iter_content(chunk_size=8192),
849-
headers={
850-
"Content-Disposition": f'attachment; filename="{file_name}"',
851-
"Content-Type": response.headers.get(
852-
"Content-Type", "application/octet-stream"
853-
),
854-
"Content-Length": response.headers.get("Content-Length"),
855-
},
856894
status_code=response.status_code,
895+
headers=headers,
896+
media_type=response.headers.get("Content-Type", "application/octet-stream"),
857897
)
858898

859899

@@ -892,16 +932,16 @@ def get_rust_package_crate(
892932
response = http_session.get(url)
893933
if response.status_code != 200:
894934
raise HTTPException(status_code=response.status_code)
935+
936+
headers = {"Content-Disposition": f'attachment; filename="{crate}"'}
937+
if response.headers.get("Content-Length"):
938+
headers["Content-Length"] = response.headers["Content-Length"]
939+
895940
return StreamingResponse(
896-
content=response.iter_content(),
897-
headers={
898-
"Content-Disposition": f'attachment; filename="{crate}"',
899-
"Content-Type": response.headers.get(
900-
"Content-Type", "application/octet-stream"
901-
),
902-
"Content-Length": response.headers.get("Content-Length"),
903-
},
941+
content=response.iter_content(chunk_size=8192),
904942
status_code=response.status_code,
943+
headers=headers,
944+
media_type=response.headers.get("Content-Type", "application/octet-stream"),
905945
)
906946

907947

@@ -997,8 +1037,8 @@ def _rewrite_pypi_url(match):
9971037

9981038
return Response(
9991039
content=content_new,
1000-
media_type=full_path_response.headers.get("Content-Type"),
10011040
status_code=full_path_response.status_code,
1041+
media_type=full_path_response.headers.get("Content-Type"),
10021042
)
10031043

10041044

@@ -1066,10 +1106,15 @@ def _expose_wheel_metadata(response_bytes: bytes) -> bytes:
10661106
original_url = selected_package_link.group(1)
10671107
response = http_session.get(original_url)
10681108

1109+
# Construct headers to return with response
1110+
headers: dict[str, str] = {}
1111+
if response.headers.get("Content-Length"):
1112+
headers["Content-Lengh"] = response.headers["Content-Length"]
10691113
return StreamingResponse(
10701114
content=response.iter_content(chunk_size=8192),
1071-
media_type=response.headers.get("Content-Type"),
10721115
status_code=response.status_code,
1116+
headers=headers,
1117+
media_type=response.headers.get("Content-Type"),
10731118
)
10741119

10751120

0 commit comments

Comments
 (0)