...
1
2
3
4
5 package handlers
6
7 import (
8 "bufio"
9 "fmt"
10 "net"
11 "net/http"
12 "sort"
13 "strings"
14 )
15
16
17
18
19
20
21
22
23
24
25
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
47
48 type responseLogger struct {
49 w http.ResponseWriter
50 status int
51 size int
52 }
53
54 func (l *responseLogger) Write(b []byte) (int, error) {
55 size, err := l.w.Write(b)
56 l.size += size
57 return size, err
58 }
59
60 func (l *responseLogger) WriteHeader(s int) {
61 l.w.WriteHeader(s)
62 l.status = s
63 }
64
65 func (l *responseLogger) Status() int {
66 return l.status
67 }
68
69 func (l *responseLogger) Size() int {
70 return l.size
71 }
72
73 func (l *responseLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) {
74 conn, rw, err := l.w.(http.Hijacker).Hijack()
75 if err == nil && l.status == 0 {
76
77
78 l.status = http.StatusSwitchingProtocols
79 }
80 return conn, rw, err
81 }
82
83
84
85 func isContentType(h http.Header, contentType string) bool {
86 ct := h.Get("Content-Type")
87 if i := strings.IndexRune(ct, ';'); i != -1 {
88 ct = ct[0:i]
89 }
90 return ct == contentType
91 }
92
93
94
95
96
97
98 func ContentTypeHandler(h http.Handler, contentTypes ...string) http.Handler {
99 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
100 if !(r.Method == "PUT" || r.Method == "POST" || r.Method == "PATCH") {
101 h.ServeHTTP(w, r)
102 return
103 }
104
105 for _, ct := range contentTypes {
106 if isContentType(r.Header, ct) {
107 h.ServeHTTP(w, r)
108 return
109 }
110 }
111 http.Error(w, fmt.Sprintf("Unsupported content type %q; expected one of %q", r.Header.Get("Content-Type"), contentTypes), http.StatusUnsupportedMediaType)
112 })
113 }
114
115 const (
116
117
118 HTTPMethodOverrideHeader = "X-HTTP-Method-Override"
119
120
121 HTTPMethodOverrideFormKey = "_method"
122 )
123
124
125
126
127
128
129
130
131
132
133
134 func HTTPMethodOverrideHandler(h http.Handler) http.Handler {
135 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
136 if r.Method == "POST" {
137 om := r.FormValue(HTTPMethodOverrideFormKey)
138 if om == "" {
139 om = r.Header.Get(HTTPMethodOverrideHeader)
140 }
141 if om == "PUT" || om == "PATCH" || om == "DELETE" {
142 r.Method = om
143 }
144 }
145 h.ServeHTTP(w, r)
146 })
147 }
148
View as plain text