Vendor CORS library.
David Braun committed
May 10, 2015 at 10:58 UTC
496e7a4b2c637e6b094faf77901e15e75912f2d5
17 files changed
+1138
-2
Godeps/Godeps.json
+4
@@ -233,6 +233,10 @@
233
"ImportPath": "github.com/mtchavez/jenkins",
234
"Rev": "5a816af6ef21ef401bff5e4b7dd255d63400f497"
235
},
236
+ {
237
+ "ImportPath": "github.com/rs/cors",
238
+ "Rev": "5e4ce6bc0ecd3472f6f943666d84876691be2ced"
239
+ },
240
{
241
"ImportPath": "github.com/steakknife/hamming",
242
"Comment": "0.0.10",
Godeps/_workspace/src/github.com/rs/cors/.travis.yml
new
+4
@@ -0,0 +1,4 @@
1
+language: go
2
+go:
3
+- 1.3
4
+- 1.4
Godeps/_workspace/src/github.com/rs/cors/LICENSE
new
+19
@@ -0,0 +1,19 @@
1
+Copyright (c) 2014 Olivier Poitrey <rs@dailymotion.com>
2
+
3
+Permission is hereby granted, free of charge, to any person obtaining a copy
4
+of this software and associated documentation files (the "Software"), to deal
5
+in the Software without restriction, including without limitation the rights
6
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+copies of the Software, and to permit persons to whom the Software is furnished
8
+to do so, subject to the following conditions:
9
+
10
+The above copyright notice and this permission notice shall be included in all
11
+copies or substantial portions of the Software.
12
+
13
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+THE SOFTWARE.
Godeps/_workspace/src/github.com/rs/cors/README.md
new
+96
@@ -0,0 +1,96 @@
1
+# Go CORS handler [](https://godoc.org/github.com/rs/cors) [](https://raw.githubusercontent.com/rs/cors/master/LICENSE) [](https://travis-ci.org/rs/cors)
2
+
3
+CORS is a `net/http` handler implementing [Cross Origin Resource Sharing W3 specification](http://www.w3.org/TR/cors/) in Golang.
4
+
5
+## Getting Started
6
+
7
+After installing Go and setting up your [GOPATH](http://golang.org/doc/code.html#GOPATH), create your first `.go` file. We'll call it `server.go`.
8
+
9
+```go
10
+package main
11
+
12
+import (
13
+ "net/http"
14
+
15
+ "github.com/rs/cors"
16
+)
17
+
18
+func main() {
19
+ h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
20
+ w.Header().Set("Content-Type", "application/json")
21
+ w.Write([]byte("{\"hello\": \"world\"}"))
22
+ })
23
+
24
+ // cors.Default() setup the middleware with default options being
25
+ // all origins accepted with simple methods (GET, POST). See
26
+ // documentation below for more options.
27
+ handler := cors.Default().Handler(h)
28
+ http.ListenAndServe(":8080", handler)
29
+}
30
+```
31
+
32
+Install `cors`:
33
+
34
+ go get github.com/rs/cors
35
+
36
+Then run your server:
37
+
38
+ go run server.go
39
+
40
+The server now runs on `localhost:8080`:
41
+
42
+ $ curl -D - -H 'Origin: http://foo.com' http://localhost:8080/
43
+ HTTP/1.1 200 OK
44
+ Access-Control-Allow-Origin: foo.com
45
+ Content-Type: application/json
46
+ Date: Sat, 25 Oct 2014 03:43:57 GMT
47
+ Content-Length: 18
48
+
49
+ {"hello": "world"}
50
+
51
+### More Examples
52
+
53
+* `net/http`: [examples/nethttp/server.go](https://github.com/rs/cors/blob/master/examples/nethttp/server.go)
54
+* [Goji](https://goji.io): [examples/goji/server.go](https://github.com/rs/cors/blob/master/examples/goji/server.go)
55
+* [Martini](http://martini.codegangsta.io): [examples/martini/server.go](https://github.com/rs/cors/blob/master/examples/martini/server.go)
56
+* [Negroni](https://github.com/codegangsta/negroni): [examples/negroni/server.go](https://github.com/rs/cors/blob/master/examples/negroni/server.go)
57
+* [Alice](https://github.com/justinas/alice): [examples/alice/server.go](https://github.com/rs/cors/blob/master/examples/alice/server.go)
58
+
59
+## Parameters
60
+
61
+Parameters are passed to the middleware thru the `cors.New` method as follow:
62
+
63
+```go
64
+c := cors.New(cors.Options{
65
+ AllowedOrigins: []string{"http://foo.com"},
66
+ AllowCredentials: true,
67
+})
68
+
69
+// Insert the middleware
70
+handler = c.Handler(handler)
71
+```
72
+
73
+* **AllowedOrigins** `[]string`: A list of origins a cross-domain request can be executed from. If the special `*` value is present in the list, all origins will be allowed. The default value is `*`.
74
+* **AllowOriginFunc** `func (origin string) bool`: A custom function to validate the origin. It take the origin as argument and returns true if allowed or false otherwise. If this option is set, the content of `AllowedOrigins` is ignored
75
+* **AllowedMethods** `[]string`: A list of methods the client is allowed to use with cross-domain requests.
76
+* **AllowedHeaders** `[]string`: A list of non simple headers the client is allowed to use with cross-domain requests. Default value is simple methods (`GET` and `POST`)
77
+* **ExposedHeaders** `[]string`: Indicates which headers are safe to expose to the API of a CORS API specification
78
+* **AllowCredentials** `bool`: Indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates. The default is `false`.
79
+* **MaxAge** `int`: Indicates how long (in seconds) the results of a preflight request can be cached. The default is `0` which stands for no max age.
80
+
81
+See [API documentation](http://godoc.org/github.com/rs/cors) for more info.
82
+
83
+## Benchmarks
84
+
85
+ BenchmarkWithout 20000000 64.6 ns/op 8 B/op 1 allocs/op
86
+ BenchmarkDefault 3000000 469 ns/op 114 B/op 2 allocs/op
87
+ BenchmarkAllowedOrigin 3000000 608 ns/op 114 B/op 2 allocs/op
88
+ BenchmarkPreflight 20000000 73.2 ns/op 0 B/op 0 allocs/op
89
+ BenchmarkPreflightHeader 20000000 73.6 ns/op 0 B/op 0 allocs/op
90
+ BenchmarkParseHeaderList 2000000 847 ns/op 184 B/op 6 allocs/op
91
+ BenchmarkParse…Single 5000000 290 ns/op 32 B/op 3 allocs/op
92
+ BenchmarkParse…Normalized 2000000 776 ns/op 160 B/op 6 allocs/op
93
+
94
+## Licenses
95
+
96
+All source code is licensed under the [MIT License](https://raw.github.com/rs/cors/master/LICENSE).
Godeps/_workspace/src/github.com/rs/cors/bench_test.go
new
+88
@@ -0,0 +1,88 @@
1
+package cors
2
+
3
+import (
4
+ "net/http"
5
+ "testing"
6
+)
7
+
8
+type FakeResponse struct {
9
+ header http.Header
10
+}
11
+
12
+func (r FakeResponse) Header() http.Header {
13
+ return r.header
14
+}
15
+
16
+func (r FakeResponse) WriteHeader(n int) {
17
+}
18
+
19
+func (r FakeResponse) Write(b []byte) (n int, err error) {
20
+ return len(b), nil
21
+}
22
+
23
+func BenchmarkWithout(b *testing.B) {
24
+ res := FakeResponse{http.Header{}}
25
+ req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
26
+
27
+ b.ReportAllocs()
28
+ b.ResetTimer()
29
+ for i := 0; i < b.N; i++ {
30
+ testHandler.ServeHTTP(res, req)
31
+ }
32
+}
33
+
34
+func BenchmarkDefault(b *testing.B) {
35
+ res := FakeResponse{http.Header{}}
36
+ req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
37
+ req.Header.Add("Origin", "somedomain.com")
38
+ handler := Default().Handler(testHandler)
39
+
40
+ b.ReportAllocs()
41
+ b.ResetTimer()
42
+ for i := 0; i < b.N; i++ {
43
+ handler.ServeHTTP(res, req)
44
+ }
45
+}
46
+
47
+func BenchmarkAllowedOrigin(b *testing.B) {
48
+ res := FakeResponse{http.Header{}}
49
+ req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
50
+ req.Header.Add("Origin", "somedomain.com")
51
+ c := New(Options{
52
+ AllowedOrigins: []string{"somedomain.com"},
53
+ })
54
+ handler := c.Handler(testHandler)
55
+
56
+ b.ReportAllocs()
57
+ b.ResetTimer()
58
+ for i := 0; i < b.N; i++ {
59
+ handler.ServeHTTP(res, req)
60
+ }
61
+}
62
+
63
+func BenchmarkPreflight(b *testing.B) {
64
+ res := FakeResponse{http.Header{}}
65
+ req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
66
+ req.Header.Add("Access-Control-Request-Method", "GET")
67
+ handler := Default().Handler(testHandler)
68
+
69
+ b.ReportAllocs()
70
+ b.ResetTimer()
71
+ for i := 0; i < b.N; i++ {
72
+ handler.ServeHTTP(res, req)
73
+ }
74
+}
75
+
76
+func BenchmarkPreflightHeader(b *testing.B) {
77
+ res := FakeResponse{http.Header{}}
78
+ req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
79
+ req.Header.Add("Access-Control-Request-Method", "GET")
80
+ req.Header.Add("Access-Control-Request-Headers", "Accept")
81
+ handler := Default().Handler(testHandler)
82
+
83
+ b.ReportAllocs()
84
+ b.ResetTimer()
85
+ for i := 0; i < b.N; i++ {
86
+ handler.ServeHTTP(res, req)
87
+ }
88
+}
Godeps/_workspace/src/github.com/rs/cors/cors.go
new
+345
@@ -0,0 +1,345 @@
1
+/*
2
+Package cors is net/http handler to handle CORS related requests
3
+as defined by http://www.w3.org/TR/cors/
4
+
5
+You can configure it by passing an option struct to cors.New:
6
+
7
+ c := cors.New(cors.Options{
8
+ AllowedOrigins: []string{"foo.com"},
9
+ AllowedMethods: []string{"GET", "POST", "DELETE"},
10
+ AllowCredentials: true,
11
+ })
12
+
13
+Then insert the handler in the chain:
14
+
15
+ handler = c.Handler(handler)
16
+
17
+See Options documentation for more options.
18
+
19
+The resulting handler is a standard net/http handler.
20
+*/
21
+package cors
22
+
23
+import (
24
+ "log"
25
+ "net/http"
26
+ "os"
27
+ "strconv"
28
+ "strings"
29
+)
30
+
31
+// Options is a configuration container to setup the CORS middleware.
32
+type Options struct {
33
+ // AllowedOrigins is a list of origins a cross-domain request can be executed from.
34
+ // If the special "*" value is present in the list, all origins will be allowed.
35
+ // Default value is ["*"]
36
+ AllowedOrigins []string
37
+ // AllowOriginFunc is a custom function to validate the origin. It take the origin
38
+ // as argument and returns true if allowed or false otherwise. If this option is
39
+ // set, the content of AllowedOrigins is ignored.
40
+ AllowOriginFunc func(origin string) bool
41
+ // AllowedMethods is a list of methods the client is allowed to use with
42
+ // cross-domain requests. Default value is simple methods (GET and POST)
43
+ AllowedMethods []string
44
+ // AllowedHeaders is list of non simple headers the client is allowed to use with
45
+ // cross-domain requests.
46
+ // If the special "*" value is present in the list, all headers will be allowed.
47
+ // Default value is [] but "Origin" is always appended to the list.
48
+ AllowedHeaders []string
49
+ // ExposedHeaders indicates which headers are safe to expose to the API of a CORS
50
+ // API specification
51
+ ExposedHeaders []string
52
+ // AllowCredentials indicates whether the request can include user credentials like
53
+ // cookies, HTTP authentication or client side SSL certificates.
54
+ AllowCredentials bool
55
+ // MaxAge indicates how long (in seconds) the results of a preflight request
56
+ // can be cached
57
+ MaxAge int
58
+ // Debugging flag adds additional output to debug server side CORS issues
59
+ Debug bool
60
+}
61
+
62
+type Cors struct {
63
+ // Debug logger
64
+ log *log.Logger
65
+ // Set to true when allowed origins contains a "*"
66
+ allowedOriginsAll bool
67
+ // Normalized list of allowed origins
68
+ allowedOrigins []string
69
+ // Optional origin validator function
70
+ allowOriginFunc func(origin string) bool
71
+ // Set to true when allowed headers contains a "*"
72
+ allowedHeadersAll bool
73
+ // Normalized list of allowed headers
74
+ allowedHeaders []string
75
+ // Normalized list of allowed methods
76
+ allowedMethods []string
77
+ // Normalized list of exposed headers
78
+ exposedHeaders []string
79
+ allowCredentials bool
80
+ maxAge int
81
+}
82
+
83
+// New creates a new Cors handler with the provided options.
84
+func New(options Options) *Cors {
85
+ c := &Cors{
86
+ exposedHeaders: convert(options.ExposedHeaders, http.CanonicalHeaderKey),
87
+ allowOriginFunc: options.AllowOriginFunc,
88
+ allowCredentials: options.AllowCredentials,
89
+ maxAge: options.MaxAge,
90
+ }
91
+ if options.Debug {
92
+ c.log = log.New(os.Stdout, "[cors] ", log.LstdFlags)
93
+ }
94
+
95
+ // Normalize options
96
+ // Note: for origins and methods matching, the spec requires a case-sensitive matching.
97
+ // As it may error prone, we chose to ignore the spec here.
98
+
99
+ // Allowed Origins
100
+ if len(options.AllowedOrigins) == 0 {
101
+ // Default is all origins
102
+ c.allowedOriginsAll = true
103
+ } else {
104
+ c.allowedOrigins = convert(options.AllowedOrigins, strings.ToLower)
105
+ for _, o := range c.allowedOrigins {
106
+ if o == "*" {
107
+ c.allowedOriginsAll = true
108
+ c.allowedOrigins = nil
109
+ break
110
+ }
111
+ }
112
+ }
113
+
114
+ // Allowed Headers
115
+ if len(options.AllowedHeaders) == 0 {
116
+ // Use sensible defaults
117
+ c.allowedHeaders = []string{"Origin", "Accept", "Content-Type"}
118
+ } else {
119
+ // Origin is always appended as some browsers will always request for this header at preflight
120
+ c.allowedHeaders = convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey)
121
+ for _, h := range options.AllowedHeaders {
122
+ if h == "*" {
123
+ c.allowedHeadersAll = true
124
+ c.allowedHeaders = nil
125
+ break
126
+ }
127
+ }
128
+ }
129
+
130
+ // Allowed Methods
131
+ if len(options.AllowedMethods) == 0 {
132
+ // Default is spec's "simple" methods
133
+ c.allowedMethods = []string{"GET", "POST"}
134
+ } else {
135
+ c.allowedMethods = convert(options.AllowedMethods, strings.ToUpper)
136
+ }
137
+
138
+ return c
139
+}
140
+
141
+// Default creates a new Cors handler with default options
142
+func Default() *Cors {
143
+ return New(Options{})
144
+}
145
+
146
+// Handler apply the CORS specification on the request, and add relevant CORS headers
147
+// as necessary.
148
+func (c *Cors) Handler(h http.Handler) http.Handler {
149
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
150
+ if r.Method == "OPTIONS" {
151
+ c.logf("Handler: Preflight request")
152
+ c.handlePreflight(w, r)
153
+ // Preflight requests are standalone and should stop the chain as some other
154
+ // middleware may not handle OPTIONS requests correctly. One typical example
155
+ // is authentication middleware ; OPTIONS requests won't carry authentication
156
+ // headers (see #1)
157
+ } else {
158
+ c.logf("Handler: Actual request")
159
+ c.handleActualRequest(w, r)
160
+ h.ServeHTTP(w, r)
161
+ }
162
+ })
163
+}
164
+
165
+// Martini compatible handler
166
+func (c *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) {
167
+ if r.Method == "OPTIONS" {
168
+ c.logf("HandlerFunc: Preflight request")
169
+ c.handlePreflight(w, r)
170
+ } else {
171
+ c.logf("HandlerFunc: Actual request")
172
+ c.handleActualRequest(w, r)
173
+ }
174
+}
175
+
176
+// Negroni compatible interface
177
+func (c *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
178
+ if r.Method == "OPTIONS" {
179
+ c.logf("ServeHTTP: Preflight request")
180
+ c.handlePreflight(w, r)
181
+ // Preflight requests are standalone and should stop the chain as some other
182
+ // middleware may not handle OPTIONS requests correctly. One typical example
183
+ // is authentication middleware ; OPTIONS requests won't carry authentication
184
+ // headers (see #1)
185
+ } else {
186
+ c.logf("ServeHTTP: Actual request")
187
+ c.handleActualRequest(w, r)
188
+ next(w, r)
189
+ }
190
+}
191
+
192
+// handlePreflight handles pre-flight CORS requests
193
+func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
194
+ headers := w.Header()
195
+ origin := r.Header.Get("Origin")
196
+
197
+ if r.Method != "OPTIONS" {
198
+ c.logf(" Preflight aborted: %s!=OPTIONS", r.Method)
199
+ return
200
+ }
201
+ if origin == "" {
202
+ c.logf(" Preflight aborted: empty origin")
203
+ return
204
+ }
205
+ if !c.isOriginAllowed(origin) {
206
+ c.logf(" Preflight aborted: origin '%s' not allowed", origin)
207
+ return
208
+ }
209
+
210
+ reqMethod := r.Header.Get("Access-Control-Request-Method")
211
+ if !c.isMethodAllowed(reqMethod) {
212
+ c.logf(" Preflight aborted: method '%s' not allowed", reqMethod)
213
+ return
214
+ }
215
+ reqHeaders := parseHeaderList(r.Header.Get("Access-Control-Request-Headers"))
216
+ if !c.areHeadersAllowed(reqHeaders) {
217
+ c.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders)
218
+ return
219
+ }
220
+ headers.Set("Access-Control-Allow-Origin", origin)
221
+ headers.Add("Vary", "Origin")
222
+ // Spec says: Since the list of methods can be unbounded, simply returning the method indicated
223
+ // by Access-Control-Request-Method (if supported) can be enough
224
+ headers.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod))
225
+ if len(reqHeaders) > 0 {
226
+
227
+ // Spec says: Since the list of headers can be unbounded, simply returning supported headers
228
+ // from Access-Control-Request-Headers can be enough
229
+ headers.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", "))
230
+ }
231
+ if c.allowCredentials {
232
+ headers.Set("Access-Control-Allow-Credentials", "true")
233
+ }
234
+ if c.maxAge > 0 {
235
+ headers.Set("Access-Control-Max-Age", strconv.Itoa(c.maxAge))
236
+ }
237
+ c.logf(" Preflight response headers: %v", headers)
238
+}
239
+
240
+// handleActualRequest handles simple cross-origin requests, actual request or redirects
241
+func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
242
+ headers := w.Header()
243
+ origin := r.Header.Get("Origin")
244
+
245
+ if r.Method == "OPTIONS" {
246
+ c.logf(" Actual request no headers added: method == %s", r.Method)
247
+ return
248
+ }
249
+ if origin == "" {
250
+ c.logf(" Actual request no headers added: missing origin")
251
+ return
252
+ }
253
+ if !c.isOriginAllowed(origin) {
254
+ c.logf(" Actual request no headers added: origin '%s' not allowed", origin)
255
+ return
256
+ }
257
+
258
+ // Note that spec does define a way to specifically disallow a simple method like GET or
259
+ // POST. Access-Control-Allow-Methods is only used for pre-flight requests and the
260
+ // spec doesn't instruct to check the allowed methods for simple cross-origin requests.
261
+ // We think it's a nice feature to be able to have control on those methods though.
262
+ if !c.isMethodAllowed(r.Method) {
263
+ if c.log != nil {
264
+ c.logf(" Actual request no headers added: method '%s' not allowed",
265
+ r.Method)
266
+ }
267
+
268
+ return
269
+ }
270
+ headers.Set("Access-Control-Allow-Origin", origin)
271
+ headers.Add("Vary", "Origin")
272
+ if len(c.exposedHeaders) > 0 {
273
+ headers.Set("Access-Control-Expose-Headers", strings.Join(c.exposedHeaders, ", "))
274
+ }
275
+ if c.allowCredentials {
276
+ headers.Set("Access-Control-Allow-Credentials", "true")
277
+ }
278
+ c.logf(" Actual response added headers: %v", headers)
279
+}
280
+
281
+// convenience method. checks if debugging is turned on before printing
282
+func (c *Cors) logf(format string, a ...interface{}) {
283
+ if c.log != nil {
284
+ c.log.Printf(format, a...)
285
+ }
286
+}
287
+
288
+// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests
289
+// on the endpoint
290
+func (c *Cors) isOriginAllowed(origin string) bool {
291
+ if c.allowOriginFunc != nil {
292
+ return c.allowOriginFunc(origin)
293
+ }
294
+ if c.allowedOriginsAll {
295
+ return true
296
+ }
297
+ origin = strings.ToLower(origin)
298
+ for _, o := range c.allowedOrigins {
299
+ if o == origin {
300
+ return true
301
+ }
302
+ }
303
+ return false
304
+}
305
+
306
+// isMethodAllowed checks if a given method can be used as part of a cross-domain request
307
+// on the endpoing
308
+func (c *Cors) isMethodAllowed(method string) bool {
309
+ if len(c.allowedMethods) == 0 {
310
+ // If no method allowed, always return false, even for preflight request
311
+ return false
312
+ }
313
+ method = strings.ToUpper(method)
314
+ if method == "OPTIONS" {
315
+ // Always allow preflight requests
316
+ return true
317
+ }
318
+ for _, m := range c.allowedMethods {
319
+ if m == method {
320
+ return true
321
+ }
322
+ }
323
+ return false
324
+}
325
+
326
+// areHeadersAllowed checks if a given list of headers are allowed to used within
327
+// a cross-domain request.
328
+func (c *Cors) areHeadersAllowed(requestedHeaders []string) bool {
329
+ if c.allowedHeadersAll || len(requestedHeaders) == 0 {
330
+ return true
331
+ }
332
+ for _, header := range requestedHeaders {
333
+ header = http.CanonicalHeaderKey(header)
334
+ found := false
335
+ for _, h := range c.allowedHeaders {
336
+ if h == header {
337
+ found = true
338
+ }
339
+ }
340
+ if !found {
341
+ return false
342
+ }
343
+ }
344
+ return true
345
+}
Godeps/_workspace/src/github.com/rs/cors/cors_test.go
new
+315
@@ -0,0 +1,315 @@
1
+package cors
2
+
3
+import (
4
+ "net/http"
5
+ "net/http/httptest"
6
+ "regexp"
7
+ "testing"
8
+)
9
+
10
+var testHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
11
+ w.Write([]byte("bar"))
12
+})
13
+
14
+func assertHeaders(t *testing.T, resHeaders http.Header, reqHeaders map[string]string) {
15
+ for name, value := range reqHeaders {
16
+ if resHeaders.Get(name) != value {
17
+ t.Errorf("Invalid header `%s', wanted `%s', got `%s'", name, value, resHeaders.Get(name))
18
+ }
19
+ }
20
+}
21
+
22
+func TestNoConfig(t *testing.T) {
23
+ s := New(Options{
24
+ // Intentionally left blank.
25
+ })
26
+
27
+ res := httptest.NewRecorder()
28
+ req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
29
+
30
+ s.Handler(testHandler).ServeHTTP(res, req)
31
+
32
+ assertHeaders(t, res.Header(), map[string]string{
33
+ "Access-Control-Allow-Origin": "",
34
+ "Access-Control-Allow-Methods": "",
35
+ "Access-Control-Allow-Headers": "",
36
+ "Access-Control-Allow-Credentials": "",
37
+ "Access-Control-Max-Age": "",
38
+ "Access-Control-Expose-Headers": "",
39
+ })
40
+}
41
+
42
+func TestWildcardOrigin(t *testing.T) {
43
+ s := New(Options{
44
+ AllowedOrigins: []string{"*"},
45
+ })
46
+
47
+ res := httptest.NewRecorder()
48
+ req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
49
+ req.Header.Add("Origin", "http://foobar.com")
50
+
51
+ s.Handler(testHandler).ServeHTTP(res, req)
52
+
53
+ assertHeaders(t, res.Header(), map[string]string{
54
+ "Access-Control-Allow-Origin": "http://foobar.com",
55
+ "Access-Control-Allow-Methods": "",
56
+ "Access-Control-Allow-Headers": "",
57
+ "Access-Control-Allow-Credentials": "",
58
+ "Access-Control-Max-Age": "",
59
+ "Access-Control-Expose-Headers": "",
60
+ })
61
+}
62
+
63
+func TestAllowedOrigin(t *testing.T) {
64
+ s := New(Options{
65
+ AllowedOrigins: []string{"http://foobar.com"},
66
+ })
67
+
68
+ res := httptest.NewRecorder()
69
+ req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
70
+ req.Header.Add("Origin", "http://foobar.com")
71
+
72
+ s.Handler(testHandler).ServeHTTP(res, req)
73
+
74
+ assertHeaders(t, res.Header(), map[string]string{
75
+ "Access-Control-Allow-Origin": "http://foobar.com",
76
+ "Access-Control-Allow-Methods": "",
77
+ "Access-Control-Allow-Headers": "",
78
+ "Access-Control-Allow-Credentials": "",
79
+ "Access-Control-Max-Age": "",
80
+ "Access-Control-Expose-Headers": "",
81
+ })
82
+}
83
+
84
+func TestDisallowedOrigin(t *testing.T) {
85
+ s := New(Options{
86
+ AllowedOrigins: []string{"http://foobar.com"},
87
+ })
88
+
89
+ res := httptest.NewRecorder()
90
+ req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
91
+ req.Header.Add("Origin", "http://barbaz.com")
92
+
93
+ s.Handler(testHandler).ServeHTTP(res, req)
94
+
95
+ assertHeaders(t, res.Header(), map[string]string{
96
+ "Access-Control-Allow-Origin": "",
97
+ "Access-Control-Allow-Methods": "",
98
+ "Access-Control-Allow-Headers": "",
99
+ "Access-Control-Allow-Credentials": "",
100
+ "Access-Control-Max-Age": "",
101
+ "Access-Control-Expose-Headers": "",
102
+ })
103
+}
104
+
105
+func TestAllowedOriginFunc(t *testing.T) {
106
+ r, _ := regexp.Compile("^http://foo")
107
+ s := New(Options{
108
+ AllowOriginFunc: func(o string) bool {
109
+ println(r.MatchString(o))
110
+ return r.MatchString(o)
111
+ },
112
+ })
113
+
114
+ req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
115
+
116
+ res := httptest.NewRecorder()
117
+ req.Header.Set("Origin", "http://foobar.com")
118
+ s.Handler(testHandler).ServeHTTP(res, req)
119
+ assertHeaders(t, res.Header(), map[string]string{
120
+ "Access-Control-Allow-Origin": "http://foobar.com",
121
+ })
122
+
123
+ res = httptest.NewRecorder()
124
+ req.Header.Set("Origin", "http://barfoo.com")
125
+ s.Handler(testHandler).ServeHTTP(res, req)
126
+ assertHeaders(t, res.Header(), map[string]string{
127
+ "Access-Control-Allow-Origin": "",
128
+ })
129
+}
130
+
131
+func TestAllowedMethod(t *testing.T) {
132
+ s := New(Options{
133
+ AllowedOrigins: []string{"http://foobar.com"},
134
+ AllowedMethods: []string{"PUT", "DELETE"},
135
+ })
136
+
137
+ res := httptest.NewRecorder()
138
+ req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
139
+ req.Header.Add("Origin", "http://foobar.com")
140
+ req.Header.Add("Access-Control-Request-Method", "PUT")
141
+
142
+ s.Handler(testHandler).ServeHTTP(res, req)
143
+
144
+ assertHeaders(t, res.Header(), map[string]string{
145
+ "Access-Control-Allow-Origin": "http://foobar.com",
146
+ "Access-Control-Allow-Methods": "PUT",
147
+ "Access-Control-Allow-Headers": "",
148
+ "Access-Control-Allow-Credentials": "",
149
+ "Access-Control-Max-Age": "",
150
+ "Access-Control-Expose-Headers": "",
151
+ })
152
+}
153
+
154
+func TestDisallowedMethod(t *testing.T) {
155
+ s := New(Options{
156
+ AllowedOrigins: []string{"http://foobar.com"},
157
+ AllowedMethods: []string{"PUT", "DELETE"},
158
+ })
159
+
160
+ res := httptest.NewRecorder()
161
+ req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
162
+ req.Header.Add("Origin", "http://foobar.com")
163
+ req.Header.Add("Access-Control-Request-Method", "PATCH")
164
+
165
+ s.Handler(testHandler).ServeHTTP(res, req)
166
+
167
+ assertHeaders(t, res.Header(), map[string]string{
168
+ "Access-Control-Allow-Origin": "",
169
+ "Access-Control-Allow-Methods": "",
170
+ "Access-Control-Allow-Headers": "",
171
+ "Access-Control-Allow-Credentials": "",
172
+ "Access-Control-Max-Age": "",
173
+ "Access-Control-Expose-Headers": "",
174
+ })
175
+}
176
+
177
+func TestAllowedHeader(t *testing.T) {
178
+ s := New(Options{
179
+ AllowedOrigins: []string{"http://foobar.com"},
180
+ AllowedHeaders: []string{"X-Header-1", "x-header-2"},
181
+ })
182
+
183
+ res := httptest.NewRecorder()
184
+ req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
185
+ req.Header.Add("Origin", "http://foobar.com")
186
+ req.Header.Add("Access-Control-Request-Method", "GET")
187
+ req.Header.Add("Access-Control-Request-Headers", "X-Header-2, X-HEADER-1")
188
+
189
+ s.Handler(testHandler).ServeHTTP(res, req)
190
+
191
+ assertHeaders(t, res.Header(), map[string]string{
192
+ "Access-Control-Allow-Origin": "http://foobar.com",
193
+ "Access-Control-Allow-Methods": "GET",
194
+ "Access-Control-Allow-Headers": "X-Header-2, X-Header-1",
195
+ "Access-Control-Allow-Credentials": "",
196
+ "Access-Control-Max-Age": "",
197
+ "Access-Control-Expose-Headers": "",
198
+ })
199
+}
200
+
201
+func TestAllowedWildcardHeader(t *testing.T) {
202
+ s := New(Options{
203
+ AllowedOrigins: []string{"http://foobar.com"},
204
+ AllowedHeaders: []string{"*"},
205
+ })
206
+
207
+ res := httptest.NewRecorder()
208
+ req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
209
+ req.Header.Add("Origin", "http://foobar.com")
210
+ req.Header.Add("Access-Control-Request-Method", "GET")
211
+ req.Header.Add("Access-Control-Request-Headers", "X-Header-2, X-HEADER-1")
212
+
213
+ s.Handler(testHandler).ServeHTTP(res, req)
214
+
215
+ assertHeaders(t, res.Header(), map[string]string{
216
+ "Access-Control-Allow-Origin": "http://foobar.com",
217
+ "Access-Control-Allow-Methods": "GET",
218
+ "Access-Control-Allow-Headers": "X-Header-2, X-Header-1",
219
+ "Access-Control-Allow-Credentials": "",
220
+ "Access-Control-Max-Age": "",
221
+ "Access-Control-Expose-Headers": "",
222
+ })
223
+}
224
+
225
+func TestDisallowedHeader(t *testing.T) {
226
+ s := New(Options{
227
+ AllowedOrigins: []string{"http://foobar.com"},
228
+ AllowedHeaders: []string{"X-Header-1", "x-header-2"},
229
+ })
230
+
231
+ res := httptest.NewRecorder()
232
+ req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
233
+ req.Header.Add("Origin", "http://foobar.com")
234
+ req.Header.Add("Access-Control-Request-Method", "GET")
235
+ req.Header.Add("Access-Control-Request-Headers", "X-Header-3, X-Header-1")
236
+
237
+ s.Handler(testHandler).ServeHTTP(res, req)
238
+
239
+ assertHeaders(t, res.Header(), map[string]string{
240
+ "Access-Control-Allow-Origin": "",
241
+ "Access-Control-Allow-Methods": "",
242
+ "Access-Control-Allow-Headers": "",
243
+ "Access-Control-Allow-Credentials": "",
244
+ "Access-Control-Max-Age": "",
245
+ "Access-Control-Expose-Headers": "",
246
+ })
247
+}
248
+
249
+func TestOriginHeader(t *testing.T) {
250
+ s := New(Options{
251
+ AllowedOrigins: []string{"http://foobar.com"},
252
+ })
253
+
254
+ res := httptest.NewRecorder()
255
+ req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
256
+ req.Header.Add("Origin", "http://foobar.com")
257
+ req.Header.Add("Access-Control-Request-Method", "GET")
258
+ req.Header.Add("Access-Control-Request-Headers", "origin")
259
+
260
+ s.Handler(testHandler).ServeHTTP(res, req)
261
+
262
+ assertHeaders(t, res.Header(), map[string]string{
263
+ "Access-Control-Allow-Origin": "http://foobar.com",
264
+ "Access-Control-Allow-Methods": "GET",
265
+ "Access-Control-Allow-Headers": "Origin",
266
+ "Access-Control-Allow-Credentials": "",
267
+ "Access-Control-Max-Age": "",
268
+ "Access-Control-Expose-Headers": "",
269
+ })
270
+}
271
+
272
+func TestExposedHeader(t *testing.T) {
273
+ s := New(Options{
274
+ AllowedOrigins: []string{"http://foobar.com"},
275
+ ExposedHeaders: []string{"X-Header-1", "x-header-2"},
276
+ })
277
+
278
+ res := httptest.NewRecorder()
279
+ req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
280
+ req.Header.Add("Origin", "http://foobar.com")
281
+
282
+ s.Handler(testHandler).ServeHTTP(res, req)
283
+
284
+ assertHeaders(t, res.Header(), map[string]string{
285
+ "Access-Control-Allow-Origin": "http://foobar.com",
286
+ "Access-Control-Allow-Methods": "",
287
+ "Access-Control-Allow-Headers": "",
288
+ "Access-Control-Allow-Credentials": "",
289
+ "Access-Control-Max-Age": "",
290
+ "Access-Control-Expose-Headers": "X-Header-1, X-Header-2",
291
+ })
292
+}
293
+
294
+func TestAllowedCredentials(t *testing.T) {
295
+ s := New(Options{
296
+ AllowedOrigins: []string{"http://foobar.com"},
297
+ AllowCredentials: true,
298
+ })
299
+
300
+ res := httptest.NewRecorder()
301
+ req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
302
+ req.Header.Add("Origin", "http://foobar.com")
303
+ req.Header.Add("Access-Control-Request-Method", "GET")
304
+
305
+ s.Handler(testHandler).ServeHTTP(res, req)
306
+
307
+ assertHeaders(t, res.Header(), map[string]string{
308
+ "Access-Control-Allow-Origin": "http://foobar.com",
309
+ "Access-Control-Allow-Methods": "GET",
310
+ "Access-Control-Allow-Headers": "",
311
+ "Access-Control-Allow-Credentials": "true",
312
+ "Access-Control-Max-Age": "",
313
+ "Access-Control-Expose-Headers": "",
314
+ })
315
+}
Godeps/_workspace/src/github.com/rs/cors/examples/alice/server.go
new
+24
@@ -0,0 +1,24 @@
1
+package main
2
+
3
+import (
4
+ "net/http"
5
+
6
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
7
+ "github.com/justinas/alice"
8
+)
9
+
10
+func main() {
11
+ c := cors.New(cors.Options{
12
+ AllowedOrigins: []string{"http://foo.com"},
13
+ })
14
+
15
+ mux := http.NewServeMux()
16
+
17
+ mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
18
+ w.Header().Set("Content-Type", "application/json")
19
+ w.Write([]byte("{\"hello\": \"world\"}"))
20
+ })
21
+
22
+ chain := alice.New(c.Handler).Then(mux)
23
+ http.ListenAndServe(":8080", chain)
24
+}
Godeps/_workspace/src/github.com/rs/cors/examples/default/server.go
new
+18
@@ -0,0 +1,18 @@
1
+package main
2
+
3
+import (
4
+ "net/http"
5
+
6
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
7
+)
8
+
9
+func main() {
10
+ h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
11
+ w.Header().Set("Content-Type", "application/json")
12
+ w.Write([]byte("{\"hello\": \"world\"}"))
13
+ })
14
+
15
+ // Use default options
16
+ handler := cors.Default().Handler(h)
17
+ http.ListenAndServe(":8080", handler)
18
+}
Godeps/_workspace/src/github.com/rs/cors/examples/goji/server.go
new
+22
@@ -0,0 +1,22 @@
1
+package main
2
+
3
+import (
4
+ "net/http"
5
+
6
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
7
+ "github.com/zenazn/goji"
8
+)
9
+
10
+func main() {
11
+ c := cors.New(cors.Options{
12
+ AllowedOrigins: []string{"http://foo.com"},
13
+ })
14
+ goji.Use(c.Handler)
15
+
16
+ goji.Get("/", func(w http.ResponseWriter, r *http.Request) {
17
+ w.Header().Set("Content-Type", "application/json")
18
+ w.Write([]byte("{\"hello\": \"world\"}"))
19
+ })
20
+
21
+ goji.Serve()
22
+}
Godeps/_workspace/src/github.com/rs/cors/examples/martini/server.go
new
+23
@@ -0,0 +1,23 @@
1
+package main
2
+
3
+import (
4
+ "github.com/go-martini/martini"
5
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
6
+ "github.com/martini-contrib/render"
7
+)
8
+
9
+func main() {
10
+ c := cors.New(cors.Options{
11
+ AllowedOrigins: []string{"http://foo.com"},
12
+ })
13
+
14
+ m := martini.Classic()
15
+ m.Use(render.Renderer())
16
+ m.Use(c.HandlerFunc)
17
+
18
+ m.Get("/", func(r render.Render) {
19
+ r.JSON(200, map[string]interface{}{"hello": "world"})
20
+ })
21
+
22
+ m.Run()
23
+}
Godeps/_workspace/src/github.com/rs/cors/examples/negroni/server.go
new
+26
@@ -0,0 +1,26 @@
1
+package main
2
+
3
+import (
4
+ "net/http"
5
+
6
+ "github.com/codegangsta/negroni"
7
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
8
+)
9
+
10
+func main() {
11
+ c := cors.New(cors.Options{
12
+ AllowedOrigins: []string{"http://foo.com"},
13
+ })
14
+
15
+ mux := http.NewServeMux()
16
+
17
+ mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
18
+ w.Header().Set("Content-Type", "application/json")
19
+ w.Write([]byte("{\"hello\": \"world\"}"))
20
+ })
21
+
22
+ n := negroni.Classic()
23
+ n.Use(c)
24
+ n.UseHandler(mux)
25
+ n.Run(":3000")
26
+}
Godeps/_workspace/src/github.com/rs/cors/examples/nethttp/server.go
new
+20
@@ -0,0 +1,20 @@
1
+package main
2
+
3
+import (
4
+ "net/http"
5
+
6
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
7
+)
8
+
9
+func main() {
10
+ c := cors.New(cors.Options{
11
+ AllowedOrigins: []string{"http://foo.com"},
12
+ })
13
+
14
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
15
+ w.Header().Set("Content-Type", "application/json")
16
+ w.Write([]byte("{\"hello\": \"world\"}"))
17
+ })
18
+
19
+ http.ListenAndServe(":8080", c.Handler(handler))
20
+}
Godeps/_workspace/src/github.com/rs/cors/examples/openbar/server.go
new
+22
@@ -0,0 +1,22 @@
1
+package main
2
+
3
+import (
4
+ "net/http"
5
+
6
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
7
+)
8
+
9
+func main() {
10
+ c := cors.New(cors.Options{
11
+ AllowedOrigins: []string{"*"},
12
+ AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
13
+ AllowCredentials: true,
14
+ })
15
+
16
+ h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
17
+ w.Header().Set("Content-Type", "application/json")
18
+ w.Write([]byte("{\"hello\": \"world\"}"))
19
+ })
20
+
21
+ http.ListenAndServe(":8080", c.Handler(h))
22
+}
Godeps/_workspace/src/github.com/rs/cors/utils.go
new
+59
@@ -0,0 +1,59 @@
1
+package cors
2
+
3
+const toLower = 'a' - 'A'
4
+
5
+type converter func(string) string
6
+
7
+// convert converts a list of string using the passed converter function
8
+func convert(s []string, c converter) []string {
9
+ out := []string{}
10
+ for _, i := range s {
11
+ out = append(out, c(i))
12
+ }
13
+ return out
14
+}
15
+
16
+// parseHeaderList tokenize + normalize a string containing a list of headers
17
+func parseHeaderList(headerList string) []string {
18
+ l := len(headerList)
19
+ h := make([]byte, 0, l)
20
+ upper := true
21
+ // Estimate the number headers in order to allocate the right splice size
22
+ t := 0
23
+ for i := 0; i < l; i++ {
24
+ if headerList[i] == ',' {
25
+ t++
26
+ }
27
+ }
28
+ headers := make([]string, 0, t)
29
+ for i := 0; i < l; i++ {
30
+ b := headerList[i]
31
+ if b >= 'a' && b <= 'z' {
32
+ if upper {
33
+ h = append(h, b-toLower)
34
+ } else {
35
+ h = append(h, b)
36
+ }
37
+ } else if b >= 'A' && b <= 'Z' {
38
+ if !upper {
39
+ h = append(h, b+toLower)
40
+ } else {
41
+ h = append(h, b)
42
+ }
43
+ } else if b == '-' || (b >= '0' && b <= '9') {
44
+ h = append(h, b)
45
+ }
46
+
47
+ if b == ' ' || b == ',' || i == l-1 {
48
+ if len(h) > 0 {
49
+ // Flush the found header
50
+ headers = append(headers, string(h))
51
+ h = h[:0]
52
+ upper = true
53
+ }
54
+ } else {
55
+ upper = b == '-'
56
+ }
57
+ }
58
+ return headers
59
+}
Godeps/_workspace/src/github.com/rs/cors/utils_test.go
new
+52
@@ -0,0 +1,52 @@
1
+package cors
2
+
3
+import (
4
+ "strings"
5
+ "testing"
6
+)
7
+
8
+func TestConvert(t *testing.T) {
9
+ s := convert([]string{"A", "b", "C"}, strings.ToLower)
10
+ e := []string{"a", "b", "c"}
11
+ if s[0] != e[0] || s[1] != e[1] || s[2] != e[2] {
12
+ t.Errorf("%v != %v", s, e)
13
+ }
14
+}
15
+
16
+func TestParseHeaderList(t *testing.T) {
17
+ h := parseHeaderList("header, second-header, THIRD-HEADER, Numb3r3d-H34d3r")
18
+ e := []string{"Header", "Second-Header", "Third-Header", "Numb3r3d-H34d3r"}
19
+ if h[0] != e[0] || h[1] != e[1] || h[2] != e[2] {
20
+ t.Errorf("%v != %v", h, e)
21
+ }
22
+}
23
+
24
+func TestParseHeaderListEmpty(t *testing.T) {
25
+ if len(parseHeaderList("")) != 0 {
26
+ t.Error("should be empty sclice")
27
+ }
28
+ if len(parseHeaderList(" , ")) != 0 {
29
+ t.Error("should be empty sclice")
30
+ }
31
+}
32
+
33
+func BenchmarkParseHeaderList(b *testing.B) {
34
+ b.ReportAllocs()
35
+ for i := 0; i < b.N; i++ {
36
+ parseHeaderList("header, second-header, THIRD-HEADER")
37
+ }
38
+}
39
+
40
+func BenchmarkParseHeaderListSingle(b *testing.B) {
41
+ b.ReportAllocs()
42
+ for i := 0; i < b.N; i++ {
43
+ parseHeaderList("header")
44
+ }
45
+}
46
+
47
+func BenchmarkParseHeaderListNormalized(b *testing.B) {
48
+ b.ReportAllocs()
49
+ for i := 0; i < b.N; i++ {
50
+ parseHeaderList("Header1, Header2, Third-Header")
51
+ }
52
+}
commands/http/handler.go
+1
-2
@@ -8,8 +8,7 @@ import (
8
"strconv"
9
"strings"
10
11
- "github.com/rs/cors"
12
-
11
+ "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
12
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
13
14
cmds "github.com/ipfs/go-ipfs/commands"