gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/handlers/handlers.go (about)

     1  // Copyright 2013 The Gorilla Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package handlers
     6  
     7  import (
     8  	"bufio"
     9  	"fmt"
    10  	"net"
    11  	"sort"
    12  	"strings"
    13  
    14  	http "gitee.com/ks-custle/core-gm/gmhttp"
    15  )
    16  
    17  // MethodHandler is an http.Handler that dispatches to a handler whose key in the
    18  // MethodHandler's map matches the name of the HTTP request's method, eg: GET
    19  //
    20  // If the request's method is OPTIONS and OPTIONS is not a key in the map then
    21  // the handler responds with a status of 200 and sets the Allow header to a
    22  // comma-separated list of available methods.
    23  //
    24  // If the request's method doesn't match any of its keys the handler responds
    25  // with a status of HTTP 405 "Method Not Allowed" and sets the Allow header to a
    26  // comma-separated list of available methods.
    27  type MethodHandler map[string]http.Handler
    28  
    29  func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    30  	if handler, ok := h[req.Method]; ok {
    31  		handler.ServeHTTP(w, req)
    32  	} else {
    33  		allow := []string{}
    34  		for k := range h {
    35  			allow = append(allow, k)
    36  		}
    37  		sort.Strings(allow)
    38  		w.Header().Set("Allow", strings.Join(allow, ", "))
    39  		if req.Method == "OPTIONS" {
    40  			w.WriteHeader(http.StatusOK)
    41  		} else {
    42  			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
    43  		}
    44  	}
    45  }
    46  
    47  // responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP
    48  // status code and body size
    49  type responseLogger struct {
    50  	w      http.ResponseWriter
    51  	status int
    52  	size   int
    53  }
    54  
    55  func (l *responseLogger) Write(b []byte) (int, error) {
    56  	size, err := l.w.Write(b)
    57  	l.size += size
    58  	return size, err
    59  }
    60  
    61  func (l *responseLogger) WriteHeader(s int) {
    62  	l.w.WriteHeader(s)
    63  	l.status = s
    64  }
    65  
    66  func (l *responseLogger) Status() int {
    67  	return l.status
    68  }
    69  
    70  func (l *responseLogger) Size() int {
    71  	return l.size
    72  }
    73  
    74  func (l *responseLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) {
    75  	conn, rw, err := l.w.(http.Hijacker).Hijack()
    76  	if err == nil && l.status == 0 {
    77  		// The status will be StatusSwitchingProtocols if there was no error and
    78  		// WriteHeader has not been called yet
    79  		l.status = http.StatusSwitchingProtocols
    80  	}
    81  	return conn, rw, err
    82  }
    83  
    84  // isContentType validates the Content-Type header matches the supplied
    85  // contentType. That is, its type and subtype match.
    86  func isContentType(h http.Header, contentType string) bool {
    87  	ct := h.Get("Content-Type")
    88  	if i := strings.IndexRune(ct, ';'); i != -1 {
    89  		ct = ct[0:i]
    90  	}
    91  	return ct == contentType
    92  }
    93  
    94  // ContentTypeHandler wraps and returns a http.Handler, validating the request
    95  // content type is compatible with the contentTypes list. It writes a HTTP 415
    96  // error if that fails.
    97  //
    98  // Only PUT, POST, and PATCH requests are considered.
    99  func ContentTypeHandler(h http.Handler, contentTypes ...string) http.Handler {
   100  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   101  		if !(r.Method == "PUT" || r.Method == "POST" || r.Method == "PATCH") {
   102  			h.ServeHTTP(w, r)
   103  			return
   104  		}
   105  
   106  		for _, ct := range contentTypes {
   107  			if isContentType(r.Header, ct) {
   108  				h.ServeHTTP(w, r)
   109  				return
   110  			}
   111  		}
   112  		http.Error(w, fmt.Sprintf("Unsupported content type %q; expected one of %q", r.Header.Get("Content-Type"), contentTypes), http.StatusUnsupportedMediaType)
   113  	})
   114  }
   115  
   116  const (
   117  	// HTTPMethodOverrideHeader is a commonly used
   118  	// http header to override a request method.
   119  	HTTPMethodOverrideHeader = "X-HTTP-Method-Override"
   120  	// HTTPMethodOverrideFormKey is a commonly used
   121  	// HTML form key to override a request method.
   122  	HTTPMethodOverrideFormKey = "_method"
   123  )
   124  
   125  // HTTPMethodOverrideHandler wraps and returns a http.Handler which checks for
   126  // the X-HTTP-Method-Override header or the _method form key, and overrides (if
   127  // valid) request.Method with its value.
   128  //
   129  // This is especially useful for HTTP clients that don't support many http verbs.
   130  // It isn't secure to override e.g a GET to a POST, so only POST requests are
   131  // considered.  Likewise, the override method can only be a "write" method: PUT,
   132  // PATCH or DELETE.
   133  //
   134  // Form method takes precedence over header method.
   135  func HTTPMethodOverrideHandler(h http.Handler) http.Handler {
   136  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   137  		if r.Method == "POST" {
   138  			om := r.FormValue(HTTPMethodOverrideFormKey)
   139  			if om == "" {
   140  				om = r.Header.Get(HTTPMethodOverrideHeader)
   141  			}
   142  			if om == "PUT" || om == "PATCH" || om == "DELETE" {
   143  				r.Method = om
   144  			}
   145  		}
   146  		h.ServeHTTP(w, r)
   147  	})
   148  }