Skip to content

Commit 4dc3c57

Browse files
committed
implement a /digest/{project_name}/{sha256,blake2b_256}:{file_digest} endpoint
This is a sketch of an implementation for #23, mostly to consider the ergonomics.
1 parent de7f427 commit 4dc3c57

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

Diff for: conveyor/config.py

+21-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from aiohttp.web_middlewares import normalize_path_middleware
2020
from aiobotocore.session import get_session as aiobotocore_get_session
2121

22-
from .views import not_found, redirect, health, documentation, documentation_top, index
22+
from .views import not_found, redirect, health, documentation, documentation_top, index, content_sha256, content_blake2b_256
2323
from .tasks import redirects_refresh_task
2424

2525

@@ -81,6 +81,26 @@ def _cached_aiobotocore_session():
8181
"/packages/{python_version}/{project_l}/{project_name}/{filename}",
8282
redirect,
8383
)
84+
app.router.add_route(
85+
"GET",
86+
"/digest/{project_name}/sha256:{file_sha256}",
87+
content_sha256,
88+
)
89+
app.router.add_route(
90+
"HEAD",
91+
"/digest/{project_name}/sha256:{file_sha256}",
92+
content_sha256,
93+
)
94+
app.router.add_route(
95+
"GET",
96+
"/digest/{project_name}/blake2b_256:{file_blake2b_256}",
97+
content_blake2b_256,
98+
)
99+
app.router.add_route(
100+
"HEAD",
101+
"/digest/{project_name}/blake2b_256:{file_blake2b_256}",
102+
content_blake2b_256,
103+
)
84104
app.router.add_route(
85105
"GET",
86106
"/packages/{tail:.*}",

Diff for: conveyor/views.py

+45
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,51 @@ async def _normalize_filename(filename):
7474
return filename
7575

7676

77+
async def content_by_hash(request, digest=None, file_digest=None):
78+
project_name = request.match_info["project_name"]
79+
json_url = urllib.parse.urljoin(
80+
request.app["settings"]["endpoint"],
81+
"/pypi/{}/json".format(project_name),
82+
)
83+
84+
async with request.app["http.session"].get(json_url) as resp:
85+
if 400 <= resp.status < 500:
86+
return web.Response(status=resp.status)
87+
elif 500 <= resp.status < 600:
88+
return web.Response(status=503)
89+
90+
# It shouldn't be possible to get a status code other than 200 here.
91+
assert resp.status == 200
92+
93+
# Get the JSON data from our request.
94+
data = await resp.json()
95+
96+
# Look at all of the files listed in the JSON response, and see if one
97+
# matches our filename and Python version. If we find one, then return a
98+
# 302 redirect to that URL.
99+
for release in data.get("releases", {}).values():
100+
for file_ in release:
101+
if file_['digests'][digest] == file_digest:
102+
return web.Response(
103+
status=302,
104+
headers={
105+
"Location": file_["url"],
106+
"Cache-Control": "max-age=604800, public",
107+
},
108+
)
109+
110+
# If we've gotten to this point, it means that we couldn't locate an url
111+
# to redirect to so we'll jsut 404.
112+
return web.Response(status=404, headers={"Reason": "no file found"})
113+
114+
115+
async def content_sha256(request):
116+
return await content_by_hash(request, digest="sha256", file_digest=request.match_info["file_sha256"])
117+
118+
async def content_blake2b_256(request):
119+
return await content_by_hash(request, digest="blake2b_256", file_digest=request.match_info["file_blake2b_256"])
120+
121+
77122
async def redirect(request):
78123
python_version = request.match_info["python_version"]
79124
project_l = request.match_info["project_l"]

0 commit comments

Comments
 (0)