Files
scrabble-game/gateway/internal/connectsrv/cors_test.go
T
Ilia Denisov f0b7ad47d4
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 23s
CI / ui (pull_request) Has been skipped
CI / conformance (pull_request) Successful in 11s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m54s
fix(gateway): allow the native WebView origin via CORS
The packaged native app (Capacitor) serves the bundled SPA from a localhost scheme, so its Connect calls to erudit-game.ru are cross-origin. The gateway had no CORS handling, so the preflight OPTIONS returned 405 with no Access-Control-Allow-Origin and the WebView blocked every RPC — a native build could never reach the gateway and stayed stuck offline (the native online path was never exercised on-device; on-device D was airplane-mode only). Add a CORS middleware that answers the preflight and sets the response headers for the native localhost origins (https/http/capacitor://localhost); web is same-origin and untouched. Fixes the native emulator 'starts offline, can't go online' report.
2026-07-14 21:21:53 +02:00

55 lines
2.2 KiB
Go

package connectsrv
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestWithNativeCORS(t *testing.T) {
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })
h := withNativeCORS(next)
// Native-origin preflight → 204 with the CORS headers, reflecting the requested headers, without
// reaching the inner handler.
req := httptest.NewRequest(http.MethodOptions, "/scrabble.edge.v1.Gateway/Execute", nil)
req.Header.Set("Origin", "https://localhost")
req.Header.Set("Access-Control-Request-Method", "POST")
req.Header.Set("Access-Control-Request-Headers", "content-type,connect-protocol-version,x-client-version")
rec := httptest.NewRecorder()
h.ServeHTTP(rec, req)
if rec.Code != http.StatusNoContent {
t.Fatalf("preflight status = %d, want 204", rec.Code)
}
if got := rec.Header().Get("Access-Control-Allow-Origin"); got != "https://localhost" {
t.Errorf("Allow-Origin = %q, want https://localhost", got)
}
if got := rec.Header().Get("Access-Control-Allow-Headers"); got != "content-type,connect-protocol-version,x-client-version" {
t.Errorf("Allow-Headers = %q, want the reflected set", got)
}
// Native-origin POST → the CORS headers are set and the inner handler runs.
req = httptest.NewRequest(http.MethodPost, "/scrabble.edge.v1.Gateway/Execute", nil)
req.Header.Set("Origin", "capacitor://localhost")
rec = httptest.NewRecorder()
h.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("POST status = %d, want 200 (passed through)", rec.Code)
}
if got := rec.Header().Get("Access-Control-Allow-Origin"); got != "capacitor://localhost" {
t.Errorf("POST Allow-Origin = %q, want capacitor://localhost", got)
}
// A non-native origin gets no CORS headers and still passes through (web is same-origin anyway).
req = httptest.NewRequest(http.MethodPost, "/scrabble.edge.v1.Gateway/Execute", nil)
req.Header.Set("Origin", "https://evil.example")
rec = httptest.NewRecorder()
h.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("non-native POST status = %d, want 200", rec.Code)
}
if got := rec.Header().Get("Access-Control-Allow-Origin"); got != "" {
t.Errorf("non-native Allow-Origin = %q, want empty", got)
}
}