-
Notifications
You must be signed in to change notification settings - Fork 55
Request forwarding interceptors
Most of the Charon's features are represented by request forwarding interceptors. An interceptor allows to modify outgoing requests and incoming responses. Every interceptor has an order that defines when the particular interceptor will be invoked. Some of the interceptors are added to Charon's configuration by default, some can be added manually. Custom interceptors can also be created. Interceptors can be freely set and unset globally and per request mapping:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.configuration.RequestMappingConfigurer.requestMapping;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(<interceptor>)
.unset(<interceptor type>)
.add(requestMapping("mapping name")
.set(<interceptor>)
.unset(<interceptor type>));
}
}
An interceptor can be overwritten by setting an interceptor of the same type. Charon contains the following request forwarding interceptors:
Description: Defines outgoing servers for incoming requests.
Load balancing can also be configured by the interceptor's API.
Default load balancer randomly chooses the outgoing host.
Custom load balancer can be created by implementing LoadBalancer
and extending LoadBalancerConfigurer
.
Set by default: No
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.rewrite.RandomLoadBalancerConfigurer.randomLoadBalancer;
import static com.github.mkopylec.charon.forwarding.interceptors.rewrite.RequestServerNameRewriterConfigurer.requestServerNameRewriter;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(requestServerNameRewriter()
.outgoingServers()
.loadBalancer(randomLoadBalancer()));
}
}
Description: Defines how the path of incoming requests must be changed before forwarding them to outgoing servers.
The interceptor takes an incoming request's path and matches it against configured incoming path regular expression.
If the path matches the expression then the request's path is rewritten according to the configured outgoing path template.
Incoming path regular expression can have named groups that fill outgoing path template's placeholders.
For example if incoming path regular expression is /path/(?<group>.*)
, outgoing path template is /other-path/<group>
and
incoming request's path is /path/sub-path then outgoing request's path will be /other-path/sub-path.
Set by default: No
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.rewrite.RegexRequestPathRewriterConfigurer.regexRequestPathRewriter;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(regexRequestPathRewriter()
.paths("/(?<path>.*)", "/<path>"));
}
}
Description: Applies a circuit breaker design pattern to request forwarding process.
Check here for detailed information about circuit breaker configuration.
Optionally a fallback can be configured. The fallback is executed when a circuit breaker is not closed.
Charon will collect circuit breaker's metrics if a MeterRegistry
is provided to the circuit breaker's configuration.
Set by default: No
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.resilience.CircuitBreakerConfigurer.circuitBreaker;
import static io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.custom;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(circuitBreaker()
.configuration(custom().recordResult(response -> response.statusCode().is5xxServerError()).recordExceptions(Throwable.class))
.fallback(null)
.meterRegistry(null));
}
}
Description: Applies rate limiting to request forwarding process.
Check here for detailed information about rate limiter configuration.
Charon will collect rate limiter's metrics if a MeterRegistry
is provided to the rate limiter's configuration.
Set by default: No
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.resilience.RateLimiterConfigurer.rateLimiter;
import static io.github.resilience4j.ratelimiter.RateLimiterConfig.custom;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(rateLimiter()
.configuration(custom().timeoutDuration(ZERO).limitRefreshPeriod(ofSeconds(1)).limitForPeriod(100))
.meterRegistry(null));
}
}
Description: Retries request forwarding process under the configured circumstances.
Check here for detailed information about retryer configuration.
Charon will collect retryer's metrics if a MeterRegistry
is provided to the retryer's configuration.
Set by default: No
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.resilience.RetryerConfigurer.retryer;
import static io.github.resilience4j.retry.RetryConfig.custom;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(retryer()
.configuration(custom().waitDuration(ZERO).retryOnResult(response -> response.getStatusCode().is5xxServerError()).retryExceptions(Throwable.class))
.meterRegistry(null));
}
}
Description: Collects latency metrics of request forwarding process if a MeterRegistry
is provided.
Set by default: No
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.metrics.LatencyMeterConfigurer.latencyMeter;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(latencyMeter()
.meterRegistry(null));
}
}
Description: Collects rate metrics of request forwarding process if a MeterRegistry
is provided.
Set by default: No
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.metrics.RateMeterConfigurer.rateMeter;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(rateMeter()
.meterRegistry(null));
}
}
Description: Forwards incoming requests asynchronously.
The forwarder does not wait for request forwarding process to finish, it immediately returns HTTP 202 Accepted response to the client.
Separate, configurable thread pool is used to forward the incoming requests.
Set by default: No
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.async.AsynchronousForwarderConfigurer.asynchronousForwarder;
import static com.github.mkopylec.charon.forwarding.interceptors.async.ThreadPoolConfigurer.threadPool;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(asynchronousForwarder()
.set(threadPool().coreSize(3).maximumSize(20)));
}
}
Description: Secures request forwarding with basic authentication.
Authentication is done using 'Authorization' incoming request's header.
If the authentication fails an HTTP 401 unathorized response is returned.
A user validator must be set in order to validate incoming request's credentials.
There is a build-in in-memory user validator that can be configured.
To add users to it use InMemoryUserValidatorConfigurer#validUser(...)
method.
Set by default: No
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.security.BasicAuthenticatorConfigurer.basicAuthenticator;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(basicAuthenticator().userValidator(null));
}
}
Description: Secures request forwarding with bearer authentication.
Authentication is done using 'Authorization' incoming request's header.
If the authentication fails an HTTP 401 unathorized response is returned.
A token validator must be set in order to validate incoming request's credentials.
There is a build-in in-memory token validator that can be configured.
To add tokens to it use InMemoryTokenValidatorConfigurer#validTokens(...)
method.
Set by default: No
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.security.BearerAuthenticatorConfigurer.bearerAuthenticator;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(bearerAuthenticator().tokenValidator(null));
}
}
Description: Rewrites incoming response's cookies by setting their path to /.
Set by default: Yes
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.rewrite.RootPathResponseCookiesRewriterConfigurer.rootPathResponseCookiesRewriter;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(rootPathResponseCookiesRewriter());
}
}
Description: Removes all incoming response's cookies.
Set by default: No
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.rewrite.RemovingResponseCookiesRewriterConfigurer.removingResponseCookiesRewriter;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(removingResponseCookiesRewriter());
}
}
Description: Rewrites incoming request's 'Host' header by setting its value to outgoing server name.
Set by default: No
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.rewrite.RequestHostHeaderRewriterConfigurer.requestHostHeaderRewriter;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(requestHostHeaderRewriter());
}
}
Description: Rewrites incoming request's protocol specific headers.
It sets 'Connection: close' header and removes 'TE' header.
Set by default: Yes
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.rewrite.RequestProtocolHeadersRewriterConfigurer.requestProtocolHeadersRewriter;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(requestProtocolHeadersRewriter());
}
}
Description: Adds 'X-Forwarded' headers to incoming request.
It updates the 'X-Forwarded-For' header and sets 'X-Forwarded-Proto', 'X-Forwarded-Host' and 'X-Forwarded-Port' headers.
Set by default: Yes
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.rewrite.RequestProxyHeadersRewriterConfigurer.requestProxyHeadersRewriter;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(requestProxyHeadersRewriter());
}
}
Description: Rewrites incoming response's protocol specific headers.
It removes 'Transfer-Encoding', 'Connection', 'Public-Key-Pins', 'Server' and 'Strict-Transport-Security' headers.
Set by default: Yes
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.rewrite.ResponseProtocolHeadersRewriterConfigurer.responseProtocolHeadersRewriter;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(responseProtocolHeadersRewriter());
}
}
Description: Logs information about request forwarding process.
Set by default: Yes
Configuration API and defaults:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static com.github.mkopylec.charon.forwarding.interceptors.log.ForwardingLoggerConfigurer.forwardingLogger;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(forwardingLogger()
.successLogLevel(DEBUG)
.clientErrorLogLevel(INFO)
.serverErrorLogLevel(ERROR)
.unexpectedErrorLogLevel(ERROR));
}
}
Additional interceptors can be created by implementing RequestForwardingInterceptor
interface.
To be able to set the created interceptor globally or per request mapping RequestForwardingInterceptorConfigurer
must be extended.
For example:
class CustomInterceptor implements RequestForwardingInterceptor {
private String property;
void setProperty(String property) {
this.property = property;
}
...
}
class CustomInterceptorConfigurer extends RequestForwardingInterceptorConfigurer<CustomInterceptor> {
private CustomInterceptorConfigurer() {
super(new CustomInterceptor());
}
static CustomInterceptorConfigurer customInterceptor() {
return new CustomInterceptorConfigurer();
}
CustomInterceptorConfigurer property(String property) {
configuredObject.setProperty(property);
return this;
}
}
To use the created interceptor simply set it via CharonConfigurer
API, analogically to other interceptors:
import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration;
import static mypackage.CustomInterceptorConfigurer.customInterceptor;
@Configuration
class CharonConfiguration {
@Bean
CharonConfigurer charonConfigurer() {
return charonConfiguration()
.set(customInterceptor()
.property("some property"));
}
}