Commit 21e6b90d authored by Michael Piatek's avatar Michael Piatek Committed by David Symonds

net/http: skip content-type sniffing if the header is explicitly unset.

Fixes #5953

R=dsymonds, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/14434044
parent 51c0d7c0
...@@ -140,9 +140,11 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, ...@@ -140,9 +140,11 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
code := StatusOK code := StatusOK
// If Content-Type isn't set, use the file's extension to find it. // If Content-Type isn't set, use the file's extension to find it, but
ctype := w.Header().Get("Content-Type") // if the Content-Type is unset explicitly, do not sniff the type.
if ctype == "" { ctypes, haveType := w.Header()["Content-Type"]
var ctype string
if !haveType {
ctype = mime.TypeByExtension(filepath.Ext(name)) ctype = mime.TypeByExtension(filepath.Ext(name))
if ctype == "" { if ctype == "" {
// read a chunk to decide between utf-8 text and binary // read a chunk to decide between utf-8 text and binary
...@@ -156,6 +158,8 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, ...@@ -156,6 +158,8 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
} }
} }
w.Header().Set("Content-Type", ctype) w.Header().Set("Content-Type", ctype)
} else if len(ctypes) > 0 {
ctype = ctypes[0]
} }
size, err := sizeFunc() size, err := sizeFunc()
......
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"os/exec" "os/exec"
"path" "path"
"path/filepath" "path/filepath"
"reflect"
"regexp" "regexp"
"runtime" "runtime"
"strconv" "strconv"
...@@ -319,24 +320,29 @@ func TestServeFileContentType(t *testing.T) { ...@@ -319,24 +320,29 @@ func TestServeFileContentType(t *testing.T) {
defer afterTest(t) defer afterTest(t)
const ctype = "icecream/chocolate" const ctype = "icecream/chocolate"
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
if r.FormValue("override") == "1" { switch r.FormValue("override") {
case "1":
w.Header().Set("Content-Type", ctype) w.Header().Set("Content-Type", ctype)
case "2":
// Explicitly inhibit sniffing.
w.Header()["Content-Type"] = []string{}
} }
ServeFile(w, r, "testdata/file") ServeFile(w, r, "testdata/file")
})) }))
defer ts.Close() defer ts.Close()
get := func(override, want string) { get := func(override string, want []string) {
resp, err := Get(ts.URL + "?override=" + override) resp, err := Get(ts.URL + "?override=" + override)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if h := resp.Header.Get("Content-Type"); h != want { if h := resp.Header["Content-Type"]; !reflect.DeepEqual(h, want) {
t.Errorf("Content-Type mismatch: got %q, want %q", h, want) t.Errorf("Content-Type mismatch: got %v, want %v", h, want)
} }
resp.Body.Close() resp.Body.Close()
} }
get("0", "text/plain; charset=utf-8") get("0", []string{"text/plain; charset=utf-8"})
get("1", ctype) get("1", []string{ctype})
get("2", nil)
} }
func TestServeFileMimeType(t *testing.T) { func TestServeFileMimeType(t *testing.T) {
......
...@@ -614,3 +614,16 @@ func TestResponseContentLengthShortBody(t *testing.T) { ...@@ -614,3 +614,16 @@ func TestResponseContentLengthShortBody(t *testing.T) {
t.Errorf("io.Copy error = %#v; want io.ErrUnexpectedEOF", err) t.Errorf("io.Copy error = %#v; want io.ErrUnexpectedEOF", err)
} }
} }
func TestNeedsSniff(t *testing.T) {
// needsSniff returns true with an empty response.
r := &response{}
if got, want := r.needsSniff(), true; got != want {
t.Errorf("needsSniff = %t; want %t", got, want)
}
// needsSniff returns false when Content-Type = nil.
r.handlerHeader = Header{"Content-Type": nil}
if got, want := r.needsSniff(), false; got != want {
t.Errorf("needsSniff empty Content-Type = %t; want %t", got, want)
}
}
...@@ -341,7 +341,8 @@ func (w *response) requestTooLarge() { ...@@ -341,7 +341,8 @@ func (w *response) requestTooLarge() {
// needsSniff reports whether a Content-Type still needs to be sniffed. // needsSniff reports whether a Content-Type still needs to be sniffed.
func (w *response) needsSniff() bool { func (w *response) needsSniff() bool {
return !w.cw.wroteHeader && w.handlerHeader.Get("Content-Type") == "" && w.written < sniffLen _, haveType := w.handlerHeader["Content-Type"]
return !w.cw.wroteHeader && !haveType && w.written < sniffLen
} }
// writerOnly hides an io.Writer value's optional ReadFrom method // writerOnly hides an io.Writer value's optional ReadFrom method
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment