@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 01:00 UTC

master
go 191 lines 5.6 KB
Raw
1 package reverseproxy
2
3 import (
4 "context"
5 "io"
6 "net/http"
7 "net/http/httptest"
8 "strings"
9 "testing"
10 "time"
11
12 "github.com/caddyserver/caddy/v2"
13 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
14 )
15
16 // 101 responses strip hop-by-hop headers (Alt-Svc, Keep-Alive, etc.) but preserve Upgrade and Connection
17 func TestFinalizeResponse_101_StripsHopByHopHeaders(t *testing.T) {
18 h := &Handler{logger: caddy.Log()}
19
20 req := httptest.NewRequest(http.MethodGet, "/ws", nil)
21 req.Header.Set("Upgrade", "websocket")
22 req.Header.Set("Connection", "Upgrade")
23 req.Header.Set("Sec-WebSocket-Version", "13")
24 req.Header.Set("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ==")
25
26 vars := map[string]any{}
27 ctx := context.WithValue(req.Context(), caddyhttp.VarsCtxKey, vars)
28 req = req.WithContext(ctx)
29
30 res := &http.Response{
31 StatusCode: http.StatusSwitchingProtocols,
32 ProtoMajor: 1,
33 ProtoMinor: 1,
34 Header: http.Header{
35 "Upgrade": {"websocket"},
36 "Connection": {"Upgrade"},
37 "Sec-Websocket-Accept": {"s3pPLMBiTxaQ9kYGzzhZRbK+xOo="},
38 "Alt-Svc": {"h2=\"evil.com:443\"; ma=86400"},
39 "Keep-Alive": {"timeout=999"},
40 "Proxy-Authenticate": {"Basic realm=\"phish\""},
41 },
42 Body: fakeRWC{strings.NewReader("")},
43 }
44
45 repl := caddy.NewReplacer()
46 rw := httptest.NewRecorder()
47
48 err := h.finalizeResponse(rw, req, res, repl, fakeStart, caddy.Log(), "")
49 if err != nil {
50 t.Logf("finalizeResponse returned error (expected, no real conn): %v", err)
51 }
52
53 if got := rw.Header().Get("Upgrade"); got != "websocket" {
54 t.Errorf("Upgrade = %q, want %q", got, "websocket")
55 }
56 if got := rw.Header().Get("Connection"); got != "Upgrade" {
57 t.Errorf("Connection = %q, want %q", got, "Upgrade")
58 }
59 for _, hdr := range []string{"Alt-Svc", "Keep-Alive", "Proxy-Authenticate"} {
60 if got := rw.Header().Get(hdr); got != "" {
61 t.Errorf("%s = %q, want empty (should be stripped)", hdr, got)
62 }
63 }
64 }
65
66 // Headers named in the upstream Connection value are stripped
67 func TestFinalizeResponse_101_StripsConnectionNamedHeaders(t *testing.T) {
68 h := &Handler{logger: caddy.Log()}
69
70 req := httptest.NewRequest(http.MethodGet, "/ws", nil)
71 req.Header.Set("Upgrade", "websocket")
72 req.Header.Set("Connection", "Upgrade")
73 req.Header.Set("Sec-WebSocket-Version", "13")
74 req.Header.Set("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ==")
75
76 vars := map[string]any{}
77 ctx := context.WithValue(req.Context(), caddyhttp.VarsCtxKey, vars)
78 req = req.WithContext(ctx)
79
80 res := &http.Response{
81 StatusCode: http.StatusSwitchingProtocols,
82 ProtoMajor: 1,
83 ProtoMinor: 1,
84 Header: http.Header{
85 "Upgrade": {"websocket"},
86 "Connection": {"Upgrade, X-Custom-ID"},
87 "Sec-Websocket-Accept": {"s3pPLMBiTxaQ9kYGzzhZRbK+xOo="},
88 "X-Custom-Id": {"should-be-stripped"},
89 },
90 Body: fakeRWC{strings.NewReader("")},
91 }
92
93 repl := caddy.NewReplacer()
94 rw := httptest.NewRecorder()
95
96 err := h.finalizeResponse(rw, req, res, repl, fakeStart, caddy.Log(), "")
97 if err != nil {
98 t.Logf("finalizeResponse returned error (no real connection): %v", err)
99 }
100
101 if got := rw.Header().Get("Upgrade"); got != "websocket" {
102 t.Errorf("Upgrade = %q, want %q", got, "websocket")
103 }
104 if got := rw.Header().Get("X-Custom-Id"); got != "" {
105 t.Errorf("X-Custom-Id = %q, want empty (named in Connection, should be stripped)", got)
106 }
107 if got := rw.Header().Get("Connection"); got != "Upgrade" {
108 t.Errorf("Connection = %q, want %q", got, "Upgrade")
109 }
110 }
111
112 // Normal 200 responses strip hop-by-hop headers
113 func TestFinalizeResponse_200_StillStripsHopByHop(t *testing.T) {
114 h := &Handler{logger: caddy.Log()}
115
116 req := httptest.NewRequest(http.MethodGet, "/", nil)
117 vars := map[string]any{}
118 ctx := context.WithValue(req.Context(), caddyhttp.VarsCtxKey, vars)
119 req = req.WithContext(ctx)
120
121 res := &http.Response{
122 StatusCode: http.StatusOK,
123 ProtoMajor: 1,
124 ProtoMinor: 1,
125 Header: http.Header{
126 "Content-Type": {"text/plain"},
127 "Alt-Svc": {"h2=\"evil.com:443\""},
128 "Keep-Alive": {"timeout=999"},
129 },
130 Body: io.NopCloser(strings.NewReader("ok")),
131 }
132
133 repl := caddy.NewReplacer()
134 rw := httptest.NewRecorder()
135
136 err := h.finalizeResponse(rw, req, res, repl, fakeStart, caddy.Log(), "")
137 if err != nil {
138 t.Fatalf("finalizeResponse returned error: %v", err)
139 }
140
141 for _, hdr := range []string{"Alt-Svc", "Keep-Alive"} {
142 if got := rw.Header().Get(hdr); got != "" {
143 t.Errorf("%s = %q on 200 response, want empty (stripped)", hdr, got)
144 }
145 }
146 }
147
148 // Stripping still runs when the client didn't request an upgrade
149 func TestFinalizeResponse_101_NoUpgradeRequest(t *testing.T) {
150 h := &Handler{logger: caddy.Log()}
151
152 req := httptest.NewRequest(http.MethodGet, "/ws", nil)
153
154 vars := map[string]any{}
155 ctx := context.WithValue(req.Context(), caddyhttp.VarsCtxKey, vars)
156 req = req.WithContext(ctx)
157
158 res := &http.Response{
159 StatusCode: http.StatusSwitchingProtocols,
160 ProtoMajor: 1,
161 ProtoMinor: 1,
162 Header: http.Header{
163 "Upgrade": {"websocket"},
164 "Connection": {"Upgrade"},
165 "Alt-Svc": {"h2=\"evil.com:443\""},
166 },
167 Body: io.NopCloser(strings.NewReader("")),
168 }
169
170 repl := caddy.NewReplacer()
171 rw := httptest.NewRecorder()
172
173 err := h.finalizeResponse(rw, req, res, repl, fakeStart, caddy.Log(), "")
174 if err != nil {
175 t.Fatalf("finalizeResponse returned error: %v", err)
176 }
177
178 if got := rw.Header().Get("Alt-Svc"); got != "" {
179 t.Errorf("Alt-Svc = %q, want empty (stripped)", got)
180 }
181 }
182
183 // fakeRWC lets handleUpgradeResponse proceed past the body type assertion in tests
184 type fakeRWC struct {
185 io.Reader
186 }
187
188 func (f fakeRWC) Write(p []byte) (n int, err error) { return len(p), nil }
189 func (f fakeRWC) Close() error { return nil }
190
191 var fakeStart = time.Time{}