@@ -5,7 +5,10 @@ import (
5
5
"bytes"
6
6
"encoding/base64"
7
7
"encoding/binary"
8
+ "fmt"
9
+ "net/textproto"
8
10
"net/url"
11
+ "reflect"
9
12
"strings"
10
13
"testing"
11
14
)
@@ -139,3 +142,93 @@ func FuzzURIParse(f *testing.F) {
139
142
}
140
143
})
141
144
}
145
+
146
+ func FuzzTestHeaderScanner (f * testing.F ) {
147
+ f .Add ([]byte ("Host: example.com\r \n User-Agent: Go-http-client/1.1\r \n Accept-Encoding: gzip, deflate\r \n \r \n " ))
148
+ f .Add ([]byte ("Content-Type: application/x-www-form-urlencoded\r \n Content-Length: 27\r \n \r \n name=John+Doe&age=30" ))
149
+
150
+ f .Fuzz (func (t * testing.T , data []byte ) {
151
+ if ! bytes .Contains (data , []byte ("\r \n \r \n " )) {
152
+ return
153
+ }
154
+
155
+ t .Logf ("%q" , data )
156
+
157
+ tmp , herr := textproto .NewReader (bufio .NewReader (bytes .NewReader (data ))).ReadMIMEHeader ()
158
+ h := map [string ][]string (tmp )
159
+
160
+ var s headerScanner
161
+ s .b = data
162
+ f := make (map [string ][]string )
163
+ for s .next () {
164
+ // ReadMIMEHeader normalizes header keys, headerScanner doesn't by default.
165
+ normalizeHeaderKey (s .key , false )
166
+
167
+ // textproto.ReadMIMEHeader will validate the header value, since we compare
168
+ // errors we should do this as well.
169
+ for _ , c := range s .value {
170
+ if ! validHeaderValueByte (c ) {
171
+ s .err = fmt .Errorf ("malformed MIME header: invalid byte %q in value %q for key %q" , c , s .value , s .key )
172
+ }
173
+ }
174
+ if s .err != nil {
175
+ break
176
+ }
177
+
178
+ key := string (s .key )
179
+ value := string (s .value )
180
+
181
+ if _ , ok := f [key ]; ! ok {
182
+ f [key ] = []string {}
183
+ }
184
+ f [key ] = append (f [key ], value )
185
+ }
186
+
187
+ if s .err != nil && herr == nil {
188
+ t .Errorf ("unexpected error from headerScanner: %v: %v" , s .err , h )
189
+ } else if s .err == nil && herr != nil {
190
+ t .Errorf ("unexpected error from textproto.NewReader: %v: %v" , herr , f )
191
+ }
192
+
193
+ if ! reflect .DeepEqual (h , f ) {
194
+ t .Errorf ("headers mismatch:\n textproto: %v\n fasthttp: %v" , h , f )
195
+ }
196
+ })
197
+ }
198
+
199
+ func FuzzRequestReadLimitBodyAllocations (f * testing.F ) {
200
+ f .Add ([]byte ("POST /a HTTP/1.1\r \n Host: a.com\r \n Transfer-Encoding: chunked\r \n Content-Type: aa\r \n \r \n 6\r \n foobar\r \n 3\r \n baz\r \n 0\r \n foobar\r \n \r \n " ), 1024 )
201
+ f .Add ([]byte ("POST /a HTTP/1.1\r \n Host: a.com\r \n WithTabs: \t v1 \t \r \n WithTabs-Start: \t \t v1 \r \n WithTabs-End: v1 \t \t \t \t \r \n WithTabs-Multi-Line: \t v1 \t ;\r \n \t v2 \t ;\r \n \t v3\r \n \r \n " ), 1024 )
202
+
203
+ f .Fuzz (func (t * testing.T , body []byte , maxBodySize int ) {
204
+ if len (body ) > 1024 * 1024 || maxBodySize > 1024 * 1024 {
205
+ return
206
+ }
207
+ // Only test with a max for the body, otherwise a very large Content-Length will just OOM.
208
+ if maxBodySize <= 0 {
209
+ return
210
+ }
211
+
212
+ t .Logf ("%d %q" , maxBodySize , body )
213
+
214
+ req := Request {}
215
+ a := bytes .NewReader (body )
216
+ b := bufio .NewReader (a )
217
+
218
+ if err := req .ReadLimitBody (b , maxBodySize ); err != nil {
219
+ return
220
+ }
221
+
222
+ n := testing .AllocsPerRun (200 , func () {
223
+ req .Reset ()
224
+ a .Reset (body )
225
+ b .Reset (a )
226
+
227
+ _ = req .ReadLimitBody (b , maxBodySize )
228
+ })
229
+
230
+ if n != 0 {
231
+ t .Fatalf ("expected 0 allocations, got %f" , n )
232
+ }
233
+ })
234
+ }
0 commit comments