-
Notifications
You must be signed in to change notification settings - Fork 32
/
handlers.go
163 lines (146 loc) · 5.06 KB
/
handlers.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// Copyright 2015 Husobee Associates, LLC. All rights reserved.
// Use of this source code is governed by The MIT License, which
// can be found in the LICENSE file included.
package vestigo
import (
"io"
"net/http"
"sync"
)
var (
notFoundOnce sync.Once
methodNotAllowedOnce sync.Once
)
// CustomNotFoundHandlerFunc - Specify a Handlerfunc to use for a custom NotFound Handler. Can only be performed once.
func CustomNotFoundHandlerFunc(f http.HandlerFunc) {
notFoundOnce.Do(func() {
notFoundHandler = f
})
}
type MethodNotAllowedHandlerFunc func(string) func(w http.ResponseWriter, r *http.Request)
// CustomMethodNotAllowedHandlerFunc - This function will allow the caller to
// set vestigo's methodNotAllowedHandler. This function needs to return an
// http.Handlerfunc and take in a formatted string of methods that ARE allowed.
// Follow the convention for methodNotAllowedHandler. Note that if you overwrite
// you will be responsible for making sure the allowed methods are put into headers
func CustomMethodNotAllowedHandlerFunc(f MethodNotAllowedHandlerFunc) {
methodNotAllowedOnce.Do(func() {
methodNotAllowedHandler = f
})
}
// headResponseWriter - implementation of http.ResponseWriter for headHandler
type headResponseWriter struct {
HeaderMap http.Header
Code int
wroteHeader bool
}
func (hrw *headResponseWriter) Header() http.Header {
if hrw.HeaderMap == nil {
hrw.HeaderMap = make(http.Header)
}
return hrw.HeaderMap
}
func (hrw *headResponseWriter) Write([]byte) (int, error) {
// Mirror http.ResponseWriter: "If WriteHeader has not yet been called,
// Write calls WriteHeader(http.StatusOK) before writing the data."
if !hrw.wroteHeader {
hrw.WriteHeader(http.StatusOK)
}
return 0, nil
}
func (hrw *headResponseWriter) WriteHeader(status int) {
hrw.wroteHeader = true
hrw.Code = status
}
var (
// traceHandler - Generic Trace Handler to echo back input
traceHandler = func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "message/http")
w.WriteHeader(http.StatusOK)
if r.Body == nil {
w.Write([]byte{})
return
}
defer r.Body.Close()
io.Copy(w, r.Body)
}
// headHandler - Generic Head Handler to return header information
headHandler = func(f http.HandlerFunc) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
fakeWriter := &headResponseWriter{}
// issue 23 - nodes that do not have handlers should not be called when HEAD
// is called
if f != nil {
f(fakeWriter, r)
for k, v := range fakeWriter.Header() {
for _, vv := range v {
w.Header().Add(k, vv)
}
}
w.WriteHeader(fakeWriter.Code)
w.Write([]byte(""))
} else {
notFoundHandler(w, r)
}
}
}
// optionsHandler - Generic Options Handler to handle when method isn't allowed for a resource
optionsHandler = func(gcors *CorsAccessControl, lcors *CorsAccessControl, allowedMethods string) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Allow", allowedMethods)
if err := corsPreflight(gcors, lcors, allowedMethods, w, r); err != nil {
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(""))
}
}
// methodNotAllowedHandler - Generic Handler to handle when method isn't allowed for a resource
methodNotAllowedHandler = func(allowedMethods string) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Allow", allowedMethods)
w.WriteHeader(http.StatusMethodNotAllowed)
w.Write([]byte(http.StatusText(http.StatusMethodNotAllowed)))
}
}
// notFoundHandler - Generic Handler to handle when resource isn't found
notFoundHandler = func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(http.StatusText(http.StatusNotFound)))
}
// corsFlightWrapper - Wrap the handler in cors
corsFlightWrapper = func(gcors *CorsAccessControl, lcors *CorsAccessControl, allowedMethods string, f func(http.ResponseWriter, *http.Request)) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if origin := r.Header.Get("Origin"); origin != "" {
cors := gcors.Merge(lcors)
if cors != nil {
// validate origin is in list of acceptable allow-origins
allowedOrigin := false
allowedOriginExact := false
for _, v := range cors.GetAllowOrigin() {
if v == origin {
w.Header().Add("Access-Control-Allow-Origin", origin)
allowedOriginExact = true
allowedOrigin = true
break
}
}
if !allowedOrigin {
for _, v := range cors.GetAllowOrigin() {
if v == "*" {
w.Header().Add("Access-Control-Allow-Origin", v)
allowedOrigin = true
break
}
}
}
// if allow credentials is allowed on this resource respond with true
if allowCredentials := cors.GetAllowCredentials(); allowedOriginExact && allowCredentials {
w.Header().Add("Access-Control-Allow-Credentials", "true")
}
}
}
f(w, r)
}
}
)