From c3285354c17d881a20234fb4759f8188899c2d1a Mon Sep 17 00:00:00 2001 From: terminal Date: Thu, 21 Apr 2022 12:40:14 +0300 Subject: [PATCH] add cors headers to http proxy --- docs/reference-configuration.adoc | 8 ++++++++ .../io/emeraldpay/dshackle/config/ProxyConfig.kt | 10 ++++++++++ .../dshackle/config/ProxyConfigReader.kt | 6 ++++++ .../io/emeraldpay/dshackle/proxy/HttpHandler.kt | 16 +++++++++++++++- .../io/emeraldpay/dshackle/proxy/ProxyServer.kt | 1 + .../dshackle/config/ProxyConfigReaderSpec.groovy | 2 ++ .../dshackle/proxy/ProxyServerSpec.groovy | 1 + src/test/resources/dshackle-proxy-max.yaml | 2 ++ 8 files changed, 45 insertions(+), 1 deletion(-) diff --git a/docs/reference-configuration.adoc b/docs/reference-configuration.adoc index 6a0be167..5e9a8950 100644 --- a/docs/reference-configuration.adoc +++ b/docs/reference-configuration.adoc @@ -401,6 +401,14 @@ See <> section If set to `true` then Dshackle preserves _batch_ order based on request order. Note that latter is ineffective and use this option only when a client cannot reference responses by their IDs. +| `сors-origin` +| +| Access-Control-Allow-Origin contents. If empty the header will be omitted in response + +| `cors-allowed-headers` +| `Content-Type` +| Access-Control-Allow-Headers contents. Takes effect only if сors-origi is present in config + | `routes` | a| Routing paths for Proxy. diff --git a/src/main/kotlin/io/emeraldpay/dshackle/config/ProxyConfig.kt b/src/main/kotlin/io/emeraldpay/dshackle/config/ProxyConfig.kt index 7d11e2aa..c5a47254 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/config/ProxyConfig.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/config/ProxyConfig.kt @@ -55,6 +55,16 @@ open class ProxyConfig { */ var preserveBatchOrder: Boolean = false + /** + * Access-Control-Allow-Origin contents. If null then will omit this header completely + */ + var corsOrigin: String? = null + + /** + * Access-Control-Allow-Headers contents. Takes effect only if corsOrigin is not null + */ + var corsAllowedHeaders: String = "Content-Type" + class Route( /** * URL binding for the route. http://$host:$port/$id diff --git a/src/main/kotlin/io/emeraldpay/dshackle/config/ProxyConfigReader.kt b/src/main/kotlin/io/emeraldpay/dshackle/config/ProxyConfigReader.kt index 10ef82a4..0dc6b4a6 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/config/ProxyConfigReader.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/config/ProxyConfigReader.kt @@ -64,6 +64,12 @@ class ProxyConfigReader : YamlConfigReader(), ConfigReader { getValueAsBool(input, "preserve-batch-order")?.let { config.preserveBatchOrder = it } + getValueAsString(input, "cors-origin")?.let { + config.corsOrigin = it + } + getValueAsString(input, "cors-allowed-headers")?.let { + config.corsAllowedHeaders = it + } val currentRoutes = HashSet() getList(input, "routes")?.let { routes -> config.routes = routes.value.map { route -> diff --git a/src/main/kotlin/io/emeraldpay/dshackle/proxy/HttpHandler.kt b/src/main/kotlin/io/emeraldpay/dshackle/proxy/HttpHandler.kt index 44c58f65..56f52313 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/proxy/HttpHandler.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/proxy/HttpHandler.kt @@ -49,6 +49,19 @@ class HttpHandler( private val log = LoggerFactory.getLogger(HttpHandler::class.java) } + private fun addCorsHeadersIfSet(resp: HttpServerResponse): HttpServerResponse { + return config.corsOrigin?.let { + resp.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, it) + .addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, config.corsAllowedHeaders) + } ?: resp + } + + fun preflight(): BiFunction> { + return BiFunction { _, resp -> + addCorsHeadersIfSet(resp).send() + } + } + fun proxy(routeConfig: ProxyConfig.Route): BiFunction> { return BiFunction { req, resp -> // handle access events @@ -59,7 +72,8 @@ class HttpHandler( val results = processRequest(routeConfig.blockchain, request, eventHandler) // make sure that the access log handler is closed at the end, so it can render the logs .doFinally { eventHandler.close() } - resp.addHeader(HttpHeaders.CONTENT_TYPE, "application/json") + addCorsHeadersIfSet(resp) + .addHeader(HttpHeaders.CONTENT_TYPE, "application/json") .send(results) } } diff --git a/src/main/kotlin/io/emeraldpay/dshackle/proxy/ProxyServer.kt b/src/main/kotlin/io/emeraldpay/dshackle/proxy/ProxyServer.kt index 629cdc97..69a2b109 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/proxy/ProxyServer.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/proxy/ProxyServer.kt @@ -114,6 +114,7 @@ class ProxyServer( config.routes.forEach { routeConfig -> // TODO implement a manual handling of the routes and WS upgrade to have a better control over the connection and improve the access logging routes.post("/" + routeConfig.id, httpHandler.proxy(routeConfig)) + routes.options("/" + routeConfig.id, httpHandler.preflight()) if (config.websocketEnabled && wsHandler != null) { routes.ws("/" + routeConfig.id, wsHandler.proxy(routeConfig)) } diff --git a/src/test/groovy/io/emeraldpay/dshackle/config/ProxyConfigReaderSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/config/ProxyConfigReaderSpec.groovy index ee57a8da..076bcdda 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/config/ProxyConfigReaderSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/config/ProxyConfigReaderSpec.groovy @@ -83,6 +83,8 @@ class ProxyConfigReaderSpec extends Specification { act.port == 8080 act.preserveBatchOrder act.routes.size() == 2 + act.corsOrigin == "*" + act.corsAllowedHeaders == "Content-Type" with(act.routes[0]) { id == "ethereum" blockchain == Chain.ETHEREUM diff --git a/src/test/groovy/io/emeraldpay/dshackle/proxy/ProxyServerSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/proxy/ProxyServerSpec.groovy index 4e225e01..3340ef2d 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/proxy/ProxyServerSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/proxy/ProxyServerSpec.groovy @@ -31,6 +31,7 @@ class ProxyServerSpec extends Specification { then: 1 * routes.post("/test", _) + 1 * routes.options("/test", _) 1 * routes.ws("/test", _) } diff --git a/src/test/resources/dshackle-proxy-max.yaml b/src/test/resources/dshackle-proxy-max.yaml index ea95cbc3..6fe2f504 100644 --- a/src/test/resources/dshackle-proxy-max.yaml +++ b/src/test/resources/dshackle-proxy-max.yaml @@ -3,6 +3,8 @@ proxy: host: 0.0.0.0 port: 8080 preserve-batch-order: true + cors-origin: "*" + cors-allowed-headers: "Content-Type" routes: - id: ethereum blockchain: ethereum