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  }