mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 22:01:06 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			133 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			133 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package runtime
 | |
| 
 | |
| import (
 | |
| 	"net/http"
 | |
| 	"strings"
 | |
| 
 | |
| 	"golang.org/x/net/context"
 | |
| 
 | |
| 	"github.com/golang/protobuf/proto"
 | |
| )
 | |
| 
 | |
| // A HandlerFunc handles a specific pair of path pattern and HTTP method.
 | |
| type HandlerFunc func(w http.ResponseWriter, r *http.Request, pathParams map[string]string)
 | |
| 
 | |
| // ServeMux is a request multiplexer for grpc-gateway.
 | |
| // It matches http requests to patterns and invokes the corresponding handler.
 | |
| type ServeMux struct {
 | |
| 	// handlers maps HTTP method to a list of handlers.
 | |
| 	handlers               map[string][]handler
 | |
| 	forwardResponseOptions []func(context.Context, http.ResponseWriter, proto.Message) error
 | |
| 	marshalers             marshalerRegistry
 | |
| }
 | |
| 
 | |
| // ServeMuxOption is an option that can be given to a ServeMux on construction.
 | |
| type ServeMuxOption func(*ServeMux)
 | |
| 
 | |
| // WithForwardResponseOption returns a ServeMuxOption representing the forwardResponseOption.
 | |
| //
 | |
| // forwardResponseOption is an option that will be called on the relevant context.Context,
 | |
| // http.ResponseWriter, and proto.Message before every forwarded response.
 | |
| //
 | |
| // The message may be nil in the case where just a header is being sent.
 | |
| func WithForwardResponseOption(forwardResponseOption func(context.Context, http.ResponseWriter, proto.Message) error) ServeMuxOption {
 | |
| 	return func(serveMux *ServeMux) {
 | |
| 		serveMux.forwardResponseOptions = append(serveMux.forwardResponseOptions, forwardResponseOption)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // NewServeMux returns a new ServeMux whose internal mapping is empty.
 | |
| func NewServeMux(opts ...ServeMuxOption) *ServeMux {
 | |
| 	serveMux := &ServeMux{
 | |
| 		handlers:               make(map[string][]handler),
 | |
| 		forwardResponseOptions: make([]func(context.Context, http.ResponseWriter, proto.Message) error, 0),
 | |
| 		marshalers:             makeMarshalerMIMERegistry(),
 | |
| 	}
 | |
| 
 | |
| 	for _, opt := range opts {
 | |
| 		opt(serveMux)
 | |
| 	}
 | |
| 	return serveMux
 | |
| }
 | |
| 
 | |
| // Handle associates "h" to the pair of HTTP method and path pattern.
 | |
| func (s *ServeMux) Handle(meth string, pat Pattern, h HandlerFunc) {
 | |
| 	s.handlers[meth] = append(s.handlers[meth], handler{pat: pat, h: h})
 | |
| }
 | |
| 
 | |
| // ServeHTTP dispatches the request to the first handler whose pattern matches to r.Method and r.Path.
 | |
| func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | |
| 	path := r.URL.Path
 | |
| 	if !strings.HasPrefix(path, "/") {
 | |
| 		OtherErrorHandler(w, r, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	components := strings.Split(path[1:], "/")
 | |
| 	l := len(components)
 | |
| 	var verb string
 | |
| 	if idx := strings.LastIndex(components[l-1], ":"); idx == 0 {
 | |
| 		OtherErrorHandler(w, r, http.StatusText(http.StatusNotFound), http.StatusNotFound)
 | |
| 		return
 | |
| 	} else if idx > 0 {
 | |
| 		c := components[l-1]
 | |
| 		components[l-1], verb = c[:idx], c[idx+1:]
 | |
| 	}
 | |
| 
 | |
| 	if override := r.Header.Get("X-HTTP-Method-Override"); override != "" && isPathLengthFallback(r) {
 | |
| 		r.Method = strings.ToUpper(override)
 | |
| 		if err := r.ParseForm(); err != nil {
 | |
| 			OtherErrorHandler(w, r, err.Error(), http.StatusBadRequest)
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	for _, h := range s.handlers[r.Method] {
 | |
| 		pathParams, err := h.pat.Match(components, verb)
 | |
| 		if err != nil {
 | |
| 			continue
 | |
| 		}
 | |
| 		h.h(w, r, pathParams)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// lookup other methods to handle fallback from GET to POST and
 | |
| 	// to determine if it is MethodNotAllowed or NotFound.
 | |
| 	for m, handlers := range s.handlers {
 | |
| 		if m == r.Method {
 | |
| 			continue
 | |
| 		}
 | |
| 		for _, h := range handlers {
 | |
| 			pathParams, err := h.pat.Match(components, verb)
 | |
| 			if err != nil {
 | |
| 				continue
 | |
| 			}
 | |
| 			// X-HTTP-Method-Override is optional. Always allow fallback to POST.
 | |
| 			if isPathLengthFallback(r) {
 | |
| 				if err := r.ParseForm(); err != nil {
 | |
| 					OtherErrorHandler(w, r, err.Error(), http.StatusBadRequest)
 | |
| 					return
 | |
| 				}
 | |
| 				h.h(w, r, pathParams)
 | |
| 				return
 | |
| 			}
 | |
| 			OtherErrorHandler(w, r, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	OtherErrorHandler(w, r, http.StatusText(http.StatusNotFound), http.StatusNotFound)
 | |
| }
 | |
| 
 | |
| // GetForwardResponseOptions returns the ForwardResponseOptions associated with this ServeMux.
 | |
| func (s *ServeMux) GetForwardResponseOptions() []func(context.Context, http.ResponseWriter, proto.Message) error {
 | |
| 	return s.forwardResponseOptions
 | |
| }
 | |
| 
 | |
| func isPathLengthFallback(r *http.Request) bool {
 | |
| 	return r.Method == "POST" && r.Header.Get("Content-Type") == "application/x-www-form-urlencoded"
 | |
| }
 | |
| 
 | |
| type handler struct {
 | |
| 	pat Pattern
 | |
| 	h   HandlerFunc
 | |
| }
 |