diff options
3 files changed, 459 insertions, 0 deletions
diff --git a/meta/recipes-devtools/go/go-1.12.inc b/meta/recipes-devtools/go/go-1.12.inc index fd2d641554..2a0680aeaa 100644 --- a/meta/recipes-devtools/go/go-1.12.inc +++ b/meta/recipes-devtools/go/go-1.12.inc | |||
| @@ -20,6 +20,8 @@ SRC_URI += "\ | |||
| 20 | file://0010-fix-CVE-2019-17596.patch \ | 20 | file://0010-fix-CVE-2019-17596.patch \ |
| 21 | file://CVE-2020-15586.patch \ | 21 | file://CVE-2020-15586.patch \ |
| 22 | file://CVE-2020-16845.patch \ | 22 | file://CVE-2020-16845.patch \ |
| 23 | file://0001-net-http-cgi-rename-a-test-file-to-be-less-cute.patch \ | ||
| 24 | file://CVE-2020-24553.patch \ | ||
| 23 | " | 25 | " |
| 24 | SRC_URI_append_libc-musl = " file://0009-ld-replace-glibc-dynamic-linker-with-musl.patch" | 26 | SRC_URI_append_libc-musl = " file://0009-ld-replace-glibc-dynamic-linker-with-musl.patch" |
| 25 | 27 | ||
diff --git a/meta/recipes-devtools/go/go-1.12/0001-net-http-cgi-rename-a-test-file-to-be-less-cute.patch b/meta/recipes-devtools/go/go-1.12/0001-net-http-cgi-rename-a-test-file-to-be-less-cute.patch new file mode 100644 index 0000000000..7c07961c03 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.12/0001-net-http-cgi-rename-a-test-file-to-be-less-cute.patch | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | From 8390c478600b852392cb116741b3cb239c94d123 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Brad Fitzpatrick <bradfitz@golang.org> | ||
| 3 | Date: Wed, 15 Jan 2020 18:08:10 +0000 | ||
| 4 | Subject: [PATCH] net/http/cgi: rename a test file to be less cute | ||
| 5 | |||
| 6 | My fault (from CL 4245070), sorry. | ||
| 7 | |||
| 8 | Change-Id: Ib95d3170dc326e74aa74c22421c4e44a8b00f577 | ||
| 9 | Reviewed-on: https://go-review.googlesource.com/c/go/+/214920 | ||
| 10 | Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> | ||
| 11 | TryBot-Result: Gobot Gobot <gobot@golang.org> | ||
| 12 | Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com> | ||
| 13 | |||
| 14 | Upstream-Status: Backport | ||
| 15 | [lz: Add this patch for merging the patch for CVE-2020-24553] | ||
| 16 | Signed-off-by: Li Zhou <li.zhou@windriver.com> | ||
| 17 | --- | ||
| 18 | src/net/http/cgi/{matryoshka_test.go => integration_test.go} | 0 | ||
| 19 | 1 file changed, 0 insertions(+), 0 deletions(-) | ||
| 20 | rename src/net/http/cgi/{matryoshka_test.go => integration_test.go} (100%) | ||
| 21 | |||
| 22 | diff --git a/src/net/http/cgi/matryoshka_test.go b/src/net/http/cgi/integration_test.go | ||
| 23 | similarity index 100% | ||
| 24 | rename from src/net/http/cgi/matryoshka_test.go | ||
| 25 | rename to src/net/http/cgi/integration_test.go | ||
| 26 | -- | ||
| 27 | 2.17.1 | ||
| 28 | |||
diff --git a/meta/recipes-devtools/go/go-1.12/CVE-2020-24553.patch b/meta/recipes-devtools/go/go-1.12/CVE-2020-24553.patch new file mode 100644 index 0000000000..18a218bc9a --- /dev/null +++ b/meta/recipes-devtools/go/go-1.12/CVE-2020-24553.patch | |||
| @@ -0,0 +1,429 @@ | |||
| 1 | From eb07103a083237414145a45f029c873d57037e06 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Roberto Clapis <roberto@golang.org> | ||
| 3 | Date: Wed, 26 Aug 2020 08:53:03 +0200 | ||
| 4 | Subject: [PATCH] [release-branch.go1.15-security] net/http/cgi,net/http/fcgi: | ||
| 5 | add Content-Type detection | ||
| 6 | |||
| 7 | This CL ensures that responses served via CGI and FastCGI | ||
| 8 | have a Content-Type header based on the content of the | ||
| 9 | response if not explicitly set by handlers. | ||
| 10 | |||
| 11 | If the implementers of the handler did not explicitly | ||
| 12 | specify a Content-Type both CGI implementations would default | ||
| 13 | to "text/html", potentially causing cross-site scripting. | ||
| 14 | |||
| 15 | Thanks to RedTeam Pentesting GmbH for reporting this. | ||
| 16 | |||
| 17 | Fixes CVE-2020-24553 | ||
| 18 | |||
| 19 | Change-Id: I82cfc396309b5ab2e8d6e9a87eda8ea7e3799473 | ||
| 20 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/823217 | ||
| 21 | Reviewed-by: Russ Cox <rsc@google.com> | ||
| 22 | (cherry picked from commit 23d675d07fdc56aafd67c0a0b63d5b7e14708ff0) | ||
| 23 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/835311 | ||
| 24 | Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> | ||
| 25 | |||
| 26 | Upstream-Status: Backport | ||
| 27 | CVE: CVE-2020-24553 | ||
| 28 | Signed-off-by: Li Zhou <li.zhou@windriver.com> | ||
| 29 | --- | ||
| 30 | src/net/http/cgi/child.go | 36 ++++++++++----- | ||
| 31 | src/net/http/cgi/child_test.go | 69 ++++++++++++++++++++++++++++ | ||
| 32 | src/net/http/cgi/integration_test.go | 53 ++++++++++++++++++++- | ||
| 33 | src/net/http/fcgi/child.go | 39 ++++++++++++---- | ||
| 34 | src/net/http/fcgi/fcgi_test.go | 52 +++++++++++++++++++++ | ||
| 35 | 5 files changed, 227 insertions(+), 22 deletions(-) | ||
| 36 | |||
| 37 | diff --git a/src/net/http/cgi/child.go b/src/net/http/cgi/child.go | ||
| 38 | index 9474175f17..61de6165f6 100644 | ||
| 39 | --- a/src/net/http/cgi/child.go | ||
| 40 | +++ b/src/net/http/cgi/child.go | ||
| 41 | @@ -163,10 +163,12 @@ func Serve(handler http.Handler) error { | ||
| 42 | } | ||
| 43 | |||
| 44 | type response struct { | ||
| 45 | - req *http.Request | ||
| 46 | - header http.Header | ||
| 47 | - bufw *bufio.Writer | ||
| 48 | - headerSent bool | ||
| 49 | + req *http.Request | ||
| 50 | + header http.Header | ||
| 51 | + code int | ||
| 52 | + wroteHeader bool | ||
| 53 | + wroteCGIHeader bool | ||
| 54 | + bufw *bufio.Writer | ||
| 55 | } | ||
| 56 | |||
| 57 | func (r *response) Flush() { | ||
| 58 | @@ -178,26 +180,38 @@ func (r *response) Header() http.Header { | ||
| 59 | } | ||
| 60 | |||
| 61 | func (r *response) Write(p []byte) (n int, err error) { | ||
| 62 | - if !r.headerSent { | ||
| 63 | + if !r.wroteHeader { | ||
| 64 | r.WriteHeader(http.StatusOK) | ||
| 65 | } | ||
| 66 | + if !r.wroteCGIHeader { | ||
| 67 | + r.writeCGIHeader(p) | ||
| 68 | + } | ||
| 69 | return r.bufw.Write(p) | ||
| 70 | } | ||
| 71 | |||
| 72 | func (r *response) WriteHeader(code int) { | ||
| 73 | - if r.headerSent { | ||
| 74 | + if r.wroteHeader { | ||
| 75 | // Note: explicitly using Stderr, as Stdout is our HTTP output. | ||
| 76 | fmt.Fprintf(os.Stderr, "CGI attempted to write header twice on request for %s", r.req.URL) | ||
| 77 | return | ||
| 78 | } | ||
| 79 | - r.headerSent = true | ||
| 80 | - fmt.Fprintf(r.bufw, "Status: %d %s\r\n", code, http.StatusText(code)) | ||
| 81 | + r.wroteHeader = true | ||
| 82 | + r.code = code | ||
| 83 | +} | ||
| 84 | |||
| 85 | - // Set a default Content-Type | ||
| 86 | +// writeCGIHeader finalizes the header sent to the client and writes it to the output. | ||
| 87 | +// p is not written by writeHeader, but is the first chunk of the body | ||
| 88 | +// that will be written. It is sniffed for a Content-Type if none is | ||
| 89 | +// set explicitly. | ||
| 90 | +func (r *response) writeCGIHeader(p []byte) { | ||
| 91 | + if r.wroteCGIHeader { | ||
| 92 | + return | ||
| 93 | + } | ||
| 94 | + r.wroteCGIHeader = true | ||
| 95 | + fmt.Fprintf(r.bufw, "Status: %d %s\r\n", r.code, http.StatusText(r.code)) | ||
| 96 | if _, hasType := r.header["Content-Type"]; !hasType { | ||
| 97 | - r.header.Add("Content-Type", "text/html; charset=utf-8") | ||
| 98 | + r.header.Set("Content-Type", http.DetectContentType(p)) | ||
| 99 | } | ||
| 100 | - | ||
| 101 | r.header.Write(r.bufw) | ||
| 102 | r.bufw.WriteString("\r\n") | ||
| 103 | r.bufw.Flush() | ||
| 104 | diff --git a/src/net/http/cgi/child_test.go b/src/net/http/cgi/child_test.go | ||
| 105 | index 14e0af475f..f6ecb6eb80 100644 | ||
| 106 | --- a/src/net/http/cgi/child_test.go | ||
| 107 | +++ b/src/net/http/cgi/child_test.go | ||
| 108 | @@ -7,6 +7,11 @@ | ||
| 109 | package cgi | ||
| 110 | |||
| 111 | import ( | ||
| 112 | + "bufio" | ||
| 113 | + "bytes" | ||
| 114 | + "net/http" | ||
| 115 | + "net/http/httptest" | ||
| 116 | + "strings" | ||
| 117 | "testing" | ||
| 118 | ) | ||
| 119 | |||
| 120 | @@ -148,3 +153,67 @@ func TestRequestWithoutRemotePort(t *testing.T) { | ||
| 121 | t.Errorf("RemoteAddr: got %q; want %q", g, e) | ||
| 122 | } | ||
| 123 | } | ||
| 124 | + | ||
| 125 | +type countingWriter int | ||
| 126 | + | ||
| 127 | +func (c *countingWriter) Write(p []byte) (int, error) { | ||
| 128 | + *c += countingWriter(len(p)) | ||
| 129 | + return len(p), nil | ||
| 130 | +} | ||
| 131 | +func (c *countingWriter) WriteString(p string) (int, error) { | ||
| 132 | + *c += countingWriter(len(p)) | ||
| 133 | + return len(p), nil | ||
| 134 | +} | ||
| 135 | + | ||
| 136 | +func TestResponse(t *testing.T) { | ||
| 137 | + var tests = []struct { | ||
| 138 | + name string | ||
| 139 | + body string | ||
| 140 | + wantCT string | ||
| 141 | + }{ | ||
| 142 | + { | ||
| 143 | + name: "no body", | ||
| 144 | + wantCT: "text/plain; charset=utf-8", | ||
| 145 | + }, | ||
| 146 | + { | ||
| 147 | + name: "html", | ||
| 148 | + body: "<html><head><title>test page</title></head><body>This is a body</body></html>", | ||
| 149 | + wantCT: "text/html; charset=utf-8", | ||
| 150 | + }, | ||
| 151 | + { | ||
| 152 | + name: "text", | ||
| 153 | + body: strings.Repeat("gopher", 86), | ||
| 154 | + wantCT: "text/plain; charset=utf-8", | ||
| 155 | + }, | ||
| 156 | + { | ||
| 157 | + name: "jpg", | ||
| 158 | + body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024), | ||
| 159 | + wantCT: "image/jpeg", | ||
| 160 | + }, | ||
| 161 | + } | ||
| 162 | + for _, tt := range tests { | ||
| 163 | + t.Run(tt.name, func(t *testing.T) { | ||
| 164 | + var buf bytes.Buffer | ||
| 165 | + resp := response{ | ||
| 166 | + req: httptest.NewRequest("GET", "/", nil), | ||
| 167 | + header: http.Header{}, | ||
| 168 | + bufw: bufio.NewWriter(&buf), | ||
| 169 | + } | ||
| 170 | + n, err := resp.Write([]byte(tt.body)) | ||
| 171 | + if err != nil { | ||
| 172 | + t.Errorf("Write: unexpected %v", err) | ||
| 173 | + } | ||
| 174 | + if want := len(tt.body); n != want { | ||
| 175 | + t.Errorf("reported short Write: got %v want %v", n, want) | ||
| 176 | + } | ||
| 177 | + resp.writeCGIHeader(nil) | ||
| 178 | + resp.Flush() | ||
| 179 | + if got := resp.Header().Get("Content-Type"); got != tt.wantCT { | ||
| 180 | + t.Errorf("wrong content-type: got %q, want %q", got, tt.wantCT) | ||
| 181 | + } | ||
| 182 | + if !bytes.HasSuffix(buf.Bytes(), []byte(tt.body)) { | ||
| 183 | + t.Errorf("body was not correctly written") | ||
| 184 | + } | ||
| 185 | + }) | ||
| 186 | + } | ||
| 187 | +} | ||
| 188 | diff --git a/src/net/http/cgi/integration_test.go b/src/net/http/cgi/integration_test.go | ||
| 189 | index 32d59c09a3..295c3b82d4 100644 | ||
| 190 | --- a/src/net/http/cgi/integration_test.go | ||
| 191 | +++ b/src/net/http/cgi/integration_test.go | ||
| 192 | @@ -16,7 +16,9 @@ import ( | ||
| 193 | "io" | ||
| 194 | "net/http" | ||
| 195 | "net/http/httptest" | ||
| 196 | + "net/url" | ||
| 197 | "os" | ||
| 198 | + "strings" | ||
| 199 | "testing" | ||
| 200 | "time" | ||
| 201 | ) | ||
| 202 | @@ -52,7 +54,7 @@ func TestHostingOurselves(t *testing.T) { | ||
| 203 | } | ||
| 204 | replay := runCgiTest(t, h, "GET /test.go?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap) | ||
| 205 | |||
| 206 | - if expected, got := "text/html; charset=utf-8", replay.Header().Get("Content-Type"); got != expected { | ||
| 207 | + if expected, got := "text/plain; charset=utf-8", replay.Header().Get("Content-Type"); got != expected { | ||
| 208 | t.Errorf("got a Content-Type of %q; expected %q", got, expected) | ||
| 209 | } | ||
| 210 | if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected { | ||
| 211 | @@ -152,6 +154,51 @@ func TestChildOnlyHeaders(t *testing.T) { | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | +func TestChildContentType(t *testing.T) { | ||
| 216 | + testenv.MustHaveExec(t) | ||
| 217 | + | ||
| 218 | + h := &Handler{ | ||
| 219 | + Path: os.Args[0], | ||
| 220 | + Root: "/test.go", | ||
| 221 | + Args: []string{"-test.run=TestBeChildCGIProcess"}, | ||
| 222 | + } | ||
| 223 | + var tests = []struct { | ||
| 224 | + name string | ||
| 225 | + body string | ||
| 226 | + wantCT string | ||
| 227 | + }{ | ||
| 228 | + { | ||
| 229 | + name: "no body", | ||
| 230 | + wantCT: "text/plain; charset=utf-8", | ||
| 231 | + }, | ||
| 232 | + { | ||
| 233 | + name: "html", | ||
| 234 | + body: "<html><head><title>test page</title></head><body>This is a body</body></html>", | ||
| 235 | + wantCT: "text/html; charset=utf-8", | ||
| 236 | + }, | ||
| 237 | + { | ||
| 238 | + name: "text", | ||
| 239 | + body: strings.Repeat("gopher", 86), | ||
| 240 | + wantCT: "text/plain; charset=utf-8", | ||
| 241 | + }, | ||
| 242 | + { | ||
| 243 | + name: "jpg", | ||
| 244 | + body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024), | ||
| 245 | + wantCT: "image/jpeg", | ||
| 246 | + }, | ||
| 247 | + } | ||
| 248 | + for _, tt := range tests { | ||
| 249 | + t.Run(tt.name, func(t *testing.T) { | ||
| 250 | + expectedMap := map[string]string{"_body": tt.body} | ||
| 251 | + req := fmt.Sprintf("GET /test.go?exact-body=%s HTTP/1.0\nHost: example.com\n\n", url.QueryEscape(tt.body)) | ||
| 252 | + replay := runCgiTest(t, h, req, expectedMap) | ||
| 253 | + if got := replay.Header().Get("Content-Type"); got != tt.wantCT { | ||
| 254 | + t.Errorf("got a Content-Type of %q; expected it to start with %q", got, tt.wantCT) | ||
| 255 | + } | ||
| 256 | + }) | ||
| 257 | + } | ||
| 258 | +} | ||
| 259 | + | ||
| 260 | // golang.org/issue/7198 | ||
| 261 | func Test500WithNoHeaders(t *testing.T) { want500Test(t, "/immediate-disconnect") } | ||
| 262 | func Test500WithNoContentType(t *testing.T) { want500Test(t, "/no-content-type") } | ||
| 263 | @@ -203,6 +250,10 @@ func TestBeChildCGIProcess(t *testing.T) { | ||
| 264 | if req.FormValue("no-body") == "1" { | ||
| 265 | return | ||
| 266 | } | ||
| 267 | + if eb, ok := req.Form["exact-body"]; ok { | ||
| 268 | + io.WriteString(rw, eb[0]) | ||
| 269 | + return | ||
| 270 | + } | ||
| 271 | if req.FormValue("write-forever") == "1" { | ||
| 272 | io.Copy(rw, neverEnding('a')) | ||
| 273 | for { | ||
| 274 | diff --git a/src/net/http/fcgi/child.go b/src/net/http/fcgi/child.go | ||
| 275 | index 30a6b2ce2d..a31273b3ec 100644 | ||
| 276 | --- a/src/net/http/fcgi/child.go | ||
| 277 | +++ b/src/net/http/fcgi/child.go | ||
| 278 | @@ -74,10 +74,12 @@ func (r *request) parseParams() { | ||
| 279 | |||
| 280 | // response implements http.ResponseWriter. | ||
| 281 | type response struct { | ||
| 282 | - req *request | ||
| 283 | - header http.Header | ||
| 284 | - w *bufWriter | ||
| 285 | - wroteHeader bool | ||
| 286 | + req *request | ||
| 287 | + header http.Header | ||
| 288 | + code int | ||
| 289 | + wroteHeader bool | ||
| 290 | + wroteCGIHeader bool | ||
| 291 | + w *bufWriter | ||
| 292 | } | ||
| 293 | |||
| 294 | func newResponse(c *child, req *request) *response { | ||
| 295 | @@ -92,11 +94,14 @@ func (r *response) Header() http.Header { | ||
| 296 | return r.header | ||
| 297 | } | ||
| 298 | |||
| 299 | -func (r *response) Write(data []byte) (int, error) { | ||
| 300 | +func (r *response) Write(p []byte) (n int, err error) { | ||
| 301 | if !r.wroteHeader { | ||
| 302 | r.WriteHeader(http.StatusOK) | ||
| 303 | } | ||
| 304 | - return r.w.Write(data) | ||
| 305 | + if !r.wroteCGIHeader { | ||
| 306 | + r.writeCGIHeader(p) | ||
| 307 | + } | ||
| 308 | + return r.w.Write(p) | ||
| 309 | } | ||
| 310 | |||
| 311 | func (r *response) WriteHeader(code int) { | ||
| 312 | @@ -104,22 +109,34 @@ func (r *response) WriteHeader(code int) { | ||
| 313 | return | ||
| 314 | } | ||
| 315 | r.wroteHeader = true | ||
| 316 | + r.code = code | ||
| 317 | if code == http.StatusNotModified { | ||
| 318 | // Must not have body. | ||
| 319 | r.header.Del("Content-Type") | ||
| 320 | r.header.Del("Content-Length") | ||
| 321 | r.header.Del("Transfer-Encoding") | ||
| 322 | - } else if r.header.Get("Content-Type") == "" { | ||
| 323 | - r.header.Set("Content-Type", "text/html; charset=utf-8") | ||
| 324 | } | ||
| 325 | - | ||
| 326 | if r.header.Get("Date") == "" { | ||
| 327 | r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat)) | ||
| 328 | } | ||
| 329 | +} | ||
| 330 | |||
| 331 | - fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code)) | ||
| 332 | +// writeCGIHeader finalizes the header sent to the client and writes it to the output. | ||
| 333 | +// p is not written by writeHeader, but is the first chunk of the body | ||
| 334 | +// that will be written. It is sniffed for a Content-Type if none is | ||
| 335 | +// set explicitly. | ||
| 336 | +func (r *response) writeCGIHeader(p []byte) { | ||
| 337 | + if r.wroteCGIHeader { | ||
| 338 | + return | ||
| 339 | + } | ||
| 340 | + r.wroteCGIHeader = true | ||
| 341 | + fmt.Fprintf(r.w, "Status: %d %s\r\n", r.code, http.StatusText(r.code)) | ||
| 342 | + if _, hasType := r.header["Content-Type"]; r.code != http.StatusNotModified && !hasType { | ||
| 343 | + r.header.Set("Content-Type", http.DetectContentType(p)) | ||
| 344 | + } | ||
| 345 | r.header.Write(r.w) | ||
| 346 | r.w.WriteString("\r\n") | ||
| 347 | + r.w.Flush() | ||
| 348 | } | ||
| 349 | |||
| 350 | func (r *response) Flush() { | ||
| 351 | @@ -290,6 +307,8 @@ func (c *child) serveRequest(req *request, body io.ReadCloser) { | ||
| 352 | httpReq = httpReq.WithContext(envVarCtx) | ||
| 353 | c.handler.ServeHTTP(r, httpReq) | ||
| 354 | } | ||
| 355 | + // Make sure we serve something even if nothing was written to r | ||
| 356 | + r.Write(nil) | ||
| 357 | r.Close() | ||
| 358 | c.mu.Lock() | ||
| 359 | delete(c.requests, req.reqId) | ||
| 360 | diff --git a/src/net/http/fcgi/fcgi_test.go b/src/net/http/fcgi/fcgi_test.go | ||
| 361 | index e9d2b34023..4a27a12c35 100644 | ||
| 362 | --- a/src/net/http/fcgi/fcgi_test.go | ||
| 363 | +++ b/src/net/http/fcgi/fcgi_test.go | ||
| 364 | @@ -10,6 +10,7 @@ import ( | ||
| 365 | "io" | ||
| 366 | "io/ioutil" | ||
| 367 | "net/http" | ||
| 368 | + "strings" | ||
| 369 | "testing" | ||
| 370 | ) | ||
| 371 | |||
| 372 | @@ -344,3 +345,54 @@ func TestChildServeReadsEnvVars(t *testing.T) { | ||
| 373 | <-done | ||
| 374 | } | ||
| 375 | } | ||
| 376 | + | ||
| 377 | +func TestResponseWriterSniffsContentType(t *testing.T) { | ||
| 378 | + var tests = []struct { | ||
| 379 | + name string | ||
| 380 | + body string | ||
| 381 | + wantCT string | ||
| 382 | + }{ | ||
| 383 | + { | ||
| 384 | + name: "no body", | ||
| 385 | + wantCT: "text/plain; charset=utf-8", | ||
| 386 | + }, | ||
| 387 | + { | ||
| 388 | + name: "html", | ||
| 389 | + body: "<html><head><title>test page</title></head><body>This is a body</body></html>", | ||
| 390 | + wantCT: "text/html; charset=utf-8", | ||
| 391 | + }, | ||
| 392 | + { | ||
| 393 | + name: "text", | ||
| 394 | + body: strings.Repeat("gopher", 86), | ||
| 395 | + wantCT: "text/plain; charset=utf-8", | ||
| 396 | + }, | ||
| 397 | + { | ||
| 398 | + name: "jpg", | ||
| 399 | + body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024), | ||
| 400 | + wantCT: "image/jpeg", | ||
| 401 | + }, | ||
| 402 | + } | ||
| 403 | + for _, tt := range tests { | ||
| 404 | + t.Run(tt.name, func(t *testing.T) { | ||
| 405 | + input := make([]byte, len(streamFullRequestStdin)) | ||
| 406 | + copy(input, streamFullRequestStdin) | ||
| 407 | + rc := nopWriteCloser{bytes.NewBuffer(input)} | ||
| 408 | + done := make(chan bool) | ||
| 409 | + var resp *response | ||
| 410 | + c := newChild(rc, http.HandlerFunc(func( | ||
| 411 | + w http.ResponseWriter, | ||
| 412 | + r *http.Request, | ||
| 413 | + ) { | ||
| 414 | + io.WriteString(w, tt.body) | ||
| 415 | + resp = w.(*response) | ||
| 416 | + done <- true | ||
| 417 | + })) | ||
| 418 | + defer c.cleanUp() | ||
| 419 | + go c.serve() | ||
| 420 | + <-done | ||
| 421 | + if got := resp.Header().Get("Content-Type"); got != tt.wantCT { | ||
| 422 | + t.Errorf("got a Content-Type of %q; expected it to start with %q", got, tt.wantCT) | ||
| 423 | + } | ||
| 424 | + }) | ||
| 425 | + } | ||
| 426 | +} | ||
| 427 | -- | ||
| 428 | 2.17.1 | ||
| 429 | |||
