// Copyright 2009 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 http// This file implements ServeMux behavior as in Go 1.21.// The behavior is controlled by a GODEBUG setting.// Most of this code is derived from commit 08e35cc334.// Changes are minimal: aside from the different receiver type,// they mostly involve renaming functions, usually by unexporting them.// servemux121.go exists solely to provide a snapshot of// the pre-Go 1.22 ServeMux implementation for backwards compatibility.// Do not modify this file, it should remain frozen.import ()var httpmuxgo121 = godebug.New("httpmuxgo121")var use121 bool// Read httpmuxgo121 once at startup, since dealing with changes to it during// program execution is too complex and error-prone.func init() {ifhttpmuxgo121.Value() == "1" {use121 = truehttpmuxgo121.IncNonDefault() }}// serveMux121 holds the state of a ServeMux needed for Go 1.21 behavior.type serveMux121 struct { mu sync.RWMutex m map[string]muxEntry es []muxEntry// slice of entries sorted from longest to shortest. hosts bool// whether any patterns contain hostnames}type muxEntry struct { h Handler pattern string}// Formerly ServeMux.Handle.func ( *serveMux121) ( string, Handler) { .mu.Lock()defer .mu.Unlock()if == "" {panic("http: invalid pattern") }if == nil {panic("http: nil handler") }if , := .m[]; {panic("http: multiple registrations for " + ) }if .m == nil { .m = make(map[string]muxEntry) } := muxEntry{h: , pattern: } .m[] = if [len()-1] == '/' { .es = appendSorted(.es, ) }if [0] != '/' { .hosts = true }}func appendSorted( []muxEntry, muxEntry) []muxEntry { := len() := sort.Search(, func( int) bool {returnlen([].pattern) < len(.pattern) })if == {returnappend(, ) }// we now know that i points at where we want to insert = append(, muxEntry{}) // try to grow the slice in place, any entry works.copy([+1:], [:]) // Move shorter entries down [] = return}// Formerly ServeMux.HandleFunc.func ( *serveMux121) ( string, func(ResponseWriter, *Request)) {if == nil {panic("http: nil handler") } .handle(, HandlerFunc())}// Formerly ServeMux.Handler.func ( *serveMux121) ( *Request) ( Handler, string) {// CONNECT requests are not canonicalized.if .Method == "CONNECT" {// If r.URL.Path is /tree and its handler is not registered, // the /tree -> /tree/ redirect applies to CONNECT requests // but the path canonicalization does not.if , := .redirectToPathSlash(.URL.Host, .URL.Path, .URL); {returnRedirectHandler(.String(), StatusMovedPermanently), .Path }return .handler(.Host, .URL.Path) }// All other requests have any port stripped and path cleaned // before passing to mux.handler. := stripHostPort(.Host) := cleanPath(.URL.Path)// If the given path is /tree and its handler is not registered, // redirect for /tree/.if , := .redirectToPathSlash(, , .URL); {returnRedirectHandler(.String(), StatusMovedPermanently), .Path }if != .URL.Path { _, = .handler(, ) := &url.URL{Path: , RawQuery: .URL.RawQuery}returnRedirectHandler(.String(), StatusMovedPermanently), }return .handler(, .URL.Path)}// handler is the main implementation of findHandler.// The path is known to be in canonical form, except for CONNECT methods.func ( *serveMux121) (, string) ( Handler, string) { .mu.RLock()defer .mu.RUnlock()// Host-specific pattern takes precedence over generic onesif .hosts { , = .match( + ) }if == nil { , = .match() }if == nil { , = NotFoundHandler(), "" }return}// Find a handler on a handler map given a path string.// Most-specific (longest) pattern wins.func ( *serveMux121) ( string) ( Handler, string) {// Check for exact match first. , := .m[]if {return .h, .pattern }// Check for longest valid match. mux.es contains all patterns // that end in / sorted from longest to shortest.for , := range .es {ifstrings.HasPrefix(, .pattern) {return .h, .pattern } }returnnil, ""}// redirectToPathSlash determines if the given path needs appending "/" to it.// This occurs when a handler for path + "/" was already registered, but// not for path itself. If the path needs appending to, it creates a new// URL, setting the path to u.Path + "/" and returning true to indicate so.func ( *serveMux121) (, string, *url.URL) (*url.URL, bool) { .mu.RLock() := .shouldRedirectRLocked(, ) .mu.RUnlock()if ! {return , false } = + "/" = &url.URL{Path: , RawQuery: .RawQuery}return , true}// shouldRedirectRLocked reports whether the given path and host should be redirected to// path+"/". This should happen if a handler is registered for path+"/" but// not path -- see comments at ServeMux.func ( *serveMux121) (, string) bool { := []string{, + }for , := range {if , := .m[]; {returnfalse } } := len()if == 0 {returnfalse }for , := range {if , := .m[+"/"]; {return [-1] != '/' } }returnfalse}
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.