podman
150 строк · 4.4 Кб
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
5package handlers
6
7import (
8"bufio"
9"fmt"
10"net"
11"net/http"
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.
26type MethodHandler map[string]http.Handler
27
28func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
29if handler, ok := h[req.Method]; ok {
30handler.ServeHTTP(w, req)
31} else {
32allow := []string{}
33for k := range h {
34allow = append(allow, k)
35}
36sort.Strings(allow)
37w.Header().Set("Allow", strings.Join(allow, ", "))
38if req.Method == http.MethodOptions {
39w.WriteHeader(http.StatusOK)
40} else {
41http.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.
48type responseLogger struct {
49w http.ResponseWriter
50status int
51size int
52}
53
54func (l *responseLogger) Write(b []byte) (int, error) {
55size, err := l.w.Write(b)
56l.size += size
57return size, err
58}
59
60func (l *responseLogger) WriteHeader(s int) {
61l.w.WriteHeader(s)
62l.status = s
63}
64
65func (l *responseLogger) Status() int {
66return l.status
67}
68
69func (l *responseLogger) Size() int {
70return l.size
71}
72
73func (l *responseLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) {
74conn, rw, err := l.w.(http.Hijacker).Hijack()
75if err == nil && l.status == 0 {
76// The status will be StatusSwitchingProtocols if there was no error and
77// WriteHeader has not been called yet
78l.status = http.StatusSwitchingProtocols
79}
80return conn, rw, err
81}
82
83// isContentType validates the Content-Type header matches the supplied
84// contentType. That is, its type and subtype match.
85func isContentType(h http.Header, contentType string) bool {
86ct := h.Get("Content-Type")
87if i := strings.IndexRune(ct, ';'); i != -1 {
88ct = ct[0:i]
89}
90return ct == contentType
91}
92
93// ContentTypeHandler wraps and returns a http.Handler, validating the request
94// content type is compatible with the contentTypes list. It writes a HTTP 415
95// error if that fails.
96//
97// Only PUT, POST, and PATCH requests are considered.
98func ContentTypeHandler(h http.Handler, contentTypes ...string) http.Handler {
99return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
100if !(r.Method == http.MethodPut || r.Method == http.MethodPost || r.Method == http.MethodPatch) {
101h.ServeHTTP(w, r)
102return
103}
104
105for _, ct := range contentTypes {
106if isContentType(r.Header, ct) {
107h.ServeHTTP(w, r)
108return
109}
110}
111http.Error(w, fmt.Sprintf("Unsupported content type %q; expected one of %q",
112r.Header.Get("Content-Type"),
113contentTypes),
114http.StatusUnsupportedMediaType)
115})
116}
117
118const (
119// HTTPMethodOverrideHeader is a commonly used
120// http header to override a request method.
121HTTPMethodOverrideHeader = "X-HTTP-Method-Override"
122// HTTPMethodOverrideFormKey is a commonly used
123// HTML form key to override a request method.
124HTTPMethodOverrideFormKey = "_method"
125)
126
127// HTTPMethodOverrideHandler wraps and returns a http.Handler which checks for
128// the X-HTTP-Method-Override header or the _method form key, and overrides (if
129// valid) request.Method with its value.
130//
131// This is especially useful for HTTP clients that don't support many http verbs.
132// It isn't secure to override e.g a GET to a POST, so only POST requests are
133// considered. Likewise, the override method can only be a "write" method: PUT,
134// PATCH or DELETE.
135//
136// Form method takes precedence over header method.
137func HTTPMethodOverrideHandler(h http.Handler) http.Handler {
138return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
139if r.Method == http.MethodPost {
140om := r.FormValue(HTTPMethodOverrideFormKey)
141if om == "" {
142om = r.Header.Get(HTTPMethodOverrideHeader)
143}
144if om == http.MethodPut || om == http.MethodPatch || om == http.MethodDelete {
145r.Method = om
146}
147}
148h.ServeHTTP(w, r)
149})
150}
151