@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 440 lines 9.95 KB
Raw
1 package integration
2
3 import (
4 "compress/gzip"
5 "context"
6 "crypto/rand"
7 "fmt"
8 "io"
9 "net/http"
10 "net/http/httputil"
11 "net/url"
12 "strings"
13 "testing"
14 "time"
15
16 "golang.org/x/net/http2"
17 "golang.org/x/net/http2/h2c"
18
19 "github.com/caddyserver/caddy/v2/caddytest"
20 )
21
22 // (see https://github.com/caddyserver/caddy/issues/3556 for use case)
23 func TestH2ToH2CStream(t *testing.T) {
24 tester := caddytest.NewTester(t)
25 tester.InitServer(`
26 {
27 "admin": {
28 "listen": "localhost:2999"
29 },
30 "apps": {
31 "http": {
32 "http_port": 9080,
33 "https_port": 9443,
34 "grace_period": 1,
35 "servers": {
36 "srv0": {
37 "listen": [
38 ":9443"
39 ],
40 "routes": [
41 {
42 "handle": [
43 {
44 "handler": "reverse_proxy",
45 "transport": {
46 "protocol": "http",
47 "compression": false,
48 "versions": [
49 "h2c",
50 "2"
51 ]
52 },
53 "upstreams": [
54 {
55 "dial": "localhost:54321"
56 }
57 ]
58 }
59 ],
60 "match": [
61 {
62 "path": [
63 "/tov2ray"
64 ]
65 }
66 ]
67 }
68 ],
69 "tls_connection_policies": [
70 {
71 "certificate_selection": {
72 "any_tag": ["cert0"]
73 },
74 "default_sni": "a.caddy.localhost"
75 }
76 ]
77 }
78 }
79 },
80 "tls": {
81 "certificates": {
82 "load_files": [
83 {
84 "certificate": "/a.caddy.localhost.crt",
85 "key": "/a.caddy.localhost.key",
86 "tags": [
87 "cert0"
88 ]
89 }
90 ]
91 }
92 },
93 "pki": {
94 "certificate_authorities" : {
95 "local" : {
96 "install_trust": false
97 }
98 }
99 }
100 }
101 }
102 `, "json")
103
104 expectedBody := "some data to be echoed"
105 // start the server
106 server := testH2ToH2CStreamServeH2C(t)
107 go server.ListenAndServe()
108 defer func() {
109 ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
110 defer cancel()
111 server.Shutdown(ctx)
112 }()
113
114 r, w := io.Pipe()
115 req := &http.Request{
116 Method: "PUT",
117 Body: io.NopCloser(r),
118 URL: &url.URL{
119 Scheme: "https",
120 Host: "127.0.0.1:9443",
121 Path: "/tov2ray",
122 },
123 Proto: "HTTP/2",
124 ProtoMajor: 2,
125 ProtoMinor: 0,
126 Header: make(http.Header),
127 }
128 // Disable any compression method from server.
129 req.Header.Set("Accept-Encoding", "identity")
130
131 resp := tester.AssertResponseCode(req, http.StatusOK)
132 if resp.StatusCode != http.StatusOK {
133 return
134 }
135 go func() {
136 fmt.Fprint(w, expectedBody)
137 w.Close()
138 }()
139
140 defer resp.Body.Close()
141 bytes, err := io.ReadAll(resp.Body)
142 if err != nil {
143 t.Fatalf("unable to read the response body %s", err)
144 }
145
146 body := string(bytes)
147
148 if !strings.Contains(body, expectedBody) {
149 t.Errorf("requesting \"%s\" expected response body \"%s\" but got \"%s\"", req.RequestURI, expectedBody, body)
150 }
151 }
152
153 func testH2ToH2CStreamServeH2C(t *testing.T) *http.Server {
154 h2s := &http2.Server{}
155 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
156 rstring, err := httputil.DumpRequest(r, false)
157 if err == nil {
158 t.Logf("h2c server received req: %s", rstring)
159 }
160 // We only accept HTTP/2!
161 if r.ProtoMajor != 2 {
162 t.Error("Not an HTTP/2 request, rejected!")
163 w.WriteHeader(http.StatusInternalServerError)
164 return
165 }
166
167 if r.Host != "127.0.0.1:9443" {
168 t.Errorf("r.Host doesn't match, %v!", r.Host)
169 w.WriteHeader(http.StatusNotFound)
170 return
171 }
172
173 if !strings.HasPrefix(r.URL.Path, "/tov2ray") {
174 w.WriteHeader(http.StatusNotFound)
175 return
176 }
177
178 w.Header().Set("Cache-Control", "no-store")
179 w.WriteHeader(200)
180 http.NewResponseController(w).Flush()
181
182 buf := make([]byte, 4*1024)
183
184 for {
185 n, err := r.Body.Read(buf)
186 if n > 0 {
187 w.Write(buf[:n])
188 }
189
190 if err != nil {
191 if err == io.EOF {
192 r.Body.Close()
193 }
194 break
195 }
196 }
197 })
198
199 server := &http.Server{
200 Addr: "127.0.0.1:54321",
201 Handler: h2c.NewHandler(handler, h2s),
202 }
203 return server
204 }
205
206 // (see https://github.com/caddyserver/caddy/issues/3606 for use case)
207 func TestH2ToH1ChunkedResponse(t *testing.T) {
208 tester := caddytest.NewTester(t)
209 tester.InitServer(`
210 {
211 "admin": {
212 "listen": "localhost:2999"
213 },
214 "logging": {
215 "logs": {
216 "default": {
217 "level": "DEBUG"
218 }
219 }
220 },
221 "apps": {
222 "http": {
223 "http_port": 9080,
224 "https_port": 9443,
225 "grace_period": 1,
226 "servers": {
227 "srv0": {
228 "listen": [
229 ":9443"
230 ],
231 "routes": [
232 {
233 "handle": [
234 {
235 "handler": "subroute",
236 "routes": [
237 {
238 "handle": [
239 {
240 "encodings": {
241 "gzip": {}
242 },
243 "handler": "encode"
244 }
245 ]
246 },
247 {
248 "handle": [
249 {
250 "handler": "reverse_proxy",
251 "upstreams": [
252 {
253 "dial": "localhost:54321"
254 }
255 ]
256 }
257 ],
258 "match": [
259 {
260 "path": [
261 "/tov2ray"
262 ]
263 }
264 ]
265 }
266 ]
267 }
268 ],
269 "terminal": true
270 }
271 ],
272 "tls_connection_policies": [
273 {
274 "certificate_selection": {
275 "any_tag": [
276 "cert0"
277 ]
278 },
279 "default_sni": "a.caddy.localhost"
280 }
281 ]
282 }
283 }
284 },
285 "tls": {
286 "certificates": {
287 "load_files": [
288 {
289 "certificate": "/a.caddy.localhost.crt",
290 "key": "/a.caddy.localhost.key",
291 "tags": [
292 "cert0"
293 ]
294 }
295 ]
296 }
297 },
298 "pki": {
299 "certificate_authorities": {
300 "local": {
301 "install_trust": false
302 }
303 }
304 }
305 }
306 }
307 `, "json")
308
309 // need a large body here to trigger caddy's compression, larger than gzip.miniLength
310 expectedBody, err := GenerateRandomString(1024)
311 if err != nil {
312 t.Fatalf("generate expected body failed, err: %s", err)
313 }
314
315 // start the server
316 server := testH2ToH1ChunkedResponseServeH1(t)
317 go server.ListenAndServe()
318 defer func() {
319 ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
320 defer cancel()
321 server.Shutdown(ctx)
322 }()
323
324 r, w := io.Pipe()
325 req := &http.Request{
326 Method: "PUT",
327 Body: io.NopCloser(r),
328 URL: &url.URL{
329 Scheme: "https",
330 Host: "127.0.0.1:9443",
331 Path: "/tov2ray",
332 },
333 Proto: "HTTP/2",
334 ProtoMajor: 2,
335 ProtoMinor: 0,
336 Header: make(http.Header),
337 }
338 // underlying transport will automatically add gzip
339 // req.Header.Set("Accept-Encoding", "gzip")
340 go func() {
341 fmt.Fprint(w, expectedBody)
342 w.Close()
343 }()
344 resp := tester.AssertResponseCode(req, http.StatusOK)
345 if resp.StatusCode != http.StatusOK {
346 return
347 }
348
349 defer resp.Body.Close()
350 bytes, err := io.ReadAll(resp.Body)
351 if err != nil {
352 t.Fatalf("unable to read the response body %s", err)
353 }
354
355 body := string(bytes)
356
357 if body != expectedBody {
358 t.Errorf("requesting \"%s\" expected response body \"%s\" but got \"%s\"", req.RequestURI, expectedBody, body)
359 }
360 }
361
362 func testH2ToH1ChunkedResponseServeH1(t *testing.T) *http.Server {
363 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
364 if r.Host != "127.0.0.1:9443" {
365 t.Errorf("r.Host doesn't match, %v!", r.Host)
366 w.WriteHeader(http.StatusNotFound)
367 return
368 }
369
370 if !strings.HasPrefix(r.URL.Path, "/tov2ray") {
371 w.WriteHeader(http.StatusNotFound)
372 return
373 }
374
375 defer r.Body.Close()
376 bytes, err := io.ReadAll(r.Body)
377 if err != nil {
378 t.Fatalf("unable to read the response body %s", err)
379 }
380
381 n := len(bytes)
382
383 var writer io.Writer
384 if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
385 gw, err := gzip.NewWriterLevel(w, 5)
386 if err != nil {
387 t.Error("can't return gzip data")
388 w.WriteHeader(http.StatusInternalServerError)
389 return
390 }
391 defer gw.Close()
392 writer = gw
393 w.Header().Set("Content-Encoding", "gzip")
394 w.Header().Del("Content-Length")
395 w.WriteHeader(200)
396 } else {
397 writer = w
398 }
399 if n > 0 {
400 writer.Write(bytes[:])
401 }
402 })
403
404 server := &http.Server{
405 Addr: "127.0.0.1:54321",
406 Handler: handler,
407 }
408 return server
409 }
410
411 // GenerateRandomBytes returns securely generated random bytes.
412 // It will return an error if the system's secure random
413 // number generator fails to function correctly, in which
414 // case the caller should not continue.
415 func GenerateRandomBytes(n int) ([]byte, error) {
416 b := make([]byte, n)
417 _, err := rand.Read(b)
418 // Note that err == nil only if we read len(b) bytes.
419 if err != nil {
420 return nil, err
421 }
422
423 return b, nil
424 }
425
426 // GenerateRandomString returns a securely generated random string.
427 // It will return an error if the system's secure random
428 // number generator fails to function correctly, in which
429 // case the caller should not continue.
430 func GenerateRandomString(n int) (string, error) {
431 const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
432 bytes, err := GenerateRandomBytes(n)
433 if err != nil {
434 return "", err
435 }
436 for i, b := range bytes {
437 bytes[i] = letters[b%byte(len(letters))]
438 }
439 return string(bytes), nil
440 }