Skip to content

Commit a31d4a4

Browse files
Add a map instead of if
1 parent dabbb54 commit a31d4a4

File tree

2 files changed

+56
-10
lines changed

2 files changed

+56
-10
lines changed

conf/nginx.conf.template

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,19 @@ http {
154154
#
155155
include /etc/nginx/mimetypes.map;
156156

157+
# Determine the backend for /builds/ rewrites.
158+
# CLI clients (no "Mozilla" in User-Agent) are redirected to a presigned S3
159+
# URL (/redirect/). Browser clients and HEAD requests always proxy through
160+
# nginx (/download/).
161+
# The map key combines method and User-Agent so HEAD takes priority over the
162+
# UA check (first matching regex wins).
163+
#
164+
map "$request_method:$http_user_agent" $builds_backend {
165+
~^HEAD: /download/; # HEAD wins regardless of UA
166+
~*:.*mozilla /download/; # Browser UA → proxy through nginx
167+
default /redirect/; # CLI client → presigned S3 redirect
168+
}
169+
157170
server {
158171
# Use plain HTTP, as we are on a private network.
159172
#
@@ -251,15 +264,8 @@ http {
251264
# URL and download directly from S3. Browser clients keep the original
252265
# proxied-through-nginx behaviour (/download/).
253266
# Directory listings (trailing slash) always go to /download/.
267+
# $builds_backend is set by the map directive in the http block above.
254268
#
255-
set $builds_backend "/download/";
256-
if ($http_user_agent !~* "Mozilla") {
257-
set $builds_backend "/redirect/";
258-
}
259-
# HEAD requests are metadata checks, not downloads: always proxy directly.
260-
if ($request_method = "HEAD") {
261-
set $builds_backend "/download/";
262-
}
263269
rewrite ^/builds/([^/]+/.+[^/])$ $builds_backend$1 last;
264270
rewrite ^/builds/(.*)$ /download/$1 last;
265271

tests/end2end/test_routing.py

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1-
"""Tests that verify which S3 bucket each build type is stored in.
1+
"""Tests that verify which S3 bucket each build type is stored in, and that
2+
the /builds/ endpoint routes to the correct backend depending on User-Agent
3+
and HTTP method.
24
3-
Artifacts routes uploads to one of three buckets based on the build name:
5+
Bucket routing based on build name:
46
*:staging-* → artifacts-staging
57
*:promoted-* → artifacts-promoted
68
anything else → artifacts-prolonged
79
810
Uploads via the artifacts API only accept staging build names (nginx regex
911
enforces this). For promoted/prolonged builds we insert objects directly via
1012
the S3 client and verify the artifacts download API can still retrieve them.
13+
14+
/builds/ backend routing:
15+
GET + non-Mozilla UA → /redirect/ → 302 presigned S3 URL
16+
GET + Mozilla UA → /download/ → 200 proxied
17+
HEAD + any UA → /download/ → 200 proxied (HEAD is always direct)
1118
"""
1219

1320
import pytest
@@ -67,6 +74,39 @@ def test_prolonged_build_downloadable_from_prolonged_bucket(
6774
assert resp.content == b'notes'
6875

6976

77+
def test_builds_cli_ua_redirects_to_presigned_url(upload_file, session, artifacts_url):
78+
"""GET /builds/ with a non-Mozilla UA is rewritten to /redirect/ → 302."""
79+
upload_file(STAGING_BUILD, 'file.txt')
80+
resp = session.get(
81+
f'{artifacts_url}/builds/{STAGING_BUILD}/file.txt',
82+
headers={'User-Agent': 'curl/7.83.1'},
83+
allow_redirects=False,
84+
)
85+
assert resp.status_code == 302
86+
87+
88+
def test_builds_browser_ua_proxied_directly(upload_file, session, artifacts_url):
89+
"""GET /builds/ with a Mozilla UA is rewritten to /download/ → 200 proxied."""
90+
upload_file(STAGING_BUILD, 'file.txt')
91+
resp = session.get(
92+
f'{artifacts_url}/builds/{STAGING_BUILD}/file.txt',
93+
headers={'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64)'},
94+
allow_redirects=False,
95+
)
96+
assert resp.status_code == 200
97+
98+
99+
def test_builds_head_always_proxied_regardless_of_ua(upload_file, session, artifacts_url):
100+
"""HEAD /builds/ with a non-Mozilla UA is still rewritten to /download/ → 200."""
101+
upload_file(STAGING_BUILD, 'file.txt')
102+
resp = session.head(
103+
f'{artifacts_url}/builds/{STAGING_BUILD}/file.txt',
104+
headers={'User-Agent': 'curl/7.83.1'},
105+
allow_redirects=False,
106+
)
107+
assert resp.status_code == 200
108+
109+
70110
def test_staging_build_not_visible_from_promoted_bucket(
71111
session, artifacts_url, s3_client
72112
):

0 commit comments

Comments
 (0)