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