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 }