Skip to content

Request forwarding interceptors

Mariusz Kopylec edited this page Jan 29, 2021 · 21 revisions

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:

Request server name rewriter

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()));
    }
}

Regex request path rewriter

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>"));
    }
}

Circuit breaker

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));
    }
}

Rate limiter

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));
    }
}

Retryer

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));
    }
}

Latency meter

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));
    }
}

Rate meter

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));
    }
}

Asynchronous forwarder

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)));
    }
}

Basic authenticator

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));
    }
}

Bearer authenticator

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));
    }
}

Root path response cookies rewriter

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());
    }
}

Removing response cookies rewriter

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());
    }
}

Request 'Host' header rewriter

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());
    }
}

Request protocol headers rewriter

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());
    }
}

Request proxy headers rewriter

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());
    }
}

Response protocol headers rewriter

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());
    }
}

Forwarding logger

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));
    }
}

Custom request forwarding interceptor

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"));
    }
}

<< previous | next >>

Clone this wiki locally