Juso Popup Return URL Protocol
Juso 주소 팝업에서 주소를 선택한 직후 Tomcat이 Invalid character found in method name [0x16...] 오류를 내는 문제를 판별하고 복구한다.
이 증상은 CORS 오류처럼 보일 수 있지만, 대표 원인은 HTTPS 요청이 TLS가 설정되지 않은 HTTP-only Spring Boot 포트로 들어오는 프로토콜 불일치다.
이 문서는 ZIP:ON의 JusoAddressPopupController, JusoAddressPopupPageRenderer, 로컬 Vite/Spring Boot 개발 환경에 한정한다.
Status: Implemented
Trigger Signals
- Browser address popup opens, but selecting an address fails.
- Backend log contains:
Error parsing HTTP request header
java.lang.IllegalArgumentException: Invalid character found in method name [0x160x030x01...
HTTP method names must be tokens- The broken callback URL points to
https://localhost:8082/.... - Spring Boot is running with the normal local command:
cd backend
./mvnw spring-boot:run- No
server.ssl.*local TLS settings or HTTPS reverse proxy/tunnel is active for backend port8082.
Required Scan
git status --short --branch
rg -n "juso-popup|returnUrl|scheme\\(\"https\"\\)|JUSO_|server.ssl|forward-headers|X-Forwarded" backend frontend docs .env.example
sed -n '1,120p' backend/src/main/java/com/zipon/controller/JusoAddressPopupController.java
sed -n '1,120p' backend/src/main/java/com/zipon/service/JusoAddressPopupPageRenderer.java
sed -n '1,120p' frontend/src/utils/jusoAddressSearch.jsIf the backend is running, inspect the generated launch page without exposing keys in chat or docs:
curl -s 'http://localhost:8082/api/address-search/juso-popup?targetOrigin=http://localhost:5173&requestId=debug' \
| rg 'returnUrl|addrLinkUrl'Expected local HTTP-only callback:
returnUrl value="http://localhost:8082/api/address-search/juso-popup/callback?targetOrigin=http://localhost:5173..."Expected HTTPS proxy/tunnel callback:
returnUrl value="https://<public-or-local-https-host>/api/address-search/juso-popup/callback?targetOrigin=..."Same-Issue Criteria
- Error bytes start with
0x16 0x03, or the compact escaped form0x160x030x01. - The callback request reaches Tomcat before controller code runs.
- Browser or Juso is calling an
https://...:8082URL. - Port
8082is configured as plain HTTP, not HTTPS. - The issue happens after address selection, not while opening ZIP:ON’s first popup URL.
Current Implementation
JusoAddressPopupController.openPopup(...)callsbuildReturnUrl(...).- Default behavior: build
returnUrlfromrequest.getRequestURL()plus/callback. - Local default result:
http://localhost:8082/api/address-search/juso-popup/callback?.... - Optional override:
JusoAddressProperties.popupReturnOrigin. - Spring property:
zipon.external.juso.popup-return-origin. - Environment variable:
JUSO_POPUP_RETURN_ORIGIN. .env.examplekeepsJUSO_POPUP_RETURN_ORIGIN=empty so local HTTP callbacks remain the default.- If
popupReturnOriginis set, the controller uses that origin and replaces the path with the current request URI plus/callback. - Tests:
JusoAddressPopupControllerTest.openPopupUsesBackendConfiguredConfirmKeyAndRequestUrlAsDefaultReturnUrlJusoAddressPopupControllerTest.openPopupUsesConfiguredReturnOriginWhenHttpsProxyOrTunnelIsReadyJusoAddressPropertiesTest.bindsJusoKeysOnBackendOnly
Fix Steps
-
Classify the protocol.
0x16 0x03means TLS handshake bytes reached an HTTP parser.- This is not fixed by adding CORS origins.
- This is not fixed by changing only
targetOrigin;targetOriginis forpostMessage, not for Juso’s backend callback.
-
For normal local development, keep Juso
returnUrlHTTP.- Do not hardcode
.scheme("https")inJusoAddressPopupController. - Build
returnUrlfrom the actual backend request URL. - Local callback should remain
http://localhost:8082/api/address-search/juso-popup/callback....
- Do not hardcode
-
For HTTPS callback testing, make HTTPS real before generating an HTTPS URL.
- Use a reverse proxy, tunnel, or Spring Boot
server.ssl.*. - Verify the callback origin opens in the browser before using it with Juso.
- Use a reverse proxy, tunnel, or Spring Boot
curl -k -I https://localhost:8082/api/address-search/juso-popup-
If a public HTTPS callback is needed while the local backend stays HTTP, use the explicit backend-owned return-origin property instead of forcing HTTPS globally.
- Spring property:
zipon.external.juso.popup-return-origin=https://<tunnel-host> - Environment variable:
JUSO_POPUP_RETURN_ORIGIN=https://<tunnel-host> - Use this origin only when it is configured and reachable.
- Keep the default empty so local HTTP works without TLS.
- Do not set it to
https://localhost:8082unless Spring Boot is actually serving TLS on port8082.
- Spring property:
-
Keep the responsibilities separate.
frontend/src/utils/jusoAddressSearch.jsopens ZIP:ON backend popup endpoint.JusoAddressPopupControllercreates JusoreturnUrl.JusoAddressPopupPageRendererposts selected address back totargetOrigin.targetOrigin=http://localhost:5173can be correct even when the JusoreturnUrlis HTTPS.
Verification
Focused backend tests:
cd backend
./mvnw -Dtest=JusoAddressPropertiesTest,JusoAddressPopupControllerTest,JusoAddressPopupPageRendererTest,CorsIntegrationTest testFull backend check after changing web configuration:
cd backend
./mvnw testManual local smoke:
curl -s 'http://localhost:8082/api/address-search/juso-popup?targetOrigin=http://localhost:5173&requestId=debug' \
| rg 'returnUrl'Manual HTTPS smoke, only when HTTPS is intentionally configured:
curl -k -I 'https://localhost:8082/api/address-search/juso-popup/callback?targetOrigin=http://localhost:5173&requestId=debug'When using JUSO_POPUP_RETURN_ORIGIN, verify both the generated launch HTML and the configured origin:
JUSO_POPUP_RETURN_ORIGIN='https://zipon-local-tunnel.example.test'
curl -I "$JUSO_POPUP_RETURN_ORIGIN/api/address-search/juso-popup/callback"
curl -s 'http://localhost:8082/api/address-search/juso-popup?targetOrigin=http://localhost:5173&requestId=debug' \
| rg 'returnUrl'Do Not
- Do not assume
https://localhost:8082works just becausereturnUrlcontainshttps. - Do not fix
0x16 0x03Tomcat parser errors by changing only CORS settings. - Do not expose
JUSO_ADDRESS_CONFIRM_KEYorJUSO_ADDRESS_SEARCH_KEYin docs, screenshots, curl output, or committed.env. - Do not move Juso approval keys to the frontend.
- Do not treat Juso popup callback HTML as persisted address data; selected addresses are only submitted later through the risk diagnosis request.
- Do not make HTTPS the default
returnUrlscheme without adding real backend TLS or a reachable HTTPS proxy.