@cryptotaxi247 / kubo / commits / 496e7a4b2

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 [![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/rs/cors) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/cors/master/LICENSE) [![build](https://img.shields.io/travis/rs/cors.svg?style=flat)](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"