feat(docker): 휴대폰 앱 받기 경로에 앱 안 받기와 같은 상한 · 흘려보내기를 둔다

APK 받기가 일반 /api 로 가면 X-Accel-Buffering 이 외부 프록시로 넘어가지 않아 받는 사람마다 임시 파일이 쌓이고,
버퍼링을 끈 받기에 둘 동시 연결 상한도 없다(리뷰). 앱 안 대용량 받기와 같은 location(대소문자를 가리지 않는 정규식 ·
같은 그릇)을 두고, CI 의 받기 경로 시험에 변형 · 정보 경로 · 그릇 공유를 더했다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-08 23:29:05 +09:00
parent e9acb7809c
commit 7c70862647
4 changed files with 59 additions and 8 deletions
+26 -3
View File
@@ -236,6 +236,11 @@ jobs:
[ "$n" -eq 2 ] || { echo "::error::internal-api-closed.conf 를 넣은 server 가 둘이 아닙니다 ($form: $n)"; exit 1; }
n=$(printf '%s\n' "$conf" | awk '$1 == "if" && $2 == "($request_target_rejected)" { c++ } END { print c + 0 }')
[ "$n" -eq 2 ] || { echo "::error::원문 요청 대상 검사(if \$request_target_rejected)가 든 server 가 둘이 아닙니다 ($form: $n)"; exit 1; }
# 앱 안 받기 · 휴대폰 앱 받기(2026-11-08)가 같은 그릇(download_app_*)을 쓴다 — 포털의 두 location 에 하나씩, 두 꼴 모두 둘이다
for want in 'download_app_ip 30;' 'download_app_total 200;'; do
n=$(printf '%s\n' "$conf" | awk -v want="$want" '$1 == "limit_conn" && ($2 " " $3) == want { c++ } END { print c + 0 }')
[ "$n" -eq 2 ] || { echo "::error::limit_conn $want 이 든 location 이 둘(앱 안 받기 · 휴대폰 앱 받기)이 아닙니다 ($form: $n)"; exit 1; }
done
done
# 잘못된 값은 기동을 멈춰야 한다 (frontend/nginx-env.envsh — 앞자리 0, 경로가 붙은 백엔드 주소, 출처가 아닌 공개 내려받기 주소)
if docker run --rm -e PROJECT_NAME=ci -e MAIL_ATTACHMENT_MAX_SIZE_MB=08 "$tag" nginx -t; then
@@ -253,7 +258,9 @@ jobs:
# 대용량 받기 경로(11단계)를 실제 요청으로 본다 — 백엔드(Express)는 경로의 대소문자를 가리지 않아, 받기 location(동시 연결 수 · 버퍼링 끔 ·
# X-Accel-Buffering 넘김 · 머리글 버퍼 16k)과 포털이 닫은 공개 API 는 대소문자 · %2F · 겹 / 변형도 같은 곳을 타야 한다 (frontend/nginx.conf 머리말).
# 위 단계의 이미지에 가짜 백엔드(같은 컨테이너의 127.0.0.1:3000 — 늘 X-Accel-Buffering: no 를 붙인다. ?hold=1 이면 10MB 를 1KB/s 로,
# ?pad=1 이면 머리글 6KB)를 더해 네트워크 없이 띄운다. 그 머리글은 받기 location 을 탄 요청만 클라이언트까지 온다 — 그것으로 location 을 가른다
# ?pad=1 이면 머리글 6KB)를 더해 네트워크 없이 띄운다. 그 머리글은 받기 location 을 탄 요청만 클라이언트까지 온다 — 그것으로 location 을 가른다.
# 휴대폰 앱(APK) 받기(/api/app-releases/…/file — 2026-11-08)도 본다: 같은 변형이 받기 location 을 타고 앱 안 받기와 그릇(주소마다 30)을 함께 쓰는지,
# 공개 호스트는 404 로 닫는지, 정보 경로(…/latest — JSON)는 받기 location 밖인지
- name: 대용량 받기 경로 시험
run: |
base="ci-nginx-config:${{ github.run_id }}"
@@ -290,26 +297,42 @@ jobs:
release() { killall wget >/dev/null 2>&1; sleep 2; }
PUBLIC="/api/public-links/$T/files/$F /api/Public-Links/$T/FILES/$F /api/PUBLIC-LINKS/$T/Files/$F /api//public-links/$T/files/$F /api/public%2Dlinks/$T/files/$F"
INAPP="/api/messages/M1/large-files/$F /api/Messages/M1/LARGE-FILES/$F /api/MESSAGES/M1/Large-Files/$F /api//messages/M1/large-files/$F /api/messages/M1%2Fx/large-files/$F"
# 휴대폰 앱 받기 — 끝의 / · %66(f) 꼴도. 정보 경로는 받기 location 밖이다
APK="/api/app-releases/android/latest/file /api/App-Releases/Android/Latest/FILE /api/APP-RELEASES/android/latest/File /api//app-releases/android/latest/file /api/app-releases/android%2Flatest/file /api/app-releases/android/latest/file/ /api/app-releases/android/latest/%66ile"
INFO="/api/app-releases/android/latest /api/App-Releases/Android/Latest /api/app-releases/android/latest/"
echo "== 받기 location 을 탄다(X-Accel-Buffering) · 포털은 공개 API 를 닫는다 — 대소문자 · %2F · 겹 / 변형도"
for p in $PUBLIC; do expect "200 xab" files.ci.test "$p"; expect "404 -" portal.ci.test "$p"; done
for p in $INAPP; do expect "200 xab" portal.ci.test "$p"; done
echo "== 휴대폰 앱 받기도 받기 location 을 탄다 · 공개 호스트는 닫는다 · 정보 경로는 /api 그대로"
for p in $APK; do expect "200 xab" portal.ci.test "$p"; expect "404 -" files.ci.test "$p"; done
for p in $INFO; do expect "200 -" portal.ci.test "$p"; expect "404 -" files.ci.test "$p"; done
expect "200 -" files.ci.test "/api/Public-Links/$T"
expect "200 -" portal.ci.test "/api/Messages/M1"
expect "200 xab" files.ci.test "/api/Public-Links/$T/files/$F?pad=1"
expect "200 xab" portal.ci.test "/api/Messages/M1/large-files/$F?pad=1"
expect "200 xab" portal.ci.test "/api/App-Releases/android/latest/file?pad=1"
echo "== nginx 가 끝내는 /api 응답은 JSON"
expect_raw "404 application/json RES_001" files.ci.test GET /api/auth/me
expect_raw "413 application/json FILE_002" files.ci.test POST /api/auth/login "Content-Length: 2000000"
expect_raw "404 application/json RES_001" portal.ci.test GET "/api/Public-Links/$T/files/$F"
echo "== 동시 연결 — 주소마다 공개 10 · 앱 안 30. 붙잡아 둔 뒤 변형으로 하나 더"
expect_raw "404 application/json RES_001" files.ci.test GET /api/app-releases/android/latest/file
echo "== 동시 연결 — 주소마다 공개 10 · 앱 안 30(휴대폰 앱 받기와 함께). 붙잡아 둔 뒤 변형으로 하나 더"
hold files.ci.test "/api/public-links/$T/files/$F" 10
for p in $PUBLIC; do expect "429 -" files.ci.test "$p"; done
expect_raw "429 application/json REQ_001" files.ci.test GET "/api/Public-Links/$T/files/$F"
expect "200 xab" portal.ci.test "/api/messages/M1/large-files/$F"
expect "200 xab" portal.ci.test "/api/app-releases/android/latest/file"
release
hold portal.ci.test "/api/messages/M1/large-files/$F" 30
for p in $INAPP; do expect "429 -" portal.ci.test "$p"; done
for p in $INAPP $APK; do expect "429 -" portal.ci.test "$p"; done
expect_raw "429 application/json REQ_001" portal.ci.test GET "/api/App-Releases/android/latest/FILE"
expect "200 -" portal.ci.test "/api/app-releases/android/latest"
expect "200 xab" files.ci.test "/api/public-links/$T/files/$F"
release
hold portal.ci.test "/api/app-releases/android/latest/file" 30
for p in $APK $INAPP; do expect "429 -" portal.ci.test "$p"; done
expect "200 -" portal.ci.test "/api/app-releases/android/latest"
expect "200 xab" files.ci.test "/api/public-links/$T/files/$F"
release
echo "fails=$fails"