Merge pull request #589 from tgerring/corssetting
Configurable CORS domain
This commit is contained in:
		
							
								
								
									
										4
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							| @@ -90,6 +90,10 @@ | |||||||
| 			"ImportPath": "github.com/robertkrimen/otto/token", | 			"ImportPath": "github.com/robertkrimen/otto/token", | ||||||
| 			"Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba" | 			"Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba" | ||||||
| 		}, | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/rs/cors", | ||||||
|  | 			"Rev": "6e0c3cb65fc0fdb064c743d176a620e3ca446dfb" | ||||||
|  | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"ImportPath": "github.com/syndtr/goleveldb/leveldb", | 			"ImportPath": "github.com/syndtr/goleveldb/leveldb", | ||||||
| 			"Rev": "832fa7ed4d28545eab80f19e1831fc004305cade" | 			"Rev": "832fa7ed4d28545eab80f19e1831fc004305cade" | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								Godeps/_workspace/src/github.com/rs/cors/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								Godeps/_workspace/src/github.com/rs/cors/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | language: go | ||||||
|  | go: | ||||||
|  | - 1.3 | ||||||
|  | - 1.4 | ||||||
							
								
								
									
										19
									
								
								Godeps/_workspace/src/github.com/rs/cors/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								Godeps/_workspace/src/github.com/rs/cors/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | Copyright (c) 2014 Olivier Poitrey <rs@dailymotion.com> | ||||||
|  |  | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is furnished | ||||||
|  | to do so, subject to the following conditions: | ||||||
|  |  | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  |  | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  | THE SOFTWARE. | ||||||
							
								
								
									
										84
									
								
								Godeps/_workspace/src/github.com/rs/cors/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								Godeps/_workspace/src/github.com/rs/cors/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | # Go CORS handler [](https://godoc.org/github.com/rs/cors) [](https://raw.githubusercontent.com/rs/cors/master/LICENSE) [](https://travis-ci.org/rs/cors) | ||||||
|  |  | ||||||
|  | CORS is a `net/http` handler implementing [Cross Origin Resource Sharing W3 specification](http://www.w3.org/TR/cors/) in Golang. | ||||||
|  |  | ||||||
|  | ## Getting Started | ||||||
|  |  | ||||||
|  | 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`. | ||||||
|  |  | ||||||
|  | ```go | ||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  |     "net/http" | ||||||
|  |  | ||||||
|  |     "github.com/rs/cors" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  |     h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
|  |         w.Header().Set("Content-Type", "application/json") | ||||||
|  |         w.Write([]byte("{\"hello\": \"world\"}")) | ||||||
|  |     }) | ||||||
|  |  | ||||||
|  |     // cors.Default() setup the middleware with default options being | ||||||
|  |     // all origins accepted with simple methods (GET, POST). See | ||||||
|  |     // documentation below for more options. | ||||||
|  |     handler = cors.Default().Handler(h) | ||||||
|  |     http.ListenAndServe(":8080", handler) | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Install `cors`: | ||||||
|  |  | ||||||
|  |     go get github.com/rs/cors | ||||||
|  |  | ||||||
|  | Then run your server: | ||||||
|  |  | ||||||
|  |     go run server.go | ||||||
|  |  | ||||||
|  | The server now runs on `localhost:8080`: | ||||||
|  |  | ||||||
|  |     $ curl -D - -H 'Origin: http://foo.com' http://localhost:8080/ | ||||||
|  |     HTTP/1.1 200 OK | ||||||
|  |     Access-Control-Allow-Origin: foo.com | ||||||
|  |     Content-Type: application/json | ||||||
|  |     Date: Sat, 25 Oct 2014 03:43:57 GMT | ||||||
|  |     Content-Length: 18 | ||||||
|  |  | ||||||
|  |     {"hello": "world"} | ||||||
|  |  | ||||||
|  | ### More Examples | ||||||
|  |  | ||||||
|  | * `net/http`: [examples/nethttp/server.go](https://github.com/rs/cors/blob/master/examples/nethttp/server.go) | ||||||
|  | * [Goji](https://goji.io): [examples/goji/server.go](https://github.com/rs/cors/blob/master/examples/goji/server.go) | ||||||
|  | * [Martini](http://martini.codegangsta.io): [examples/martini/server.go](https://github.com/rs/cors/blob/master/examples/martini/server.go) | ||||||
|  | * [Negroni](https://github.com/codegangsta/negroni): [examples/negroni/server.go](https://github.com/rs/cors/blob/master/examples/negroni/server.go) | ||||||
|  | * [Alice](https://github.com/justinas/alice): [examples/alice/server.go](https://github.com/rs/cors/blob/master/examples/alice/server.go) | ||||||
|  |  | ||||||
|  | ## Parameters | ||||||
|  |  | ||||||
|  | Parameters are passed to the middleware thru the `cors.New` method as follow: | ||||||
|  |  | ||||||
|  | ```go | ||||||
|  | c := cors.New(cors.Options{ | ||||||
|  |     AllowedOrigins: []string{"http://foo.com"}, | ||||||
|  |     AllowCredentials: true, | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | // Insert the middleware | ||||||
|  | handler = c.Handler(handler) | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | * **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 `*`. | ||||||
|  | * **AllowedMethods** `[]string`: A list of methods the client is allowed to use with cross-domain requests. | ||||||
|  | * **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`) | ||||||
|  | * **ExposedHeaders** `[]string`: Indicates which headers are safe to expose to the API of a CORS API specification | ||||||
|  | * **AllowCredentials** `bool`: Indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates. The default is `false`. | ||||||
|  | * **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. | ||||||
|  |  | ||||||
|  | See [API documentation](http://godoc.org/github.com/rs/cors) for more info. | ||||||
|  |  | ||||||
|  | ## Licenses | ||||||
|  |  | ||||||
|  | All source code is licensed under the [MIT License](https://raw.github.com/rs/cors/master/LICENSE). | ||||||
							
								
								
									
										37
									
								
								Godeps/_workspace/src/github.com/rs/cors/bench_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								Godeps/_workspace/src/github.com/rs/cors/bench_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | package cors | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/http/httptest" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func BenchmarkWithout(b *testing.B) { | ||||||
|  | 	res := httptest.NewRecorder() | ||||||
|  | 	req, _ := http.NewRequest("GET", "http://example.com/foo", nil) | ||||||
|  |  | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		testHandler.ServeHTTP(res, req) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func BenchmarkDefault(b *testing.B) { | ||||||
|  | 	res := httptest.NewRecorder() | ||||||
|  | 	req, _ := http.NewRequest("GET", "http://example.com/foo", nil) | ||||||
|  | 	handler := Default() | ||||||
|  |  | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		handler.Handler(testHandler).ServeHTTP(res, req) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func BenchmarkPreflight(b *testing.B) { | ||||||
|  | 	res := httptest.NewRecorder() | ||||||
|  | 	req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) | ||||||
|  | 	req.Header.Add("Access-Control-Request-Method", "GET") | ||||||
|  | 	handler := Default() | ||||||
|  |  | ||||||
|  | 	for i := 0; i < b.N; i++ { | ||||||
|  | 		handler.Handler(testHandler).ServeHTTP(res, req) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										308
									
								
								Godeps/_workspace/src/github.com/rs/cors/cors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										308
									
								
								Godeps/_workspace/src/github.com/rs/cors/cors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,308 @@ | |||||||
|  | /* | ||||||
|  | Package cors is net/http handler to handle CORS related requests | ||||||
|  | as defined by http://www.w3.org/TR/cors/ | ||||||
|  |  | ||||||
|  | You can configure it by passing an option struct to cors.New: | ||||||
|  |  | ||||||
|  |     c := cors.New(cors.Options{ | ||||||
|  |         AllowedOrigins: []string{"foo.com"}, | ||||||
|  |         AllowedMethods: []string{"GET", "POST", "DELETE"}, | ||||||
|  |         AllowCredentials: true, | ||||||
|  |     }) | ||||||
|  |  | ||||||
|  | Then insert the handler in the chain: | ||||||
|  |  | ||||||
|  |     handler = c.Handler(handler) | ||||||
|  |  | ||||||
|  | See Options documentation for more options. | ||||||
|  |  | ||||||
|  | The resulting handler is a standard net/http handler. | ||||||
|  | */ | ||||||
|  | package cors | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"log" | ||||||
|  | 	"net/http" | ||||||
|  | 	"os" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Options is a configuration container to setup the CORS middleware. | ||||||
|  | type Options struct { | ||||||
|  | 	// AllowedOrigins is 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. | ||||||
|  | 	// Default value is ["*"] | ||||||
|  | 	AllowedOrigins []string | ||||||
|  | 	// AllowedMethods is a list of methods the client is allowed to use with | ||||||
|  | 	// cross-domain requests. Default value is simple methods (GET and POST) | ||||||
|  | 	AllowedMethods []string | ||||||
|  | 	// AllowedHeaders is list of non simple headers the client is allowed to use with | ||||||
|  | 	// cross-domain requests. | ||||||
|  | 	// If the special "*" value is present in the list, all headers will be allowed. | ||||||
|  | 	// Default value is [] but "Origin" is always appended to the list. | ||||||
|  | 	AllowedHeaders []string | ||||||
|  | 	// ExposedHeaders indicates which headers are safe to expose to the API of a CORS | ||||||
|  | 	// API specification | ||||||
|  | 	ExposedHeaders []string | ||||||
|  | 	// AllowCredentials indicates whether the request can include user credentials like | ||||||
|  | 	// cookies, HTTP authentication or client side SSL certificates. | ||||||
|  | 	AllowCredentials bool | ||||||
|  | 	// MaxAge indicates how long (in seconds) the results of a preflight request | ||||||
|  | 	// can be cached | ||||||
|  | 	MaxAge int | ||||||
|  | 	// Debugging flag adds additional output to debug server side CORS issues | ||||||
|  | 	Debug bool | ||||||
|  | 	// log object to use when debugging | ||||||
|  | 	log *log.Logger | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Cors struct { | ||||||
|  | 	// The CORS Options | ||||||
|  | 	options Options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // New creates a new Cors handler with the provided options. | ||||||
|  | func New(options Options) *Cors { | ||||||
|  | 	// Normalize options | ||||||
|  | 	// Note: for origins and methods matching, the spec requires a case-sensitive matching. | ||||||
|  | 	// As it may error prone, we chose to ignore the spec here. | ||||||
|  | 	normOptions := Options{ | ||||||
|  | 		AllowedOrigins: convert(options.AllowedOrigins, strings.ToLower), | ||||||
|  | 		AllowedMethods: convert(options.AllowedMethods, strings.ToUpper), | ||||||
|  | 		// Origin is always appended as some browsers will always request | ||||||
|  | 		// for this header at preflight | ||||||
|  | 		AllowedHeaders:   convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey), | ||||||
|  | 		ExposedHeaders:   convert(options.ExposedHeaders, http.CanonicalHeaderKey), | ||||||
|  | 		AllowCredentials: options.AllowCredentials, | ||||||
|  | 		MaxAge:           options.MaxAge, | ||||||
|  | 		Debug:            options.Debug, | ||||||
|  | 		log:              log.New(os.Stdout, "[cors] ", log.LstdFlags), | ||||||
|  | 	} | ||||||
|  | 	if len(normOptions.AllowedOrigins) == 0 { | ||||||
|  | 		// Default is all origins | ||||||
|  | 		normOptions.AllowedOrigins = []string{"*"} | ||||||
|  | 	} | ||||||
|  | 	if len(normOptions.AllowedHeaders) == 1 { | ||||||
|  | 		// Add some sensible defaults | ||||||
|  | 		normOptions.AllowedHeaders = []string{"Origin", "Accept", "Content-Type"} | ||||||
|  | 	} | ||||||
|  | 	if len(normOptions.AllowedMethods) == 0 { | ||||||
|  | 		// Default is simple methods | ||||||
|  | 		normOptions.AllowedMethods = []string{"GET", "POST"} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if normOptions.Debug { | ||||||
|  | 		normOptions.log.Printf("Options: %v", normOptions) | ||||||
|  | 	} | ||||||
|  | 	return &Cors{ | ||||||
|  | 		options: normOptions, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Default creates a new Cors handler with default options | ||||||
|  | func Default() *Cors { | ||||||
|  | 	return New(Options{}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Handler apply the CORS specification on the request, and add relevant CORS headers | ||||||
|  | // as necessary. | ||||||
|  | func (cors *Cors) Handler(h http.Handler) http.Handler { | ||||||
|  | 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		if r.Method == "OPTIONS" { | ||||||
|  | 			cors.logf("Handler: Preflight request") | ||||||
|  | 			cors.handlePreflight(w, r) | ||||||
|  | 			// Preflight requests are standalone and should stop the chain as some other | ||||||
|  | 			// middleware may not handle OPTIONS requests correctly. One typical example | ||||||
|  | 			// is authentication middleware ; OPTIONS requests won't carry authentication | ||||||
|  | 			// headers (see #1) | ||||||
|  | 		} else { | ||||||
|  | 			cors.logf("Handler: Actual request") | ||||||
|  | 			cors.handleActualRequest(w, r) | ||||||
|  | 			h.ServeHTTP(w, r) | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Martini compatible handler | ||||||
|  | func (cors *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 	if r.Method == "OPTIONS" { | ||||||
|  | 		cors.logf("HandlerFunc: Preflight request") | ||||||
|  | 		cors.handlePreflight(w, r) | ||||||
|  | 	} else { | ||||||
|  | 		cors.logf("HandlerFunc: Actual request") | ||||||
|  | 		cors.handleActualRequest(w, r) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Negroni compatible interface | ||||||
|  | func (cors *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { | ||||||
|  | 	if r.Method == "OPTIONS" { | ||||||
|  | 		cors.logf("ServeHTTP: Preflight request") | ||||||
|  | 		cors.handlePreflight(w, r) | ||||||
|  | 		// Preflight requests are standalone and should stop the chain as some other | ||||||
|  | 		// middleware may not handle OPTIONS requests correctly. One typical example | ||||||
|  | 		// is authentication middleware ; OPTIONS requests won't carry authentication | ||||||
|  | 		// headers (see #1) | ||||||
|  | 	} else { | ||||||
|  | 		cors.logf("ServeHTTP: Actual request") | ||||||
|  | 		cors.handleActualRequest(w, r) | ||||||
|  | 		next(w, r) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // handlePreflight handles pre-flight CORS requests | ||||||
|  | func (cors *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 	options := cors.options | ||||||
|  | 	headers := w.Header() | ||||||
|  | 	origin := r.Header.Get("Origin") | ||||||
|  |  | ||||||
|  | 	if r.Method != "OPTIONS" { | ||||||
|  | 		cors.logf("  Preflight aborted: %s!=OPTIONS", r.Method) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if origin == "" { | ||||||
|  | 		cors.logf("  Preflight aborted: empty origin") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if !cors.isOriginAllowed(origin) { | ||||||
|  | 		cors.logf("  Preflight aborted: origin '%s' not allowed", origin) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	reqMethod := r.Header.Get("Access-Control-Request-Method") | ||||||
|  | 	if !cors.isMethodAllowed(reqMethod) { | ||||||
|  | 		cors.logf("  Preflight aborted: method '%s' not allowed", reqMethod) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	reqHeaders := parseHeaderList(r.Header.Get("Access-Control-Request-Headers")) | ||||||
|  | 	if !cors.areHeadersAllowed(reqHeaders) { | ||||||
|  | 		cors.logf("  Preflight aborted: headers '%v' not allowed", reqHeaders) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	headers.Set("Access-Control-Allow-Origin", origin) | ||||||
|  | 	headers.Add("Vary", "Origin") | ||||||
|  | 	// Spec says: Since the list of methods can be unbounded, simply returning the method indicated | ||||||
|  | 	// by Access-Control-Request-Method (if supported) can be enough | ||||||
|  | 	headers.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod)) | ||||||
|  | 	if len(reqHeaders) > 0 { | ||||||
|  |  | ||||||
|  | 		// Spec says: Since the list of headers can be unbounded, simply returning supported headers | ||||||
|  | 		// from Access-Control-Request-Headers can be enough | ||||||
|  | 		headers.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", ")) | ||||||
|  | 	} | ||||||
|  | 	if options.AllowCredentials { | ||||||
|  | 		headers.Set("Access-Control-Allow-Credentials", "true") | ||||||
|  | 	} | ||||||
|  | 	if options.MaxAge > 0 { | ||||||
|  | 		headers.Set("Access-Control-Max-Age", strconv.Itoa(options.MaxAge)) | ||||||
|  | 	} | ||||||
|  | 	cors.logf("  Preflight response headers: %v", headers) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // handleActualRequest handles simple cross-origin requests, actual request or redirects | ||||||
|  | func (cors *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 	options := cors.options | ||||||
|  | 	headers := w.Header() | ||||||
|  | 	origin := r.Header.Get("Origin") | ||||||
|  |  | ||||||
|  | 	if r.Method == "OPTIONS" { | ||||||
|  | 		cors.logf("  Actual request no headers added: method == %s", r.Method) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if origin == "" { | ||||||
|  | 		cors.logf("  Actual request no headers added: missing origin") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if !cors.isOriginAllowed(origin) { | ||||||
|  | 		cors.logf("  Actual request no headers added: origin '%s' not allowed", origin) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Note that spec does define a way to specifically disallow a simple method like GET or | ||||||
|  | 	// POST. Access-Control-Allow-Methods is only used for pre-flight requests and the | ||||||
|  | 	// spec doesn't instruct to check the allowed methods for simple cross-origin requests. | ||||||
|  | 	// We think it's a nice feature to be able to have control on those methods though. | ||||||
|  | 	if !cors.isMethodAllowed(r.Method) { | ||||||
|  | 		if cors.options.Debug { | ||||||
|  | 			cors.logf("  Actual request no headers added: method '%s' not allowed", | ||||||
|  | 				r.Method) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	headers.Set("Access-Control-Allow-Origin", origin) | ||||||
|  | 	headers.Add("Vary", "Origin") | ||||||
|  | 	if len(options.ExposedHeaders) > 0 { | ||||||
|  | 		headers.Set("Access-Control-Expose-Headers", strings.Join(options.ExposedHeaders, ", ")) | ||||||
|  | 	} | ||||||
|  | 	if options.AllowCredentials { | ||||||
|  | 		headers.Set("Access-Control-Allow-Credentials", "true") | ||||||
|  | 	} | ||||||
|  | 	cors.logf("  Actual response added headers: %v", headers) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // convenience method. checks if debugging is turned on before printing | ||||||
|  | func (cors *Cors) logf(format string, a ...interface{}) { | ||||||
|  | 	if cors.options.Debug { | ||||||
|  | 		cors.options.log.Printf(format, a...) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // isOriginAllowed checks if a given origin is allowed to perform cross-domain requests | ||||||
|  | // on the endpoint | ||||||
|  | func (cors *Cors) isOriginAllowed(origin string) bool { | ||||||
|  | 	allowedOrigins := cors.options.AllowedOrigins | ||||||
|  | 	origin = strings.ToLower(origin) | ||||||
|  | 	for _, allowedOrigin := range allowedOrigins { | ||||||
|  | 		switch allowedOrigin { | ||||||
|  | 		case "*": | ||||||
|  | 			return true | ||||||
|  | 		case origin: | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // isMethodAllowed checks if a given method can be used as part of a cross-domain request | ||||||
|  | // on the endpoing | ||||||
|  | func (cors *Cors) isMethodAllowed(method string) bool { | ||||||
|  | 	allowedMethods := cors.options.AllowedMethods | ||||||
|  | 	if len(allowedMethods) == 0 { | ||||||
|  | 		// If no method allowed, always return false, even for preflight request | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	method = strings.ToUpper(method) | ||||||
|  | 	if method == "OPTIONS" { | ||||||
|  | 		// Always allow preflight requests | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	for _, allowedMethod := range allowedMethods { | ||||||
|  | 		if allowedMethod == method { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // areHeadersAllowed checks if a given list of headers are allowed to used within | ||||||
|  | // a cross-domain request. | ||||||
|  | func (cors *Cors) areHeadersAllowed(requestedHeaders []string) bool { | ||||||
|  | 	if len(requestedHeaders) == 0 { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	for _, header := range requestedHeaders { | ||||||
|  | 		found := false | ||||||
|  | 		for _, allowedHeader := range cors.options.AllowedHeaders { | ||||||
|  | 			if allowedHeader == "*" || allowedHeader == header { | ||||||
|  | 				found = true | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if !found { | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | } | ||||||
							
								
								
									
										288
									
								
								Godeps/_workspace/src/github.com/rs/cors/cors_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										288
									
								
								Godeps/_workspace/src/github.com/rs/cors/cors_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,288 @@ | |||||||
|  | package cors | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/http/httptest" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var testHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 	w.Write([]byte("bar")) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | func assertHeaders(t *testing.T, resHeaders http.Header, reqHeaders map[string]string) { | ||||||
|  | 	for name, value := range reqHeaders { | ||||||
|  | 		if resHeaders.Get(name) != value { | ||||||
|  | 			t.Errorf("Invalid header `%s', wanted `%s', got `%s'", name, value, resHeaders.Get(name)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestNoConfig(t *testing.T) { | ||||||
|  | 	s := New(Options{ | ||||||
|  | 	// Intentionally left blank. | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	res := httptest.NewRecorder() | ||||||
|  | 	req, _ := http.NewRequest("GET", "http://example.com/foo", nil) | ||||||
|  |  | ||||||
|  | 	s.Handler(testHandler).ServeHTTP(res, req) | ||||||
|  |  | ||||||
|  | 	assertHeaders(t, res.Header(), map[string]string{ | ||||||
|  | 		"Access-Control-Allow-Origin":      "", | ||||||
|  | 		"Access-Control-Allow-Methods":     "", | ||||||
|  | 		"Access-Control-Allow-Headers":     "", | ||||||
|  | 		"Access-Control-Allow-Credentials": "", | ||||||
|  | 		"Access-Control-Max-Age":           "", | ||||||
|  | 		"Access-Control-Expose-Headers":    "", | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestWildcardOrigin(t *testing.T) { | ||||||
|  | 	s := New(Options{ | ||||||
|  | 		AllowedOrigins: []string{"*"}, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	res := httptest.NewRecorder() | ||||||
|  | 	req, _ := http.NewRequest("GET", "http://example.com/foo", nil) | ||||||
|  | 	req.Header.Add("Origin", "http://foobar.com") | ||||||
|  |  | ||||||
|  | 	s.Handler(testHandler).ServeHTTP(res, req) | ||||||
|  |  | ||||||
|  | 	assertHeaders(t, res.Header(), map[string]string{ | ||||||
|  | 		"Access-Control-Allow-Origin":      "http://foobar.com", | ||||||
|  | 		"Access-Control-Allow-Methods":     "", | ||||||
|  | 		"Access-Control-Allow-Headers":     "", | ||||||
|  | 		"Access-Control-Allow-Credentials": "", | ||||||
|  | 		"Access-Control-Max-Age":           "", | ||||||
|  | 		"Access-Control-Expose-Headers":    "", | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestAllowedOrigin(t *testing.T) { | ||||||
|  | 	s := New(Options{ | ||||||
|  | 		AllowedOrigins: []string{"http://foobar.com"}, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	res := httptest.NewRecorder() | ||||||
|  | 	req, _ := http.NewRequest("GET", "http://example.com/foo", nil) | ||||||
|  | 	req.Header.Add("Origin", "http://foobar.com") | ||||||
|  |  | ||||||
|  | 	s.Handler(testHandler).ServeHTTP(res, req) | ||||||
|  |  | ||||||
|  | 	assertHeaders(t, res.Header(), map[string]string{ | ||||||
|  | 		"Access-Control-Allow-Origin":      "http://foobar.com", | ||||||
|  | 		"Access-Control-Allow-Methods":     "", | ||||||
|  | 		"Access-Control-Allow-Headers":     "", | ||||||
|  | 		"Access-Control-Allow-Credentials": "", | ||||||
|  | 		"Access-Control-Max-Age":           "", | ||||||
|  | 		"Access-Control-Expose-Headers":    "", | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDisallowedOrigin(t *testing.T) { | ||||||
|  | 	s := New(Options{ | ||||||
|  | 		AllowedOrigins: []string{"http://foobar.com"}, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	res := httptest.NewRecorder() | ||||||
|  | 	req, _ := http.NewRequest("GET", "http://example.com/foo", nil) | ||||||
|  | 	req.Header.Add("Origin", "http://barbaz.com") | ||||||
|  |  | ||||||
|  | 	s.Handler(testHandler).ServeHTTP(res, req) | ||||||
|  |  | ||||||
|  | 	assertHeaders(t, res.Header(), map[string]string{ | ||||||
|  | 		"Access-Control-Allow-Origin":      "", | ||||||
|  | 		"Access-Control-Allow-Methods":     "", | ||||||
|  | 		"Access-Control-Allow-Headers":     "", | ||||||
|  | 		"Access-Control-Allow-Credentials": "", | ||||||
|  | 		"Access-Control-Max-Age":           "", | ||||||
|  | 		"Access-Control-Expose-Headers":    "", | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestAllowedMethod(t *testing.T) { | ||||||
|  | 	s := New(Options{ | ||||||
|  | 		AllowedOrigins: []string{"http://foobar.com"}, | ||||||
|  | 		AllowedMethods: []string{"PUT", "DELETE"}, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	res := httptest.NewRecorder() | ||||||
|  | 	req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) | ||||||
|  | 	req.Header.Add("Origin", "http://foobar.com") | ||||||
|  | 	req.Header.Add("Access-Control-Request-Method", "PUT") | ||||||
|  |  | ||||||
|  | 	s.Handler(testHandler).ServeHTTP(res, req) | ||||||
|  |  | ||||||
|  | 	assertHeaders(t, res.Header(), map[string]string{ | ||||||
|  | 		"Access-Control-Allow-Origin":      "http://foobar.com", | ||||||
|  | 		"Access-Control-Allow-Methods":     "PUT", | ||||||
|  | 		"Access-Control-Allow-Headers":     "", | ||||||
|  | 		"Access-Control-Allow-Credentials": "", | ||||||
|  | 		"Access-Control-Max-Age":           "", | ||||||
|  | 		"Access-Control-Expose-Headers":    "", | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDisallowedMethod(t *testing.T) { | ||||||
|  | 	s := New(Options{ | ||||||
|  | 		AllowedOrigins: []string{"http://foobar.com"}, | ||||||
|  | 		AllowedMethods: []string{"PUT", "DELETE"}, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	res := httptest.NewRecorder() | ||||||
|  | 	req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) | ||||||
|  | 	req.Header.Add("Origin", "http://foobar.com") | ||||||
|  | 	req.Header.Add("Access-Control-Request-Method", "PATCH") | ||||||
|  |  | ||||||
|  | 	s.Handler(testHandler).ServeHTTP(res, req) | ||||||
|  |  | ||||||
|  | 	assertHeaders(t, res.Header(), map[string]string{ | ||||||
|  | 		"Access-Control-Allow-Origin":      "", | ||||||
|  | 		"Access-Control-Allow-Methods":     "", | ||||||
|  | 		"Access-Control-Allow-Headers":     "", | ||||||
|  | 		"Access-Control-Allow-Credentials": "", | ||||||
|  | 		"Access-Control-Max-Age":           "", | ||||||
|  | 		"Access-Control-Expose-Headers":    "", | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestAllowedHeader(t *testing.T) { | ||||||
|  | 	s := New(Options{ | ||||||
|  | 		AllowedOrigins: []string{"http://foobar.com"}, | ||||||
|  | 		AllowedHeaders: []string{"X-Header-1", "x-header-2"}, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	res := httptest.NewRecorder() | ||||||
|  | 	req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) | ||||||
|  | 	req.Header.Add("Origin", "http://foobar.com") | ||||||
|  | 	req.Header.Add("Access-Control-Request-Method", "GET") | ||||||
|  | 	req.Header.Add("Access-Control-Request-Headers", "X-Header-2, X-HEADER-1") | ||||||
|  |  | ||||||
|  | 	s.Handler(testHandler).ServeHTTP(res, req) | ||||||
|  |  | ||||||
|  | 	assertHeaders(t, res.Header(), map[string]string{ | ||||||
|  | 		"Access-Control-Allow-Origin":      "http://foobar.com", | ||||||
|  | 		"Access-Control-Allow-Methods":     "GET", | ||||||
|  | 		"Access-Control-Allow-Headers":     "X-Header-2, X-Header-1", | ||||||
|  | 		"Access-Control-Allow-Credentials": "", | ||||||
|  | 		"Access-Control-Max-Age":           "", | ||||||
|  | 		"Access-Control-Expose-Headers":    "", | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestAllowedWildcardHeader(t *testing.T) { | ||||||
|  | 	s := New(Options{ | ||||||
|  | 		AllowedOrigins: []string{"http://foobar.com"}, | ||||||
|  | 		AllowedHeaders: []string{"*"}, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	res := httptest.NewRecorder() | ||||||
|  | 	req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) | ||||||
|  | 	req.Header.Add("Origin", "http://foobar.com") | ||||||
|  | 	req.Header.Add("Access-Control-Request-Method", "GET") | ||||||
|  | 	req.Header.Add("Access-Control-Request-Headers", "X-Header-2, X-HEADER-1") | ||||||
|  |  | ||||||
|  | 	s.Handler(testHandler).ServeHTTP(res, req) | ||||||
|  |  | ||||||
|  | 	assertHeaders(t, res.Header(), map[string]string{ | ||||||
|  | 		"Access-Control-Allow-Origin":      "http://foobar.com", | ||||||
|  | 		"Access-Control-Allow-Methods":     "GET", | ||||||
|  | 		"Access-Control-Allow-Headers":     "X-Header-2, X-Header-1", | ||||||
|  | 		"Access-Control-Allow-Credentials": "", | ||||||
|  | 		"Access-Control-Max-Age":           "", | ||||||
|  | 		"Access-Control-Expose-Headers":    "", | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDisallowedHeader(t *testing.T) { | ||||||
|  | 	s := New(Options{ | ||||||
|  | 		AllowedOrigins: []string{"http://foobar.com"}, | ||||||
|  | 		AllowedHeaders: []string{"X-Header-1", "x-header-2"}, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	res := httptest.NewRecorder() | ||||||
|  | 	req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) | ||||||
|  | 	req.Header.Add("Origin", "http://foobar.com") | ||||||
|  | 	req.Header.Add("Access-Control-Request-Method", "GET") | ||||||
|  | 	req.Header.Add("Access-Control-Request-Headers", "X-Header-3, X-Header-1") | ||||||
|  |  | ||||||
|  | 	s.Handler(testHandler).ServeHTTP(res, req) | ||||||
|  |  | ||||||
|  | 	assertHeaders(t, res.Header(), map[string]string{ | ||||||
|  | 		"Access-Control-Allow-Origin":      "", | ||||||
|  | 		"Access-Control-Allow-Methods":     "", | ||||||
|  | 		"Access-Control-Allow-Headers":     "", | ||||||
|  | 		"Access-Control-Allow-Credentials": "", | ||||||
|  | 		"Access-Control-Max-Age":           "", | ||||||
|  | 		"Access-Control-Expose-Headers":    "", | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestOriginHeader(t *testing.T) { | ||||||
|  | 	s := New(Options{ | ||||||
|  | 		AllowedOrigins: []string{"http://foobar.com"}, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	res := httptest.NewRecorder() | ||||||
|  | 	req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) | ||||||
|  | 	req.Header.Add("Origin", "http://foobar.com") | ||||||
|  | 	req.Header.Add("Access-Control-Request-Method", "GET") | ||||||
|  | 	req.Header.Add("Access-Control-Request-Headers", "origin") | ||||||
|  |  | ||||||
|  | 	s.Handler(testHandler).ServeHTTP(res, req) | ||||||
|  |  | ||||||
|  | 	assertHeaders(t, res.Header(), map[string]string{ | ||||||
|  | 		"Access-Control-Allow-Origin":      "http://foobar.com", | ||||||
|  | 		"Access-Control-Allow-Methods":     "GET", | ||||||
|  | 		"Access-Control-Allow-Headers":     "Origin", | ||||||
|  | 		"Access-Control-Allow-Credentials": "", | ||||||
|  | 		"Access-Control-Max-Age":           "", | ||||||
|  | 		"Access-Control-Expose-Headers":    "", | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestExposedHeader(t *testing.T) { | ||||||
|  | 	s := New(Options{ | ||||||
|  | 		AllowedOrigins: []string{"http://foobar.com"}, | ||||||
|  | 		ExposedHeaders: []string{"X-Header-1", "x-header-2"}, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	res := httptest.NewRecorder() | ||||||
|  | 	req, _ := http.NewRequest("GET", "http://example.com/foo", nil) | ||||||
|  | 	req.Header.Add("Origin", "http://foobar.com") | ||||||
|  |  | ||||||
|  | 	s.Handler(testHandler).ServeHTTP(res, req) | ||||||
|  |  | ||||||
|  | 	assertHeaders(t, res.Header(), map[string]string{ | ||||||
|  | 		"Access-Control-Allow-Origin":      "http://foobar.com", | ||||||
|  | 		"Access-Control-Allow-Methods":     "", | ||||||
|  | 		"Access-Control-Allow-Headers":     "", | ||||||
|  | 		"Access-Control-Allow-Credentials": "", | ||||||
|  | 		"Access-Control-Max-Age":           "", | ||||||
|  | 		"Access-Control-Expose-Headers":    "X-Header-1, X-Header-2", | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestAllowedCredentials(t *testing.T) { | ||||||
|  | 	s := New(Options{ | ||||||
|  | 		AllowedOrigins:   []string{"http://foobar.com"}, | ||||||
|  | 		AllowCredentials: true, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	res := httptest.NewRecorder() | ||||||
|  | 	req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) | ||||||
|  | 	req.Header.Add("Origin", "http://foobar.com") | ||||||
|  | 	req.Header.Add("Access-Control-Request-Method", "GET") | ||||||
|  |  | ||||||
|  | 	s.Handler(testHandler).ServeHTTP(res, req) | ||||||
|  |  | ||||||
|  | 	assertHeaders(t, res.Header(), map[string]string{ | ||||||
|  | 		"Access-Control-Allow-Origin":      "http://foobar.com", | ||||||
|  | 		"Access-Control-Allow-Methods":     "GET", | ||||||
|  | 		"Access-Control-Allow-Headers":     "", | ||||||
|  | 		"Access-Control-Allow-Credentials": "true", | ||||||
|  | 		"Access-Control-Max-Age":           "", | ||||||
|  | 		"Access-Control-Expose-Headers":    "", | ||||||
|  | 	}) | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/alice/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/alice/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  |  | ||||||
|  | 	"github.com/justinas/alice" | ||||||
|  | 	"github.com/rs/cors" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	c := cors.New(cors.Options{ | ||||||
|  | 		AllowedOrigins: []string{"http://foo.com"}, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	mux := http.NewServeMux() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		w.Header().Set("Content-Type", "application/json") | ||||||
|  | 		w.Write([]byte("{\"hello\": \"world\"}")) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	chain := alice.New(c.Handler).Then(mux) | ||||||
|  | 	http.ListenAndServe(":8080", chain) | ||||||
|  | } | ||||||
							
								
								
									
										18
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/default/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/default/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  |  | ||||||
|  | 	"github.com/rs/cors" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		w.Header().Set("Content-Type", "application/json") | ||||||
|  | 		w.Write([]byte("{\"hello\": \"world\"}")) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	// Use default options | ||||||
|  | 	handler := cors.Default().Handler(h) | ||||||
|  | 	http.ListenAndServe(":8080", handler) | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/goji/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/goji/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  |  | ||||||
|  | 	"github.com/rs/cors" | ||||||
|  | 	"github.com/zenazn/goji" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	c := cors.New(cors.Options{ | ||||||
|  | 		AllowedOrigins: []string{"http://foo.com"}, | ||||||
|  | 	}) | ||||||
|  | 	goji.Use(c.Handler) | ||||||
|  |  | ||||||
|  | 	goji.Get("/", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		w.Header().Set("Content-Type", "application/json") | ||||||
|  | 		w.Write([]byte("{\"hello\": \"world\"}")) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	goji.Serve() | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/martini/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/martini/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/go-martini/martini" | ||||||
|  | 	"github.com/martini-contrib/render" | ||||||
|  | 	"github.com/rs/cors" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	c := cors.New(cors.Options{ | ||||||
|  | 		AllowedOrigins: []string{"http://foo.com"}, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	m := martini.Classic() | ||||||
|  | 	m.Use(render.Renderer()) | ||||||
|  | 	m.Use(c.HandlerFunc) | ||||||
|  |  | ||||||
|  | 	m.Get("/", func(r render.Render) { | ||||||
|  | 		r.JSON(200, map[string]interface{}{"hello": "world"}) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	m.Run() | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/negroni/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/negroni/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  |  | ||||||
|  | 	"github.com/codegangsta/negroni" | ||||||
|  | 	"github.com/rs/cors" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	c := cors.New(cors.Options{ | ||||||
|  | 		AllowedOrigins: []string{"http://foo.com"}, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	mux := http.NewServeMux() | ||||||
|  |  | ||||||
|  | 	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		w.Header().Set("Content-Type", "application/json") | ||||||
|  | 		w.Write([]byte("{\"hello\": \"world\"}")) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	n := negroni.Classic() | ||||||
|  | 	n.Use(c) | ||||||
|  | 	n.UseHandler(mux) | ||||||
|  | 	n.Run(":3000") | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/nethttp/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/nethttp/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  |  | ||||||
|  | 	"github.com/rs/cors" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	c := cors.New(cors.Options{ | ||||||
|  | 		AllowedOrigins: []string{"http://foo.com"}, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		w.Header().Set("Content-Type", "application/json") | ||||||
|  | 		w.Write([]byte("{\"hello\": \"world\"}")) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	http.ListenAndServe(":8080", c.Handler(handler)) | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/openbar/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								Godeps/_workspace/src/github.com/rs/cors/examples/openbar/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  |  | ||||||
|  | 	"github.com/rs/cors" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	c := cors.New(cors.Options{ | ||||||
|  | 		AllowedOrigins:   []string{"*"}, | ||||||
|  | 		AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE"}, | ||||||
|  | 		AllowCredentials: true, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		w.Header().Set("Content-Type", "application/json") | ||||||
|  | 		w.Write([]byte("{\"hello\": \"world\"}")) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	http.ListenAndServe(":8080", c.Handler(h)) | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								Godeps/_workspace/src/github.com/rs/cors/utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								Godeps/_workspace/src/github.com/rs/cors/utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | package cors | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type converter func(string) string | ||||||
|  |  | ||||||
|  | // convert converts a list of string using the passed converter function | ||||||
|  | func convert(s []string, c converter) []string { | ||||||
|  | 	out := []string{} | ||||||
|  | 	for _, i := range s { | ||||||
|  | 		out = append(out, c(i)) | ||||||
|  | 	} | ||||||
|  | 	return out | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func parseHeaderList(headerList string) (headers []string) { | ||||||
|  | 	for _, header := range strings.Split(headerList, ",") { | ||||||
|  | 		header = http.CanonicalHeaderKey(strings.TrimSpace(header)) | ||||||
|  | 		if header != "" { | ||||||
|  | 			headers = append(headers, header) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return headers | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								Godeps/_workspace/src/github.com/rs/cors/utils_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								Godeps/_workspace/src/github.com/rs/cors/utils_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | package cors | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestConvert(t *testing.T) { | ||||||
|  | 	s := convert([]string{"A", "b", "C"}, strings.ToLower) | ||||||
|  | 	e := []string{"a", "b", "c"} | ||||||
|  | 	if s[0] != e[0] || s[1] != e[1] || s[2] != e[2] { | ||||||
|  | 		t.Errorf("%v != %v", s, e) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestParseHeaderList(t *testing.T) { | ||||||
|  | 	h := parseHeaderList("header, second-header, THIRD-HEADER") | ||||||
|  | 	e := []string{"Header", "Second-Header", "Third-Header"} | ||||||
|  | 	if h[0] != e[0] || h[1] != e[1] || h[2] != e[2] { | ||||||
|  | 		t.Errorf("%v != %v", h, e) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestParseHeaderListEmpty(t *testing.T) { | ||||||
|  | 	if len(parseHeaderList("")) != 0 { | ||||||
|  | 		t.Error("should be empty sclice") | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -2,8 +2,6 @@ package main | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net" |  | ||||||
| 	"net/http" |  | ||||||
| 	"os" | 	"os" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| @@ -70,12 +68,20 @@ func (js *jsre) startRPC(call otto.FunctionCall) otto.Value { | |||||||
| 		return otto.FalseValue() | 		return otto.FalseValue() | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port)) | 	config := rpc.RpcConfig{ | ||||||
|  | 		ListenAddress: addr, | ||||||
|  | 		ListenPort:    uint(port), | ||||||
|  | 		// CorsDomain:    ctx.GlobalString(RPCCORSDomainFlag.Name), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	xeth := xeth.New(js.ethereum, nil) | ||||||
|  | 	err = rpc.Start(xeth, config) | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		fmt.Printf("Can't listen on %s:%d: %v", addr, port, err) | 		fmt.Printf(err.Error()) | ||||||
| 		return otto.FalseValue() | 		return otto.FalseValue() | ||||||
| 	} | 	} | ||||||
| 	go http.Serve(l, rpc.JSONRPC(xeth.New(js.ethereum, nil))) |  | ||||||
| 	return otto.TrueValue() | 	return otto.TrueValue() | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -233,6 +233,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso | |||||||
| 		utils.VMDebugFlag, | 		utils.VMDebugFlag, | ||||||
| 		utils.ProtocolVersionFlag, | 		utils.ProtocolVersionFlag, | ||||||
| 		utils.NetworkIdFlag, | 		utils.NetworkIdFlag, | ||||||
|  | 		utils.RPCCORSDomainFlag, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// missing: | 	// missing: | ||||||
|   | |||||||
| @@ -47,12 +47,19 @@ var ( | |||||||
| 		Usage: "absolute path to GUI assets directory", | 		Usage: "absolute path to GUI assets directory", | ||||||
| 		Value: common.DefaultAssetPath(), | 		Value: common.DefaultAssetPath(), | ||||||
| 	} | 	} | ||||||
|  | 	rpcCorsFlag = utils.RPCCORSDomainFlag | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
|  | 	// Mist-specific default | ||||||
|  | 	if len(rpcCorsFlag.Value) == 0 { | ||||||
|  | 		rpcCorsFlag.Value = "http://localhost" | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	app.Action = run | 	app.Action = run | ||||||
| 	app.Flags = []cli.Flag{ | 	app.Flags = []cli.Flag{ | ||||||
| 		assetPathFlag, | 		assetPathFlag, | ||||||
|  | 		rpcCorsFlag, | ||||||
|  |  | ||||||
| 		utils.BootnodesFlag, | 		utils.BootnodesFlag, | ||||||
| 		utils.DataDirFlag, | 		utils.DataDirFlag, | ||||||
|   | |||||||
| @@ -2,9 +2,6 @@ package utils | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"crypto/ecdsa" | 	"crypto/ecdsa" | ||||||
| 	"fmt" |  | ||||||
| 	"net" |  | ||||||
| 	"net/http" |  | ||||||
| 	"os" | 	"os" | ||||||
| 	"path" | 	"path" | ||||||
| 	"runtime" | 	"runtime" | ||||||
| @@ -148,7 +145,11 @@ var ( | |||||||
| 		Usage: "Port on which the JSON-RPC server should listen", | 		Usage: "Port on which the JSON-RPC server should listen", | ||||||
| 		Value: 8545, | 		Value: 8545, | ||||||
| 	} | 	} | ||||||
|  | 	RPCCORSDomainFlag = cli.StringFlag{ | ||||||
|  | 		Name:  "rpccorsdomain", | ||||||
|  | 		Usage: "Domain on which to send Access-Control-Allow-Origin header", | ||||||
|  | 		Value: "", | ||||||
|  | 	} | ||||||
| 	// Network Settings | 	// Network Settings | ||||||
| 	MaxPeersFlag = cli.IntFlag{ | 	MaxPeersFlag = cli.IntFlag{ | ||||||
| 		Name:  "maxpeers", | 		Name:  "maxpeers", | ||||||
| @@ -255,12 +256,12 @@ func GetAccountManager(ctx *cli.Context) *accounts.Manager { | |||||||
| } | } | ||||||
|  |  | ||||||
| func StartRPC(eth *eth.Ethereum, ctx *cli.Context) { | func StartRPC(eth *eth.Ethereum, ctx *cli.Context) { | ||||||
| 	addr := ctx.GlobalString(RPCListenAddrFlag.Name) | 	config := rpc.RpcConfig{ | ||||||
| 	port := ctx.GlobalInt(RPCPortFlag.Name) | 		ListenAddress: ctx.GlobalString(RPCListenAddrFlag.Name), | ||||||
| 	fmt.Println("Starting RPC on port: ", port) | 		ListenPort:    uint(ctx.GlobalInt(RPCPortFlag.Name)), | ||||||
| 	l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port)) | 		CorsDomain:    ctx.GlobalString(RPCCORSDomainFlag.Name), | ||||||
| 	if err != nil { |  | ||||||
| 		Fatalf("Can't listen on %s:%d: %v", addr, port, err) |  | ||||||
| 	} | 	} | ||||||
| 	go http.Serve(l, rpc.JSONRPC(xeth.New(eth, nil))) |  | ||||||
|  | 	xeth := xeth.New(eth, nil) | ||||||
|  | 	_ = rpc.Start(xeth, config) | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								rpc/http.go
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								rpc/http.go
									
									
									
									
									
								
							| @@ -2,12 +2,15 @@ package rpc | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
|  | 	"net" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  |  | ||||||
| 	"github.com/ethereum/go-ethereum/logger" | 	"github.com/ethereum/go-ethereum/logger" | ||||||
| 	"github.com/ethereum/go-ethereum/xeth" | 	"github.com/ethereum/go-ethereum/xeth" | ||||||
|  | 	"github.com/rs/cors" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var rpclogger = logger.NewLogger("RPC") | var rpclogger = logger.NewLogger("RPC") | ||||||
| @@ -17,13 +20,36 @@ const ( | |||||||
| 	maxSizeReqLength = 1024 * 1024 // 1MB | 	maxSizeReqLength = 1024 * 1024 // 1MB | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | func Start(pipe *xeth.XEth, config RpcConfig) error { | ||||||
|  | 	l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		rpclogger.Errorf("Can't listen on %s:%d: %v", config.ListenAddress, config.ListenPort, err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var handler http.Handler | ||||||
|  | 	if len(config.CorsDomain) > 0 { | ||||||
|  | 		var opts cors.Options | ||||||
|  | 		opts.AllowedMethods = []string{"POST"} | ||||||
|  | 		opts.AllowedOrigins = []string{config.CorsDomain} | ||||||
|  |  | ||||||
|  | 		c := cors.New(opts) | ||||||
|  | 		handler = c.Handler(JSONRPC(pipe)) | ||||||
|  | 	} else { | ||||||
|  | 		handler = JSONRPC(pipe) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	go http.Serve(l, handler) | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // JSONRPC returns a handler that implements the Ethereum JSON-RPC API. | // JSONRPC returns a handler that implements the Ethereum JSON-RPC API. | ||||||
| func JSONRPC(pipe *xeth.XEth) http.Handler { | func JSONRPC(pipe *xeth.XEth) http.Handler { | ||||||
| 	api := NewEthereumApi(pipe) | 	api := NewEthereumApi(pipe) | ||||||
|  |  | ||||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | 	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||||||
| 		// TODO this needs to be configurable | 		w.Header().Set("Content-Type", "application/json") | ||||||
| 		w.Header().Set("Access-Control-Allow-Origin", "*") |  | ||||||
|  |  | ||||||
| 		// Limit request size to resist DoS | 		// Limit request size to resist DoS | ||||||
| 		if req.ContentLength > maxSizeReqLength { | 		if req.ContentLength > maxSizeReqLength { | ||||||
|   | |||||||
| @@ -21,6 +21,12 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | type RpcConfig struct { | ||||||
|  | 	ListenAddress string | ||||||
|  | 	ListenPort    uint | ||||||
|  | 	CorsDomain    string | ||||||
|  | } | ||||||
|  |  | ||||||
| type InvalidTypeError struct { | type InvalidTypeError struct { | ||||||
| 	method string | 	method string | ||||||
| 	msg    string | 	msg    string | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user