// 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() {
	if httpmuxgo121.Value() == "1" {
		use121 = true
		httpmuxgo121.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 {
		return len([].pattern) < len(.pattern)
	})
	if  ==  {
		return append(, )
	}
	// 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);  {
			return RedirectHandler(.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);  {
		return RedirectHandler(.String(), StatusMovedPermanently), .Path
	}

	if  != .URL.Path {
		_,  = .handler(, )
		 := &url.URL{Path: , RawQuery: .URL.RawQuery}
		return RedirectHandler(.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 ones
	if .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 {
		if strings.HasPrefix(, .pattern) {
			return .h, .pattern
		}
	}
	return nil, ""
}

// 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[];  {
			return false
		}
	}

	 := len()
	if  == 0 {
		return false
	}
	for ,  := range  {
		if ,  := .m[+"/"];  {
			return [-1] != '/'
		}
	}

	return false
}