// Copyright 2010 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 httpimport ()// A Header represents the key-value pairs in an HTTP header.//// The keys should be in canonical form, as returned by// [CanonicalHeaderKey].typeHeadermap[string][]string// Add adds the key, value pair to the header.// It appends to any existing values associated with key.// The key is case insensitive; it is canonicalized by// [CanonicalHeaderKey].func ( Header) (, string) {textproto.MIMEHeader().Add(, )}// Set sets the header entries associated with key to the// single element value. It replaces any existing values// associated with key. The key is case insensitive; it is// canonicalized by [textproto.CanonicalMIMEHeaderKey].// To use non-canonical keys, assign to the map directly.func ( Header) (, string) {textproto.MIMEHeader().Set(, )}// Get gets the first value associated with the given key. If// there are no values associated with the key, Get returns "".// It is case insensitive; [textproto.CanonicalMIMEHeaderKey] is// used to canonicalize the provided key. Get assumes that all// keys are stored in canonical form. To use non-canonical keys,// access the map directly.func ( Header) ( string) string {returntextproto.MIMEHeader().Get()}// Values returns all values associated with the given key.// It is case insensitive; [textproto.CanonicalMIMEHeaderKey] is// used to canonicalize the provided key. To use non-canonical// keys, access the map directly.// The returned slice is not a copy.func ( Header) ( string) []string {returntextproto.MIMEHeader().Values()}// get is like Get, but key must already be in CanonicalHeaderKey form.func ( Header) ( string) string {if := []; len() > 0 {return [0] }return""}// has reports whether h has the provided key defined, even if it's// set to 0-length slice.func ( Header) ( string) bool { , := []return}// Del deletes the values associated with key.// The key is case insensitive; it is canonicalized by// [CanonicalHeaderKey].func ( Header) ( string) {textproto.MIMEHeader().Del()}// Write writes a header in wire format.func ( Header) ( io.Writer) error {return .write(, nil)}func ( Header) ( io.Writer, *httptrace.ClientTrace) error {return .writeSubset(, nil, )}// Clone returns a copy of h or nil if h is nil.func ( Header) () Header {if == nil {returnnil }// Find total number of values. := 0for , := range { += len() } := make([]string, ) // shared backing array for headers' values := make(Header, len())for , := range {if == nil {// Preserve nil values. ReverseProxy distinguishes // between nil and zero-length header values. [] = nilcontinue } := copy(, ) [] = [::] = [:] }return}var timeFormats = []string{TimeFormat,time.RFC850,time.ANSIC,}// ParseTime parses a time header (such as the Date: header),// trying each of the three formats allowed by HTTP/1.1:// [TimeFormat], [time.RFC850], and [time.ANSIC].func ( string) ( time.Time, error) {for , := rangetimeFormats { , = time.Parse(, )if == nil {return } }return}var headerNewlineToSpace = strings.NewReplacer("\n", " ", "\r", " ")// stringWriter implements WriteString on a Writer.type stringWriter struct { w io.Writer}func ( stringWriter) ( string) ( int, error) {return .w.Write([]byte())}type keyValues struct { key string values []string}// headerSorter contains a slice of keyValues sorted by keyValues.key.type headerSorter struct { kvs []keyValues}var headerSorterPool = sync.Pool{New: func() any { returnnew(headerSorter) },}// sortedKeyValues returns h's keys sorted in the returned kvs// slice. The headerSorter used to sort is also returned, for possible// return to headerSorterCache.func ( Header) ( map[string]bool) ( []keyValues, *headerSorter) { = headerSorterPool.Get().(*headerSorter)ifcap(.kvs) < len() { .kvs = make([]keyValues, 0, len()) } = .kvs[:0]for , := range {if ![] { = append(, keyValues{, }) } } .kvs = slices.SortFunc(.kvs, func(, keyValues) int { returnstrings.Compare(.key, .key) })return , }// WriteSubset writes a header in wire format.// If exclude is not nil, keys where exclude[key] == true are not written.// Keys are not canonicalized before checking the exclude map.func ( Header) ( io.Writer, map[string]bool) error {return .writeSubset(, , nil)}func ( Header) ( io.Writer, map[string]bool, *httptrace.ClientTrace) error { , := .(io.StringWriter)if ! { = stringWriter{} } , := .sortedKeyValues()var []stringfor , := range {if !httpguts.ValidHeaderFieldName(.key) {// This could be an error. In the common case of // writing response headers, however, we have no good // way to provide the error back to the server // handler, so just drop invalid headers instead.continue }for , := range .values { = headerNewlineToSpace.Replace() = textproto.TrimString()for , := range []string{.key, ": ", , "\r\n"} {if , := .WriteString(); != nil {headerSorterPool.Put()return } }if != nil && .WroteHeaderField != nil { = append(, ) } }if != nil && .WroteHeaderField != nil { .WroteHeaderField(.key, ) = nil } }headerSorterPool.Put()returnnil}// CanonicalHeaderKey returns the canonical format of the// header key s. The canonicalization converts the first// letter and any letter following a hyphen to upper case;// the rest are converted to lowercase. For example, the// canonical key for "accept-encoding" is "Accept-Encoding".// If s contains a space or invalid header field bytes, it is// returned without modifications.func ( string) string { returntextproto.CanonicalMIMEHeaderKey() }// hasToken reports whether token appears with v, ASCII// case-insensitive, with space or comma boundaries.// token must be all lowercase.// v may contain mixed cased.func hasToken(, string) bool {iflen() > len() || == "" {returnfalse }if == {returntrue }for := 0; <= len()-len(); ++ {// Check that first character is good. // The token is ASCII, so checking only a single byte // is sufficient. We skip this potential starting // position if both the first byte and its potential // ASCII uppercase equivalent (b|0x20) don't match. // False positives ('^' => '~') are caught by EqualFold.if := []; != [0] && |0x20 != [0] {continue }// Check that start pos is on a valid token boundary.if > 0 && !isTokenBoundary([-1]) {continue }// Check that end pos is on a valid token boundary.if := + len(); != len() && !isTokenBoundary([]) {continue }ifascii.EqualFold([:+len()], ) {returntrue } }returnfalse}func isTokenBoundary( byte) bool {return == ' ' || == ',' || == '\t'}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.