Ver código fonte

Update Godeps

Taylor Gerring 10 anos atrás
pai
commit
35d00e00c5

+ 4 - 0
Godeps/Godeps.json

@@ -90,6 +90,10 @@
 			"ImportPath": "github.com/robertkrimen/otto/token",
 			"Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba"
 		},
+		{
+			"ImportPath": "github.com/rs/cors",
+			"Rev": "6e0c3cb65fc0fdb064c743d176a620e3ca446dfb"
+		},
 		{
 			"ImportPath": "github.com/syndtr/goleveldb/leveldb",
 			"Rev": "832fa7ed4d28545eab80f19e1831fc004305cade"

+ 4 - 0
Godeps/_workspace/src/github.com/rs/cors/.travis.yml

@@ -0,0 +1,4 @@
+language: go
+go:
+- 1.3
+- 1.4

+ 19 - 0
Godeps/_workspace/src/github.com/rs/cors/LICENSE

@@ -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 - 0
Godeps/_workspace/src/github.com/rs/cors/README.md

@@ -0,0 +1,84 @@
+# 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)
+
+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 - 0
Godeps/_workspace/src/github.com/rs/cors/bench_test.go

@@ -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 - 0
Godeps/_workspace/src/github.com/rs/cors/cors.go

@@ -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 - 0
Godeps/_workspace/src/github.com/rs/cors/cors_test.go

@@ -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 - 0
Godeps/_workspace/src/github.com/rs/cors/examples/alice/server.go

@@ -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 - 0
Godeps/_workspace/src/github.com/rs/cors/examples/default/server.go

@@ -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 - 0
Godeps/_workspace/src/github.com/rs/cors/examples/goji/server.go

@@ -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 - 0
Godeps/_workspace/src/github.com/rs/cors/examples/martini/server.go

@@ -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 - 0
Godeps/_workspace/src/github.com/rs/cors/examples/negroni/server.go

@@ -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 - 0
Godeps/_workspace/src/github.com/rs/cors/examples/nethttp/server.go

@@ -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 - 0
Godeps/_workspace/src/github.com/rs/cors/examples/openbar/server.go

@@ -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 - 0
Godeps/_workspace/src/github.com/rs/cors/utils.go

@@ -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 - 0
Godeps/_workspace/src/github.com/rs/cors/utils_test.go

@@ -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")
+	}
+}