Skip to content

Commit

Permalink
feat: add cors support (#72)
Browse files Browse the repository at this point in the history
* feat: added handlers dependency for cors management

* feat: added cors config

* feat: load cors middleware

* use cors middle ware in service

* feat: make raccoon depend on cors env instead of ws specific env

* fix: correct env variable name

* feat: added default values for CORS changes

* feat: update .env.test files

* add  default Cors function

* feat: add default headers used by raccoon

* fix: update env variables

* fix: added debug cache files to gitignore

* feat: added integration test to check cors headers

* refactor: change to have standard middleware apply/hook layer

* fix: test names for CORS feature

* refactor: remove unwanted viper.SetDefault call in setAllowedHeaders

* refactor: remove deprecated env variable

* feat: use cors enabled config in upgrader config init

* refactor: move middleware apply layer to service

* feat: added integration tests for preflight options request with correct and incorrect headers

* feat: improve allowed headers fetching logic

* feat: update to more sensible defaults

* docs: add documentation for cors configs

* revert changes to introduce `SERVER_WEBSOCKET_CHECK_ORIGIN`

* fix: uncomment commented integration test

* refactor: fix formatting of the docs

* fix: incorrect logic for deduplication in cors headers config

* feat: updated the config docs
  • Loading branch information
punit-kulal authored Sep 9, 2023
1 parent b7d1fcf commit db7f808
Show file tree
Hide file tree
Showing 14 changed files with 322 additions and 6 deletions.
8 changes: 7 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
SERVER_WEBSOCKET_PORT="8080"
SERVER_WEBSOCKET_CHECK_ORIGIN="true"
SERVER_WEBSOCKET_MAX_CONN="30000"
SERVER_WEBSOCKET_READ_BUFFER_SIZE="10240"
SERVER_WEBSOCKET_WRITE_BUFFER_SIZE="10240"
Expand All @@ -8,6 +7,13 @@ SERVER_WEBSOCKET_PING_INTERVAL_MS=30000
SERVER_WEBSOCKET_PONG_WAIT_INTERVAL_MS=60000
SERVER_WEBSOCKET_WRITE_WAIT_INTERVAL_MS=5000
SERVER_WEBSOCKET_PINGER_SIZE=1
SERVER_WEBSOCKET_CHECK_ORIGIN="true"

SERVER_CORS_ENABLED="true"
SERVER_CORS_ALLOWED_ORIGIN="http://localhost:3000 http://localhost:8080"
SERVER_CORS_ALLOWED_METHODS="GET HEAD POST OPTIONS"
SERVER_CORS_PREFLIGHT_MAX_AGE_SECONDS=60
SERVER_CORS_ALLOWED_HEADERS=""

SERVER_GRPC_PORT=8081

Expand Down
8 changes: 7 additions & 1 deletion .env.test
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
SERVER_WEBSOCKET_PORT="8080"
SERVER_WEBSOCKET_CHECK_ORIGIN="true"
SERVER_WEBSOCKET_MAX_CONN="30000"
SERVER_WEBSOCKET_READ_BUFFER_SIZE="10240"
SERVER_WEBSOCKET_WRITE_BUFFER_SIZE="10240"
Expand All @@ -9,9 +8,16 @@ SERVER_WEBSOCKET_PING_INTERVAL_MS=10000
SERVER_WEBSOCKET_PONG_WAIT_INTERVAL_MS=10000
SERVER_WEBSOCKET_WRITE_WAIT_INTERVAL_MS=1000
SERVER_WEBSOCKET_PINGER_SIZE=1
SERVER_WEBSOCKET_CHECK_ORIGIN="true"

SERVER_GRPC_PORT=8081


SERVER_CORS_ENABLED="true"
SERVER_CORS_ALLOWED_ORIGIN="http://localhost:3000 http://localhost:8080"
SERVER_CORS_ALLOWED_METHODS="GET HEAD POST OPTIONS"
SERVER_CORS_PREFLIGHT_MAX_AGE_SECONDS=60

WORKER_BUFFER_CHANNEL_SIZE=5
WORKER_BUFFER_FLUSH_TIMEOUT_MS=5000
WORKER_POOL_SIZE=5
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ coverage
*.idea/
raccoon
.temp
__debug.*
1 change: 1 addition & 0 deletions config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func Load() {
serverConfigLoader()
serverWsConfigLoader()
serverGRPCConfigLoader()
serverCorsConfigLoader()
workerConfigLoader()
metricStatsdConfigLoader()
eventDistributionConfigLoader()
Expand Down
53 changes: 50 additions & 3 deletions config/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
var Server server
var ServerWs serverWs
var ServerGRPC serverGRPC
var ServerCors serverCors

type server struct {
DedupEnabled bool
Expand All @@ -20,33 +21,79 @@ type serverWs struct {
ServerMaxConn int
ReadBufferSize int
WriteBufferSize int
CheckOrigin bool
PingInterval time.Duration
PongWaitInterval time.Duration
WriteWaitInterval time.Duration
PingerSize int
ConnIDHeader string
ConnGroupHeader string
ConnGroupDefault string
CheckOrigin bool
}

type serverGRPC struct {
Port string
}

type serverCors struct {
Enabled bool
AllowedOrigin []string
AllowedMethods []string
AllowedHeaders []string
AllowCredentials bool
MaxAge int
}

func serverConfigLoader() {
viper.SetDefault("SERVER_BATCH_DEDUP_IN_CONNECTION_ENABLED", "false")
Server = server{
DedupEnabled: util.MustGetBool("SERVER_BATCH_DEDUP_IN_CONNECTION_ENABLED"),
}
}

func serverCorsConfigLoader() {
viper.SetDefault("SERVER_CORS_ENABLED", false)
viper.SetDefault("SERVER_CORS_ALLOWED_ORIGIN", "")
viper.SetDefault("SERVER_CORS_ALLOWED_METHODS", []string{"GET", "HEAD", "POST", "OPTIONS"})
viper.SetDefault("SERVER_CORS_ALLOWED_HEADERS", "")
viper.SetDefault("SERVER_CORS_ALLOW_CREDENTIALS", false)
viper.SetDefault("SERVER_CORS_PREFLIGHT_MAX_AGE_SECONDS", 0)
ServerCors = serverCors{
Enabled: util.MustGetBool("SERVER_CORS_ENABLED"),
AllowedOrigin: viper.GetStringSlice("SERVER_CORS_ALLOWED_ORIGIN"),
AllowedMethods: viper.GetStringSlice("SERVER_CORS_ALLOWED_METHODS"),
AllowCredentials: util.MustGetBool("SERVER_CORS_ALLOW_CREDENTIALS"),
AllowedHeaders: getAllowedHeaders(),
MaxAge: util.MustGetInt("SERVER_CORS_PREFLIGHT_MAX_AGE_SECONDS"),
}
}

func getAllowedHeaders() []string {
allowedHeaders := []string{"Content-Type"}
allowedHeaders = setAllowedHeaders(allowedHeaders, "SERVER_WEBSOCKET_CONN_GROUP_HEADER")
allowedHeaders = setAllowedHeaders(allowedHeaders, "SERVER_WEBSOCKET_CONN_ID_HEADER")
inputHeaders := viper.GetStringSlice("SERVER_CORS_ALLOWED_HEADERS")
for _, input := range inputHeaders {
if input != "" && !util.Contains(input, allowedHeaders) {
allowedHeaders = append(allowedHeaders, input)
}
}
return allowedHeaders
}

func setAllowedHeaders(allowedHeaders []string, envKey string) []string {
if header := viper.GetString(envKey); header != "" {
allowedHeaders = append(allowedHeaders, header)
}
return allowedHeaders
}

func serverWsConfigLoader() {
viper.SetDefault("SERVER_WEBSOCKET_CHECK_ORIGIN", true)
viper.SetDefault("SERVER_WEBSOCKET_PORT", "8080")
viper.SetDefault("SERVER_WEBSOCKET_MAX_CONN", 30000)
viper.SetDefault("SERVER_WEBSOCKET_READ_BUFFER_SIZE", 10240)
viper.SetDefault("SERVER_WEBSOCKET_WRITE_BUFFER_SIZE", 10240)
viper.SetDefault("SERVER_WEBSOCKET_CHECK_ORIGIN", true)
viper.SetDefault("SERVER_WEBSOCKET_PING_INTERVAL_MS", "30000")
viper.SetDefault("SERVER_WEBSOCKET_PONG_WAIT_INTERVAL_MS", "60000") //should be more than the ping period
viper.SetDefault("SERVER_WEBSOCKET_WRITE_WAIT_INTERVAL_MS", "5000")
Expand All @@ -59,14 +106,14 @@ func serverWsConfigLoader() {
ServerMaxConn: util.MustGetInt("SERVER_WEBSOCKET_MAX_CONN"),
ReadBufferSize: util.MustGetInt("SERVER_WEBSOCKET_READ_BUFFER_SIZE"),
WriteBufferSize: util.MustGetInt("SERVER_WEBSOCKET_WRITE_BUFFER_SIZE"),
CheckOrigin: util.MustGetBool("SERVER_WEBSOCKET_CHECK_ORIGIN"),
PingInterval: util.MustGetDuration("SERVER_WEBSOCKET_PING_INTERVAL_MS", time.Millisecond),
PongWaitInterval: util.MustGetDuration("SERVER_WEBSOCKET_PONG_WAIT_INTERVAL_MS", time.Millisecond),
WriteWaitInterval: util.MustGetDuration("SERVER_WEBSOCKET_WRITE_WAIT_INTERVAL_MS", time.Millisecond),
PingerSize: util.MustGetInt("SERVER_WEBSOCKET_PINGER_SIZE"),
ConnIDHeader: util.MustGetString("SERVER_WEBSOCKET_CONN_ID_HEADER"),
ConnGroupHeader: util.MustGetString("SERVER_WEBSOCKET_CONN_GROUP_HEADER"),
ConnGroupDefault: util.MustGetString("SERVER_WEBSOCKET_CONN_GROUP_DEFAULT"),
CheckOrigin: util.MustGetBool("SERVER_WEBSOCKET_CHECK_ORIGIN"),
}
}

Expand Down
11 changes: 11 additions & 0 deletions config/util/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,14 @@ func mustHave(key string) {
panic(fmt.Sprintf("key %s is not set", key))
}
}

func Contains(key string, slice []string) bool {
for _, v := range slice {
if v == key {
return true
}
}

return false

}
42 changes: 42 additions & 0 deletions docs/docs/reference/configurations.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,48 @@ Toggle CORS check function. Set `true` to check each request origin. Set `false`
- Type: `Optional`
- Default value: `true`

### `SERVER_CORS_ENABLED`

The config decides whether to enable the cors middleware and thus allow CORS requests. This config only enables CORS for rest services. For websocket, refer `SERVER_WEBSOCKET_CHECK_ORIGIN`

- Type `Optional`
- Default value: `false`

### `SERVER_CORS_ALLOWED_ORIGIN`

The server decides which origin to allow. The configuration is expected to space separated. Multiple values are supported. The value requies `SERVER_CORS_ENABLED` to be true to take effect. If you want to allow all host headers. You can pass `*` as the value.

- Type `Optional`
- Default Value ``

### `SERVER_CORS_ALLOWED_METHODS`

The http methods allowed when it's a cross origin request. The http methods are expected to be space separated.

- Type `Optional`
- Default Value `GET HEAD POST OPTIONS`

### `SERVER_CORS_ALLOWED_HEADERS`

The http request headers which are allowed when request is cross origin. The input expects to add any additional headers which is going to be sent by the client ex: `Authorization`. Headers which are essential for the functioning of Raccoon like Content-Type, Connection-Id & Group headers are added by default and need not be passed as configuration.

- Type `Optional`
- Default Value ``

### `SERVER_CORS_ALLOW_CREDENTIALS`

AllowCredentials can be used to specify that the user agent may pass authentication details along with the request.

- Type `Optional`
- Default Value `false`

### `SERVER_CORS_PREFLIGHT_MAX_AGE_SECONDS`

Replies with a header for clients on how long to cache the response of the preflight request. It's not enforceable. The max value is 600s

- Type `Optional`
- Default Value `0`

### `SERVER_BATCH_DEDUP_IN_CONNECTION_ENABLED`

The server decides whether or not to handle duplicate batches for the active connection. If a batch is sent with a duplicate ReqGUID, the server uses best attempts to discard the duplicate batches. Set `true` to enable the setting.
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.14

require (
github.com/confluentinc/confluent-kafka-go v1.9.3-RC3
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.7.4
github.com/gorilla/websocket v1.4.2
github.com/sirupsen/logrus v1.6.0
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20=
github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
Expand Down Expand Up @@ -122,6 +124,8 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
Expand Down
Loading

0 comments on commit db7f808

Please sign in to comment.