From afef73a5aacdb4f921ca5fddfb03b0a29998cbfe Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Tue, 3 Mar 2026 08:12:25 -0800 Subject: net/http: move DetectContentType into net/http/internal The http2 package needs access to DetectContentType, so move it into a common location. For #67810 Change-Id: Ibff3d57a4931106c2f69c5717c06bd5f6a6a6964 Reviewed-on: https://go-review.googlesource.com/c/go/+/751701 Reviewed-by: Nicholas Husin Auto-Submit: Damien Neil Reviewed-by: Nicholas Husin LUCI-TryBot-Result: Go LUCI --- src/go/build/deps_test.go | 2 +- src/net/http/fs.go | 3 +- src/net/http/internal/sniff.go | 304 +++++++++++++++++++++++++++++++++++++++++ src/net/http/server.go | 5 +- src/net/http/sniff.go | 291 +-------------------------------------- 5 files changed, 312 insertions(+), 293 deletions(-) create mode 100644 src/net/http/internal/sniff.go (limited to 'src') diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 986a5356e9..50346c35cd 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -636,7 +636,7 @@ var depsRules = ` context < internal/gate; - FMT + FMT, encoding/binary < golang.org/x/net/http2/hpack < net/http/internal, net/http/internal/ascii, net/http/internal/testcert; diff --git a/src/net/http/fs.go b/src/net/http/fs.go index 92bd94f72d..821b9b1266 100644 --- a/src/net/http/fs.go +++ b/src/net/http/fs.go @@ -14,6 +14,7 @@ import ( "io/fs" "mime" "mime/multipart" + "net/http/internal" "net/textproto" "net/url" "os" @@ -288,7 +289,7 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, ctype = mime.TypeByExtension(filepath.Ext(name)) if ctype == "" { // read a chunk to decide between utf-8 text and binary - var buf [sniffLen]byte + var buf [internal.SniffLen]byte n, _ := io.ReadFull(content, buf[:]) ctype = DetectContentType(buf[:n]) _, err := content.Seek(0, io.SeekStart) // rewind to output whole file diff --git a/src/net/http/internal/sniff.go b/src/net/http/internal/sniff.go new file mode 100644 index 0000000000..23b96e8992 --- /dev/null +++ b/src/net/http/internal/sniff.go @@ -0,0 +1,304 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package internal + +import ( + "bytes" + "encoding/binary" +) + +// The algorithm uses at most SniffLen bytes to make its decision. +const SniffLen = 512 + +// DetectContentType implements the algorithm described +// at https://mimesniff.spec.whatwg.org/ to determine the +// Content-Type of the given data. It considers at most the +// first 512 bytes of data. DetectContentType always returns +// a valid MIME type: if it cannot determine a more specific one, it +// returns "application/octet-stream". +func DetectContentType(data []byte) string { + if len(data) > SniffLen { + data = data[:SniffLen] + } + + // Index of the first non-whitespace byte in data. + firstNonWS := 0 + for ; firstNonWS < len(data) && isWS(data[firstNonWS]); firstNonWS++ { + } + + for _, sig := range sniffSignatures { + if ct := sig.match(data, firstNonWS); ct != "" { + return ct + } + } + + return "application/octet-stream" // fallback +} + +// isWS reports whether the provided byte is a whitespace byte (0xWS) +// as defined in https://mimesniff.spec.whatwg.org/#terminology. +func isWS(b byte) bool { + switch b { + case '\t', '\n', '\x0c', '\r', ' ': + return true + } + return false +} + +// isTT reports whether the provided byte is a tag-terminating byte (0xTT) +// as defined in https://mimesniff.spec.whatwg.org/#terminology. +func isTT(b byte) bool { + switch b { + case ' ', '>': + return true + } + return false +} + +type sniffSig interface { + // match returns the MIME type of the data, or "" if unknown. + match(data []byte, firstNonWS int) string +} + +// Data matching the table in section 6. +var sniffSignatures = []sniffSig{ + htmlSig(" sniffLen { - data = data[:sniffLen] - } - - // Index of the first non-whitespace byte in data. - firstNonWS := 0 - for ; firstNonWS < len(data) && isWS(data[firstNonWS]); firstNonWS++ { - } - - for _, sig := range sniffSignatures { - if ct := sig.match(data, firstNonWS); ct != "" { - return ct - } - } - - return "application/octet-stream" // fallback -} - -// isWS reports whether the provided byte is a whitespace byte (0xWS) -// as defined in https://mimesniff.spec.whatwg.org/#terminology. -func isWS(b byte) bool { - switch b { - case '\t', '\n', '\x0c', '\r', ' ': - return true - } - return false -} - -// isTT reports whether the provided byte is a tag-terminating byte (0xTT) -// as defined in https://mimesniff.spec.whatwg.org/#terminology. -func isTT(b byte) bool { - switch b { - case ' ', '>': - return true - } - return false -} - -type sniffSig interface { - // match returns the MIME type of the data, or "" if unknown. - match(data []byte, firstNonWS int) string -} - -// Data matching the table in section 6. -var sniffSignatures = []sniffSig{ - htmlSig("