package gateway import ( "bufio" "fmt" "io" "net" "net/http" "net/http/httptest" "strings" "testing" ) // Drive the real authenticated plugin route, including the access-log wrapper. // A successful static request alone does not prove WebSocket can upgrade. func TestPluginPrefixProxyHTTPAndUpgrade(t *testing.T) { backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("Authorization") != "" || r.Header.Get("Cookie") != "" || r.URL.Query().Get("api_key") != "" { t.Error("gateway credentials leaked to plugin backend") } if r.URL.Path == "/websockify" { c, b, err := w.(http.Hijacker).Hijack() if err != nil { t.Error(err) return } defer c.Close() fmt.Fprint(b, "HTTP/1.1 101 Switching Protocols\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\n") b.Flush() io.Copy(c, c) return } fmt.Fprint(w, r.URL.Path+"?"+r.URL.RawQuery) })) defer backend.Close() g, _ := gatewayWithSpy(t) src := fmt.Sprintf(`local p={name="relay",http={{path="relay/:session",prefix=true,method="GET",roles={"admin"},handler="proxy"}}} function p.proxy(req) if req.params.session ~= "live" then return {status=404,body="expired"} end return {proxy=%q} end return p`, backend.URL) if err := g.core.Plugins().LoadSource("relay", src); err != nil { t.Fatal(err) } server := httptest.NewServer(g.Handler()) defer server.Close() req, _ := http.NewRequest("GET", server.URL+"/api/plugins/http/relay/live/app/main.js?x=1&api_key=sk-test", nil) req.Header.Set("Authorization", "Bearer sk-test") req.AddCookie(&http.Cookie{Name: "gw_key", Value: "sk-test"}) resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } body, _ := io.ReadAll(resp.Body) resp.Body.Close() if resp.StatusCode != 200 || string(body) != "/app/main.js?x=1" { t.Fatalf("HTTP proxy: %d %s", resp.StatusCode, body) } c, err := net.Dial("tcp", strings.TrimPrefix(server.URL, "http://")) if err != nil { t.Fatal(err) } defer c.Close() fmt.Fprintf(c, "GET /api/plugins/http/relay/live/websockify HTTP/1.1\r\nHost: %s\r\nAuthorization: Bearer sk-test\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\n", strings.TrimPrefix(server.URL, "http://")) b := bufio.NewReader(c) reply, err := http.ReadResponse(b, nil) if err != nil { t.Fatal(err) } if reply.StatusCode != 101 { t.Fatalf("upgrade failed: %s", reply.Status) } c.Write([]byte("ping")) got := make([]byte, 4) if _, err = io.ReadFull(b, got); err != nil { t.Fatal(err) } if string(got) != "ping" { t.Fatalf("tunnel payload=%q", got) } }