@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 155 lines 4.25 KB
Raw
1 package reverseproxy
2
3 import (
4 "errors"
5 "io"
6 "strings"
7 "testing"
8 "testing/iotest"
9 )
10
11 type zeroReader struct{}
12
13 func (zeroReader) Read(p []byte) (int, error) {
14 for i := range p {
15 p[i] = 0
16 }
17 return len(p), nil
18 }
19
20 func TestBuffering(t *testing.T) {
21 var (
22 h Handler
23 zr zeroReader
24 )
25 type args struct {
26 body io.ReadCloser
27 limit int64
28 }
29 tests := []struct {
30 name string
31 args args
32 resultCheck func(io.ReadCloser, int64, args) bool
33 }{
34 {
35 name: "0 limit, body is returned as is",
36 args: args{
37 body: io.NopCloser(&zr),
38 limit: 0,
39 },
40 resultCheck: func(res io.ReadCloser, read int64, args args) bool {
41 return res == args.body && read == args.limit && read == 0
42 },
43 },
44 {
45 name: "negative limit, body is read completely",
46 args: args{
47 body: io.NopCloser(io.LimitReader(&zr, 100)),
48 limit: -1,
49 },
50 resultCheck: func(res io.ReadCloser, read int64, args args) bool {
51 brc, ok := res.(bodyReadCloser)
52 return ok && brc.body == nil && brc.buf.Len() == 100 && read == 100
53 },
54 },
55 {
56 name: "positive limit, body is read partially",
57 args: args{
58 body: io.NopCloser(io.LimitReader(&zr, 100)),
59 limit: 50,
60 },
61 resultCheck: func(res io.ReadCloser, read int64, args args) bool {
62 brc, ok := res.(bodyReadCloser)
63 return ok && brc.body != nil && brc.buf.Len() == 50 && read == 50
64 },
65 },
66 {
67 name: "positive limit, body is read completely",
68 args: args{
69 body: io.NopCloser(io.LimitReader(&zr, 100)),
70 limit: 101,
71 },
72 resultCheck: func(res io.ReadCloser, read int64, args args) bool {
73 brc, ok := res.(bodyReadCloser)
74 return ok && brc.body == nil && brc.buf.Len() == 100 && read == 100
75 },
76 },
77 }
78 for _, tt := range tests {
79 t.Run(tt.name, func(t *testing.T) {
80 res, read := h.bufferedBody(tt.args.body, tt.args.limit)
81 if !tt.resultCheck(res, read, tt.args) {
82 t.Error("Handler.bufferedBody() test failed")
83 return
84 }
85 })
86 }
87 }
88
89 // TestBufferingBoundary documents where a buffered body stops being fully
90 // buffered, which is what decides whether the caller can learn its length and
91 // set Content-Length. A body the size of the limit counts as partial: io.CopyN
92 // filling the buffer cannot be told apart from a body with more to come, and
93 // finding out would mean a blocking read on a stream that may never deliver.
94 func TestBufferingBoundary(t *testing.T) {
95 const body = "0123456789"
96
97 for limit := int64(1); limit <= int64(len(body))+2; limit++ {
98 var h Handler
99 res, read := h.bufferedBody(io.NopCloser(strings.NewReader(body)), limit)
100
101 // Whatever the limit, the upstream must still receive every byte.
102 got, err := io.ReadAll(res)
103 if err != nil {
104 t.Fatalf("limit %d: reading buffered body: %v", limit, err)
105 }
106 if string(got) != body {
107 t.Errorf("limit %d: body changed: got %q, want %q", limit, got, body)
108 }
109 if err := res.Close(); err != nil {
110 t.Errorf("limit %d: closing buffered body: %v", limit, err)
111 }
112
113 brc, ok := res.(bodyReadCloser)
114 if !ok {
115 t.Fatalf("limit %d: expected a bodyReadCloser", limit)
116 }
117 wantFull := limit > int64(len(body))
118 if gotFull := brc.body == nil; gotFull != wantFull {
119 t.Errorf("limit %d: fully buffered = %v, want %v", limit, gotFull, wantFull)
120 }
121 if wantFull && read != int64(len(body)) {
122 t.Errorf("limit %d: read %d bytes, want %d", limit, read, len(body))
123 }
124 }
125 }
126
127 // TestBufferingKeepsReadError checks that a body which fails partway through
128 // (a request_body max_size limit, a truncated upstream response) is not
129 // passed on as if it were complete.
130 func TestBufferingKeepsReadError(t *testing.T) {
131 errRead := errors.New("read failed")
132
133 for _, limit := range []int64{-1, 5, 100} {
134 var h Handler
135 body := io.NopCloser(io.MultiReader(strings.NewReader("0123456789"), iotest.ErrReader(errRead)))
136 res, _ := h.bufferedBody(body, limit)
137
138 got, err := io.ReadAll(res)
139 if !errors.Is(err, errRead) {
140 t.Errorf("limit %d: reading buffered body: got error %v, want %v", limit, err, errRead)
141 }
142 if string(got) != "0123456789" {
143 t.Errorf("limit %d: got %q, want %q", limit, got, "0123456789")
144 }
145
146 brc, ok := res.(bodyReadCloser)
147 if !ok {
148 t.Fatalf("limit %d: expected a bodyReadCloser", limit)
149 }
150 if brc.body == nil {
151 t.Errorf("limit %d: body reported as fully buffered", limit)
152 }
153 res.Close()
154 }
155 }