Source file
src/net/http/sniff_test.go
1
2
3
4
5 package http_test
6
7 import (
8 "bytes"
9 "fmt"
10 "io"
11 "log"
12 . "net/http"
13 "reflect"
14 "strconv"
15 "strings"
16 "testing"
17 )
18
19 var sniffTests = []struct {
20 desc string
21 data []byte
22 contentType string
23 }{
24
25 {"Empty", []byte{}, "text/plain; charset=utf-8"},
26 {"Binary", []byte{1, 2, 3}, "application/octet-stream"},
27
28 {"HTML document #1", []byte(`<HtMl><bOdY>blah blah blah</body></html>`), "text/html; charset=utf-8"},
29 {"HTML document #2", []byte(`<HTML></HTML>`), "text/html; charset=utf-8"},
30 {"HTML document #3 (leading whitespace)", []byte(` <!DOCTYPE HTML>...`), "text/html; charset=utf-8"},
31 {"HTML document #4 (leading CRLF)", []byte("\r\n<html>..."), "text/html; charset=utf-8"},
32
33 {"Plain text", []byte(`This is not HTML. It has ☃ though.`), "text/plain; charset=utf-8"},
34
35 {"XML", []byte("\n<?xml!"), "text/xml; charset=utf-8"},
36
37
38 {"Windows icon", []byte("\x00\x00\x01\x00"), "image/x-icon"},
39 {"Windows cursor", []byte("\x00\x00\x02\x00"), "image/x-icon"},
40 {"BMP image", []byte("BM..."), "image/bmp"},
41 {"GIF 87a", []byte(`GIF87a`), "image/gif"},
42 {"GIF 89a", []byte(`GIF89a...`), "image/gif"},
43 {"WEBP image", []byte("RIFF\x00\x00\x00\x00WEBPVP"), "image/webp"},
44 {"PNG image", []byte("\x89PNG\x0D\x0A\x1A\x0A"), "image/png"},
45 {"JPEG image", []byte("\xFF\xD8\xFF"), "image/jpeg"},
46
47
48 {"MIDI audio", []byte("MThd\x00\x00\x00\x06\x00\x01"), "audio/midi"},
49 {"MP3 audio/MPEG audio", []byte("ID3\x03\x00\x00\x00\x00\x0f"), "audio/mpeg"},
50 {"WAV audio #1", []byte("RIFFb\xb8\x00\x00WAVEfmt \x12\x00\x00\x00\x06"), "audio/wave"},
51 {"WAV audio #2", []byte("RIFF,\x00\x00\x00WAVEfmt \x12\x00\x00\x00\x06"), "audio/wave"},
52 {"AIFF audio #1", []byte("FORM\x00\x00\x00\x00AIFFCOMM\x00\x00\x00\x12\x00\x01\x00\x00\x57\x55\x00\x10\x40\x0d\xf3\x34"), "audio/aiff"},
53
54 {"OGG audio", []byte("OggS\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x7e\x46\x00\x00\x00\x00\x00\x00\x1f\xf6\xb4\xfc\x01\x1e\x01\x76\x6f\x72"), "application/ogg"},
55 {"Must not match OGG", []byte("owow\x00"), "application/octet-stream"},
56 {"Must not match OGG", []byte("oooS\x00"), "application/octet-stream"},
57 {"Must not match OGG", []byte("oggS\x00"), "application/octet-stream"},
58
59
60 {"MP4 video", []byte("\x00\x00\x00\x18ftypmp42\x00\x00\x00\x00mp42isom<\x06t\xbfmdat"), "video/mp4"},
61 {"AVI video #1", []byte("RIFF,O\n\x00AVI LISTÀ"), "video/avi"},
62 {"AVI video #2", []byte("RIFF,\n\x00\x00AVI LISTÀ"), "video/avi"},
63
64
65
66 {"TTF sample I", []byte("\x00\x01\x00\x00\x00\x17\x01\x00\x00\x04\x01\x60\x4f"), "font/ttf"},
67 {"TTF sample II", []byte("\x00\x01\x00\x00\x00\x0e\x00\x80\x00\x03\x00\x60\x46"), "font/ttf"},
68
69 {"OTTO sample I", []byte("\x4f\x54\x54\x4f\x00\x0e\x00\x80\x00\x03\x00\x60\x42\x41\x53\x45"), "font/otf"},
70
71 {"woff sample I", []byte("\x77\x4f\x46\x46\x00\x01\x00\x00\x00\x00\x30\x54\x00\x0d\x00\x00"), "font/woff"},
72 {"woff2 sample", []byte("\x77\x4f\x46\x32\x00\x01\x00\x00\x00"), "font/woff2"},
73 {"wasm sample", []byte("\x00\x61\x73\x6d\x01\x00"), "application/wasm"},
74
75
76 {"RAR v1.5-v4.0", []byte("Rar!\x1A\x07\x00"), "application/x-rar-compressed"},
77 {"RAR v5+", []byte("Rar!\x1A\x07\x01\x00"), "application/x-rar-compressed"},
78 {"Incorrect RAR v1.5-v4.0", []byte("Rar \x1A\x07\x00"), "application/octet-stream"},
79 {"Incorrect RAR v5+", []byte("Rar \x1A\x07\x01\x00"), "application/octet-stream"},
80 }
81
82 func TestDetectContentType(t *testing.T) {
83 for _, tt := range sniffTests {
84 ct := DetectContentType(tt.data)
85 if ct != tt.contentType {
86 t.Errorf("%v: DetectContentType = %q, want %q", tt.desc, ct, tt.contentType)
87 }
88 }
89 }
90
91 func TestServerContentType_h1(t *testing.T) { testServerContentType(t, h1Mode) }
92 func TestServerContentType_h2(t *testing.T) { testServerContentType(t, h2Mode) }
93
94 func testServerContentType(t *testing.T, h2 bool) {
95 setParallel(t)
96 defer afterTest(t)
97 cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
98 i, _ := strconv.Atoi(r.FormValue("i"))
99 tt := sniffTests[i]
100 n, err := w.Write(tt.data)
101 if n != len(tt.data) || err != nil {
102 log.Fatalf("%v: Write(%q) = %v, %v want %d, nil", tt.desc, tt.data, n, err, len(tt.data))
103 }
104 }))
105 defer cst.close()
106
107 for i, tt := range sniffTests {
108 resp, err := cst.c.Get(cst.ts.URL + "/?i=" + strconv.Itoa(i))
109 if err != nil {
110 t.Errorf("%v: %v", tt.desc, err)
111 continue
112 }
113
114
115
116
117
118 wantContentType := tt.contentType
119 if len(tt.data) == 0 {
120 wantContentType = ""
121 }
122 if ct := resp.Header.Get("Content-Type"); ct != wantContentType {
123 t.Errorf("%v: Content-Type = %q, want %q", tt.desc, ct, wantContentType)
124 }
125 data, err := io.ReadAll(resp.Body)
126 if err != nil {
127 t.Errorf("%v: reading body: %v", tt.desc, err)
128 } else if !bytes.Equal(data, tt.data) {
129 t.Errorf("%v: data is %q, want %q", tt.desc, data, tt.data)
130 }
131 resp.Body.Close()
132 }
133 }
134
135
136
137 func TestServerIssue5953_h1(t *testing.T) { testServerIssue5953(t, h1Mode) }
138 func TestServerIssue5953_h2(t *testing.T) { testServerIssue5953(t, h2Mode) }
139 func testServerIssue5953(t *testing.T, h2 bool) {
140 defer afterTest(t)
141 cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
142 w.Header()["Content-Type"] = []string{""}
143 fmt.Fprintf(w, "<html><head></head><body>hi</body></html>")
144 }))
145 defer cst.close()
146
147 resp, err := cst.c.Get(cst.ts.URL)
148 if err != nil {
149 t.Fatal(err)
150 }
151
152 got := resp.Header["Content-Type"]
153 want := []string{""}
154 if !reflect.DeepEqual(got, want) {
155 t.Errorf("Content-Type = %q; want %q", got, want)
156 }
157 resp.Body.Close()
158 }
159
160 type byteAtATimeReader struct {
161 buf []byte
162 }
163
164 func (b *byteAtATimeReader) Read(p []byte) (n int, err error) {
165 if len(p) < 1 {
166 return 0, nil
167 }
168 if len(b.buf) == 0 {
169 return 0, io.EOF
170 }
171 p[0] = b.buf[0]
172 b.buf = b.buf[1:]
173 return 1, nil
174 }
175
176 func TestContentTypeWithVariousSources_h1(t *testing.T) { testContentTypeWithVariousSources(t, h1Mode) }
177 func TestContentTypeWithVariousSources_h2(t *testing.T) { testContentTypeWithVariousSources(t, h2Mode) }
178 func testContentTypeWithVariousSources(t *testing.T, h2 bool) {
179 defer afterTest(t)
180
181 const (
182 input = "\n<html>\n\t<head>\n"
183 expected = "text/html; charset=utf-8"
184 )
185
186 for _, test := range []struct {
187 name string
188 handler func(ResponseWriter, *Request)
189 }{{
190 name: "write",
191 handler: func(w ResponseWriter, r *Request) {
192
193 n, err := w.Write([]byte(input))
194 if int(n) != len(input) || err != nil {
195 t.Errorf("w.Write(%q) = %v, %v want %d, nil", input, n, err, len(input))
196 }
197 },
198 }, {
199 name: "write one byte at a time",
200 handler: func(w ResponseWriter, r *Request) {
201
202 buf := []byte(input)
203 for i := range buf {
204 n, err := w.Write(buf[i : i+1])
205 if n != 1 || err != nil {
206 t.Errorf("w.Write(%q) = %v, %v want 1, nil", input, n, err)
207 }
208 }
209 },
210 }, {
211 name: "copy from Reader",
212 handler: func(w ResponseWriter, r *Request) {
213
214 type readerOnly struct{ io.Reader }
215 buf := bytes.NewBuffer([]byte(input))
216 n, err := io.Copy(w, readerOnly{buf})
217 if int(n) != len(input) || err != nil {
218 t.Errorf("io.Copy(w, %q) = %v, %v want %d, nil", input, n, err, len(input))
219 }
220 },
221 }, {
222 name: "copy from bytes.Buffer",
223 handler: func(w ResponseWriter, r *Request) {
224
225 buf := bytes.NewBuffer([]byte(input))
226 n, err := io.Copy(w, buf)
227 if int(n) != len(input) || err != nil {
228 t.Errorf("io.Copy(w, %q) = %v, %v want %d, nil", input, n, err, len(input))
229 }
230 },
231 }, {
232 name: "copy one byte at a time",
233 handler: func(w ResponseWriter, r *Request) {
234
235 n, err := io.Copy(w, &byteAtATimeReader{[]byte(input)})
236 if int(n) != len(input) || err != nil {
237 t.Errorf("io.Copy(w, %q) = %v, %v want %d, nil", input, n, err, len(input))
238 }
239 },
240 }} {
241 t.Run(test.name, func(t *testing.T) {
242 cst := newClientServerTest(t, h2, HandlerFunc(test.handler))
243 defer cst.close()
244
245 resp, err := cst.c.Get(cst.ts.URL)
246 if err != nil {
247 t.Fatalf("Get: %v", err)
248 }
249 if ct := resp.Header.Get("Content-Type"); ct != expected {
250 t.Errorf("Content-Type = %q, want %q", ct, expected)
251 }
252 if want, got := resp.Header.Get("Content-Length"), fmt.Sprint(len(input)); want != got {
253 t.Errorf("Content-Length = %q, want %q", want, got)
254 }
255 data, err := io.ReadAll(resp.Body)
256 if err != nil {
257 t.Errorf("reading body: %v", err)
258 } else if !bytes.Equal(data, []byte(input)) {
259 t.Errorf("data is %q, want %q", data, input)
260 }
261 resp.Body.Close()
262
263 })
264
265 }
266 }
267
268 func TestSniffWriteSize_h1(t *testing.T) { testSniffWriteSize(t, h1Mode) }
269 func TestSniffWriteSize_h2(t *testing.T) { testSniffWriteSize(t, h2Mode) }
270 func testSniffWriteSize(t *testing.T, h2 bool) {
271 setParallel(t)
272 defer afterTest(t)
273 cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
274 size, _ := strconv.Atoi(r.FormValue("size"))
275 written, err := io.WriteString(w, strings.Repeat("a", size))
276 if err != nil {
277 t.Errorf("write of %d bytes: %v", size, err)
278 return
279 }
280 if written != size {
281 t.Errorf("write of %d bytes wrote %d bytes", size, written)
282 }
283 }))
284 defer cst.close()
285 for _, size := range []int{0, 1, 200, 600, 999, 1000, 1023, 1024, 512 << 10, 1 << 20} {
286 res, err := cst.c.Get(fmt.Sprintf("%s/?size=%d", cst.ts.URL, size))
287 if err != nil {
288 t.Fatalf("size %d: %v", size, err)
289 }
290 if _, err := io.Copy(io.Discard, res.Body); err != nil {
291 t.Fatalf("size %d: io.Copy of body = %v", size, err)
292 }
293 if err := res.Body.Close(); err != nil {
294 t.Fatalf("size %d: body Close = %v", size, err)
295 }
296 }
297 }
298
View as plain text