From f1638cc124d861a50464d007ba9a2d5fab7b0d69 Mon Sep 17 00:00:00 2001 From: dalbodeule <11470513+dalbodeule@users.noreply.github.com> Date: Tue, 2 Dec 2025 22:40:41 +0900 Subject: [PATCH 1/2] [fix](server): enforce static asset handling for `/__hopgate_assets__/` path - Added safeguard to ensure `/__hopgate_assets__/` requests always serve static files, regardless of proxy routing or backend state. - Implemented fallback to a 500 error page when embedded asset FS is unavailable. --- cmd/server/main.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index c3fb774..93933ad 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -175,12 +175,12 @@ var hopGateOwnedHeaders = map[string]struct{}{ // writeErrorPage renders static HTML error pages for key HTTP error codes (400/404/500/525). (en) // // 템플릿 로딩 우선순위: (ko) -// 1) HOP_ERROR_PAGES_DIR/.html (또는 ./errors/.html) (ko) -// 2) go:embed 로 내장된 templates/.html (ko) +// 1. HOP_ERROR_PAGES_DIR/.html (또는 ./errors/.html) (ko) +// 2. go:embed 로 내장된 templates/.html (ko) // // Template loading priority: (en) -// 1) HOP_ERROR_PAGES_DIR/.html (or ./errors/.html) (en) -// 2) go:embed'ed templates/.html (en) +// 1. HOP_ERROR_PAGES_DIR/.html (or ./errors/.html) (en) +// 2. go:embed'ed templates/.html (en) func writeErrorPage(w http.ResponseWriter, r *http.Request, status int) { // 공통 보안/식별 헤더를 best-effort 로 설정합니다. (ko) // Configure common security and identity headers (best-effort). (en) @@ -278,6 +278,26 @@ func newHTTPHandler(logger logging.Logger) http.Handler { webroot := strings.TrimSpace(os.Getenv("HOP_ACME_WEBROOT")) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // NOTE: /__hopgate_assets__/ 경로는 DTLS/백엔드와 무관하게 항상 정적 에셋만 서빙해야 합니다. (ko) + // 이 핸들러(newHTTPHandler)는 일반 프록시 경로(/)에만 사용되어야 하지만, + // 혹시라도 라우팅/구성이 꼬여서 이쪽으로 들어오는 경우를 방지하기 위해 + // /__hopgate_assets__/ 요청은 여기서도 강제로 정적 핸들러로 처리합니다. (ko) + // + // The /__hopgate_assets__/ path must always serve static assets independently + // of DTLS/backend state. This handler is intended for the generic proxy path (/), + // but as a safety net, we short-circuit asset requests here as well. (en) + if strings.HasPrefix(r.URL.Path, "/__hopgate_assets/") { + if sub, err := stdfs.Sub(errorpages.AssetsFS, "assets"); err == nil { + staticFS := http.FileServer(http.FS(sub)) + http.StripPrefix("/__hopgate_assets/", staticFS).ServeHTTP(w, r) + return + } + // embed FS 가 초기화되지 않은 비정상 상황에서는 500 에러 페이지로 폴백합니다. (ko) + // If embedded FS is not available for some reason, fall back to a 500 error page. (en) + writeErrorPage(w, r, http.StatusInternalServerError) + return + } + start := time.Now() method := r.Method From f813308818e5c27572873a778066211cf3c56ee6 Mon Sep 17 00:00:00 2001 From: dalbodeule <11470513+dalbodeule@users.noreply.github.com> Date: Tue, 2 Dec 2025 22:47:25 +0900 Subject: [PATCH 2/2] [fix](server): correct static asset routing for `/__hopgate_assets__/` - Fixed path prefix to ensure proper handling of `/__hopgate_assets__/` requests. - Adjusted `http.StripPrefix` to match the corrected route for consistent file serving. --- cmd/server/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 93933ad..8b73011 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -286,10 +286,10 @@ func newHTTPHandler(logger logging.Logger) http.Handler { // The /__hopgate_assets__/ path must always serve static assets independently // of DTLS/backend state. This handler is intended for the generic proxy path (/), // but as a safety net, we short-circuit asset requests here as well. (en) - if strings.HasPrefix(r.URL.Path, "/__hopgate_assets/") { + if strings.HasPrefix(r.URL.Path, "/__hopgate_assets__/") { if sub, err := stdfs.Sub(errorpages.AssetsFS, "assets"); err == nil { staticFS := http.FileServer(http.FS(sub)) - http.StripPrefix("/__hopgate_assets/", staticFS).ServeHTTP(w, r) + http.StripPrefix("/__hopgate_assets__/", staticFS).ServeHTTP(w, r) return } // embed FS 가 초기화되지 않은 비정상 상황에서는 500 에러 페이지로 폴백합니다. (ko)