// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.//go:generate bundle -prefix= -o=httpcommon.go golang.org/x/net/internal/httpcommonpackage httpcommonimport ()// The HTTP protocols are defined in terms of ASCII, not Unicode. This file// contains helper functions which may use Unicode-aware functions which would// otherwise be unsafe and could introduce vulnerabilities if used improperly.// asciiEqualFold is strings.EqualFold, ASCII only. It reports whether s and t// are equal, ASCII-case-insensitively.func asciiEqualFold(, string) bool {iflen() != len() {returnfalse }for := 0; < len(); ++ {iflower([]) != lower([]) {returnfalse } }returntrue}// lower returns the ASCII lowercase version of b.func lower( byte) byte {if'A' <= && <= 'Z' {return + ('a' - 'A') }return}// isASCIIPrint returns whether s is ASCII and printable according to// https://tools.ietf.org/html/rfc20#section-4.2.func isASCIIPrint( string) bool {for := 0; < len(); ++ {if [] < ' ' || [] > '~' {returnfalse } }returntrue}// asciiToLower returns the lowercase version of s if s is ASCII and printable,// and whether or not it was.func asciiToLower( string) ( string, bool) {if !isASCIIPrint() {return"", false }returnstrings.ToLower(), true}var ( commonBuildOnce sync.Once commonLowerHeader map[string]string// Go-Canonical-Case -> lower-case commonCanonHeader map[string]string// lower-case -> Go-Canonical-Case)func buildCommonHeaderMapsOnce() {commonBuildOnce.Do(buildCommonHeaderMaps)}func buildCommonHeaderMaps() { := []string{"accept","accept-charset","accept-encoding","accept-language","accept-ranges","age","access-control-allow-credentials","access-control-allow-headers","access-control-allow-methods","access-control-allow-origin","access-control-expose-headers","access-control-max-age","access-control-request-headers","access-control-request-method","allow","authorization","cache-control","content-disposition","content-encoding","content-language","content-length","content-location","content-range","content-type","cookie","date","etag","expect","expires","from","host","if-match","if-modified-since","if-none-match","if-unmodified-since","last-modified","link","location","max-forwards","origin","proxy-authenticate","proxy-authorization","range","referer","refresh","retry-after","server","set-cookie","strict-transport-security","trailer","transfer-encoding","user-agent","vary","via","www-authenticate","x-forwarded-for","x-forwarded-proto", }commonLowerHeader = make(map[string]string, len())commonCanonHeader = make(map[string]string, len())for , := range { := textproto.CanonicalMIMEHeaderKey()commonLowerHeader[] = commonCanonHeader[] = }}// LowerHeader returns the lowercase form of a header name,// used on the wire for HTTP/2 and HTTP/3 requests.func ( string) ( string, bool) {buildCommonHeaderMapsOnce()if , := commonLowerHeader[]; {return , true }returnasciiToLower()}// CanonicalHeader canonicalizes a header name. (For example, "host" becomes "Host".)func ( string) string {buildCommonHeaderMapsOnce()if , := commonCanonHeader[]; {return }returntextproto.CanonicalMIMEHeaderKey()}// CachedCanonicalHeader returns the canonical form of a well-known header name.func ( string) (string, bool) {buildCommonHeaderMapsOnce() , := commonCanonHeader[]return , }var (ErrRequestHeaderListSize = errors.New("request header list larger than peer's advertised limit"))// Request is a subset of http.Request.// It'd be simpler to pass an *http.Request, of course, but we can't depend on net/http// without creating a dependency cycle.typeRequeststruct { URL *url.URL Method string Host string Header map[string][]string Trailer map[string][]string ActualContentLength int64// 0 means 0, -1 means unknown}// EncodeHeadersParam is parameters to EncodeHeaders.typeEncodeHeadersParamstruct { Request Request// AddGzipHeader indicates that an "accept-encoding: gzip" header should be // added to the request. AddGzipHeader bool// PeerMaxHeaderListSize, when non-zero, is the peer's MAX_HEADER_LIST_SIZE setting. PeerMaxHeaderListSize uint64// DefaultUserAgent is the User-Agent header to send when the request // neither contains a User-Agent nor disables it. DefaultUserAgent string}// EncodeHeadersParam is the result of EncodeHeaders.typeEncodeHeadersResultstruct { HasBody bool HasTrailers bool}// EncodeHeaders constructs request headers common to HTTP/2 and HTTP/3.// It validates a request and calls headerf with each pseudo-header and header// for the request.// The headerf function is called with the validated, canonicalized header name.func ( context.Context, EncodeHeadersParam, func(, string)) ( EncodeHeadersResult, error) { := .Request// Check for invalid connection-level headers.if := checkConnHeaders(.Header); != nil {return , }if .URL == nil {return , errors.New("Request.URL is nil") } := .Hostif == "" { = .URL.Host } , := httpguts.PunycodeHostPort()if != nil {return , }if !httpguts.ValidHostHeader() {return , errors.New("invalid Host header") }// isNormalConnect is true if this is a non-extended CONNECT request. := falsevarstringif := .Header[":protocol"]; len() > 0 { = [0] }if .Method == "CONNECT" && == "" { = true } elseif != "" && .Method != "CONNECT" {return , errors.New("invalid :protocol header in non-CONNECT request") }// Validate the path, except for non-extended CONNECT requests which have no path.varstringif ! { = .URL.RequestURI()if !validPseudoPath() { := = strings.TrimPrefix(, .URL.Scheme+"://"+)if !validPseudoPath() {if .URL.Opaque != "" {return , fmt.Errorf("invalid request :path %q from URL.Opaque = %q", , .URL.Opaque) } else {return , fmt.Errorf("invalid request :path %q", ) } } } }// Check for any invalid headers+trailers and return an error before we // potentially pollute our hpack state. (We want to be able to // continue to reuse the hpack encoder for future requests)if := validateHeaders(.Header); != "" {return , fmt.Errorf("invalid HTTP header %s", ) }if := validateHeaders(.Trailer); != "" {return , fmt.Errorf("invalid HTTP trailer %s", ) } , := commaSeparatedTrailers(.Trailer)if != nil {return , } := func( func(, string)) {// 8.1.2.3 Request Pseudo-Header Fields // The :path pseudo-header field includes the path and query parts of the // target URI (the path-absolute production and optionally a '?' character // followed by the query production, see Sections 3.3 and 3.4 of // [RFC3986]). (":authority", ) := .Methodif == "" { = "GET" } (":method", )if ! { (":path", ) (":scheme", .URL.Scheme) }if != "" { (":protocol", ) }if != "" { ("trailer", ) }varboolfor , := range .Header {ifasciiEqualFold(, "host") || asciiEqualFold(, "content-length") {// Host is :authority, already sent. // Content-Length is automatic, set below.continue } elseifasciiEqualFold(, "connection") ||asciiEqualFold(, "proxy-connection") ||asciiEqualFold(, "transfer-encoding") ||asciiEqualFold(, "upgrade") ||asciiEqualFold(, "keep-alive") {// Per 8.1.2.2 Connection-Specific Header // Fields, don't send connection-specific // fields. We have already checked if any // are error-worthy so just ignore the rest.continue } elseifasciiEqualFold(, "user-agent") {// Match Go's http1 behavior: at most one // User-Agent. If set to nil or empty string, // then omit it. Otherwise if not mentioned, // include the default (below). = trueiflen() < 1 {continue } = [:1]if [0] == "" {continue } } elseifasciiEqualFold(, "cookie") {// Per 8.1.2.5 To allow for better compression efficiency, the // Cookie header field MAY be split into separate header fields, // each with one or more cookie-pairs.for , := range {for { := strings.IndexByte(, ';')if < 0 {break } ("cookie", [:]) ++// strip space after semicolon if any.for +1 <= len() && [] == ' ' { ++ } = [:] }iflen() > 0 { ("cookie", ) } }continue } elseif == ":protocol" {// :protocol pseudo-header was already sent above.continue }for , := range { (, ) } }ifshouldSendReqContentLength(.Method, .ActualContentLength) { ("content-length", strconv.FormatInt(.ActualContentLength, 10)) }if .AddGzipHeader { ("accept-encoding", "gzip") }if ! { ("user-agent", .DefaultUserAgent) } }// Do a first pass over the headers counting bytes to ensure // we don't exceed cc.peerMaxHeaderListSize. This is done as a // separate pass before encoding the headers to prevent // modifying the hpack state.if .PeerMaxHeaderListSize > 0 { := uint64(0) (func(, string) { := hpack.HeaderField{Name: , Value: } += uint64(.Size()) })if > .PeerMaxHeaderListSize {return , ErrRequestHeaderListSize } } := httptrace.ContextClientTrace()// Header list size is ok. Write the headers. (func(, string) { , := LowerHeader()if ! {// Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header // field names have to be ASCII characters (just as in HTTP/1.x).return } (, )if != nil && .WroteHeaderField != nil { .WroteHeaderField(, []string{}) } }) .HasBody = .ActualContentLength != 0 .HasTrailers = != ""return , nil}// IsRequestGzip reports whether we should add an Accept-Encoding: gzip header// for a request.func ( string, map[string][]string, bool) bool {// TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere?if ! &&len(["Accept-Encoding"]) == 0 &&len(["Range"]) == 0 && != "HEAD" {// Request gzip only, not deflate. Deflate is ambiguous and // not as universally supported anyway. // See: https://zlib.net/zlib_faq.html#faq39 // // Note that we don't request this for HEAD requests, // due to a bug in nginx: // http://trac.nginx.org/nginx/ticket/358 // https://golang.org/issue/5522 // // We don't request gzip if the request is for a range, since // auto-decoding a portion of a gzipped document will just fail // anyway. See https://golang.org/issue/8923returntrue }returnfalse}// checkConnHeaders checks whether req has any invalid connection-level headers.//// https://www.rfc-editor.org/rfc/rfc9114.html#section-4.2-3// https://www.rfc-editor.org/rfc/rfc9113.html#section-8.2.2-1//// Certain headers are special-cased as okay but not transmitted later.// For example, we allow "Transfer-Encoding: chunked", but drop the header when encoding.func checkConnHeaders( map[string][]string) error {if := ["Upgrade"]; len() > 0 && ([0] != "" && [0] != "chunked") {returnfmt.Errorf("invalid Upgrade request header: %q", ) }if := ["Transfer-Encoding"]; len() > 0 && (len() > 1 || [0] != "" && [0] != "chunked") {returnfmt.Errorf("invalid Transfer-Encoding request header: %q", ) }if := ["Connection"]; len() > 0 && (len() > 1 || [0] != "" && !asciiEqualFold([0], "close") && !asciiEqualFold([0], "keep-alive")) {returnfmt.Errorf("invalid Connection request header: %q", ) }returnnil}func commaSeparatedTrailers( map[string][]string) (string, error) { := make([]string, 0, len())for := range { = CanonicalHeader()switch {case"Transfer-Encoding", "Trailer", "Content-Length":return"", fmt.Errorf("invalid Trailer key %q", ) } = append(, ) }iflen() > 0 {sort.Strings()returnstrings.Join(, ","), nil }return"", nil}// validPseudoPath reports whether v is a valid :path pseudo-header// value. It must be either://// - a non-empty string starting with '/'// - the string '*', for OPTIONS requests.//// For now this is only used a quick check for deciding when to clean// up Opaque URLs before sending requests from the Transport.// See golang.org/issue/16847//// We used to enforce that the path also didn't start with "//", but// Google's GFE accepts such paths and Chrome sends them, so ignore// that part of the spec. See golang.org/issue/19103.func validPseudoPath( string) bool {return (len() > 0 && [0] == '/') || == "*"}func validateHeaders( map[string][]string) string {for , := range {if !httpguts.ValidHeaderFieldName() && != ":protocol" {returnfmt.Sprintf("name %q", ) }for , := range {if !httpguts.ValidHeaderFieldValue() {// Don't include the value in the error, // because it may be sensitive.returnfmt.Sprintf("value for header %q", ) } } }return""}// shouldSendReqContentLength reports whether we should send// a "content-length" request header. This logic is basically a copy of the net/http// transferWriter.shouldSendContentLength.// The contentLength is the corrected contentLength (so 0 means actually 0, not unknown).// -1 means unknown.func shouldSendReqContentLength( string, int64) bool {if > 0 {returntrue }if < 0 {returnfalse }// For zero bodies, whether we send a content-length depends on the method. // It also kinda doesn't matter for http2 either way, with END_STREAM.switch {case"POST", "PUT", "PATCH":returntruedefault:returnfalse }}// ServerRequestParam is parameters to NewServerRequest.typeServerRequestParamstruct { Method string Scheme, Authority, Path string Protocol string Header map[string][]string}// ServerRequestResult is the result of NewServerRequest.typeServerRequestResultstruct {// Various http.Request fields. URL *url.URL RequestURI string Trailer map[string][]string NeedsContinue bool// client provided an "Expect: 100-continue" header// If the request should be rejected, this is a short string suitable for passing // to the http2 package's CountError function. // It might be a bit odd to return errors this way rather than returing an error, // but this ensures we don't forget to include a CountError reason. InvalidReason string}func ( ServerRequestParam) ServerRequestResult { := httpguts.HeaderValuesContainsToken(.Header["Expect"], "100-continue")if {delete(.Header, "Expect") }// Merge Cookie headers into one "; "-delimited value.if := .Header["Cookie"]; len() > 1 { .Header["Cookie"] = []string{strings.Join(, "; ")} }// Setup Trailersvarmap[string][]stringfor , := range .Header["Trailer"] {for , := rangestrings.Split(, ",") { = textproto.CanonicalMIMEHeaderKey(textproto.TrimString())switch {case"Transfer-Encoding", "Trailer", "Content-Length":// Bogus. (copy of http1 rules) // Ignore.default:if == nil { = make(map[string][]string) } [] = nil } } }delete(.Header, "Trailer")// "':authority' MUST NOT include the deprecated userinfo subcomponent // for "http" or "https" schemed URIs." // https://www.rfc-editor.org/rfc/rfc9113.html#section-8.3.1-2.3.8ifstrings.IndexByte(.Authority, '@') != -1 && (.Scheme == "http" || .Scheme == "https") {returnServerRequestResult{InvalidReason: "userinfo_in_authority", } }var *url.URLvarstringif .Method == "CONNECT" && .Protocol == "" { = &url.URL{Host: .Authority} = .Authority// mimic HTTP/1 server behavior } else {varerror , = url.ParseRequestURI(.Path)if != nil {returnServerRequestResult{InvalidReason: "bad_path", } } = .Path }returnServerRequestResult{URL: ,NeedsContinue: ,RequestURI: ,Trailer: , }}
The pages are generated with Goldsv0.7.7-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.