Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
4.4 KiB
147 lines
4.4 KiB
// Copyright 2013 The Gorilla Authors. All rights reserved. |
|
// Use of this source code is governed by a BSD-style |
|
// license that can be found in the LICENSE file. |
|
|
|
package handlers |
|
|
|
import ( |
|
"bufio" |
|
"fmt" |
|
"net" |
|
"net/http" |
|
"sort" |
|
"strings" |
|
) |
|
|
|
// MethodHandler is an http.Handler that dispatches to a handler whose key in the |
|
// MethodHandler's map matches the name of the HTTP request's method, eg: GET |
|
// |
|
// If the request's method is OPTIONS and OPTIONS is not a key in the map then |
|
// the handler responds with a status of 200 and sets the Allow header to a |
|
// comma-separated list of available methods. |
|
// |
|
// If the request's method doesn't match any of its keys the handler responds |
|
// with a status of HTTP 405 "Method Not Allowed" and sets the Allow header to a |
|
// comma-separated list of available methods. |
|
type MethodHandler map[string]http.Handler |
|
|
|
func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { |
|
if handler, ok := h[req.Method]; ok { |
|
handler.ServeHTTP(w, req) |
|
} else { |
|
allow := []string{} |
|
for k := range h { |
|
allow = append(allow, k) |
|
} |
|
sort.Strings(allow) |
|
w.Header().Set("Allow", strings.Join(allow, ", ")) |
|
if req.Method == "OPTIONS" { |
|
w.WriteHeader(http.StatusOK) |
|
} else { |
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) |
|
} |
|
} |
|
} |
|
|
|
// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP |
|
// status code and body size |
|
type responseLogger struct { |
|
w http.ResponseWriter |
|
status int |
|
size int |
|
} |
|
|
|
func (l *responseLogger) Write(b []byte) (int, error) { |
|
size, err := l.w.Write(b) |
|
l.size += size |
|
return size, err |
|
} |
|
|
|
func (l *responseLogger) WriteHeader(s int) { |
|
l.w.WriteHeader(s) |
|
l.status = s |
|
} |
|
|
|
func (l *responseLogger) Status() int { |
|
return l.status |
|
} |
|
|
|
func (l *responseLogger) Size() int { |
|
return l.size |
|
} |
|
|
|
func (l *responseLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) { |
|
conn, rw, err := l.w.(http.Hijacker).Hijack() |
|
if err == nil && l.status == 0 { |
|
// The status will be StatusSwitchingProtocols if there was no error and |
|
// WriteHeader has not been called yet |
|
l.status = http.StatusSwitchingProtocols |
|
} |
|
return conn, rw, err |
|
} |
|
|
|
// isContentType validates the Content-Type header matches the supplied |
|
// contentType. That is, its type and subtype match. |
|
func isContentType(h http.Header, contentType string) bool { |
|
ct := h.Get("Content-Type") |
|
if i := strings.IndexRune(ct, ';'); i != -1 { |
|
ct = ct[0:i] |
|
} |
|
return ct == contentType |
|
} |
|
|
|
// ContentTypeHandler wraps and returns a http.Handler, validating the request |
|
// content type is compatible with the contentTypes list. It writes a HTTP 415 |
|
// error if that fails. |
|
// |
|
// Only PUT, POST, and PATCH requests are considered. |
|
func ContentTypeHandler(h http.Handler, contentTypes ...string) http.Handler { |
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
if !(r.Method == "PUT" || r.Method == "POST" || r.Method == "PATCH") { |
|
h.ServeHTTP(w, r) |
|
return |
|
} |
|
|
|
for _, ct := range contentTypes { |
|
if isContentType(r.Header, ct) { |
|
h.ServeHTTP(w, r) |
|
return |
|
} |
|
} |
|
http.Error(w, fmt.Sprintf("Unsupported content type %q; expected one of %q", r.Header.Get("Content-Type"), contentTypes), http.StatusUnsupportedMediaType) |
|
}) |
|
} |
|
|
|
const ( |
|
// HTTPMethodOverrideHeader is a commonly used |
|
// http header to override a request method. |
|
HTTPMethodOverrideHeader = "X-HTTP-Method-Override" |
|
// HTTPMethodOverrideFormKey is a commonly used |
|
// HTML form key to override a request method. |
|
HTTPMethodOverrideFormKey = "_method" |
|
) |
|
|
|
// HTTPMethodOverrideHandler wraps and returns a http.Handler which checks for |
|
// the X-HTTP-Method-Override header or the _method form key, and overrides (if |
|
// valid) request.Method with its value. |
|
// |
|
// This is especially useful for HTTP clients that don't support many http verbs. |
|
// It isn't secure to override e.g a GET to a POST, so only POST requests are |
|
// considered. Likewise, the override method can only be a "write" method: PUT, |
|
// PATCH or DELETE. |
|
// |
|
// Form method takes precedence over header method. |
|
func HTTPMethodOverrideHandler(h http.Handler) http.Handler { |
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
if r.Method == "POST" { |
|
om := r.FormValue(HTTPMethodOverrideFormKey) |
|
if om == "" { |
|
om = r.Header.Get(HTTPMethodOverrideHeader) |
|
} |
|
if om == "PUT" || om == "PATCH" || om == "DELETE" { |
|
r.Method = om |
|
} |
|
} |
|
h.ServeHTTP(w, r) |
|
}) |
|
}
|
|
|