Skip to content

Commit 2f37838

Browse files
authored
adds CORS request support (#20)
- enabled by default, this middleware will handle CORS requests from all origins. - configured via environment variables: `CORS_ENABLED`, `CORS_ORIGINS`,`CORS_HEADERS`, `CORS_CREDENTIALS` fixes #4
1 parent 0b983a1 commit 2f37838

File tree

5 files changed

+83
-7
lines changed

5 files changed

+83
-7
lines changed

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@ OR
1616

1717
Configuration of core features are done via Environment Variables, in accordence with [12 factor](https://12factor.net/config) principles.
1818

19-
- `HYPERDRIVE_ENV`: (default: `development`, type: `string`) The stage in your deployment pipeline the api is currently running in. A value of `production` changes behviour for some features (such as whether stack traces are logged during panic recovery). Other values, such as `staging`, can be used but currently have no meanin in the framework other than the one you give it in your own code.
20-
- `PORT` - (default: `5000`, type: `int`) The port the server should listen on.
21-
- `GZIP_LEVEL` - (default: `-1`, type: `int`) Accepts a value between `-2` and `9`. Invalid values will be silently discarded and the default of `-1` will be used. More info on compression levels can be found in the docs, but corresponds to `zlib` compression levels.
19+
- `HYPERDRIVE_ENV`: (default: `development`, type: `string`) The stage in your deployment pipeline the api is currently running in. A value of `production` changes behviour for some features (such as whether stack traces are logged during panic recovery). Other values, such as `staging`, can be used but currently have no meanin in the framework other than the one you give it in your own code.
20+
- `PORT`: (default: `5000`, type: `int`) The port the server should listen on.
21+
- `GZIP_LEVEL`: (default: `-1`, type: `int`) Accepts a value between `-2` and `9`. Invalid values will be silently discarded and the default of `-1` will be used. More info on compression levels can be found in the docs, but corresponds to `zlib` compression levels.
22+
- `CORS_ENABLED`: (default: `true`, type: `bool`) Set this to `false` to disable CORS support.
23+
- `CORS_HEADERS`: (default: ``, type: `string`) A comma-seperated list of headers to allow and expose during CORS requests. These will be appended to the default set of headers that are always allowed: `Accept`, `Accept-Language`, `Content-Language`, and `Content-Type`.
24+
- `CORS_ORIGINS`: (default: `*`, type: `string`) A comma-seperated list of origins to allow during CORS requests. These will replace the default value, which is to allow all origins.
25+
- `CORS_CREDENTIALS`: (default: `true`, type: `bool`) Set this to `false` to disable authenticated CORS requests.
2226

2327
## Docs
2428

config.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@ import (
1111
// (where possible). Required configuration will throw a Fatal error if they
1212
// are missing.
1313
type Config struct {
14-
Port int `env:"PORT" envDefault:"5000"`
15-
Env string `env:"HYPERDRIVE_ENV" envDefault:"development"`
16-
GzipLevel int `env:"GZIP_LEVEL" envDefault:"-1"`
14+
Port int `env:"PORT" envDefault:"5000"`
15+
Env string `env:"HYPERDRIVE_ENV" envDefault:"development"`
16+
GzipLevel int `env:"GZIP_LEVEL" envDefault:"-1"`
17+
CorsEnabled bool `env:"CORS_ENABLED" envDefault:"true"`
18+
CorsOrigins string `env:"CORS_ORIGINS" envDefault:"*"`
19+
CorsHeaders string `env:"CORS_HEADERS" envDefault:""`
20+
CorsCredentials bool `env:"CORS_CREDENTIALS" envDefault:"true"`
1721
}
1822

1923
// GetPort returns the formatted value of config.Port, for use by the

config_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,47 @@ func (suite *HyperdriveTestSuite) TestGzipLevelConfigFromEnv() {
4343
c := NewConfig()
4444
suite.Equal(9, c.GzipLevel, "GzipLevel should be equal to GZIP_LEVEL value set via ENV var")
4545
}
46+
47+
func (suite *HyperdriveTestSuite) TestCorsEnabledConfigFromDefault() {
48+
c := NewConfig()
49+
suite.Equal(true, c.CorsEnabled, "CorsEnabled should be equal to default value")
50+
}
51+
52+
func (suite *HyperdriveTestSuite) TestCorsEnabledConfigFromEnv() {
53+
os.Setenv("CORS_ENABLED", "false")
54+
c := NewConfig()
55+
suite.Equal(false, c.CorsEnabled, "CorsEnabled should be equal to CORS_ENABLED value set via ENV var")
56+
}
57+
58+
func (suite *HyperdriveTestSuite) TestCorsOriginsConfigFromDefault() {
59+
c := NewConfig()
60+
suite.Equal("*", c.CorsOrigins, "CorsOrigins should be equal to default value")
61+
}
62+
63+
func (suite *HyperdriveTestSuite) TestCorsOriginsConfigFromEnv() {
64+
os.Setenv("CORS_ORIGINS", "example.com")
65+
c := NewConfig()
66+
suite.Equal("example.com", c.CorsOrigins, "CorsOrigins should be equal to CORS_ORIGINS value set via ENV var")
67+
}
68+
69+
func (suite *HyperdriveTestSuite) TestCorsHeadersConfigFromDefault() {
70+
c := NewConfig()
71+
suite.Equal("", c.CorsHeaders, "CorsHeaders should be equal to default value")
72+
}
73+
74+
func (suite *HyperdriveTestSuite) TestCorsHeadersConfigFromEnv() {
75+
os.Setenv("CORS_HEADERS", "example.com")
76+
c := NewConfig()
77+
suite.Equal("example.com", c.CorsHeaders, "CorsHeaders should be equal to CORS_HEADERS value set via ENV var")
78+
}
79+
80+
func (suite *HyperdriveTestSuite) TestCorsCredentialsConfigFromDefault() {
81+
c := NewConfig()
82+
suite.Equal(true, c.CorsCredentials, "CorsCredentials should be equal to default value")
83+
}
84+
85+
func (suite *HyperdriveTestSuite) TestCorsCredentialsConfigFromEnv() {
86+
os.Setenv("CORS_CREDENTIALS", "false")
87+
c := NewConfig()
88+
suite.Equal(false, c.CorsCredentials, "CorsCredentials should be equal to CORS_CREDENTIALS value set via ENV var")
89+
}

middleware.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ package hyperdrive
33
import (
44
"net/http"
55
"os"
6+
"strings"
67

78
"github.com/gorilla/handlers"
89
)
910

1011
// DefaultMiddlewareChain wraps the given http.Handler in the following chain
1112
// of middleware: LoggingMiddleware, RecoveryMiddleware.
1213
func (api *API) DefaultMiddlewareChain(h http.Handler) http.Handler {
13-
return api.CompressionMiddleware(api.LoggingMiddleware(api.RecoveryMiddleware(h)))
14+
return api.CorsMiddleware(api.CompressionMiddleware(api.LoggingMiddleware(api.RecoveryMiddleware(h))))
1415
}
1516

1617
// LoggingMiddleware wraps the given http.Handler and outputs requests in Apache-style
@@ -55,3 +56,22 @@ func (api *API) CompressionMiddleware(h http.Handler) http.Handler {
5556
func (api *API) MethodOverrideMiddleware(h http.Handler) http.Handler {
5657
return handlers.HTTPMethodOverrideHandler(h)
5758
}
59+
60+
// CorsMiddleware allows cross-origin HTTP requests to your API. The middleware is enabled
61+
// by default, and can be configured via the following environment variables:
62+
//
63+
// - CORS_ENABLED (bool)
64+
// - CORS_ORIGINS (string)
65+
// - CORS_HEADERS (string)
66+
// - CORS_CREDENTIALS (bool)
67+
func (api *API) CorsMiddleware(h http.Handler) http.Handler {
68+
if api.conf.CorsEnabled == true {
69+
return h
70+
}
71+
headers := handlers.AllowedHeaders(append([]string{"Content-Type"}, strings.Split(api.conf.CorsHeaders, ",")...))
72+
origins := handlers.AllowedOrigins(strings.Split(api.conf.CorsOrigins, ","))
73+
if api.conf.CorsCredentials == true {
74+
handlers.AllowCredentials()
75+
}
76+
return handlers.CORS(headers, origins)(h)
77+
}

middleware_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ func (suite *HyperdriveTestSuite) TestCompressionMiddleware() {
2121
func (suite *HyperdriveTestSuite) TestMethodOverrideMiddleware() {
2222
suite.Implements((*http.Handler)(nil), suite.TestAPI.MethodOverrideMiddleware(suite.TestHandler), "return an implementation of http.Handler")
2323
}
24+
25+
func (suite *HyperdriveTestSuite) TestCorsMiddleware() {
26+
suite.Implements((*http.Handler)(nil), suite.TestAPI.CorsMiddleware(suite.TestHandler), "return an implementation of http.Handler")
27+
}

0 commit comments

Comments
 (0)