diff --git a/components/app/app.go b/components/app/app.go index 0d857473d..1236568db 100644 --- a/components/app/app.go +++ b/components/app/app.go @@ -34,7 +34,7 @@ var ( Name = "HORNET" // Version of the app. - Version = "2.0.0" + Version = "2.0.1" ) func App() *app.App { diff --git a/components/restapi/component.go b/components/restapi/component.go index 0dbb9535c..290c4d8ff 100644 --- a/components/restapi/component.go +++ b/components/restapi/component.go @@ -97,7 +97,9 @@ func provide(c *dig.Container) error { ParamsRestAPI.DebugRequestLoggerEnabled, ) e.Use(middleware.CORS()) - e.Use(middleware.Gzip()) + if ParamsRestAPI.UseGZIP { + e.Use(middleware.Gzip()) + } e.Use(middleware.BodyLimit(ParamsRestAPI.Limits.MaxBodyLength)) return e diff --git a/components/restapi/params.go b/components/restapi/params.go index 469d2cefb..96f66ab64 100644 --- a/components/restapi/params.go +++ b/components/restapi/params.go @@ -14,6 +14,8 @@ type ParametersRestAPI struct { PublicRoutes []string `usage:"the HTTP REST routes which can be called without authorization. Wildcards using * are allowed"` // the HTTP REST routes which need to be called with authorization. Wildcards using * are allowed ProtectedRoutes []string `usage:"the HTTP REST routes which need to be called with authorization. Wildcards using * are allowed"` + // UseGZIP defines whether to use the gzip middleware to compress HTTP responses + UseGZIP bool `default:"true" usage:"use the gzip middleware to compress HTTP responses"` // whether the debug logging for requests should be enabled DebugRequestLoggerEnabled bool `default:"false" usage:"whether the debug logging for requests should be enabled"` @@ -55,6 +57,8 @@ var ParamsRestAPI = &ParametersRestAPI{ "/api/participation/v1/events*", "/api/participation/v1/outputs*", "/api/participation/v1/addresses*", + "/api/core/v0/*", + "/api/core/v1/*", }, ProtectedRoutes: []string{ "/api/*", diff --git a/config_defaults.json b/config_defaults.json index 8c6e10f8c..00c39a2ac 100644 --- a/config_defaults.json +++ b/config_defaults.json @@ -191,11 +191,14 @@ "/api/mqtt/v1", "/api/participation/v1/events*", "/api/participation/v1/outputs*", - "/api/participation/v1/addresses*" + "/api/participation/v1/addresses*", + "/api/core/v0/*", + "/api/core/v1/*" ], "protectedRoutes": [ "/api/*" ], + "useGZIP": true, "debugRequestLoggerEnabled": false, "jwtAuth": { "salt": "HORNET" diff --git a/configuration.md b/configuration.md index 6b9840c9f..04cd1bd40 100755 --- a/configuration.md +++ b/configuration.md @@ -481,16 +481,17 @@ Example: ## 13. RestAPI -| Name | Description | Type | Default value | -| --------------------------- | ---------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| enabled | Whether the REST API plugin is enabled | boolean | true | -| bindAddress | The bind address on which the REST API listens on | string | "0.0.0.0:14265" | -| publicRoutes | The HTTP REST routes which can be called without authorization. Wildcards using \* are allowed | array | /health
/api/routes
/api/core/v2/info
/api/core/v2/tips
/api/core/v2/blocks\*
/api/core/v2/transactions\*
/api/core/v2/milestones\*
/api/core/v2/outputs\*
/api/core/v2/treasury
/api/core/v2/receipts\*
/api/debug/v1/\*
/api/indexer/v1/\*
/api/mqtt/v1
/api/participation/v1/events\*
/api/participation/v1/outputs\*
/api/participation/v1/addresses\* | -| protectedRoutes | The HTTP REST routes which need to be called with authorization. Wildcards using \* are allowed | array | /api/\* | -| debugRequestLoggerEnabled | Whether the debug logging for requests should be enabled | boolean | false | -| [jwtAuth](#restapi_jwtauth) | Configuration for JWT Auth | object | | -| [pow](#restapi_pow) | Configuration for Proof of Work | object | | -| [limits](#restapi_limits) | Configuration for limits | object | | +| Name | Description | Type | Default value | +| --------------------------- | ---------------------------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| enabled | Whether the REST API plugin is enabled | boolean | true | +| bindAddress | The bind address on which the REST API listens on | string | "0.0.0.0:14265" | +| publicRoutes | The HTTP REST routes which can be called without authorization. Wildcards using \* are allowed | array | /health
/api/routes
/api/core/v2/info
/api/core/v2/tips
/api/core/v2/blocks\*
/api/core/v2/transactions\*
/api/core/v2/milestones\*
/api/core/v2/outputs\*
/api/core/v2/treasury
/api/core/v2/receipts\*
/api/debug/v1/\*
/api/indexer/v1/\*
/api/mqtt/v1
/api/participation/v1/events\*
/api/participation/v1/outputs\*
/api/participation/v1/addresses\*
/api/core/v0/\*
/api/core/v1/\* | +| protectedRoutes | The HTTP REST routes which need to be called with authorization. Wildcards using \* are allowed | array | /api/\* | +| useGZIP | Use the gzip middleware to compress HTTP responses | boolean | true | +| debugRequestLoggerEnabled | Whether the debug logging for requests should be enabled | boolean | false | +| [jwtAuth](#restapi_jwtauth) | Configuration for JWT Auth | object | | +| [pow](#restapi_pow) | Configuration for Proof of Work | object | | +| [limits](#restapi_limits) | Configuration for limits | object | | ### JWT Auth @@ -535,11 +536,14 @@ Example: "/api/mqtt/v1", "/api/participation/v1/events*", "/api/participation/v1/outputs*", - "/api/participation/v1/addresses*" + "/api/participation/v1/addresses*", + "/api/core/v0/*", + "/api/core/v1/*" ], "protectedRoutes": [ "/api/*" ], + "useGZIP": true, "debugRequestLoggerEnabled": false, "jwtAuth": { "salt": "HORNET" diff --git a/pkg/restapi/proxy.go b/pkg/restapi/proxy.go index dc4890f6d..b7bf9eff5 100644 --- a/pkg/restapi/proxy.go +++ b/pkg/restapi/proxy.go @@ -3,6 +3,7 @@ package restapi import ( "errors" "fmt" + "net/http" "net/url" "strings" "sync" @@ -110,6 +111,10 @@ func NewDynamicProxy(e *echo.Echo, prefix string) *DynamicProxy { } func (p *DynamicProxy) middleware(prefix string) echo.MiddlewareFunc { + hasAcceptEncodingGZIP := func(header http.Header) bool { + return strings.Contains(header.Get(echo.HeaderAcceptEncoding), "gzip") + } + config := middleware.DefaultProxyConfig config.Skipper = p.balancer.skipper config.Balancer = p.balancer @@ -117,7 +122,33 @@ func (p *DynamicProxy) middleware(prefix string) echo.MiddlewareFunc { fmt.Sprintf("^%s/%s/*", p.balancer.prefix, prefix): "/$1", } - return middleware.ProxyWithConfig(config) + configUncompressed := middleware.DefaultProxyConfig + configUncompressed.Skipper = p.balancer.skipper + configUncompressed.Balancer = p.balancer + configUncompressed.Rewrite = map[string]string{ + fmt.Sprintf("^%s/%s/*", p.balancer.prefix, prefix): "/$1", + } + //nolint:forcetypeassert // we can safely assume that the DefaultTransport is a http.Transport + transportUncompressed := http.DefaultTransport.(*http.Transport).Clone() + transportUncompressed.DisableCompression = true + configUncompressed.Transport = transportUncompressed + + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + // we forward compressed requests if the client also supports compressed responses + compressed := hasAcceptEncodingGZIP(c.Request().Header) + + // we need to remove "Accept-Encoding" headers in the request, + // because the transport handles this automatically if not set + c.Request().Header.Del(echo.HeaderAcceptEncoding) + + if !compressed { + return middleware.ProxyWithConfig(configUncompressed)(next)(c) + } + + return middleware.ProxyWithConfig(config)(next)(c) + } + } } func (p *DynamicProxy) AddGroup(prefix string) *echo.Group {