├── .DS_Store ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── charon-common ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── github │ └── mkopylec │ └── charon │ ├── configuration │ ├── Configurer.java │ └── Valid.java │ └── forwarding │ ├── RequestForwardingException.java │ ├── TimeoutConfiguration.java │ ├── TimeoutConfigurer.java │ ├── Utils.java │ └── interceptors │ ├── RequestForwardingInterceptorType.java │ ├── async │ ├── CommonAsynchronousForwarder.java │ ├── ThreadPool.java │ └── ThreadPoolConfigurer.java │ ├── log │ ├── CommonForwardingLogger.java │ └── LogLevel.java │ ├── metrics │ ├── CommonLatencyMeter.java │ ├── CommonMeter.java │ └── CommonRateMeter.java │ ├── resilience │ ├── CommonCircuitBreaker.java │ ├── CommonRateLimiter.java │ ├── CommonResilienceHandler.java │ └── CommonRetryer.java │ ├── rewrite │ ├── BodilessHttpRequest.java │ ├── CommonRegexRequestPathRewriter.java │ ├── CommonRemovingResponseCookiesRewriter.java │ ├── CommonRequestHostHeaderRewriter.java │ ├── CommonRequestProtocolHeadersRewriter.java │ ├── CommonRequestProxyHeadersRewriter.java │ ├── CommonRequestServerNameRewriter.java │ ├── CommonResponseProtocolHeadersRewriter.java │ ├── CommonRootPathResponseCookiesRewriter.java │ ├── LoadBalancer.java │ ├── LoadBalancerConfigurer.java │ ├── PathTemplate.java │ ├── RandomLoadBalancer.java │ └── RandomLoadBalancerConfigurer.java │ └── security │ ├── AuthenticationType.java │ ├── BasicUserValidator.java │ ├── CommonAuthenticator.java │ └── User.java ├── charon-spring-webflux ├── build.gradle └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── github │ │ │ └── mkopylec │ │ │ └── charon │ │ │ ├── configuration │ │ │ ├── CharonAutoConfiguration.java │ │ │ ├── CharonConfiguration.java │ │ │ ├── CharonConfigurer.java │ │ │ ├── RequestMappingConfiguration.java │ │ │ └── RequestMappingConfigurer.java │ │ │ └── forwarding │ │ │ ├── ClientHttpConnectorCreator.java │ │ │ ├── ClientHttpConnectorCreatorConfigurer.java │ │ │ ├── HttpRequestMapper.java │ │ │ ├── HttpResponseMapper.java │ │ │ ├── ReactorClientHttpConnectorCreator.java │ │ │ ├── ReactorClientHttpConnectorCreatorConfigurer.java │ │ │ ├── RequestMappingResolver.java │ │ │ ├── ReverseProxyFilter.java │ │ │ ├── WebClientConfiguration.java │ │ │ ├── WebClientConfigurer.java │ │ │ ├── WebClientProvider.java │ │ │ └── interceptors │ │ │ ├── HttpRequest.java │ │ │ ├── HttpRequestExecution.java │ │ │ ├── HttpRequestInterceptor.java │ │ │ ├── HttpResponse.java │ │ │ ├── RequestForwardingInterceptor.java │ │ │ ├── RequestForwardingInterceptorConfigurer.java │ │ │ ├── async │ │ │ ├── AsynchronousForwarder.java │ │ │ └── AsynchronousForwarderConfigurer.java │ │ │ ├── log │ │ │ ├── ForwardingLogger.java │ │ │ └── ForwardingLoggerConfigurer.java │ │ │ ├── metrics │ │ │ ├── LatencyMeter.java │ │ │ ├── LatencyMeterConfigurer.java │ │ │ ├── RateMeter.java │ │ │ └── RateMeterConfigurer.java │ │ │ ├── resilience │ │ │ ├── CircuitBreaker.java │ │ │ ├── CircuitBreakerConfigurer.java │ │ │ ├── RateLimiter.java │ │ │ ├── RateLimiterConfigurer.java │ │ │ ├── Retryer.java │ │ │ └── RetryerConfigurer.java │ │ │ ├── rewrite │ │ │ ├── RegexRequestPathRewriter.java │ │ │ ├── RegexRequestPathRewriterConfigurer.java │ │ │ ├── RemovingResponseCookiesRewriter.java │ │ │ ├── RemovingResponseCookiesRewriterConfigurer.java │ │ │ ├── RequestHostHeaderRewriter.java │ │ │ ├── RequestHostHeaderRewriterConfigurer.java │ │ │ ├── RequestProtocolHeadersRewriter.java │ │ │ ├── RequestProtocolHeadersRewriterConfigurer.java │ │ │ ├── RequestProxyHeadersRewriter.java │ │ │ ├── RequestProxyHeadersRewriterConfigurer.java │ │ │ ├── RequestServerNameRewriter.java │ │ │ ├── RequestServerNameRewriterConfigurer.java │ │ │ ├── ResponseProtocolHeadersRewriter.java │ │ │ ├── ResponseProtocolHeadersRewriterConfigurer.java │ │ │ ├── RootPathResponseCookiesRewriter.java │ │ │ └── RootPathResponseCookiesRewriterConfigurer.java │ │ │ └── security │ │ │ ├── Authenticator.java │ │ │ ├── BasicAuthenticator.java │ │ │ ├── BasicAuthenticatorConfigurer.java │ │ │ ├── BearerAuthenticator.java │ │ │ ├── BearerAuthenticatorConfigurer.java │ │ │ ├── CredentialsValidator.java │ │ │ ├── CredentialsValidatorConfigurer.java │ │ │ ├── InMemoryTokenValidator.java │ │ │ ├── InMemoryTokenValidatorConfigurer.java │ │ │ ├── InMemoryUserValidator.java │ │ │ ├── InMemoryUserValidatorConfigurer.java │ │ │ ├── TokenValidator.java │ │ │ ├── TokenValidatorConfigurer.java │ │ │ ├── UserValidator.java │ │ │ └── UserValidatorConfigurer.java │ └── resources │ │ └── META-INF │ │ └── spring │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── test │ ├── groovy │ └── com │ │ └── github │ │ └── mkopylec │ │ └── charon │ │ └── test │ │ ├── AsynchronousForwardingSpec.groovy │ │ ├── BasicAuthenticationSpec.groovy │ │ ├── BearerAuthenticationSpec.groovy │ │ ├── CharonConfigurationSpec.groovy │ │ ├── CircuitBreakingSpec.groovy │ │ ├── CustomLoadBalancerSpec.groovy │ │ ├── DefaultCharonConfigurationSpec.groovy │ │ ├── DefinedProfileSpec.groovy │ │ ├── ExchangeStrategiesSpec.groovy │ │ ├── LatencyMeteringSpec.groovy │ │ ├── MappingResolvingSpec.groovy │ │ ├── RateLimitingSpec.groovy │ │ ├── RateMeteringSpec.groovy │ │ ├── RegexRequestPathRewritingSpec.groovy │ │ ├── RemovingResponseCookiesRewritingSpec.groovy │ │ ├── RequestBodyRewritingSpec.groovy │ │ ├── RequestForwardingSpec.groovy │ │ ├── RequestHostHeaderRewritingSpec.groovy │ │ ├── RequestProtocolHeadersRewritingSpec.groovy │ │ ├── RequestProxyHeadersRewritingSpec.groovy │ │ ├── ResponseBodyRewritingSpec.groovy │ │ ├── ResponseForwardingSpec.groovy │ │ ├── ResponseProtocolHeadersRewritingSpec.groovy │ │ ├── RetryingSpec.groovy │ │ ├── RootPathResponseCookiesRewritingSpec.groovy │ │ └── TimeoutSpec.groovy │ ├── java │ └── com │ │ └── github │ │ └── mkopylec │ │ └── charon │ │ └── test │ │ ├── CircuitBreakerFallback.java │ │ ├── DataBufferSizeSetter.java │ │ ├── ExceptionThrower.java │ │ ├── ExceptionThrowerConfigurer.java │ │ ├── LowestPortLoadBalancer.java │ │ ├── LowestPortLoadBalancerConfigurer.java │ │ ├── RequestBodyRewriter.java │ │ ├── RequestBodyRewriterConfigurer.java │ │ ├── ResponseBodyRewriter.java │ │ ├── ResponseBodyRewriterConfigurer.java │ │ ├── RetryAttemptsResponseHeaderSetter.java │ │ └── ReverseProxyConfiguration.java │ └── resources │ └── application.yml ├── charon-spring-webmvc ├── build.gradle └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── github │ │ │ └── mkopylec │ │ │ └── charon │ │ │ ├── configuration │ │ │ ├── CharonAutoConfiguration.java │ │ │ ├── CharonConfiguration.java │ │ │ ├── CharonConfigurer.java │ │ │ ├── RequestMappingConfiguration.java │ │ │ └── RequestMappingConfigurer.java │ │ │ └── forwarding │ │ │ ├── ClientHttpRequestFactoryCreator.java │ │ │ ├── ClientHttpRequestFactoryCreatorConfigurer.java │ │ │ ├── HttpRequestMapper.java │ │ │ ├── HttpResponseMapper.java │ │ │ ├── NoExceptionErrorHandler.java │ │ │ ├── OkClientHttpRequestFactoryCreator.java │ │ │ ├── OkClientHttpRequestFactoryCreatorConfigurer.java │ │ │ ├── RequestMappingResolver.java │ │ │ ├── RestTemplateConfiguration.java │ │ │ ├── RestTemplateConfigurer.java │ │ │ ├── RestTemplateProvider.java │ │ │ ├── RetryAwareRestTemplate.java │ │ │ ├── ReverseProxyFilter.java │ │ │ └── interceptors │ │ │ ├── HttpRequest.java │ │ │ ├── HttpRequestExecution.java │ │ │ ├── HttpRequestInterceptor.java │ │ │ ├── HttpResponse.java │ │ │ ├── RequestForwardingInterceptor.java │ │ │ ├── RequestForwardingInterceptorConfigurer.java │ │ │ ├── async │ │ │ ├── AsynchronousForwarder.java │ │ │ └── AsynchronousForwarderConfigurer.java │ │ │ ├── log │ │ │ ├── ForwardingLogger.java │ │ │ └── ForwardingLoggerConfigurer.java │ │ │ ├── metrics │ │ │ ├── LatencyMeter.java │ │ │ ├── LatencyMeterConfigurer.java │ │ │ ├── RateMeter.java │ │ │ └── RateMeterConfigurer.java │ │ │ ├── resilience │ │ │ ├── CircuitBreaker.java │ │ │ ├── CircuitBreakerConfigurer.java │ │ │ ├── RateLimiter.java │ │ │ ├── RateLimiterConfigurer.java │ │ │ ├── RetryAwareList.java │ │ │ ├── Retryer.java │ │ │ ├── RetryerConfigurer.java │ │ │ └── RetryingState.java │ │ │ ├── rewrite │ │ │ ├── RegexRequestPathRewriter.java │ │ │ ├── RegexRequestPathRewriterConfigurer.java │ │ │ ├── RemovingResponseCookiesRewriter.java │ │ │ ├── RemovingResponseCookiesRewriterConfigurer.java │ │ │ ├── RequestHostHeaderRewriter.java │ │ │ ├── RequestHostHeaderRewriterConfigurer.java │ │ │ ├── RequestProtocolHeadersRewriter.java │ │ │ ├── RequestProtocolHeadersRewriterConfigurer.java │ │ │ ├── RequestProxyHeadersRewriter.java │ │ │ ├── RequestProxyHeadersRewriterConfigurer.java │ │ │ ├── RequestServerNameRewriter.java │ │ │ ├── RequestServerNameRewriterConfigurer.java │ │ │ ├── ResponseProtocolHeadersRewriter.java │ │ │ ├── ResponseProtocolHeadersRewriterConfigurer.java │ │ │ ├── RootPathResponseCookiesRewriter.java │ │ │ └── RootPathResponseCookiesRewriterConfigurer.java │ │ │ └── security │ │ │ ├── Authenticator.java │ │ │ ├── BasicAuthenticator.java │ │ │ ├── BasicAuthenticatorConfigurer.java │ │ │ ├── BearerAuthenticator.java │ │ │ ├── BearerAuthenticatorConfigurer.java │ │ │ ├── CredentialsValidator.java │ │ │ ├── CredentialsValidatorConfigurer.java │ │ │ ├── InMemoryTokenValidator.java │ │ │ ├── InMemoryTokenValidatorConfigurer.java │ │ │ ├── InMemoryUserValidator.java │ │ │ ├── InMemoryUserValidatorConfigurer.java │ │ │ ├── TokenValidator.java │ │ │ ├── TokenValidatorConfigurer.java │ │ │ ├── UserValidator.java │ │ │ └── UserValidatorConfigurer.java │ └── resources │ │ └── META-INF │ │ └── spring │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── test │ ├── groovy │ └── com │ │ └── github │ │ └── mkopylec │ │ └── charon │ │ └── test │ │ ├── AsynchronousForwardingSpec.groovy │ │ ├── BasicAuthenticationSpec.groovy │ │ ├── BearerAuthenticationSpec.groovy │ │ ├── CharonConfigurationSpec.groovy │ │ ├── CircuitBreakingSpec.groovy │ │ ├── CustomLoadBalancerSpec.groovy │ │ ├── DefaultCharonConfigurationSpec.groovy │ │ ├── DefinedProfileSpec.groovy │ │ ├── LatencyMeteringSpec.groovy │ │ ├── MappingResolvingSpec.groovy │ │ ├── RateLimitingSpec.groovy │ │ ├── RateMeteringSpec.groovy │ │ ├── RegexRequestPathRewritingSpec.groovy │ │ ├── RemovingResponseCookiesRewritingSpec.groovy │ │ ├── RequestBodyRewritingSpec.groovy │ │ ├── RequestForwardingSpec.groovy │ │ ├── RequestHostHeaderRewritingSpec.groovy │ │ ├── RequestProtocolHeadersRewritingSpec.groovy │ │ ├── RequestProxyHeadersRewritingSpec.groovy │ │ ├── ResponseBodyRewritingSpec.groovy │ │ ├── ResponseForwardingSpec.groovy │ │ ├── ResponseProtocolHeadersRewritingSpec.groovy │ │ ├── RetryingSpec.groovy │ │ ├── RootPathResponseCookiesRewritingSpec.groovy │ │ └── TimeoutSpec.groovy │ ├── java │ └── com │ │ └── github │ │ └── mkopylec │ │ └── charon │ │ └── test │ │ ├── CircuitBreakerFallback.java │ │ ├── ExceptionThrower.java │ │ ├── ExceptionThrowerConfigurer.java │ │ ├── LowestPortLoadBalancer.java │ │ ├── LowestPortLoadBalancerConfigurer.java │ │ ├── RequestBodyRewriter.java │ │ ├── RequestBodyRewriterConfigurer.java │ │ ├── ResponseBodyRewriter.java │ │ ├── ResponseBodyRewriterConfigurer.java │ │ ├── RetryAttemptsResponseHeaderSetter.java │ │ └── ReverseProxyConfiguration.java │ └── resources │ └── application.yml ├── charon-test ├── build.gradle └── src │ └── main │ ├── groovy │ └── com │ │ └── github │ │ └── mkopylec │ │ └── charon │ │ └── test │ │ ├── assertions │ │ ├── Assertions.groovy │ │ ├── ExceptionAssertion.groovy │ │ ├── MetricsAssertion.groovy │ │ ├── OutgoingServerAssertion.groovy │ │ └── ResponseAssertion.groovy │ │ ├── specification │ │ ├── AsynchronousForwardingBasicSpec.groovy │ │ ├── BasicAuthenticationBasicSpec.groovy │ │ ├── BasicSpec.groovy │ │ ├── BearerAuthenticationBasicSpec.groovy │ │ ├── CircuitBreakingBasicSpec.groovy │ │ ├── CustomLoadBalancerBasicSpec.groovy │ │ ├── DefaultCharonConfigurationBasicSpec.groovy │ │ ├── DefinedProfileBasicSpec.groovy │ │ ├── LatencyMeteringBasicSpec.groovy │ │ ├── MappingResolvingBasicSpec.groovy │ │ ├── RateLimitingBasicSpec.groovy │ │ ├── RateMeteringBasicSpec.groovy │ │ ├── RegexRequestPathRewritingBasicSpec.groovy │ │ ├── RemovingResponseCookiesRewritingBasicSpec.groovy │ │ ├── RequestBodyRewritingBasicSpec.groovy │ │ ├── RequestForwardingBasicSpec.groovy │ │ ├── RequestHostHeaderRewritingBasicSpec.groovy │ │ ├── RequestProtocolHeadersRewritingBasicSpec.groovy │ │ ├── RequestProxyHeadersRewritingBasicSpec.groovy │ │ ├── ResponseBodyRewritingBasicSpec.groovy │ │ ├── ResponseForwardingBasicSpec.groovy │ │ ├── ResponseProtocolHeadersRewritingBasicSpec.groovy │ │ ├── RetryingBasicSpec.groovy │ │ ├── RootPathResponseCookiesRewritingBasicSpec.groovy │ │ └── TimeoutBasicSpec.groovy │ │ ├── stubs │ │ ├── OutgoingServer.groovy │ │ ├── OutgoingServersStubs.groovy │ │ └── ResponseDelay.groovy │ │ └── utils │ │ ├── HttpClient.groovy │ │ └── MeterRegistryProvider.groovy │ └── java │ └── com │ └── github │ └── mkopylec │ └── charon │ └── test │ ├── CommonExceptionThrower.java │ ├── CommonRequestBodyRewriter.java │ ├── CommonResponseBodyRewriter.java │ ├── LocalEndpoint.java │ └── ReverseProxyApplication.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── release └── settings.gradle /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkopylec/charon-spring-boot-starter/194084171e63c69e77a6b2950910246b0ac80dba/.DS_Store -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ master ] 7 | pull_request: 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | CI: 15 | name: CI 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | ref: ${{ github.head_ref }} 22 | - uses: gradle/actions/wrapper-validation@v3 23 | - uses: actions/setup-java@v4 24 | with: 25 | distribution: temurin 26 | java-version: 17 27 | 28 | - name: Run tests 29 | run: ./gradlew test 30 | 31 | - name: Report tests 32 | uses: mikepenz/action-junit-report@v5 33 | if: success() || failure() 34 | with: 35 | report_paths: '**/build/test-results/test/TEST-*.xml' 36 | 37 | - name: Report code coverage 38 | run: ./gradlew jacocoTestReport 39 | 40 | - name: Upload code coverage 41 | uses: codecov/codecov-action@v5 42 | env: 43 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | gradle.properties 4 | .gradle 5 | build 6 | classes 7 | out 8 | -------------------------------------------------------------------------------- /charon-common/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation group: 'org.springframework.boot', name: 'spring-boot-starter-logging', version: springBootVersion 3 | } 4 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/configuration/Configurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.configuration; 2 | 3 | public abstract class Configurer { 4 | 5 | protected O configuredObject; 6 | 7 | protected Configurer(O configuredObject) { 8 | this.configuredObject = configuredObject; 9 | } 10 | 11 | protected O configure() { 12 | configuredObject.validate(); 13 | return configuredObject; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/configuration/Valid.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.configuration; 2 | 3 | public interface Valid { 4 | 5 | default void validate() { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/RequestForwardingException.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import java.util.function.Supplier; 4 | 5 | public class RequestForwardingException extends RuntimeException { 6 | 7 | private RequestForwardingException(String message) { 8 | super(message); 9 | } 10 | 11 | private RequestForwardingException(String message, Throwable cause) { 12 | super(message, cause); 13 | } 14 | 15 | public static void requestForwardingErrorIf(boolean condition, Supplier errorMessage) { 16 | if (condition) { 17 | throw new RequestForwardingException(errorMessage.get()); 18 | } 19 | } 20 | 21 | public static RequestForwardingException requestForwardingError(String errorMessage) { 22 | return new RequestForwardingException(errorMessage); 23 | } 24 | 25 | public static RequestForwardingException requestForwardingError(String errorMessage, Throwable cause) { 26 | return new RequestForwardingException(errorMessage, cause); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/TimeoutConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import com.github.mkopylec.charon.configuration.Configurer; 4 | 5 | import java.time.Duration; 6 | 7 | public class TimeoutConfigurer extends Configurer { 8 | 9 | private TimeoutConfigurer() { 10 | super(new TimeoutConfiguration()); 11 | } 12 | 13 | public static TimeoutConfigurer timeout() { 14 | return new TimeoutConfigurer(); 15 | } 16 | 17 | public TimeoutConfigurer connection(Duration connection) { 18 | configuredObject.setConnection(connection); 19 | return this; 20 | } 21 | 22 | public TimeoutConfigurer read(Duration read) { 23 | configuredObject.setRead(read); 24 | return this; 25 | } 26 | 27 | public TimeoutConfigurer write(Duration write) { 28 | configuredObject.setWrite(write); 29 | return this; 30 | } 31 | 32 | @Override 33 | protected TimeoutConfiguration configure() { 34 | return super.configure(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/Utils.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import org.springframework.http.HttpHeaders; 4 | 5 | import java.time.Duration; 6 | 7 | import static java.lang.String.join; 8 | 9 | public class Utils { 10 | 11 | public static int toMillis(Duration duration) { 12 | return (int) duration.toMillis(); 13 | } 14 | 15 | public static String metricName(String... parts) { 16 | return "charon." + join(".", parts); 17 | } 18 | 19 | public static HttpHeaders copyHeaders(HttpHeaders headers) { 20 | HttpHeaders copy = new HttpHeaders(); 21 | copy.putAll(headers); 22 | return copy; 23 | } 24 | 25 | private Utils() { 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/async/ThreadPool.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.async; 2 | 3 | import com.github.mkopylec.charon.configuration.Valid; 4 | 5 | import java.util.concurrent.LinkedBlockingQueue; 6 | import java.util.concurrent.ThreadPoolExecutor; 7 | 8 | import static java.util.concurrent.TimeUnit.SECONDS; 9 | 10 | class ThreadPool extends ThreadPoolExecutor implements Valid { 11 | 12 | ThreadPool() { 13 | super(3, 20, 60, SECONDS, new LinkedBlockingQueue<>(50)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/async/ThreadPoolConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.async; 2 | 3 | import com.github.mkopylec.charon.configuration.Configurer; 4 | 5 | public class ThreadPoolConfigurer extends Configurer { 6 | 7 | private ThreadPoolConfigurer() { 8 | super(new ThreadPool()); 9 | } 10 | 11 | public static ThreadPoolConfigurer threadPool() { 12 | return new ThreadPoolConfigurer(); 13 | } 14 | 15 | public ThreadPoolConfigurer coreSize(int coreSize) { 16 | configuredObject.setCorePoolSize(coreSize); 17 | return this; 18 | } 19 | 20 | public ThreadPoolConfigurer maximumSize(int maximumSize) { 21 | configuredObject.setMaximumPoolSize(maximumSize); 22 | return this; 23 | } 24 | 25 | @Override 26 | protected ThreadPool configure() { 27 | return super.configure(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/log/LogLevel.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.log; 2 | 3 | public enum LogLevel { 4 | 5 | ERROR, WARN, INFO, DEBUG, TRACE 6 | } 7 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/metrics/CommonLatencyMeter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.metrics; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorType; 4 | import org.slf4j.Logger; 5 | 6 | import java.time.Duration; 7 | 8 | import static com.github.mkopylec.charon.forwarding.Utils.metricName; 9 | import static com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorType.LATENCY_METER; 10 | import static java.lang.System.nanoTime; 11 | import static java.time.Duration.ofNanos; 12 | 13 | abstract class CommonLatencyMeter extends CommonMeter { 14 | 15 | CommonLatencyMeter(Logger log) { 16 | super(log); 17 | } 18 | 19 | public RequestForwardingInterceptorType getType() { 20 | return LATENCY_METER; 21 | } 22 | 23 | void captureLatencyMetric(String mappingName, long startingTime) { 24 | String metricName = metricName(mappingName, "latency"); 25 | Duration responseTime = ofNanos(nanoTime() - startingTime); 26 | getMeterRegistry().timer(metricName).record(responseTime); 27 | } 28 | 29 | void logStart(String mappingName) { 30 | getLog().trace("[Start] Collect latency metrics of '{}' request mapping", mappingName); 31 | } 32 | 33 | void logEnd(String mappingName) { 34 | getLog().trace("[End] Collect latency metrics of '{}' request mapping", mappingName); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/metrics/CommonMeter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.metrics; 2 | 3 | import com.github.mkopylec.charon.configuration.Valid; 4 | import io.micrometer.core.instrument.MeterRegistry; 5 | import org.slf4j.Logger; 6 | 7 | import static org.springframework.util.Assert.notNull; 8 | 9 | abstract class CommonMeter implements Valid { 10 | 11 | private Logger log; 12 | private MeterRegistry meterRegistry; 13 | 14 | CommonMeter(Logger log) { 15 | this.log = log; 16 | } 17 | 18 | @Override 19 | public void validate() { 20 | notNull(meterRegistry, "No meter registry set"); 21 | } 22 | 23 | Logger getLog() { 24 | return log; 25 | } 26 | 27 | MeterRegistry getMeterRegistry() { 28 | return meterRegistry; 29 | } 30 | 31 | void setMeterRegistry(MeterRegistry meterRegistry) { 32 | this.meterRegistry = meterRegistry; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/resilience/CommonResilienceHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.resilience; 2 | 3 | import io.micrometer.core.instrument.MeterRegistry; 4 | import io.micrometer.core.instrument.binder.MeterBinder; 5 | import org.slf4j.Logger; 6 | 7 | import java.util.function.Function; 8 | 9 | abstract class CommonResilienceHandler { 10 | 11 | private Logger log; 12 | private R registry; 13 | private MeterBinder metrics; 14 | private MeterRegistry meterRegistry; 15 | 16 | CommonResilienceHandler(Logger log, R registry) { 17 | this.log = log; 18 | this.registry = registry; 19 | } 20 | 21 | Logger getLog() { 22 | return log; 23 | } 24 | 25 | R getRegistry() { 26 | return registry; 27 | } 28 | 29 | void setRegistry(R registry) { 30 | this.registry = registry; 31 | } 32 | 33 | void setMeterRegistry(MeterRegistry meterRegistry) { 34 | this.meterRegistry = meterRegistry; 35 | } 36 | 37 | void setupMetrics(Function metricsCreator) { 38 | if (meterRegistry == null) { 39 | return; 40 | } 41 | if (metrics == null) { 42 | metrics = metricsCreator.apply(registry); 43 | metrics.bindTo(meterRegistry); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/BodilessHttpRequest.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import org.springframework.http.HttpHeaders; 4 | import org.springframework.http.HttpMethod; 5 | 6 | import java.net.URI; 7 | 8 | import static org.springframework.http.HttpHeaders.readOnlyHttpHeaders; 9 | 10 | public class BodilessHttpRequest { 11 | 12 | private HttpMethod method; 13 | private URI url; 14 | private String serverName; 15 | private HttpHeaders headers; 16 | 17 | BodilessHttpRequest(HttpMethod method, URI url, HttpHeaders headers) { 18 | this.method = method; 19 | this.url = url; 20 | serverName = url.getScheme() + "://" + url.getAuthority(); 21 | this.headers = readOnlyHttpHeaders(headers); 22 | } 23 | 24 | public HttpMethod getMethod() { 25 | return method; 26 | } 27 | 28 | public URI getUrl() { 29 | return url; 30 | } 31 | 32 | public String getServerName() { 33 | return serverName; 34 | } 35 | 36 | public HttpHeaders getHeaders() { 37 | return headers; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/LoadBalancer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.configuration.Valid; 4 | 5 | import java.net.URI; 6 | import java.util.List; 7 | 8 | public interface LoadBalancer extends Valid { 9 | 10 | URI chooseServer(List servers, BodilessHttpRequest request); 11 | } 12 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/LoadBalancerConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.configuration.Configurer; 4 | 5 | public abstract class LoadBalancerConfigurer extends Configurer { 6 | 7 | protected LoadBalancerConfigurer(B configuredObject) { 8 | super(configuredObject); 9 | } 10 | 11 | @Override 12 | protected B configure() { 13 | return super.configure(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/PathTemplate.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | import static java.util.regex.Pattern.compile; 9 | 10 | class PathTemplate { 11 | 12 | private static final Pattern placeholderSearchPattern = compile("<([^>]*)"); 13 | 14 | private String value; 15 | private List placeholders = new ArrayList<>(); 16 | 17 | PathTemplate(String value) { 18 | this.value = value; 19 | findPlaceholders(value); 20 | } 21 | 22 | String fill(Matcher matcher) { 23 | String filledValue = value; 24 | for (String placeholder : placeholders) { 25 | String group = matcher.group(placeholder); 26 | filledValue = filledValue.replace("<" + placeholder + ">", group); 27 | } 28 | return filledValue; 29 | } 30 | 31 | private void findPlaceholders(String value) { 32 | Matcher matcher = placeholderSearchPattern.matcher(value); 33 | while (matcher.find()) { 34 | placeholders.add(matcher.group(1)); 35 | } 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return value; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RandomLoadBalancer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import java.net.URI; 4 | import java.util.List; 5 | 6 | import static java.util.concurrent.ThreadLocalRandom.current; 7 | 8 | class RandomLoadBalancer implements LoadBalancer { 9 | 10 | @Override 11 | public URI chooseServer(List servers, BodilessHttpRequest request) { 12 | int index = servers.size() == 1 ? 0 : current().nextInt(0, servers.size()); 13 | return servers.get(index); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RandomLoadBalancerConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | public class RandomLoadBalancerConfigurer extends LoadBalancerConfigurer { 4 | 5 | private RandomLoadBalancerConfigurer() { 6 | super(new RandomLoadBalancer()); 7 | } 8 | 9 | public static RandomLoadBalancerConfigurer randomLoadBalancer() { 10 | return new RandomLoadBalancerConfigurer(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/AuthenticationType.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | enum AuthenticationType { 4 | 5 | BASIC("Basic"), BEARER("Bearer"); 6 | 7 | private String string; 8 | 9 | AuthenticationType(String string) { 10 | this.string = string; 11 | } 12 | 13 | @Override 14 | public String toString() { 15 | return string; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/BasicUserValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import static java.nio.charset.StandardCharsets.UTF_8; 4 | import static java.util.Base64.getDecoder; 5 | 6 | interface BasicUserValidator { 7 | 8 | default User extractUser(String credentials) { 9 | try { 10 | byte[] bytes = credentials.isEmpty() ? new byte[0] : getDecoder().decode(credentials); 11 | String[] splitCredentials = new String(bytes, UTF_8).split(":"); 12 | String password = splitCredentials.length > 1 ? splitCredentials[1] : null; 13 | return new User(splitCredentials[0], password); 14 | } catch (Exception e) { 15 | return new User(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /charon-common/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/User.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import org.apache.commons.lang3.builder.EqualsBuilder; 4 | import org.apache.commons.lang3.builder.HashCodeBuilder; 5 | 6 | import static org.apache.commons.lang3.StringUtils.isAllBlank; 7 | 8 | public class User { 9 | 10 | private String name; 11 | private String password; 12 | 13 | User() { 14 | } 15 | 16 | User(String name, String password) { 17 | this.name = name; 18 | this.password = password; 19 | } 20 | 21 | public String getName() { 22 | return name; 23 | } 24 | 25 | public String getPassword() { 26 | return password; 27 | } 28 | 29 | boolean isEmpty() { 30 | return isAllBlank(name, password); 31 | } 32 | 33 | @Override 34 | public boolean equals(Object obj) { 35 | if (obj == null) { 36 | return false; 37 | } 38 | if (obj == this) { 39 | return true; 40 | } 41 | if (obj.getClass() != getClass()) { 42 | return false; 43 | } 44 | User rhs = (User) obj; 45 | return new EqualsBuilder() 46 | .append(this.name, rhs.name) 47 | .append(this.password, rhs.password) 48 | .isEquals(); 49 | } 50 | 51 | @Override 52 | public int hashCode() { 53 | return new HashCodeBuilder() 54 | .append(name) 55 | .append(password) 56 | .toHashCode(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /charon-spring-webflux/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'jacoco' 2 | 3 | dependencies { 4 | api project(':charon-common').sourceSets.main.output 5 | api group: 'org.springframework.boot', name: 'spring-boot-starter-webflux', version: springBootVersion 6 | api group: 'org.springframework.boot', name: 'spring-boot-starter-logging', version: springBootVersion 7 | api group: 'io.github.resilience4j', name: 'resilience4j-reactor', version: resilience4jVersion 8 | 9 | testImplementation project(':charon-test') 10 | } 11 | 12 | jacoco { 13 | toolVersion = '0.8.12' 14 | } 15 | 16 | jacocoTestReport { 17 | sourceSets project(':charon-common').sourceSets.main 18 | reports { 19 | xml.required = true 20 | } 21 | } 22 | 23 | jar { 24 | from project(':charon-common').sourceSets.main.output 25 | } 26 | 27 | task sourceJar(type: Jar) { 28 | archiveClassifier.set('sources') 29 | from sourceSets.main.allSource 30 | from project(':charon-common').sourceSets.main.allSource 31 | } 32 | 33 | task javadocJar(type: Jar) { 34 | archiveClassifier.set('javadoc') 35 | from javadoc 36 | } 37 | 38 | artifacts { 39 | archives sourceJar 40 | } 41 | 42 | publishing { 43 | publications { 44 | charonSpringWebflux(MavenPublication) { 45 | from components.java 46 | artifact sourceJar 47 | artifact javadocJar 48 | pom pomContent 49 | } 50 | } 51 | } 52 | 53 | signing { 54 | if (project.ext.has('signArtifacts')) { 55 | sign publishing.publications.charonSpringWebflux 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/configuration/CharonAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.configuration; 2 | 3 | import com.github.mkopylec.charon.forwarding.ReverseProxyFilter; 4 | import org.slf4j.Logger; 5 | import org.springframework.beans.factory.ObjectProvider; 6 | import org.springframework.boot.autoconfigure.AutoConfiguration; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 9 | import org.springframework.context.annotation.Bean; 10 | 11 | import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration; 12 | import static org.slf4j.LoggerFactory.getLogger; 13 | 14 | @AutoConfiguration 15 | @ConditionalOnBean(CharonConfigurer.class) 16 | class CharonAutoConfiguration { 17 | 18 | private static final Logger log = getLogger(CharonAutoConfiguration.class); 19 | 20 | @Bean 21 | @ConditionalOnMissingBean 22 | ReverseProxyFilter reverseProxyFilter(ObjectProvider charonConfigurer) { 23 | CharonConfigurer configurer = charonConfigurer.getIfAvailable(); 24 | if (configurer == null) { 25 | log.warn("No Charon configuration detected, all incoming requests will be handled locally"); 26 | configurer = charonConfiguration(); 27 | } 28 | CharonConfiguration configuration = configurer.configure(); 29 | return new ReverseProxyFilter(configuration.getFilterOrder(), configuration.getRequestMappingConfigurations()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/ClientHttpConnectorCreator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import com.github.mkopylec.charon.configuration.Valid; 4 | import org.springframework.http.client.reactive.ClientHttpConnector; 5 | 6 | public interface ClientHttpConnectorCreator extends Valid { 7 | 8 | ClientHttpConnector createConnector(TimeoutConfiguration configuration); 9 | } 10 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/ClientHttpConnectorCreatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import com.github.mkopylec.charon.configuration.Configurer; 4 | 5 | public abstract class ClientHttpConnectorCreatorConfigurer extends Configurer { 6 | 7 | protected ClientHttpConnectorCreatorConfigurer(C configuredObject) { 8 | super(configuredObject); 9 | } 10 | 11 | @Override 12 | protected C configure() { 13 | return super.configure(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/HttpRequestMapper.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import org.springframework.core.io.buffer.DataBuffer; 4 | import org.springframework.http.HttpHeaders; 5 | import org.springframework.http.HttpMethod; 6 | import org.springframework.http.ReactiveHttpOutputMessage; 7 | import org.springframework.http.server.reactive.ServerHttpRequest; 8 | import org.springframework.web.reactive.function.BodyInserter; 9 | import reactor.core.publisher.Flux; 10 | 11 | import java.net.URI; 12 | 13 | import static org.springframework.web.reactive.function.BodyInserters.fromDataBuffers; 14 | 15 | class HttpRequestMapper { 16 | 17 | URI extractUri(ServerHttpRequest request) { 18 | return request.getURI(); 19 | } 20 | 21 | HttpMethod extractMethod(ServerHttpRequest request) { 22 | return request.getMethod(); 23 | } 24 | 25 | HttpHeaders extractHeaders(ServerHttpRequest request) { 26 | return request.getHeaders(); 27 | } 28 | 29 | BodyInserter, ReactiveHttpOutputMessage> extractBody(ServerHttpRequest request) { 30 | return fromDataBuffers(request.getBody()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/HttpResponseMapper.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import org.springframework.core.io.buffer.DataBuffer; 4 | import org.springframework.http.server.reactive.ServerHttpResponse; 5 | import org.springframework.web.reactive.function.client.ClientResponse; 6 | import reactor.core.publisher.Mono; 7 | 8 | class HttpResponseMapper { 9 | 10 | Mono map(ClientResponse clientResponse, ServerHttpResponse response) { 11 | setStatus(clientResponse, response); 12 | setHeaders(clientResponse, response); 13 | return setBody(clientResponse, response); 14 | } 15 | 16 | private void setStatus(ClientResponse clientResponse, ServerHttpResponse response) { 17 | response.setStatusCode(clientResponse.statusCode()); 18 | } 19 | 20 | private void setHeaders(ClientResponse clientResponse, ServerHttpResponse response) { 21 | clientResponse.headers().asHttpHeaders().forEach((name, values) -> values.forEach(value -> response.getHeaders().put(name, values))); 22 | } 23 | 24 | private Mono setBody(ClientResponse clientResponse, ServerHttpResponse response) { 25 | Mono body = clientResponse.bodyToMono(byte[].class) 26 | .map(bytes -> response.bufferFactory().wrap(bytes)); 27 | return response.writeWith(body); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/ReactorClientHttpConnectorCreator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import io.netty.handler.timeout.ReadTimeoutHandler; 4 | import io.netty.handler.timeout.WriteTimeoutHandler; 5 | import org.springframework.http.client.reactive.ClientHttpConnector; 6 | import org.springframework.http.client.reactive.ReactorClientHttpConnector; 7 | import reactor.netty.http.client.HttpClient; 8 | 9 | import static com.github.mkopylec.charon.forwarding.Utils.toMillis; 10 | import static io.netty.channel.ChannelOption.CONNECT_TIMEOUT_MILLIS; 11 | import static java.util.concurrent.TimeUnit.MILLISECONDS; 12 | import static reactor.netty.http.client.HttpClient.create; 13 | 14 | class ReactorClientHttpConnectorCreator implements ClientHttpConnectorCreator { 15 | 16 | private HttpClient httpClient; 17 | 18 | ReactorClientHttpConnectorCreator() { 19 | httpClient = create().followRedirect(false); 20 | } 21 | 22 | @Override 23 | public ClientHttpConnector createConnector(TimeoutConfiguration configuration) { 24 | return new ReactorClientHttpConnector(httpClient.option(CONNECT_TIMEOUT_MILLIS, toMillis(configuration.getConnection())) 25 | .doOnConnected(connection -> connection 26 | .addHandlerLast(new ReadTimeoutHandler(configuration.getRead().toMillis(), MILLISECONDS)) 27 | .addHandlerLast(new WriteTimeoutHandler(configuration.getWrite().toMillis(), MILLISECONDS)))); 28 | } 29 | 30 | void setHttpClient(HttpClient httpClient) { 31 | this.httpClient = httpClient; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/ReactorClientHttpConnectorCreatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import reactor.netty.http.client.HttpClient; 4 | 5 | public class ReactorClientHttpConnectorCreatorConfigurer extends ClientHttpConnectorCreatorConfigurer { 6 | 7 | private ReactorClientHttpConnectorCreatorConfigurer() { 8 | super(new ReactorClientHttpConnectorCreator()); 9 | } 10 | 11 | public static ReactorClientHttpConnectorCreatorConfigurer reactorClientHttpConnectorCreator() { 12 | return new ReactorClientHttpConnectorCreatorConfigurer(); 13 | } 14 | 15 | public ReactorClientHttpConnectorCreatorConfigurer httpClient(HttpClient httpClient) { 16 | configuredObject.setHttpClient(httpClient); 17 | return this; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/WebClientConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import com.github.mkopylec.charon.configuration.Configurer; 4 | import org.springframework.web.reactive.function.client.ExchangeFilterFunction; 5 | import org.springframework.web.reactive.function.client.ExchangeStrategies; 6 | 7 | import java.util.List; 8 | 9 | public class WebClientConfigurer extends Configurer { 10 | 11 | private WebClientConfigurer() { 12 | super(new WebClientConfiguration()); 13 | } 14 | 15 | public static WebClientConfigurer webClient() { 16 | return new WebClientConfigurer(); 17 | } 18 | 19 | public WebClientConfigurer set(TimeoutConfigurer timeoutConfigurer) { 20 | configuredObject.setTimeoutConfiguration(timeoutConfigurer.configure()); 21 | return this; 22 | } 23 | 24 | public WebClientConfigurer set(ClientHttpConnectorCreatorConfigurer clientHttpConnectorCreatorConfigurer) { 25 | configuredObject.setClientHttpConnectorCreator(clientHttpConnectorCreatorConfigurer.configure()); 26 | return this; 27 | } 28 | 29 | public WebClientConfigurer set(List exchangeFilterFunctions) { 30 | configuredObject.setExchangeFilterFunctions(exchangeFilterFunctions); 31 | return this; 32 | } 33 | 34 | public WebClientConfigurer set(ExchangeStrategies exchangeStrategies) { 35 | configuredObject.setExchangeStrategies(exchangeStrategies); 36 | return this; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/WebClientProvider.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import com.github.mkopylec.charon.configuration.RequestMappingConfiguration; 4 | import org.springframework.web.reactive.function.client.WebClient; 5 | 6 | import java.util.concurrent.ConcurrentHashMap; 7 | import java.util.concurrent.ConcurrentMap; 8 | 9 | class WebClientProvider { 10 | 11 | private ConcurrentMap restTemplates; 12 | 13 | WebClientProvider() { 14 | restTemplates = new ConcurrentHashMap<>(); 15 | } 16 | 17 | WebClient getWebClient(RequestMappingConfiguration configuration) { 18 | return restTemplates.computeIfAbsent(configuration.getName(), mappingName -> configuration.getWebClientConfiguration().configure(configuration)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/HttpRequestExecution.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors; 2 | 3 | import io.netty.channel.ChannelException; 4 | import org.springframework.web.reactive.function.client.ExchangeFunction; 5 | import reactor.core.publisher.Mono; 6 | 7 | import static com.github.mkopylec.charon.forwarding.RequestForwardingException.requestForwardingError; 8 | 9 | public class HttpRequestExecution { 10 | 11 | private String mappingName; 12 | private ExchangeFunction exchange; 13 | 14 | HttpRequestExecution(String mappingName, ExchangeFunction exchange) { 15 | this.mappingName = mappingName; 16 | this.exchange = exchange; 17 | } 18 | 19 | public Mono execute(HttpRequest request) { 20 | return exchange.exchange(request) 21 | .map(response -> response instanceof HttpResponse 22 | ? (HttpResponse) response 23 | : new HttpResponse(response, request)) 24 | .doOnError(ChannelException.class, e -> { 25 | throw requestForwardingError("Error executing request: " + e.getMessage(), e); 26 | }); 27 | } 28 | 29 | public String getMappingName() { 30 | return mappingName; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/HttpRequestInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors; 2 | 3 | import org.springframework.web.reactive.function.client.ClientRequest; 4 | import org.springframework.web.reactive.function.client.ClientResponse; 5 | import org.springframework.web.reactive.function.client.ExchangeFilterFunction; 6 | import org.springframework.web.reactive.function.client.ExchangeFunction; 7 | import reactor.core.publisher.Mono; 8 | 9 | public class HttpRequestInterceptor implements ExchangeFilterFunction { 10 | 11 | private String mappingName; 12 | private RequestForwardingInterceptor requestForwardingInterceptor; 13 | 14 | public HttpRequestInterceptor(String mappingName, RequestForwardingInterceptor requestForwardingInterceptor) { 15 | this.mappingName = mappingName; 16 | this.requestForwardingInterceptor = requestForwardingInterceptor; 17 | } 18 | 19 | @Override 20 | public Mono filter(ClientRequest request, ExchangeFunction exchange) { 21 | HttpRequest httpRequest = request instanceof HttpRequest 22 | ? (HttpRequest) request 23 | : new HttpRequest(request); 24 | HttpRequestExecution requestExecution = exchange instanceof HttpRequestExecution 25 | ? (HttpRequestExecution) exchange 26 | : new HttpRequestExecution(mappingName, exchange); 27 | return requestForwardingInterceptor.forward(httpRequest, requestExecution).cast(ClientResponse.class); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/RequestForwardingInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors; 2 | 3 | import com.github.mkopylec.charon.configuration.Valid; 4 | import reactor.core.publisher.Mono; 5 | 6 | public interface RequestForwardingInterceptor extends Valid, Comparable { 7 | 8 | Mono forward(HttpRequest request, HttpRequestExecution execution); 9 | 10 | RequestForwardingInterceptorType getType(); 11 | 12 | @Override 13 | default int compareTo(RequestForwardingInterceptor requestForwardingInterceptor) { 14 | return getType().compareTo(requestForwardingInterceptor.getType()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/RequestForwardingInterceptorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors; 2 | 3 | import com.github.mkopylec.charon.configuration.Configurer; 4 | 5 | public abstract class RequestForwardingInterceptorConfigurer extends Configurer { 6 | 7 | protected RequestForwardingInterceptorConfigurer(I configuredObject) { 8 | super(configuredObject); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/async/AsynchronousForwarderConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.async; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class AsynchronousForwarderConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private AsynchronousForwarderConfigurer() { 8 | super(new AsynchronousForwarder()); 9 | } 10 | 11 | public static AsynchronousForwarderConfigurer asynchronousForwarder() { 12 | return new AsynchronousForwarderConfigurer(); 13 | } 14 | 15 | public AsynchronousForwarderConfigurer set(ThreadPoolConfigurer threadPoolConfigurer) { 16 | configuredObject.setThreadPool(threadPoolConfigurer.configure()); 17 | return this; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/log/ForwardingLogger.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.log; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | import org.springframework.http.HttpMethod; 9 | import reactor.core.publisher.Mono; 10 | 11 | import java.net.URI; 12 | 13 | import static org.slf4j.LoggerFactory.getLogger; 14 | 15 | class ForwardingLogger extends CommonForwardingLogger implements RequestForwardingInterceptor { 16 | 17 | private static final Logger log = getLogger(ForwardingLogger.class); 18 | 19 | ForwardingLogger() { 20 | super(log); 21 | } 22 | 23 | @Override 24 | public Mono forward(HttpRequest request, HttpRequestExecution execution) { 25 | HttpMethod originalMethod = request.method(); 26 | URI originalUri = request.url(); 27 | String mappingName = execution.getMappingName(); 28 | return execution.execute(request) 29 | .doOnSuccess(response -> logForwardingResult(response.statusCode(), originalMethod, request.method(), originalUri, request.url(), mappingName)) 30 | .doOnError(RuntimeException.class, e -> { 31 | logForwardingError(e, originalMethod, originalUri, mappingName); 32 | throw e; 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/log/ForwardingLoggerConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.log; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class ForwardingLoggerConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private ForwardingLoggerConfigurer() { 8 | super(new ForwardingLogger()); 9 | } 10 | 11 | public static ForwardingLoggerConfigurer forwardingLogger() { 12 | return new ForwardingLoggerConfigurer(); 13 | } 14 | 15 | public ForwardingLoggerConfigurer successLogLevel(LogLevel successLogLevel) { 16 | configuredObject.setSuccessLogLevel(successLogLevel); 17 | return this; 18 | } 19 | 20 | public ForwardingLoggerConfigurer clientErrorLogLevel(LogLevel clientErrorLogLevel) { 21 | configuredObject.setClientErrorLogLevel(clientErrorLogLevel); 22 | return this; 23 | } 24 | 25 | public ForwardingLoggerConfigurer serverErrorLogLevel(LogLevel serverErrorLogLevel) { 26 | configuredObject.setServerErrorLogLevel(serverErrorLogLevel); 27 | return this; 28 | } 29 | 30 | public ForwardingLoggerConfigurer unexpectedErrorLogLevel(LogLevel unexpectedErrorLogLevel) { 31 | configuredObject.setUnexpectedErrorLogLevel(unexpectedErrorLogLevel); 32 | return this; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/metrics/LatencyMeter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.metrics; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | import reactor.core.publisher.Mono; 9 | 10 | import static java.lang.System.nanoTime; 11 | import static org.slf4j.LoggerFactory.getLogger; 12 | 13 | class LatencyMeter extends CommonLatencyMeter implements RequestForwardingInterceptor { 14 | 15 | private static final Logger log = getLogger(LatencyMeter.class); 16 | 17 | LatencyMeter() { 18 | super(log); 19 | } 20 | 21 | @Override 22 | public Mono forward(HttpRequest request, HttpRequestExecution execution) { 23 | logStart(execution.getMappingName()); 24 | long startingTime = nanoTime(); 25 | return execution.execute(request) 26 | .doFinally(signalType -> { 27 | captureLatencyMetric(execution.getMappingName(), startingTime); 28 | logEnd(execution.getMappingName()); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/metrics/LatencyMeterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.metrics; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | import io.micrometer.core.instrument.MeterRegistry; 5 | 6 | public class LatencyMeterConfigurer extends RequestForwardingInterceptorConfigurer { 7 | 8 | private LatencyMeterConfigurer() { 9 | super(new LatencyMeter()); 10 | } 11 | 12 | public static LatencyMeterConfigurer latencyMeter() { 13 | return new LatencyMeterConfigurer(); 14 | } 15 | 16 | public LatencyMeterConfigurer meterRegistry(MeterRegistry meterRegistry) { 17 | configuredObject.setMeterRegistry(meterRegistry); 18 | return this; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/metrics/RateMeter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.metrics; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | import reactor.core.publisher.Mono; 9 | 10 | import static org.slf4j.LoggerFactory.getLogger; 11 | 12 | class RateMeter extends CommonRateMeter implements RequestForwardingInterceptor { 13 | 14 | private static final Logger log = getLogger(RateMeter.class); 15 | 16 | RateMeter() { 17 | super(log); 18 | } 19 | 20 | @Override 21 | public Mono forward(HttpRequest request, HttpRequestExecution execution) { 22 | logStart(execution.getMappingName()); 23 | return execution.execute(request) 24 | .doOnSuccess(response -> captureResponseStatusMetric(execution.getMappingName(), response.statusCode())) 25 | .doOnError(ex -> captureExceptionMetric(execution.getMappingName(), ex)) 26 | .doFinally(signalType -> logEnd(execution.getMappingName())); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/metrics/RateMeterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.metrics; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | import io.micrometer.core.instrument.MeterRegistry; 5 | 6 | public class RateMeterConfigurer extends RequestForwardingInterceptorConfigurer { 7 | 8 | private RateMeterConfigurer() { 9 | super(new RateMeter()); 10 | } 11 | 12 | public static RateMeterConfigurer rateMeter() { 13 | return new RateMeterConfigurer(); 14 | } 15 | 16 | public RateMeterConfigurer meterRegistry(MeterRegistry meterRegistry) { 17 | configuredObject.setMeterRegistry(meterRegistry); 18 | return this; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/resilience/CircuitBreakerConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.resilience; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 4 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 5 | import io.github.resilience4j.circuitbreaker.CallNotPermittedException; 6 | import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.Builder; 7 | import io.micrometer.core.instrument.MeterRegistry; 8 | 9 | import java.util.function.Function; 10 | 11 | import static io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry.of; 12 | 13 | public class CircuitBreakerConfigurer extends RequestForwardingInterceptorConfigurer { 14 | 15 | private CircuitBreakerConfigurer() { 16 | super(new CircuitBreaker()); 17 | } 18 | 19 | public static CircuitBreakerConfigurer circuitBreaker() { 20 | return new CircuitBreakerConfigurer(); 21 | } 22 | 23 | public CircuitBreakerConfigurer configuration(Builder circuitBreakerConfiguration) { 24 | configuredObject.setRegistry(of(circuitBreakerConfiguration.build())); 25 | return this; 26 | } 27 | 28 | public CircuitBreakerConfigurer fallback(Function fallback) { 29 | configuredObject.setFallback(fallback); 30 | return this; 31 | } 32 | 33 | public CircuitBreakerConfigurer meterRegistry(MeterRegistry meterRegistry) { 34 | configuredObject.setMeterRegistry(meterRegistry); 35 | return this; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/resilience/RateLimiter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.resilience; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | import reactor.core.publisher.Mono; 9 | 10 | import static io.github.resilience4j.reactor.ratelimiter.operator.RateLimiterOperator.of; 11 | import static org.slf4j.LoggerFactory.getLogger; 12 | 13 | class RateLimiter extends CommonRateLimiter implements RequestForwardingInterceptor { 14 | 15 | private static final Logger log = getLogger(RateLimiter.class); 16 | 17 | RateLimiter() { 18 | super(log); 19 | } 20 | 21 | @Override 22 | public Mono forward(HttpRequest request, HttpRequestExecution execution) { 23 | logStart(execution.getMappingName()); 24 | io.github.resilience4j.ratelimiter.RateLimiter rateLimiter = getRegistry().rateLimiter(execution.getMappingName()); 25 | setupMetrics(registry -> createMetrics(registry, execution.getMappingName())); 26 | return execution.execute(request) 27 | .transform(of(rateLimiter)) 28 | .doOnSuccess(response -> logEnd(execution.getMappingName())); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/resilience/RateLimiterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.resilience; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | import io.github.resilience4j.ratelimiter.RateLimiterConfig.Builder; 5 | import io.micrometer.core.instrument.MeterRegistry; 6 | 7 | import static io.github.resilience4j.ratelimiter.RateLimiterRegistry.of; 8 | 9 | public class RateLimiterConfigurer extends RequestForwardingInterceptorConfigurer { 10 | 11 | private RateLimiterConfigurer() { 12 | super(new RateLimiter()); 13 | } 14 | 15 | public static RateLimiterConfigurer rateLimiter() { 16 | return new RateLimiterConfigurer(); 17 | } 18 | 19 | public RateLimiterConfigurer configuration(Builder rateLimiterConfiguration) { 20 | configuredObject.setRegistry(of(rateLimiterConfiguration.build())); 21 | return this; 22 | } 23 | 24 | public RateLimiterConfigurer meterRegistry(MeterRegistry meterRegistry) { 25 | configuredObject.setMeterRegistry(meterRegistry); 26 | return this; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/resilience/Retryer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.resilience; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import io.github.resilience4j.retry.Retry; 8 | import org.slf4j.Logger; 9 | import reactor.core.publisher.Mono; 10 | 11 | import static io.github.resilience4j.reactor.retry.RetryOperator.of; 12 | import static org.slf4j.LoggerFactory.getLogger; 13 | 14 | class Retryer extends CommonRetryer implements RequestForwardingInterceptor { 15 | 16 | private static final Logger log = getLogger(Retryer.class); 17 | 18 | Retryer() { 19 | super(response -> response.statusCode().is5xxServerError(), log); 20 | } 21 | 22 | @Override 23 | public Mono forward(HttpRequest request, HttpRequestExecution execution) { 24 | logStart(execution.getMappingName()); 25 | Retry retry = getRegistry().retry(execution.getMappingName()); 26 | setupMetrics(registry -> createMetrics(registry, execution.getMappingName())); 27 | return execution.execute(request) 28 | .transform(of(retry)) 29 | .doOnSuccess(response -> logEnd(execution.getMappingName())); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/resilience/RetryerConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.resilience; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 4 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 5 | import io.github.resilience4j.retry.RetryConfig.Builder; 6 | import io.micrometer.core.instrument.MeterRegistry; 7 | 8 | import static io.github.resilience4j.retry.RetryRegistry.of; 9 | 10 | public class RetryerConfigurer extends RequestForwardingInterceptorConfigurer { 11 | 12 | private RetryerConfigurer() { 13 | super(new Retryer()); 14 | } 15 | 16 | public static RetryerConfigurer retryer() { 17 | return new RetryerConfigurer(); 18 | } 19 | 20 | public RetryerConfigurer configuration(Builder retryConfiguration) { 21 | configuredObject.setRegistry(of(retryConfiguration.build())); 22 | return this; 23 | } 24 | 25 | public RetryerConfigurer meterRegistry(MeterRegistry meterRegistry) { 26 | configuredObject.setMeterRegistry(meterRegistry); 27 | return this; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RegexRequestPathRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | import reactor.core.publisher.Mono; 9 | 10 | import static org.slf4j.LoggerFactory.getLogger; 11 | 12 | class RegexRequestPathRewriter extends CommonRegexRequestPathRewriter implements RequestForwardingInterceptor { 13 | 14 | private static final Logger log = getLogger(RegexRequestPathRewriter.class); 15 | 16 | RegexRequestPathRewriter() { 17 | super(log); 18 | } 19 | 20 | @Override 21 | public Mono forward(HttpRequest request, HttpRequestExecution execution) { 22 | logStart(execution.getMappingName()); 23 | rewritePath(request.url(), request::setUrl); 24 | return execution.execute(request) 25 | .doOnSuccess(response -> logEnd(execution.getMappingName())); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RegexRequestPathRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class RegexRequestPathRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private RegexRequestPathRewriterConfigurer() { 8 | super(new RegexRequestPathRewriter()); 9 | } 10 | 11 | public static RegexRequestPathRewriterConfigurer regexRequestPathRewriter() { 12 | return new RegexRequestPathRewriterConfigurer(); 13 | } 14 | 15 | public RegexRequestPathRewriterConfigurer paths(String incomingRequestPathRegex, String outgoingRequestPathTemplate) { 16 | configuredObject.setPaths(incomingRequestPathRegex, outgoingRequestPathTemplate); 17 | return this; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RemovingResponseCookiesRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | import org.springframework.http.HttpHeaders; 9 | import reactor.core.publisher.Mono; 10 | 11 | import static org.slf4j.LoggerFactory.getLogger; 12 | import static org.springframework.http.HttpHeaders.SET_COOKIE; 13 | import static org.springframework.http.HttpHeaders.SET_COOKIE2; 14 | 15 | class RemovingResponseCookiesRewriter extends CommonRemovingResponseCookiesRewriter implements RequestForwardingInterceptor { 16 | 17 | private static final Logger log = getLogger(RemovingResponseCookiesRewriter.class); 18 | 19 | RemovingResponseCookiesRewriter() { 20 | super(log); 21 | } 22 | 23 | @Override 24 | public Mono forward(HttpRequest request, HttpRequestExecution execution) { 25 | logStart(execution.getMappingName()); 26 | return execution.execute(request) 27 | .doOnSuccess(response -> { 28 | HttpHeaders headers = response.headers().asHttpHeaders(); 29 | removeCookies(headers, SET_COOKIE, response::setHeaders); 30 | removeCookies(headers, SET_COOKIE2, response::setHeaders); 31 | logEnd(execution.getMappingName()); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RemovingResponseCookiesRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class RemovingResponseCookiesRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private RemovingResponseCookiesRewriterConfigurer() { 8 | super(new RemovingResponseCookiesRewriter()); 9 | } 10 | 11 | public static RemovingResponseCookiesRewriterConfigurer removingResponseCookiesRewriter() { 12 | return new RemovingResponseCookiesRewriterConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RequestHostHeaderRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | import reactor.core.publisher.Mono; 9 | 10 | import static org.slf4j.LoggerFactory.getLogger; 11 | 12 | class RequestHostHeaderRewriter extends CommonRequestHostHeaderRewriter implements RequestForwardingInterceptor { 13 | 14 | private static final Logger log = getLogger(RequestHostHeaderRewriter.class); 15 | 16 | RequestHostHeaderRewriter() { 17 | super(log); 18 | } 19 | 20 | @Override 21 | public Mono forward(HttpRequest request, HttpRequestExecution execution) { 22 | logStart(execution.getMappingName()); 23 | rewriteHeaders(request.headers(), request.url(), request::setHeaders); 24 | return execution.execute(request) 25 | .doOnSuccess(response -> logEnd(execution.getMappingName())); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RequestHostHeaderRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class RequestHostHeaderRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private RequestHostHeaderRewriterConfigurer() { 8 | super(new RequestHostHeaderRewriter()); 9 | } 10 | 11 | public static RequestHostHeaderRewriterConfigurer requestHostHeaderRewriter() { 12 | return new RequestHostHeaderRewriterConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RequestProtocolHeadersRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | import reactor.core.publisher.Mono; 9 | 10 | import static org.slf4j.LoggerFactory.getLogger; 11 | 12 | class RequestProtocolHeadersRewriter extends CommonRequestProtocolHeadersRewriter implements RequestForwardingInterceptor { 13 | 14 | private static final Logger log = getLogger(RequestProtocolHeadersRewriter.class); 15 | 16 | RequestProtocolHeadersRewriter() { 17 | super(log); 18 | } 19 | 20 | @Override 21 | public Mono forward(HttpRequest request, HttpRequestExecution execution) { 22 | logStart(execution.getMappingName()); 23 | rewriteHeaders(request.headers(), request::setHeaders); 24 | return execution.execute(request) 25 | .doOnSuccess(response -> logEnd(execution.getMappingName())); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RequestProtocolHeadersRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class RequestProtocolHeadersRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private RequestProtocolHeadersRewriterConfigurer() { 8 | super(new RequestProtocolHeadersRewriter()); 9 | } 10 | 11 | public static RequestProtocolHeadersRewriterConfigurer requestProtocolHeadersRewriter() { 12 | return new RequestProtocolHeadersRewriterConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RequestProxyHeadersRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | import reactor.core.publisher.Mono; 9 | 10 | import static org.slf4j.LoggerFactory.getLogger; 11 | 12 | class RequestProxyHeadersRewriter extends CommonRequestProxyHeadersRewriter implements RequestForwardingInterceptor { 13 | 14 | private static final Logger log = getLogger(RequestProxyHeadersRewriter.class); 15 | 16 | RequestProxyHeadersRewriter() { 17 | super(log); 18 | } 19 | 20 | @Override 21 | public Mono forward(HttpRequest request, HttpRequestExecution execution) { 22 | logStart(execution.getMappingName()); 23 | rewriteHeaders(request.headers(), request.url(), request::setHeaders); 24 | return execution.execute(request) 25 | .doOnSuccess(response -> logEnd(execution.getMappingName())); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RequestProxyHeadersRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class RequestProxyHeadersRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private RequestProxyHeadersRewriterConfigurer() { 8 | super(new RequestProxyHeadersRewriter()); 9 | } 10 | 11 | public static RequestProxyHeadersRewriterConfigurer requestProxyHeadersRewriter() { 12 | return new RequestProxyHeadersRewriterConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RequestServerNameRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | import reactor.core.publisher.Mono; 9 | 10 | import static org.slf4j.LoggerFactory.getLogger; 11 | 12 | class RequestServerNameRewriter extends CommonRequestServerNameRewriter implements RequestForwardingInterceptor { 13 | 14 | private static final Logger log = getLogger(RequestServerNameRewriter.class); 15 | 16 | RequestServerNameRewriter() { 17 | super(log); 18 | } 19 | 20 | @Override 21 | public Mono forward(HttpRequest request, HttpRequestExecution execution) { 22 | logStart(execution.getMappingName()); 23 | BodilessHttpRequest bodilessRequest = new BodilessHttpRequest(request.method(), request.url(), request.headers()); 24 | rewriteServerName(bodilessRequest, request::setUrl); 25 | return execution.execute(request) 26 | .doOnSuccess(response -> logEnd(execution.getMappingName())); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RequestServerNameRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | import java.util.List; 6 | 7 | import static java.util.Arrays.asList; 8 | 9 | public class RequestServerNameRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 10 | 11 | private RequestServerNameRewriterConfigurer() { 12 | super(new RequestServerNameRewriter()); 13 | } 14 | 15 | public static RequestServerNameRewriterConfigurer requestServerNameRewriter() { 16 | return new RequestServerNameRewriterConfigurer(); 17 | } 18 | 19 | public RequestServerNameRewriterConfigurer loadBalancer(LoadBalancerConfigurer loadBalancerConfigurer) { 20 | configuredObject.setLoadBalancer(loadBalancerConfigurer.configure()); 21 | return this; 22 | } 23 | 24 | public RequestServerNameRewriterConfigurer outgoingServers(String... outgoingServers) { 25 | configuredObject.setOutgoingServers(asList(outgoingServers)); 26 | return this; 27 | } 28 | 29 | public RequestServerNameRewriterConfigurer outgoingServers(List outgoingServers) { 30 | configuredObject.setOutgoingServers(outgoingServers); 31 | return this; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/ResponseProtocolHeadersRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | import reactor.core.publisher.Mono; 9 | 10 | import static org.slf4j.LoggerFactory.getLogger; 11 | 12 | class ResponseProtocolHeadersRewriter extends CommonResponseProtocolHeadersRewriter implements RequestForwardingInterceptor { 13 | 14 | private static final Logger log = getLogger(ResponseProtocolHeadersRewriter.class); 15 | 16 | ResponseProtocolHeadersRewriter() { 17 | super(log); 18 | } 19 | 20 | @Override 21 | public Mono forward(HttpRequest request, HttpRequestExecution execution) { 22 | logStart(execution.getMappingName()); 23 | return execution.execute(request) 24 | .doOnSuccess(response -> { 25 | rewriteHeaders(response.headers().asHttpHeaders(), response::setHeaders); 26 | logEnd(execution.getMappingName()); 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/ResponseProtocolHeadersRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class ResponseProtocolHeadersRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private ResponseProtocolHeadersRewriterConfigurer() { 8 | super(new ResponseProtocolHeadersRewriter()); 9 | } 10 | 11 | public static ResponseProtocolHeadersRewriterConfigurer responseProtocolHeadersRewriter() { 12 | return new ResponseProtocolHeadersRewriterConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RootPathResponseCookiesRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | import org.springframework.http.HttpHeaders; 9 | import reactor.core.publisher.Mono; 10 | 11 | import static org.slf4j.LoggerFactory.getLogger; 12 | import static org.springframework.http.HttpHeaders.SET_COOKIE; 13 | import static org.springframework.http.HttpHeaders.SET_COOKIE2; 14 | 15 | class RootPathResponseCookiesRewriter extends CommonRootPathResponseCookiesRewriter implements RequestForwardingInterceptor { 16 | 17 | private static final Logger log = getLogger(RootPathResponseCookiesRewriter.class); 18 | 19 | RootPathResponseCookiesRewriter() { 20 | super(log); 21 | } 22 | 23 | @Override 24 | public Mono forward(HttpRequest request, HttpRequestExecution execution) { 25 | logStart(execution.getMappingName()); 26 | return execution.execute(request) 27 | .doOnSuccess(response -> { 28 | HttpHeaders headers = response.headers().asHttpHeaders(); 29 | rewriteCookies(headers, SET_COOKIE, response::setHeaders); 30 | rewriteCookies(headers, SET_COOKIE2, response::setHeaders); 31 | logEnd(execution.getMappingName()); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RootPathResponseCookiesRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class RootPathResponseCookiesRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private RootPathResponseCookiesRewriterConfigurer() { 8 | super(new RootPathResponseCookiesRewriter()); 9 | } 10 | 11 | public static RootPathResponseCookiesRewriterConfigurer rootPathResponseCookiesRewriter() { 12 | return new RootPathResponseCookiesRewriterConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/BasicAuthenticator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import org.slf4j.Logger; 4 | 5 | import static com.github.mkopylec.charon.forwarding.interceptors.security.AuthenticationType.BASIC; 6 | import static org.slf4j.LoggerFactory.getLogger; 7 | 8 | class BasicAuthenticator extends Authenticator { 9 | 10 | private static final Logger log = getLogger(BasicAuthenticator.class); 11 | 12 | BasicAuthenticator() { 13 | super(log, BASIC); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/BasicAuthenticatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class BasicAuthenticatorConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private BasicAuthenticatorConfigurer() { 8 | super(new BasicAuthenticator()); 9 | } 10 | 11 | public static BasicAuthenticatorConfigurer basicAuthenticator() { 12 | return new BasicAuthenticatorConfigurer(); 13 | } 14 | 15 | public BasicAuthenticatorConfigurer userValidator(UserValidatorConfigurer userValidatorConfigurer) { 16 | configuredObject.setCredentialsValidator(userValidatorConfigurer.configure()); 17 | return this; 18 | } 19 | 20 | public BasicAuthenticatorConfigurer realm(String realm) { 21 | configuredObject.setRealm(realm); 22 | return this; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/BearerAuthenticator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import org.slf4j.Logger; 4 | 5 | import static com.github.mkopylec.charon.forwarding.interceptors.security.AuthenticationType.BEARER; 6 | import static org.slf4j.LoggerFactory.getLogger; 7 | 8 | class BearerAuthenticator extends Authenticator { 9 | 10 | private static final Logger log = getLogger(BearerAuthenticator.class); 11 | 12 | BearerAuthenticator() { 13 | super(log, BEARER); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/BearerAuthenticatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class BearerAuthenticatorConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private BearerAuthenticatorConfigurer() { 8 | super(new BearerAuthenticator()); 9 | } 10 | 11 | public static BearerAuthenticatorConfigurer bearerAuthenticator() { 12 | return new BearerAuthenticatorConfigurer(); 13 | } 14 | 15 | public BearerAuthenticatorConfigurer tokenValidator(TokenValidatorConfigurer tokenValidatorConfigurer) { 16 | configuredObject.setCredentialsValidator(tokenValidatorConfigurer.configure()); 17 | return this; 18 | } 19 | 20 | public BearerAuthenticatorConfigurer realm(String realm) { 21 | configuredObject.setRealm(realm); 22 | return this; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/CredentialsValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import com.github.mkopylec.charon.configuration.Valid; 4 | import reactor.core.publisher.Mono; 5 | 6 | interface CredentialsValidator extends Valid { 7 | 8 | Mono validate(String credentials); 9 | } 10 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/CredentialsValidatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import com.github.mkopylec.charon.configuration.Configurer; 4 | 5 | abstract class CredentialsValidatorConfigurer extends Configurer { 6 | 7 | CredentialsValidatorConfigurer(V configuredObject) { 8 | super(configuredObject); 9 | } 10 | 11 | @Override 12 | protected V configure() { 13 | return super.configure(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/InMemoryTokenValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | import java.util.List; 6 | 7 | import static java.util.Collections.emptyList; 8 | import static org.apache.commons.collections4.ListUtils.emptyIfNull; 9 | import static reactor.core.publisher.Mono.just; 10 | 11 | class InMemoryTokenValidator implements TokenValidator { 12 | 13 | private List validTokens; 14 | 15 | InMemoryTokenValidator() { 16 | validTokens = emptyList(); 17 | } 18 | 19 | @Override 20 | public Mono validate(String token) { 21 | return just(validTokens.contains(token)); 22 | } 23 | 24 | void setValidTokens(List validTokens) { 25 | this.validTokens = emptyIfNull(validTokens); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/InMemoryTokenValidatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import java.util.List; 4 | 5 | import static java.util.Arrays.asList; 6 | 7 | public class InMemoryTokenValidatorConfigurer extends TokenValidatorConfigurer { 8 | 9 | private InMemoryTokenValidatorConfigurer() { 10 | super(new InMemoryTokenValidator()); 11 | } 12 | 13 | public static InMemoryTokenValidatorConfigurer inMemoryTokenValidator() { 14 | return new InMemoryTokenValidatorConfigurer(); 15 | } 16 | 17 | public InMemoryTokenValidatorConfigurer validTokens(String... validTokens) { 18 | configuredObject.setValidTokens(asList(validTokens)); 19 | return this; 20 | } 21 | 22 | public InMemoryTokenValidatorConfigurer validTokens(List validTokens) { 23 | configuredObject.setValidTokens(validTokens); 24 | return this; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/InMemoryUserValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import static reactor.core.publisher.Mono.just; 9 | 10 | class InMemoryUserValidator implements UserValidator { 11 | 12 | private List validUsers; 13 | 14 | InMemoryUserValidator() { 15 | validUsers = new ArrayList<>(); 16 | } 17 | 18 | @Override 19 | public Mono validate(User user) { 20 | return just(validUsers.contains(user)); 21 | } 22 | 23 | void addValidUser(User validUser) { 24 | validUsers.add(validUser); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/InMemoryUserValidatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | public class InMemoryUserValidatorConfigurer extends UserValidatorConfigurer { 4 | 5 | private InMemoryUserValidatorConfigurer() { 6 | super(new InMemoryUserValidator()); 7 | } 8 | 9 | public static InMemoryUserValidatorConfigurer inMemoryUserValidator() { 10 | return new InMemoryUserValidatorConfigurer(); 11 | } 12 | 13 | public InMemoryUserValidatorConfigurer validUser(String name, String password) { 14 | User user = new User(name, password); 15 | configuredObject.addValidUser(user); 16 | return this; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/TokenValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | public interface TokenValidator extends CredentialsValidator { 6 | 7 | @Override 8 | Mono validate(String token); 9 | } 10 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/TokenValidatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | public abstract class TokenValidatorConfigurer extends CredentialsValidatorConfigurer { 4 | 5 | protected TokenValidatorConfigurer(V configuredObject) { 6 | super(configuredObject); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/UserValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | import static reactor.core.publisher.Mono.just; 6 | 7 | public interface UserValidator extends BasicUserValidator, CredentialsValidator { 8 | 9 | @Override 10 | default Mono validate(String credentials) { 11 | User user = extractUser(credentials); 12 | return user.isEmpty() ? just(false) : validate(user); 13 | } 14 | 15 | Mono validate(User user); 16 | } 17 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/UserValidatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | public abstract class UserValidatorConfigurer extends CredentialsValidatorConfigurer { 4 | 5 | protected UserValidatorConfigurer(V configuredObject) { 6 | super(configuredObject); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | com.github.mkopylec.charon.configuration.CharonAutoConfiguration -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/AsynchronousForwardingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.AsynchronousForwardingBasicSpec 4 | 5 | class AsynchronousForwardingSpec extends AsynchronousForwardingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/BasicAuthenticationSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.BasicAuthenticationBasicSpec 4 | 5 | class BasicAuthenticationSpec extends BasicAuthenticationBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/BearerAuthenticationSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.BearerAuthenticationBasicSpec 4 | 5 | class BearerAuthenticationSpec extends BearerAuthenticationBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/CircuitBreakingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.CircuitBreakingBasicSpec 4 | 5 | class CircuitBreakingSpec extends CircuitBreakingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/CustomLoadBalancerSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.CustomLoadBalancerBasicSpec 4 | 5 | class CustomLoadBalancerSpec extends CustomLoadBalancerBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/DefaultCharonConfigurationSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.DefaultCharonConfigurationBasicSpec 4 | 5 | class DefaultCharonConfigurationSpec extends DefaultCharonConfigurationBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/DefinedProfileSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.DefinedProfileBasicSpec 4 | 5 | class DefinedProfileSpec extends DefinedProfileBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/ExchangeStrategiesSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.BasicSpec 4 | 5 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThat 6 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThatServers 7 | import static com.github.mkopylec.charon.test.stubs.OutgoingServersStubs.outgoingServers 8 | import static org.springframework.http.HttpMethod.GET 9 | import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR 10 | import static org.springframework.http.HttpStatus.OK 11 | 12 | class ExchangeStrategiesSpec extends BasicSpec { 13 | 14 | def "Should fail to forward request when response size is larger than exchange strategy allows"() { 15 | given: 16 | outgoingServers(localhost8080, localhost8081) 17 | .stubResponse(OK, 'to large response body') 18 | 19 | when: 20 | def response = http.sendRequest(GET, '/exchange/strategies') 21 | 22 | then: 23 | assertThat(response) 24 | .hasStatus(INTERNAL_SERVER_ERROR) 25 | assertThatServers(localhost8080, localhost8081) 26 | .haveReceivedRequest(GET, '/exchange/strategies') 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/LatencyMeteringSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.LatencyMeteringBasicSpec 4 | 5 | class LatencyMeteringSpec extends LatencyMeteringBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/MappingResolvingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.MappingResolvingBasicSpec 4 | 5 | class MappingResolvingSpec extends MappingResolvingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/RateLimitingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RateLimitingBasicSpec 4 | 5 | class RateLimitingSpec extends RateLimitingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/RateMeteringSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RateMeteringBasicSpec 4 | 5 | class RateMeteringSpec extends RateMeteringBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/RegexRequestPathRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RegexRequestPathRewritingBasicSpec 4 | 5 | class RegexRequestPathRewritingSpec extends RegexRequestPathRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/RemovingResponseCookiesRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RemovingResponseCookiesRewritingBasicSpec 4 | 5 | class RemovingResponseCookiesRewritingSpec extends RemovingResponseCookiesRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/RequestBodyRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RequestBodyRewritingBasicSpec 4 | 5 | class RequestBodyRewritingSpec extends RequestBodyRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/RequestForwardingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RequestForwardingBasicSpec 4 | 5 | class RequestForwardingSpec extends RequestForwardingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/RequestHostHeaderRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RequestHostHeaderRewritingBasicSpec 4 | 5 | class RequestHostHeaderRewritingSpec extends RequestHostHeaderRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/RequestProtocolHeadersRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RequestProtocolHeadersRewritingBasicSpec 4 | 5 | class RequestProtocolHeadersRewritingSpec extends RequestProtocolHeadersRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/RequestProxyHeadersRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RequestProxyHeadersRewritingBasicSpec 4 | 5 | class RequestProxyHeadersRewritingSpec extends RequestProxyHeadersRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/ResponseBodyRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.ResponseBodyRewritingBasicSpec 4 | 5 | class ResponseBodyRewritingSpec extends ResponseBodyRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/ResponseForwardingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.ResponseForwardingBasicSpec 4 | 5 | class ResponseForwardingSpec extends ResponseForwardingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/ResponseProtocolHeadersRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.ResponseProtocolHeadersRewritingBasicSpec 4 | 5 | class ResponseProtocolHeadersRewritingSpec extends ResponseProtocolHeadersRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/RetryingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RetryingBasicSpec 4 | 5 | class RetryingSpec extends RetryingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/RootPathResponseCookiesRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RootPathResponseCookiesRewritingBasicSpec 4 | 5 | class RootPathResponseCookiesRewritingSpec extends RootPathResponseCookiesRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/groovy/com/github/mkopylec/charon/test/TimeoutSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.TimeoutBasicSpec 4 | 5 | class TimeoutSpec extends TimeoutBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/java/com/github/mkopylec/charon/test/CircuitBreakerFallback.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 4 | import io.github.resilience4j.circuitbreaker.CallNotPermittedException; 5 | 6 | import java.util.function.Function; 7 | 8 | import static org.springframework.http.HttpStatus.CREATED; 9 | 10 | class CircuitBreakerFallback implements Function { 11 | 12 | @Override 13 | public HttpResponse apply(CallNotPermittedException e) { 14 | return new HttpResponse(CREATED); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/java/com/github/mkopylec/charon/test/DataBufferSizeSetter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import org.springframework.http.codec.HttpMessageReader; 4 | import org.springframework.http.codec.HttpMessageWriter; 5 | import org.springframework.web.reactive.function.client.ExchangeStrategies; 6 | 7 | import java.util.List; 8 | 9 | import static org.springframework.web.reactive.function.client.ExchangeStrategies.builder; 10 | 11 | class DataBufferSizeSetter implements ExchangeStrategies { 12 | 13 | private ExchangeStrategies delegate = builder() 14 | .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(1)) 15 | .build(); 16 | 17 | @Override 18 | public List> messageReaders() { 19 | return delegate.messageReaders(); 20 | } 21 | 22 | @Override 23 | public List> messageWriters() { 24 | return delegate.messageWriters(); 25 | } 26 | 27 | @Override 28 | public Builder mutate() { 29 | return delegate.mutate(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/java/com/github/mkopylec/charon/test/ExceptionThrower.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | import reactor.core.publisher.Mono; 9 | 10 | import static org.slf4j.LoggerFactory.getLogger; 11 | import static reactor.core.publisher.Mono.error; 12 | 13 | class ExceptionThrower extends CommonExceptionThrower implements RequestForwardingInterceptor { 14 | 15 | private static final Logger log = getLogger(ExceptionThrower.class); 16 | 17 | ExceptionThrower() { 18 | super(log); 19 | } 20 | 21 | @Override 22 | public Mono forward(HttpRequest request, HttpRequestExecution execution) { 23 | logStart(execution.getMappingName()); 24 | return error(createFailure()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/java/com/github/mkopylec/charon/test/ExceptionThrowerConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | class ExceptionThrowerConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private ExceptionThrowerConfigurer() { 8 | super(new ExceptionThrower()); 9 | } 10 | 11 | static ExceptionThrowerConfigurer exceptionThrower() { 12 | return new ExceptionThrowerConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/java/com/github/mkopylec/charon/test/LowestPortLoadBalancer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.rewrite.BodilessHttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.rewrite.LoadBalancer; 5 | 6 | import java.net.URI; 7 | import java.util.List; 8 | 9 | import static java.util.Comparator.comparingInt; 10 | 11 | class LowestPortLoadBalancer implements LoadBalancer { 12 | 13 | @Override 14 | public URI chooseServer(List servers, BodilessHttpRequest request) { 15 | return servers.stream() 16 | .min(comparingInt(URI::getPort)) 17 | .get(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/java/com/github/mkopylec/charon/test/LowestPortLoadBalancerConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.rewrite.LoadBalancerConfigurer; 4 | 5 | public class LowestPortLoadBalancerConfigurer extends LoadBalancerConfigurer { 6 | 7 | private LowestPortLoadBalancerConfigurer() { 8 | super(new LowestPortLoadBalancer()); 9 | } 10 | 11 | public static LowestPortLoadBalancerConfigurer lowestPortLoadBalancer() { 12 | return new LowestPortLoadBalancerConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/java/com/github/mkopylec/charon/test/RequestBodyRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | import reactor.core.publisher.Mono; 9 | 10 | import static org.slf4j.LoggerFactory.getLogger; 11 | import static org.springframework.web.reactive.function.BodyInserters.fromValue; 12 | 13 | class RequestBodyRewriter extends CommonRequestBodyRewriter implements RequestForwardingInterceptor { 14 | 15 | private static final Logger log = getLogger(RequestBodyRewriter.class); 16 | 17 | RequestBodyRewriter() { 18 | super(log); 19 | } 20 | 21 | @Override 22 | public Mono forward(HttpRequest request, HttpRequestExecution execution) { 23 | logStart(execution.getMappingName()); 24 | rewriteRequest(request); 25 | return execution.execute(request) 26 | .doOnSuccess(response -> logEnd(execution.getMappingName())); 27 | } 28 | 29 | private void rewriteRequest(HttpRequest request) { 30 | String rewrittenBody = "Rewritten request body"; 31 | request.setBody(fromValue(rewrittenBody)); 32 | log.debug("Request body rewritten to '{}'", rewrittenBody); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/java/com/github/mkopylec/charon/test/RequestBodyRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | class RequestBodyRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private RequestBodyRewriterConfigurer() { 8 | super(new RequestBodyRewriter()); 9 | } 10 | 11 | static RequestBodyRewriterConfigurer requestBodyRewriter() { 12 | return new RequestBodyRewriterConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/java/com/github/mkopylec/charon/test/ResponseBodyRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | class ResponseBodyRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private ResponseBodyRewriterConfigurer() { 8 | super(new ResponseBodyRewriter()); 9 | } 10 | 11 | static ResponseBodyRewriterConfigurer responseBodyRewriter() { 12 | return new ResponseBodyRewriterConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/java/com/github/mkopylec/charon/test/RetryAttemptsResponseHeaderSetter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import org.springframework.web.reactive.function.client.ClientRequest; 4 | import org.springframework.web.reactive.function.client.ClientResponse; 5 | import org.springframework.web.reactive.function.client.ExchangeFilterFunction; 6 | import org.springframework.web.reactive.function.client.ExchangeFunction; 7 | import reactor.core.publisher.Mono; 8 | 9 | import java.util.concurrent.atomic.AtomicInteger; 10 | 11 | import static java.lang.String.valueOf; 12 | 13 | class RetryAttemptsResponseHeaderSetter implements ExchangeFilterFunction { 14 | 15 | private AtomicInteger attempt = new AtomicInteger(); // Works only because of @DirtiesContext 16 | 17 | @Override 18 | public Mono filter(ClientRequest request, ExchangeFunction next) { 19 | // Exceptions thrown before return WILL NOT trigger circuit breaker nor retryer. 20 | return next.exchange(request) 21 | .map(response -> response.mutate() 22 | .headers(httpHeaders -> httpHeaders.set("Retry-Attempts", valueOf(attempt.incrementAndGet()))) 23 | .build()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /charon-spring-webflux/src/test/resources/application.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | level: 3 | com.github.mkopylec.charon: trace 4 | 5 | server: 6 | error: 7 | include-message: always 8 | -------------------------------------------------------------------------------- /charon-spring-webmvc/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'jacoco' 2 | 3 | dependencies { 4 | api project(':charon-common').sourceSets.main.output 5 | api group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion 6 | api group: 'com.squareup.okhttp3', name: 'okhttp', version: okHttpVersion 7 | 8 | testImplementation project(':charon-test') 9 | } 10 | 11 | jacoco { 12 | toolVersion = '0.8.12' 13 | } 14 | 15 | jacocoTestReport { 16 | sourceSets project(':charon-common').sourceSets.main 17 | reports { 18 | xml.required = true 19 | } 20 | } 21 | 22 | jar { 23 | from project(':charon-common').sourceSets.main.output 24 | } 25 | 26 | task sourceJar(type: Jar) { 27 | archiveClassifier.set('sources') 28 | from sourceSets.main.allSource 29 | from project(':charon-common').sourceSets.main.allSource 30 | } 31 | 32 | task javadocJar(type: Jar) { 33 | archiveClassifier.set('javadoc') 34 | from javadoc 35 | } 36 | 37 | artifacts { 38 | archives sourceJar 39 | } 40 | 41 | publishing { 42 | publications { 43 | charonSpringWebmvc(MavenPublication) { 44 | from components.java 45 | artifact sourceJar 46 | artifact javadocJar 47 | pom pomContent 48 | } 49 | } 50 | } 51 | 52 | signing { 53 | if (project.ext.has('signArtifacts')) { 54 | sign publishing.publications.charonSpringWebmvc 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/configuration/CharonAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.configuration; 2 | 3 | import com.github.mkopylec.charon.forwarding.ReverseProxyFilter; 4 | import org.slf4j.Logger; 5 | import org.springframework.beans.factory.ObjectProvider; 6 | import org.springframework.boot.autoconfigure.AutoConfiguration; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 9 | import org.springframework.context.annotation.Bean; 10 | 11 | import static com.github.mkopylec.charon.configuration.CharonConfigurer.charonConfiguration; 12 | import static org.slf4j.LoggerFactory.getLogger; 13 | 14 | @AutoConfiguration 15 | @ConditionalOnBean(CharonConfigurer.class) 16 | class CharonAutoConfiguration { 17 | 18 | private static final Logger log = getLogger(CharonAutoConfiguration.class); 19 | 20 | @Bean 21 | @ConditionalOnMissingBean 22 | ReverseProxyFilter reverseProxyFilter(ObjectProvider charonConfigurer) { 23 | CharonConfigurer configurer = charonConfigurer.getIfAvailable(); 24 | if (configurer == null) { 25 | log.warn("No Charon configuration detected, all incoming requests will be handled locally"); 26 | configurer = charonConfiguration(); 27 | } 28 | CharonConfiguration configuration = configurer.configure(); 29 | return new ReverseProxyFilter(configuration.getFilterOrder(), configuration.getRequestMappingConfigurations()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/ClientHttpRequestFactoryCreator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import com.github.mkopylec.charon.configuration.Valid; 4 | import org.springframework.http.client.ClientHttpRequestFactory; 5 | 6 | public interface ClientHttpRequestFactoryCreator extends Valid { 7 | 8 | ClientHttpRequestFactory createRequestFactory(TimeoutConfiguration configuration); 9 | } 10 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/ClientHttpRequestFactoryCreatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import com.github.mkopylec.charon.configuration.Configurer; 4 | 5 | public abstract class ClientHttpRequestFactoryCreatorConfigurer extends Configurer { 6 | 7 | protected ClientHttpRequestFactoryCreatorConfigurer(C configuredObject) { 8 | super(configuredObject); 9 | } 10 | 11 | @Override 12 | protected C configure() { 13 | return super.configure(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/HttpResponseMapper.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import jakarta.servlet.http.HttpServletResponse; 4 | import org.springframework.http.ResponseEntity; 5 | 6 | import java.io.IOException; 7 | 8 | class HttpResponseMapper { 9 | 10 | void map(ResponseEntity responseEntity, HttpServletResponse response) throws IOException { 11 | setStatus(responseEntity, response); 12 | setHeaders(responseEntity, response); 13 | setBody(responseEntity, response); 14 | } 15 | 16 | private void setStatus(ResponseEntity responseEntity, HttpServletResponse response) { 17 | response.setStatus(responseEntity.getStatusCode().value()); 18 | } 19 | 20 | private void setHeaders(ResponseEntity responseEntity, HttpServletResponse response) { 21 | responseEntity.getHeaders().forEach((name, values) -> values.forEach(value -> response.addHeader(name, value))); 22 | } 23 | 24 | private void setBody(ResponseEntity responseEntity, HttpServletResponse response) throws IOException { 25 | if (responseEntity.getBody() != null) { 26 | response.getOutputStream().write(responseEntity.getBody()); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/NoExceptionErrorHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import org.springframework.http.client.ClientHttpResponse; 4 | import org.springframework.web.client.ResponseErrorHandler; 5 | 6 | class NoExceptionErrorHandler implements ResponseErrorHandler { 7 | 8 | @Override 9 | public boolean hasError(ClientHttpResponse response) { 10 | return false; 11 | } 12 | 13 | @Override 14 | public void handleError(ClientHttpResponse response) { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/OkClientHttpRequestFactoryCreator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import okhttp3.OkHttpClient; 4 | import okhttp3.OkHttpClient.Builder; 5 | import org.springframework.http.client.ClientHttpRequestFactory; 6 | import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; 7 | 8 | import static com.github.mkopylec.charon.forwarding.Utils.toMillis; 9 | 10 | class OkClientHttpRequestFactoryCreator implements ClientHttpRequestFactoryCreator { 11 | 12 | private OkHttpClient httpClient; 13 | 14 | OkClientHttpRequestFactoryCreator() { 15 | httpClient = new Builder() 16 | .followRedirects(false) 17 | .followSslRedirects(false) 18 | .build(); 19 | } 20 | 21 | @Override 22 | public ClientHttpRequestFactory createRequestFactory(TimeoutConfiguration configuration) { 23 | OkHttp3ClientHttpRequestFactory requestFactory = new OkHttp3ClientHttpRequestFactory(httpClient); 24 | requestFactory.setConnectTimeout(toMillis(configuration.getConnection())); 25 | requestFactory.setReadTimeout(toMillis(configuration.getRead())); 26 | requestFactory.setWriteTimeout(toMillis(configuration.getWrite())); 27 | return requestFactory; 28 | } 29 | 30 | void setHttpClient(OkHttpClient httpClient) { 31 | this.httpClient = httpClient; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/OkClientHttpRequestFactoryCreatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import okhttp3.OkHttpClient.Builder; 4 | 5 | public class OkClientHttpRequestFactoryCreatorConfigurer extends ClientHttpRequestFactoryCreatorConfigurer { 6 | 7 | private OkClientHttpRequestFactoryCreatorConfigurer() { 8 | super(new OkClientHttpRequestFactoryCreator()); 9 | } 10 | 11 | public static OkClientHttpRequestFactoryCreatorConfigurer okClientHttpRequestFactoryCreator() { 12 | return new OkClientHttpRequestFactoryCreatorConfigurer(); 13 | } 14 | 15 | public OkClientHttpRequestFactoryCreatorConfigurer httpClient(Builder httpClient) { 16 | configuredObject.setHttpClient(httpClient.build()); 17 | return this; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/RestTemplateConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import com.github.mkopylec.charon.configuration.Configurer; 4 | import org.springframework.http.client.ClientHttpRequestInterceptor; 5 | 6 | import java.util.List; 7 | 8 | public class RestTemplateConfigurer extends Configurer { 9 | 10 | private RestTemplateConfigurer() { 11 | super(new RestTemplateConfiguration()); 12 | } 13 | 14 | public static RestTemplateConfigurer restTemplate() { 15 | return new RestTemplateConfigurer(); 16 | } 17 | 18 | public RestTemplateConfigurer set(TimeoutConfigurer timeoutConfigurer) { 19 | configuredObject.setTimeoutConfiguration(timeoutConfigurer.configure()); 20 | return this; 21 | } 22 | 23 | public RestTemplateConfigurer set(ClientHttpRequestFactoryCreatorConfigurer clientHttpRequestFactoryCreatorConfigurer) { 24 | configuredObject.setClientHttpRequestFactoryCreator(clientHttpRequestFactoryCreatorConfigurer.configure()); 25 | return this; 26 | } 27 | 28 | public RestTemplateConfigurer set(List clientHttpRequestInterceptors) { 29 | configuredObject.setClientHttpRequestInterceptors(clientHttpRequestInterceptors); 30 | return this; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/RestTemplateProvider.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import com.github.mkopylec.charon.configuration.RequestMappingConfiguration; 4 | import org.springframework.web.client.RestTemplate; 5 | 6 | import java.util.concurrent.ConcurrentHashMap; 7 | import java.util.concurrent.ConcurrentMap; 8 | 9 | class RestTemplateProvider { 10 | 11 | private ConcurrentMap restTemplates; 12 | 13 | RestTemplateProvider() { 14 | restTemplates = new ConcurrentHashMap<>(); 15 | } 16 | 17 | RestTemplate getRestTemplate(RequestMappingConfiguration configuration) { 18 | return restTemplates.computeIfAbsent(configuration.getName(), mappingName -> configuration.getRestTemplateConfiguration().configure(configuration)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/RetryAwareRestTemplate.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.resilience.RetryAwareList; 4 | import org.springframework.http.client.ClientHttpRequestInterceptor; 5 | import org.springframework.web.client.RestTemplate; 6 | 7 | import java.util.List; 8 | 9 | class RetryAwareRestTemplate extends RestTemplate { 10 | 11 | private List interceptors = new RetryAwareList<>(); 12 | 13 | @Override 14 | public void setInterceptors(List interceptors) { 15 | this.interceptors.clear(); 16 | this.interceptors.addAll(interceptors); 17 | } 18 | 19 | @Override 20 | public List getInterceptors() { 21 | return interceptors; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/HttpRequestExecution.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors; 2 | 3 | import org.springframework.http.client.ClientHttpRequestExecution; 4 | import org.springframework.http.client.ClientHttpResponse; 5 | 6 | import java.io.IOException; 7 | 8 | import static com.github.mkopylec.charon.forwarding.RequestForwardingException.requestForwardingError; 9 | 10 | public class HttpRequestExecution { 11 | 12 | private String mappingName; 13 | private ClientHttpRequestExecution requestExecution; 14 | 15 | HttpRequestExecution(String mappingName, ClientHttpRequestExecution requestExecution) { 16 | this.mappingName = mappingName; 17 | this.requestExecution = requestExecution; 18 | } 19 | 20 | public HttpResponse execute(HttpRequest request) { 21 | try { 22 | ClientHttpResponse response = requestExecution.execute(request, request.getBody()); 23 | return response instanceof HttpResponse 24 | ? (HttpResponse) response 25 | : new HttpResponse(response); 26 | } catch (IOException e) { 27 | throw requestForwardingError("Error executing request: " + e.getMessage(), e); 28 | } 29 | } 30 | 31 | public String getMappingName() { 32 | return mappingName; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/HttpRequestInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors; 2 | 3 | import org.springframework.http.client.ClientHttpRequestExecution; 4 | import org.springframework.http.client.ClientHttpRequestInterceptor; 5 | import org.springframework.http.client.ClientHttpResponse; 6 | 7 | public class HttpRequestInterceptor implements ClientHttpRequestInterceptor { 8 | 9 | private String mappingName; 10 | private RequestForwardingInterceptor requestForwardingInterceptor; 11 | 12 | public HttpRequestInterceptor(String mappingName, RequestForwardingInterceptor requestForwardingInterceptor) { 13 | this.mappingName = mappingName; 14 | this.requestForwardingInterceptor = requestForwardingInterceptor; 15 | } 16 | 17 | @Override 18 | public ClientHttpResponse intercept(org.springframework.http.HttpRequest request, byte[] body, ClientHttpRequestExecution execution) { 19 | HttpRequest httpRequest = request instanceof HttpRequest 20 | ? (HttpRequest) request 21 | : new HttpRequest(request, body); 22 | HttpRequestExecution requestExecution = execution instanceof HttpRequestExecution 23 | ? (HttpRequestExecution) execution 24 | : new HttpRequestExecution(mappingName, execution); 25 | return requestForwardingInterceptor.forward(httpRequest, requestExecution); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/RequestForwardingInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors; 2 | 3 | import com.github.mkopylec.charon.configuration.Valid; 4 | 5 | public interface RequestForwardingInterceptor extends Valid, Comparable { 6 | 7 | HttpResponse forward(HttpRequest request, HttpRequestExecution execution); 8 | 9 | RequestForwardingInterceptorType getType(); 10 | 11 | @Override 12 | default int compareTo(RequestForwardingInterceptor requestForwardingInterceptor) { 13 | return getType().compareTo(requestForwardingInterceptor.getType()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/RequestForwardingInterceptorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors; 2 | 3 | import com.github.mkopylec.charon.configuration.Configurer; 4 | 5 | public abstract class RequestForwardingInterceptorConfigurer extends Configurer { 6 | 7 | protected RequestForwardingInterceptorConfigurer(I configuredObject) { 8 | super(configuredObject); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/async/AsynchronousForwarder.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.async; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | 9 | import static org.slf4j.LoggerFactory.getLogger; 10 | 11 | class AsynchronousForwarder extends CommonAsynchronousForwarder implements RequestForwardingInterceptor { 12 | 13 | private static final Logger log = getLogger(AsynchronousForwarder.class); 14 | 15 | AsynchronousForwarder() { 16 | super(log); 17 | } 18 | 19 | @Override 20 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 21 | getThreadPool().execute(() -> forwardAsynchronously(request, execution)); 22 | return new HttpResponse(getResponseStatus()); 23 | } 24 | 25 | private void forwardAsynchronously(HttpRequest request, HttpRequestExecution execution) { 26 | logStart(execution.getMappingName()); 27 | try { 28 | HttpResponse response = execution.execute(request); 29 | logForwardingResult(response.getStatusCode(), execution.getMappingName()); 30 | } catch (Exception e) { 31 | logError(execution.getMappingName(), e); 32 | } 33 | logEnd(execution.getMappingName()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/async/AsynchronousForwarderConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.async; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class AsynchronousForwarderConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private AsynchronousForwarderConfigurer() { 8 | super(new AsynchronousForwarder()); 9 | } 10 | 11 | public static AsynchronousForwarderConfigurer asynchronousForwarder() { 12 | return new AsynchronousForwarderConfigurer(); 13 | } 14 | 15 | public AsynchronousForwarderConfigurer set(ThreadPoolConfigurer threadPoolConfigurer) { 16 | configuredObject.setThreadPool(threadPoolConfigurer.configure()); 17 | return this; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/log/ForwardingLogger.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.log; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | import org.springframework.http.HttpMethod; 9 | 10 | import java.net.URI; 11 | 12 | import static org.slf4j.LoggerFactory.getLogger; 13 | 14 | class ForwardingLogger extends CommonForwardingLogger implements RequestForwardingInterceptor { 15 | 16 | private static final Logger log = getLogger(ForwardingLogger.class); 17 | 18 | ForwardingLogger() { 19 | super(log); 20 | } 21 | 22 | @Override 23 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 24 | HttpMethod originalMethod = request.getMethod(); 25 | URI originalUri = request.getURI(); 26 | String mappingName = execution.getMappingName(); 27 | try { 28 | HttpResponse response = execution.execute(request); 29 | logForwardingResult(response.getStatusCode(), originalMethod, request.getMethod(), originalUri, request.getURI(), mappingName); 30 | return response; 31 | } catch (RuntimeException e) { 32 | logForwardingError(e, originalMethod, originalUri, mappingName); 33 | throw e; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/log/ForwardingLoggerConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.log; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class ForwardingLoggerConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private ForwardingLoggerConfigurer() { 8 | super(new ForwardingLogger()); 9 | } 10 | 11 | public static ForwardingLoggerConfigurer forwardingLogger() { 12 | return new ForwardingLoggerConfigurer(); 13 | } 14 | 15 | public ForwardingLoggerConfigurer successLogLevel(LogLevel successLogLevel) { 16 | configuredObject.setSuccessLogLevel(successLogLevel); 17 | return this; 18 | } 19 | 20 | public ForwardingLoggerConfigurer clientErrorLogLevel(LogLevel clientErrorLogLevel) { 21 | configuredObject.setClientErrorLogLevel(clientErrorLogLevel); 22 | return this; 23 | } 24 | 25 | public ForwardingLoggerConfigurer serverErrorLogLevel(LogLevel serverErrorLogLevel) { 26 | configuredObject.setServerErrorLogLevel(serverErrorLogLevel); 27 | return this; 28 | } 29 | 30 | public ForwardingLoggerConfigurer unexpectedErrorLogLevel(LogLevel unexpectedErrorLogLevel) { 31 | configuredObject.setUnexpectedErrorLogLevel(unexpectedErrorLogLevel); 32 | return this; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/metrics/LatencyMeter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.metrics; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | 9 | import static java.lang.System.nanoTime; 10 | import static org.slf4j.LoggerFactory.getLogger; 11 | 12 | class LatencyMeter extends CommonLatencyMeter implements RequestForwardingInterceptor { 13 | 14 | private static final Logger log = getLogger(LatencyMeter.class); 15 | 16 | LatencyMeter() { 17 | super(log); 18 | } 19 | 20 | @Override 21 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 22 | logStart(execution.getMappingName()); 23 | long startingTime = nanoTime(); 24 | try { 25 | return execution.execute(request); 26 | } finally { 27 | captureLatencyMetric(execution.getMappingName(), startingTime); 28 | logEnd(execution.getMappingName()); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/metrics/LatencyMeterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.metrics; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | import io.micrometer.core.instrument.MeterRegistry; 5 | 6 | public class LatencyMeterConfigurer extends RequestForwardingInterceptorConfigurer { 7 | 8 | private LatencyMeterConfigurer() { 9 | super(new LatencyMeter()); 10 | } 11 | 12 | public static LatencyMeterConfigurer latencyMeter() { 13 | return new LatencyMeterConfigurer(); 14 | } 15 | 16 | public LatencyMeterConfigurer meterRegistry(MeterRegistry meterRegistry) { 17 | configuredObject.setMeterRegistry(meterRegistry); 18 | return this; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/metrics/RateMeter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.metrics; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | 9 | import static org.slf4j.LoggerFactory.getLogger; 10 | 11 | class RateMeter extends CommonRateMeter implements RequestForwardingInterceptor { 12 | 13 | private static final Logger log = getLogger(RateMeter.class); 14 | 15 | RateMeter() { 16 | super(log); 17 | } 18 | 19 | @Override 20 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 21 | logStart(execution.getMappingName()); 22 | try { 23 | HttpResponse response = execution.execute(request); 24 | captureResponseStatusMetric(execution.getMappingName(), response.getStatusCode()); 25 | return response; 26 | } catch (RuntimeException ex) { 27 | captureExceptionMetric(execution.getMappingName(), ex); 28 | throw ex; 29 | } finally { 30 | logEnd(execution.getMappingName()); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/metrics/RateMeterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.metrics; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | import io.micrometer.core.instrument.MeterRegistry; 5 | 6 | public class RateMeterConfigurer extends RequestForwardingInterceptorConfigurer { 7 | 8 | private RateMeterConfigurer() { 9 | super(new RateMeter()); 10 | } 11 | 12 | public static RateMeterConfigurer rateMeter() { 13 | return new RateMeterConfigurer(); 14 | } 15 | 16 | public RateMeterConfigurer meterRegistry(MeterRegistry meterRegistry) { 17 | configuredObject.setMeterRegistry(meterRegistry); 18 | return this; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/resilience/CircuitBreaker.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.resilience; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import io.github.resilience4j.circuitbreaker.CallNotPermittedException; 8 | import org.slf4j.Logger; 9 | 10 | import static org.slf4j.LoggerFactory.getLogger; 11 | 12 | class CircuitBreaker extends CommonCircuitBreaker implements RequestForwardingInterceptor { 13 | 14 | private static final Logger log = getLogger(CircuitBreaker.class); 15 | 16 | CircuitBreaker() { 17 | super(response -> response.getStatusCode().is5xxServerError(), log); 18 | } 19 | 20 | @Override 21 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 22 | logStart(execution.getMappingName()); 23 | io.github.resilience4j.circuitbreaker.CircuitBreaker circuitBreaker = getRegistry().circuitBreaker(execution.getMappingName()); 24 | setupMetrics(registry -> createMetrics(registry, execution.getMappingName())); 25 | HttpResponse response; 26 | try { 27 | response = circuitBreaker.executeSupplier(() -> execution.execute(request)); 28 | } catch (CallNotPermittedException ex) { 29 | response = executeFallback(ex); 30 | } 31 | logEnd(execution.getMappingName()); 32 | return response; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/resilience/CircuitBreakerConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.resilience; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 4 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 5 | import io.github.resilience4j.circuitbreaker.CallNotPermittedException; 6 | import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.Builder; 7 | import io.micrometer.core.instrument.MeterRegistry; 8 | 9 | import java.util.function.Function; 10 | 11 | import static io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry.of; 12 | 13 | public class CircuitBreakerConfigurer extends RequestForwardingInterceptorConfigurer { 14 | 15 | private CircuitBreakerConfigurer() { 16 | super(new CircuitBreaker()); 17 | } 18 | 19 | public static CircuitBreakerConfigurer circuitBreaker() { 20 | return new CircuitBreakerConfigurer(); 21 | } 22 | 23 | public CircuitBreakerConfigurer configuration(Builder circuitBreakerConfiguration) { 24 | configuredObject.setRegistry(of(circuitBreakerConfiguration.build())); 25 | return this; 26 | } 27 | 28 | public CircuitBreakerConfigurer fallback(Function fallback) { 29 | configuredObject.setFallback(fallback); 30 | return this; 31 | } 32 | 33 | public CircuitBreakerConfigurer meterRegistry(MeterRegistry meterRegistry) { 34 | configuredObject.setMeterRegistry(meterRegistry); 35 | return this; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/resilience/RateLimiter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.resilience; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | 9 | import static org.slf4j.LoggerFactory.getLogger; 10 | 11 | class RateLimiter extends CommonRateLimiter implements RequestForwardingInterceptor { 12 | 13 | private static final Logger log = getLogger(RateLimiter.class); 14 | 15 | RateLimiter() { 16 | super(log); 17 | } 18 | 19 | @Override 20 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 21 | logStart(execution.getMappingName()); 22 | io.github.resilience4j.ratelimiter.RateLimiter rateLimiter = getRegistry().rateLimiter(execution.getMappingName()); 23 | setupMetrics(registry -> createMetrics(registry, execution.getMappingName())); 24 | HttpResponse response = rateLimiter.executeSupplier(() -> execution.execute(request)); 25 | logEnd(execution.getMappingName()); 26 | return response; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/resilience/RateLimiterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.resilience; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | import io.github.resilience4j.ratelimiter.RateLimiterConfig.Builder; 5 | import io.micrometer.core.instrument.MeterRegistry; 6 | 7 | import static io.github.resilience4j.ratelimiter.RateLimiterRegistry.of; 8 | 9 | public class RateLimiterConfigurer extends RequestForwardingInterceptorConfigurer { 10 | 11 | private RateLimiterConfigurer() { 12 | super(new RateLimiter()); 13 | } 14 | 15 | public static RateLimiterConfigurer rateLimiter() { 16 | return new RateLimiterConfigurer(); 17 | } 18 | 19 | public RateLimiterConfigurer configuration(Builder rateLimiterConfiguration) { 20 | configuredObject.setRegistry(of(rateLimiterConfiguration.build())); 21 | return this; 22 | } 23 | 24 | public RateLimiterConfigurer meterRegistry(MeterRegistry meterRegistry) { 25 | configuredObject.setMeterRegistry(meterRegistry); 26 | return this; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/resilience/RetryerConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.resilience; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 4 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 5 | import io.github.resilience4j.retry.RetryConfig.Builder; 6 | import io.micrometer.core.instrument.MeterRegistry; 7 | 8 | import static io.github.resilience4j.retry.RetryRegistry.of; 9 | 10 | public class RetryerConfigurer extends RequestForwardingInterceptorConfigurer { 11 | 12 | private RetryerConfigurer() { 13 | super(new Retryer()); 14 | } 15 | 16 | public static RetryerConfigurer retryer() { 17 | return new RetryerConfigurer(); 18 | } 19 | 20 | public RetryerConfigurer configuration(Builder retryConfiguration) { 21 | configuredObject.setRegistry(of(retryConfiguration.build())); 22 | return this; 23 | } 24 | 25 | public RetryerConfigurer meterRegistry(MeterRegistry meterRegistry) { 26 | configuredObject.setMeterRegistry(meterRegistry); 27 | return this; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/resilience/RetryingState.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.resilience; 2 | 3 | class RetryingState { 4 | 5 | private static ThreadLocal state = new ThreadLocal<>(); 6 | 7 | static State getRetryState() { 8 | if (state.get() == null) { 9 | state.set(new State()); 10 | } 11 | return state.get(); 12 | } 13 | 14 | static void clearRetryState() { 15 | state.remove(); 16 | } 17 | 18 | static class State { 19 | 20 | private int retryAttempts; 21 | private boolean firstAttempt; 22 | private boolean nextAttempt; 23 | private Integer afterRetryerIndex; 24 | 25 | boolean isFirstRetryAttempt() { 26 | return retryAttempts == 1 && firstAttempt; 27 | } 28 | 29 | void firstRetryAttemptApplied() { 30 | firstAttempt = false; 31 | } 32 | 33 | boolean isSucceedingRetryAttempt() { 34 | return retryAttempts > 1 && nextAttempt && afterRetryerIndex != null; 35 | } 36 | 37 | void succeedingRetryAttemptApplied() { 38 | nextAttempt = false; 39 | } 40 | 41 | int getAfterRetryerIndex() { 42 | return afterRetryerIndex; 43 | } 44 | 45 | void setAfterRetryerIndex(int index) { 46 | afterRetryerIndex = index; 47 | } 48 | 49 | void nextRetryAttempt() { 50 | retryAttempts++; 51 | if (retryAttempts == 1) { 52 | firstAttempt = true; 53 | } 54 | nextAttempt = true; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RegexRequestPathRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | 9 | import static org.slf4j.LoggerFactory.getLogger; 10 | 11 | class RegexRequestPathRewriter extends CommonRegexRequestPathRewriter implements RequestForwardingInterceptor { 12 | 13 | private static final Logger log = getLogger(RegexRequestPathRewriter.class); 14 | 15 | RegexRequestPathRewriter() { 16 | super(log); 17 | } 18 | 19 | @Override 20 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 21 | logStart(execution.getMappingName()); 22 | rewritePath(request.getURI(), request::setUri); 23 | HttpResponse response = execution.execute(request); 24 | logEnd(execution.getMappingName()); 25 | return response; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RegexRequestPathRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class RegexRequestPathRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private RegexRequestPathRewriterConfigurer() { 8 | super(new RegexRequestPathRewriter()); 9 | } 10 | 11 | public static RegexRequestPathRewriterConfigurer regexRequestPathRewriter() { 12 | return new RegexRequestPathRewriterConfigurer(); 13 | } 14 | 15 | public RegexRequestPathRewriterConfigurer paths(String incomingRequestPathRegex, String outgoingRequestPathTemplate) { 16 | configuredObject.setPaths(incomingRequestPathRegex, outgoingRequestPathTemplate); 17 | return this; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RemovingResponseCookiesRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | import org.springframework.http.HttpHeaders; 9 | 10 | import static org.slf4j.LoggerFactory.getLogger; 11 | import static org.springframework.http.HttpHeaders.SET_COOKIE; 12 | import static org.springframework.http.HttpHeaders.SET_COOKIE2; 13 | 14 | class RemovingResponseCookiesRewriter extends CommonRemovingResponseCookiesRewriter implements RequestForwardingInterceptor { 15 | 16 | private static final Logger log = getLogger(RemovingResponseCookiesRewriter.class); 17 | 18 | RemovingResponseCookiesRewriter() { 19 | super(log); 20 | } 21 | 22 | @Override 23 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 24 | logStart(execution.getMappingName()); 25 | HttpResponse response = execution.execute(request); 26 | HttpHeaders headers = response.getHeaders(); 27 | removeCookies(headers, SET_COOKIE, response::setHeaders); 28 | removeCookies(headers, SET_COOKIE2, response::setHeaders); 29 | logEnd(execution.getMappingName()); 30 | return response; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RemovingResponseCookiesRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class RemovingResponseCookiesRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private RemovingResponseCookiesRewriterConfigurer() { 8 | super(new RemovingResponseCookiesRewriter()); 9 | } 10 | 11 | public static RemovingResponseCookiesRewriterConfigurer removingResponseCookiesRewriter() { 12 | return new RemovingResponseCookiesRewriterConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RequestHostHeaderRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | 9 | import static org.slf4j.LoggerFactory.getLogger; 10 | 11 | class RequestHostHeaderRewriter extends CommonRequestHostHeaderRewriter implements RequestForwardingInterceptor { 12 | 13 | private static final Logger log = getLogger(RequestHostHeaderRewriter.class); 14 | 15 | RequestHostHeaderRewriter() { 16 | super(log); 17 | } 18 | 19 | @Override 20 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 21 | logStart(execution.getMappingName()); 22 | rewriteHeaders(request.getHeaders(), request.getURI(), request::setHeaders); 23 | HttpResponse response = execution.execute(request); 24 | logEnd(execution.getMappingName()); 25 | return response; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RequestHostHeaderRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class RequestHostHeaderRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private RequestHostHeaderRewriterConfigurer() { 8 | super(new RequestHostHeaderRewriter()); 9 | } 10 | 11 | public static RequestHostHeaderRewriterConfigurer requestHostHeaderRewriter() { 12 | return new RequestHostHeaderRewriterConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RequestProtocolHeadersRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | 9 | import static org.slf4j.LoggerFactory.getLogger; 10 | 11 | class RequestProtocolHeadersRewriter extends CommonRequestProtocolHeadersRewriter implements RequestForwardingInterceptor { 12 | 13 | private static final Logger log = getLogger(RequestProtocolHeadersRewriter.class); 14 | 15 | RequestProtocolHeadersRewriter() { 16 | super(log); 17 | } 18 | 19 | @Override 20 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 21 | logStart(execution.getMappingName()); 22 | rewriteHeaders(request.getHeaders(), request::setHeaders); 23 | HttpResponse response = execution.execute(request); 24 | logEnd(execution.getMappingName()); 25 | return response; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RequestProtocolHeadersRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class RequestProtocolHeadersRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private RequestProtocolHeadersRewriterConfigurer() { 8 | super(new RequestProtocolHeadersRewriter()); 9 | } 10 | 11 | public static RequestProtocolHeadersRewriterConfigurer requestProtocolHeadersRewriter() { 12 | return new RequestProtocolHeadersRewriterConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RequestProxyHeadersRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | 9 | import static org.slf4j.LoggerFactory.getLogger; 10 | 11 | class RequestProxyHeadersRewriter extends CommonRequestProxyHeadersRewriter implements RequestForwardingInterceptor { 12 | 13 | private static final Logger log = getLogger(RequestProxyHeadersRewriter.class); 14 | 15 | RequestProxyHeadersRewriter() { 16 | super(log); 17 | } 18 | 19 | @Override 20 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 21 | logStart(execution.getMappingName()); 22 | rewriteHeaders(request.getHeaders(), request.getURI(), request::setHeaders); 23 | HttpResponse response = execution.execute(request); 24 | logEnd(execution.getMappingName()); 25 | return response; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RequestProxyHeadersRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class RequestProxyHeadersRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private RequestProxyHeadersRewriterConfigurer() { 8 | super(new RequestProxyHeadersRewriter()); 9 | } 10 | 11 | public static RequestProxyHeadersRewriterConfigurer requestProxyHeadersRewriter() { 12 | return new RequestProxyHeadersRewriterConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RequestServerNameRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | 9 | import static org.slf4j.LoggerFactory.getLogger; 10 | 11 | class RequestServerNameRewriter extends CommonRequestServerNameRewriter implements RequestForwardingInterceptor { 12 | 13 | private static final Logger log = getLogger(RequestServerNameRewriter.class); 14 | 15 | RequestServerNameRewriter() { 16 | super(log); 17 | } 18 | 19 | @Override 20 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 21 | logStart(execution.getMappingName()); 22 | BodilessHttpRequest bodilessRequest = new BodilessHttpRequest(request.getMethod(), request.getURI(), request.getHeaders()); 23 | rewriteServerName(bodilessRequest, request::setUri); 24 | HttpResponse response = execution.execute(request); 25 | logEnd(execution.getMappingName()); 26 | return response; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RequestServerNameRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | import java.util.List; 6 | 7 | import static java.util.Arrays.asList; 8 | 9 | public class RequestServerNameRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 10 | 11 | private RequestServerNameRewriterConfigurer() { 12 | super(new RequestServerNameRewriter()); 13 | } 14 | 15 | public static RequestServerNameRewriterConfigurer requestServerNameRewriter() { 16 | return new RequestServerNameRewriterConfigurer(); 17 | } 18 | 19 | public RequestServerNameRewriterConfigurer loadBalancer(LoadBalancerConfigurer loadBalancerConfigurer) { 20 | configuredObject.setLoadBalancer(loadBalancerConfigurer.configure()); 21 | return this; 22 | } 23 | 24 | public RequestServerNameRewriterConfigurer outgoingServers(String... outgoingServers) { 25 | configuredObject.setOutgoingServers(asList(outgoingServers)); 26 | return this; 27 | } 28 | 29 | public RequestServerNameRewriterConfigurer outgoingServers(List outgoingServers) { 30 | configuredObject.setOutgoingServers(outgoingServers); 31 | return this; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/ResponseProtocolHeadersRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | 9 | import static org.slf4j.LoggerFactory.getLogger; 10 | 11 | class ResponseProtocolHeadersRewriter extends CommonResponseProtocolHeadersRewriter implements RequestForwardingInterceptor { 12 | 13 | private static final Logger log = getLogger(ResponseProtocolHeadersRewriter.class); 14 | 15 | ResponseProtocolHeadersRewriter() { 16 | super(log); 17 | } 18 | 19 | @Override 20 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 21 | logStart(execution.getMappingName()); 22 | HttpResponse response = execution.execute(request); 23 | rewriteHeaders(response.getHeaders(), response::setHeaders); 24 | logEnd(execution.getMappingName()); 25 | return response; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/ResponseProtocolHeadersRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class ResponseProtocolHeadersRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private ResponseProtocolHeadersRewriterConfigurer() { 8 | super(new ResponseProtocolHeadersRewriter()); 9 | } 10 | 11 | public static ResponseProtocolHeadersRewriterConfigurer responseProtocolHeadersRewriter() { 12 | return new ResponseProtocolHeadersRewriterConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RootPathResponseCookiesRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | 9 | import static org.slf4j.LoggerFactory.getLogger; 10 | import static org.springframework.http.HttpHeaders.SET_COOKIE; 11 | import static org.springframework.http.HttpHeaders.SET_COOKIE2; 12 | 13 | class RootPathResponseCookiesRewriter extends CommonRootPathResponseCookiesRewriter implements RequestForwardingInterceptor { 14 | 15 | private static final Logger log = getLogger(RootPathResponseCookiesRewriter.class); 16 | 17 | RootPathResponseCookiesRewriter() { 18 | super(log); 19 | } 20 | 21 | @Override 22 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 23 | logStart(execution.getMappingName()); 24 | HttpResponse response = execution.execute(request); 25 | rewriteCookies(response.getHeaders(), SET_COOKIE, response::setHeaders); 26 | rewriteCookies(response.getHeaders(), SET_COOKIE2, response::setHeaders); 27 | logEnd(execution.getMappingName()); 28 | return response; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/rewrite/RootPathResponseCookiesRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.rewrite; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class RootPathResponseCookiesRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private RootPathResponseCookiesRewriterConfigurer() { 8 | super(new RootPathResponseCookiesRewriter()); 9 | } 10 | 11 | public static RootPathResponseCookiesRewriterConfigurer rootPathResponseCookiesRewriter() { 12 | return new RootPathResponseCookiesRewriterConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/BasicAuthenticator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import org.slf4j.Logger; 4 | 5 | import static com.github.mkopylec.charon.forwarding.interceptors.security.AuthenticationType.BASIC; 6 | import static org.slf4j.LoggerFactory.getLogger; 7 | 8 | class BasicAuthenticator extends Authenticator { 9 | 10 | private static final Logger log = getLogger(BasicAuthenticator.class); 11 | 12 | BasicAuthenticator() { 13 | super(log, BASIC); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/BasicAuthenticatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class BasicAuthenticatorConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private BasicAuthenticatorConfigurer() { 8 | super(new BasicAuthenticator()); 9 | } 10 | 11 | public static BasicAuthenticatorConfigurer basicAuthenticator() { 12 | return new BasicAuthenticatorConfigurer(); 13 | } 14 | 15 | public BasicAuthenticatorConfigurer userValidator(UserValidatorConfigurer userValidatorConfigurer) { 16 | configuredObject.setCredentialsValidator(userValidatorConfigurer.configure()); 17 | return this; 18 | } 19 | 20 | public BasicAuthenticatorConfigurer realm(String realm) { 21 | configuredObject.setRealm(realm); 22 | return this; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/BearerAuthenticator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import org.slf4j.Logger; 4 | 5 | import static com.github.mkopylec.charon.forwarding.interceptors.security.AuthenticationType.BEARER; 6 | import static org.slf4j.LoggerFactory.getLogger; 7 | 8 | class BearerAuthenticator extends Authenticator { 9 | 10 | private static final Logger log = getLogger(BearerAuthenticator.class); 11 | 12 | BearerAuthenticator() { 13 | super(log, BEARER); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/BearerAuthenticatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | public class BearerAuthenticatorConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private BearerAuthenticatorConfigurer() { 8 | super(new BearerAuthenticator()); 9 | } 10 | 11 | public static BearerAuthenticatorConfigurer bearerAuthenticator() { 12 | return new BearerAuthenticatorConfigurer(); 13 | } 14 | 15 | public BearerAuthenticatorConfigurer tokenValidator(TokenValidatorConfigurer tokenValidatorConfigurer) { 16 | configuredObject.setCredentialsValidator(tokenValidatorConfigurer.configure()); 17 | return this; 18 | } 19 | 20 | public BearerAuthenticatorConfigurer realm(String realm) { 21 | configuredObject.setRealm(realm); 22 | return this; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/CredentialsValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import com.github.mkopylec.charon.configuration.Valid; 4 | 5 | interface CredentialsValidator extends Valid { 6 | 7 | boolean validate(String credentials); 8 | } 9 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/CredentialsValidatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import com.github.mkopylec.charon.configuration.Configurer; 4 | 5 | abstract class CredentialsValidatorConfigurer extends Configurer { 6 | 7 | CredentialsValidatorConfigurer(V configuredObject) { 8 | super(configuredObject); 9 | } 10 | 11 | @Override 12 | protected V configure() { 13 | return super.configure(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/InMemoryTokenValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import java.util.List; 4 | 5 | import static java.util.Collections.emptyList; 6 | import static org.apache.commons.collections4.ListUtils.emptyIfNull; 7 | 8 | class InMemoryTokenValidator implements TokenValidator { 9 | 10 | private List validTokens; 11 | 12 | InMemoryTokenValidator() { 13 | validTokens = emptyList(); 14 | } 15 | 16 | @Override 17 | public boolean validate(String token) { 18 | return validTokens.contains(token); 19 | } 20 | 21 | void setValidTokens(List validTokens) { 22 | this.validTokens = emptyIfNull(validTokens); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/InMemoryTokenValidatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import java.util.List; 4 | 5 | import static java.util.Arrays.asList; 6 | 7 | public class InMemoryTokenValidatorConfigurer extends TokenValidatorConfigurer { 8 | 9 | private InMemoryTokenValidatorConfigurer() { 10 | super(new InMemoryTokenValidator()); 11 | } 12 | 13 | public static InMemoryTokenValidatorConfigurer inMemoryTokenValidator() { 14 | return new InMemoryTokenValidatorConfigurer(); 15 | } 16 | 17 | public InMemoryTokenValidatorConfigurer validTokens(String... validTokens) { 18 | configuredObject.setValidTokens(asList(validTokens)); 19 | return this; 20 | } 21 | 22 | public InMemoryTokenValidatorConfigurer validTokens(List validTokens) { 23 | configuredObject.setValidTokens(validTokens); 24 | return this; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/InMemoryUserValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | class InMemoryUserValidator implements UserValidator { 7 | 8 | private List validUsers; 9 | 10 | InMemoryUserValidator() { 11 | validUsers = new ArrayList<>(); 12 | } 13 | 14 | @Override 15 | public boolean validate(User user) { 16 | return validUsers.contains(user); 17 | } 18 | 19 | void addValidUser(User validUser) { 20 | validUsers.add(validUser); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/InMemoryUserValidatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | public class InMemoryUserValidatorConfigurer extends UserValidatorConfigurer { 4 | 5 | private InMemoryUserValidatorConfigurer() { 6 | super(new InMemoryUserValidator()); 7 | } 8 | 9 | public static InMemoryUserValidatorConfigurer inMemoryUserValidator() { 10 | return new InMemoryUserValidatorConfigurer(); 11 | } 12 | 13 | public InMemoryUserValidatorConfigurer validUser(String name, String password) { 14 | User user = new User(name, password); 15 | configuredObject.addValidUser(user); 16 | return this; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/TokenValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | public interface TokenValidator extends CredentialsValidator { 4 | 5 | @Override 6 | boolean validate(String token); 7 | } 8 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/TokenValidatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | public abstract class TokenValidatorConfigurer extends CredentialsValidatorConfigurer { 4 | 5 | protected TokenValidatorConfigurer(V configuredObject) { 6 | super(configuredObject); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/UserValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | public interface UserValidator extends BasicUserValidator, CredentialsValidator { 4 | 5 | @Override 6 | default boolean validate(String credentials) { 7 | User user = extractUser(credentials); 8 | return !user.isEmpty() && validate(user); 9 | } 10 | 11 | boolean validate(User user); 12 | } 13 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/security/UserValidatorConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.forwarding.interceptors.security; 2 | 3 | public abstract class UserValidatorConfigurer extends CredentialsValidatorConfigurer { 4 | 5 | protected UserValidatorConfigurer(V configuredObject) { 6 | super(configuredObject); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | com.github.mkopylec.charon.configuration.CharonAutoConfiguration -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/AsynchronousForwardingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.AsynchronousForwardingBasicSpec 4 | 5 | class AsynchronousForwardingSpec extends AsynchronousForwardingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/BasicAuthenticationSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.BasicAuthenticationBasicSpec 4 | 5 | class BasicAuthenticationSpec extends BasicAuthenticationBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/BearerAuthenticationSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.BearerAuthenticationBasicSpec 4 | 5 | class BearerAuthenticationSpec extends BearerAuthenticationBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/CircuitBreakingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.CircuitBreakingBasicSpec 4 | 5 | class CircuitBreakingSpec extends CircuitBreakingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/CustomLoadBalancerSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.CustomLoadBalancerBasicSpec 4 | 5 | class CustomLoadBalancerSpec extends CustomLoadBalancerBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/DefaultCharonConfigurationSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.DefaultCharonConfigurationBasicSpec 4 | 5 | class DefaultCharonConfigurationSpec extends DefaultCharonConfigurationBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/DefinedProfileSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.DefinedProfileBasicSpec 4 | 5 | class DefinedProfileSpec extends DefinedProfileBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/LatencyMeteringSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.LatencyMeteringBasicSpec 4 | 5 | class LatencyMeteringSpec extends LatencyMeteringBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/MappingResolvingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.MappingResolvingBasicSpec 4 | 5 | class MappingResolvingSpec extends MappingResolvingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/RateLimitingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RateLimitingBasicSpec 4 | 5 | class RateLimitingSpec extends RateLimitingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/RateMeteringSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RateMeteringBasicSpec 4 | 5 | class RateMeteringSpec extends RateMeteringBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/RegexRequestPathRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RegexRequestPathRewritingBasicSpec 4 | 5 | class RegexRequestPathRewritingSpec extends RegexRequestPathRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/RemovingResponseCookiesRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RemovingResponseCookiesRewritingBasicSpec 4 | 5 | class RemovingResponseCookiesRewritingSpec extends RemovingResponseCookiesRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/RequestBodyRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RequestBodyRewritingBasicSpec 4 | 5 | class RequestBodyRewritingSpec extends RequestBodyRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/RequestForwardingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RequestForwardingBasicSpec 4 | 5 | class RequestForwardingSpec extends RequestForwardingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/RequestHostHeaderRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RequestHostHeaderRewritingBasicSpec 4 | 5 | class RequestHostHeaderRewritingSpec extends RequestHostHeaderRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/RequestProtocolHeadersRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RequestProtocolHeadersRewritingBasicSpec 4 | 5 | class RequestProtocolHeadersRewritingSpec extends RequestProtocolHeadersRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/RequestProxyHeadersRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RequestProxyHeadersRewritingBasicSpec 4 | 5 | class RequestProxyHeadersRewritingSpec extends RequestProxyHeadersRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/ResponseBodyRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.ResponseBodyRewritingBasicSpec 4 | 5 | class ResponseBodyRewritingSpec extends ResponseBodyRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/ResponseForwardingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.ResponseForwardingBasicSpec 4 | 5 | class ResponseForwardingSpec extends ResponseForwardingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/ResponseProtocolHeadersRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.ResponseProtocolHeadersRewritingBasicSpec 4 | 5 | class ResponseProtocolHeadersRewritingSpec extends ResponseProtocolHeadersRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/RetryingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RetryingBasicSpec 4 | 5 | class RetryingSpec extends RetryingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/RootPathResponseCookiesRewritingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.RootPathResponseCookiesRewritingBasicSpec 4 | 5 | class RootPathResponseCookiesRewritingSpec extends RootPathResponseCookiesRewritingBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/groovy/com/github/mkopylec/charon/test/TimeoutSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test 2 | 3 | import com.github.mkopylec.charon.test.specification.TimeoutBasicSpec 4 | 5 | class TimeoutSpec extends TimeoutBasicSpec { 6 | } 7 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/java/com/github/mkopylec/charon/test/CircuitBreakerFallback.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 4 | import io.github.resilience4j.circuitbreaker.CallNotPermittedException; 5 | 6 | import java.util.function.Function; 7 | 8 | import static org.springframework.http.HttpStatus.CREATED; 9 | 10 | class CircuitBreakerFallback implements Function { 11 | 12 | @Override 13 | public HttpResponse apply(CallNotPermittedException e) { 14 | return new HttpResponse(CREATED); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/java/com/github/mkopylec/charon/test/ExceptionThrower.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | 9 | import static org.slf4j.LoggerFactory.getLogger; 10 | 11 | class ExceptionThrower extends CommonExceptionThrower implements RequestForwardingInterceptor { 12 | 13 | private static final Logger log = getLogger(ExceptionThrower.class); 14 | 15 | ExceptionThrower() { 16 | super(log); 17 | } 18 | 19 | @Override 20 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 21 | logStart(execution.getMappingName()); 22 | throw createFailure(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/java/com/github/mkopylec/charon/test/ExceptionThrowerConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | class ExceptionThrowerConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private ExceptionThrowerConfigurer() { 8 | super(new ExceptionThrower()); 9 | } 10 | 11 | static ExceptionThrowerConfigurer exceptionThrower() { 12 | return new ExceptionThrowerConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/java/com/github/mkopylec/charon/test/LowestPortLoadBalancer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.rewrite.BodilessHttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.rewrite.LoadBalancer; 5 | 6 | import java.net.URI; 7 | import java.util.List; 8 | 9 | import static java.util.Comparator.comparingInt; 10 | 11 | class LowestPortLoadBalancer implements LoadBalancer { 12 | 13 | @Override 14 | public URI chooseServer(List servers, BodilessHttpRequest request) { 15 | return servers.stream() 16 | .min(comparingInt(URI::getPort)) 17 | .get(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/java/com/github/mkopylec/charon/test/LowestPortLoadBalancerConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.rewrite.LoadBalancerConfigurer; 4 | 5 | public class LowestPortLoadBalancerConfigurer extends LoadBalancerConfigurer { 6 | 7 | private LowestPortLoadBalancerConfigurer() { 8 | super(new LowestPortLoadBalancer()); 9 | } 10 | 11 | public static LowestPortLoadBalancerConfigurer lowestPortLoadBalancer() { 12 | return new LowestPortLoadBalancerConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/java/com/github/mkopylec/charon/test/RequestBodyRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | 9 | import static org.slf4j.LoggerFactory.getLogger; 10 | 11 | class RequestBodyRewriter extends CommonRequestBodyRewriter implements RequestForwardingInterceptor { 12 | 13 | private static final Logger log = getLogger(RequestBodyRewriter.class); 14 | 15 | RequestBodyRewriter() { 16 | super(log); 17 | } 18 | 19 | @Override 20 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 21 | logStart(execution.getMappingName()); 22 | rewriteRequest(request); 23 | HttpResponse response = execution.execute(request); 24 | logEnd(execution.getMappingName()); 25 | return response; 26 | } 27 | 28 | private void rewriteRequest(HttpRequest request) { 29 | String oldBody = new String(request.getBody()); 30 | request.setBody("Rewritten request body".getBytes()); 31 | log.debug("Request body rewritten from '{}' to '{}'", oldBody, new String(request.getBody())); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/java/com/github/mkopylec/charon/test/RequestBodyRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | class RequestBodyRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private RequestBodyRewriterConfigurer() { 8 | super(new RequestBodyRewriter()); 9 | } 10 | 11 | static RequestBodyRewriterConfigurer requestBodyRewriter() { 12 | return new RequestBodyRewriterConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/java/com/github/mkopylec/charon/test/ResponseBodyRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequest; 4 | import com.github.mkopylec.charon.forwarding.interceptors.HttpRequestExecution; 5 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 6 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptor; 7 | import org.slf4j.Logger; 8 | 9 | import static org.slf4j.LoggerFactory.getLogger; 10 | 11 | class ResponseBodyRewriter extends CommonResponseBodyRewriter implements RequestForwardingInterceptor { 12 | 13 | private static final Logger log = getLogger(ResponseBodyRewriter.class); 14 | 15 | ResponseBodyRewriter() { 16 | super(log); 17 | } 18 | 19 | @Override 20 | public HttpResponse forward(HttpRequest request, HttpRequestExecution execution) { 21 | logStart(execution.getMappingName()); 22 | HttpResponse response = execution.execute(request); 23 | rewriteResponse(response); 24 | logEnd(execution.getMappingName()); 25 | return response; 26 | } 27 | 28 | private void rewriteResponse(HttpResponse response) { 29 | String oldBody = new String(response.getBodyAsBytes()); 30 | response.setBody("Rewritten response body".getBytes()); 31 | log.debug("Response body rewritten from '{}' to '{}'", oldBody, new String(response.getBodyAsBytes())); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/java/com/github/mkopylec/charon/test/ResponseBodyRewriterConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorConfigurer; 4 | 5 | class ResponseBodyRewriterConfigurer extends RequestForwardingInterceptorConfigurer { 6 | 7 | private ResponseBodyRewriterConfigurer() { 8 | super(new ResponseBodyRewriter()); 9 | } 10 | 11 | static ResponseBodyRewriterConfigurer responseBodyRewriter() { 12 | return new ResponseBodyRewriterConfigurer(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/java/com/github/mkopylec/charon/test/RetryAttemptsResponseHeaderSetter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.HttpResponse; 4 | import org.springframework.http.HttpHeaders; 5 | import org.springframework.http.HttpRequest; 6 | import org.springframework.http.client.ClientHttpRequestExecution; 7 | import org.springframework.http.client.ClientHttpRequestInterceptor; 8 | import org.springframework.http.client.ClientHttpResponse; 9 | 10 | import java.io.IOException; 11 | import java.util.concurrent.atomic.AtomicInteger; 12 | 13 | import static com.github.mkopylec.charon.forwarding.Utils.copyHeaders; 14 | import static java.lang.String.valueOf; 15 | import static org.apache.commons.io.IOUtils.toByteArray; 16 | 17 | class RetryAttemptsResponseHeaderSetter implements ClientHttpRequestInterceptor { 18 | 19 | private AtomicInteger attempt = new AtomicInteger(); // Works only because of @DirtiesContext 20 | 21 | @Override 22 | public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { 23 | ClientHttpResponse response = execution.execute(request, body); 24 | HttpResponse httpResponse = new HttpResponse(response.getStatusCode()); 25 | HttpHeaders headers = copyHeaders(response.getHeaders()); 26 | headers.set("Retry-Attempts", valueOf(attempt.incrementAndGet())); 27 | httpResponse.setHeaders(headers); 28 | httpResponse.setBody(toByteArray(response.getBody())); 29 | return httpResponse; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /charon-spring-webmvc/src/test/resources/application.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | level: 3 | com.github.mkopylec.charon: trace 4 | 5 | server: 6 | tomcat: 7 | connection-timeout: 0 8 | error: 9 | include-message: always 10 | -------------------------------------------------------------------------------- /charon-test/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | api project(':charon-common') 3 | api group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: springBootVersion 4 | api group: 'org.spockframework', name: 'spock-spring', version: '2.4-M5-groovy-4.0' 5 | api group: 'org.mock-server', name: 'mockserver-netty', version: '5.15.0' 6 | api group: 'com.squareup.okhttp3', name: 'okhttp', version: okHttpVersion 7 | } 8 | -------------------------------------------------------------------------------- /charon-test/src/main/groovy/com/github/mkopylec/charon/test/assertions/Assertions.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test.assertions 2 | 3 | import com.github.mkopylec.charon.test.stubs.OutgoingServer 4 | import org.springframework.http.ResponseEntity 5 | 6 | import static com.github.mkopylec.charon.test.utils.MeterRegistryProvider.meterRegistry 7 | 8 | class Assertions { 9 | 10 | static ResponseAssertion assertThat(ResponseEntity response) { 11 | return new ResponseAssertion(response) 12 | } 13 | 14 | static OutgoingServerAssertion assertThatServers(OutgoingServer... outgoingServers) { 15 | return new OutgoingServerAssertion(outgoingServers) 16 | } 17 | 18 | static MetricsAssertion assertThatMetrics() { 19 | return new MetricsAssertion(meterRegistry()) 20 | } 21 | 22 | static ExceptionAssertion assertThatException(Throwable throwable) { 23 | return new ExceptionAssertion(throwable) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /charon-test/src/main/groovy/com/github/mkopylec/charon/test/assertions/ExceptionAssertion.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test.assertions 2 | 3 | class ExceptionAssertion { 4 | 5 | private Throwable throwable 6 | 7 | protected ExceptionAssertion(Throwable throwable) { 8 | assert throwable != null 9 | this.throwable = throwable 10 | } 11 | 12 | ExceptionAssertion hasMessage(String message) { 13 | assert throwable.message == message 14 | return this 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /charon-test/src/main/groovy/com/github/mkopylec/charon/test/assertions/MetricsAssertion.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test.assertions 2 | 3 | import io.micrometer.core.instrument.MeterRegistry 4 | 5 | class MetricsAssertion { 6 | 7 | private MeterRegistry meterRegistry 8 | 9 | protected MetricsAssertion(MeterRegistry meterRegistry) { 10 | assert meterRegistry != null 11 | this.meterRegistry = meterRegistry 12 | } 13 | 14 | MetricsAssertion haveCaptured(String metricsName) { 15 | assert meterRegistry.meters.find { it.id.name == metricsName } 16 | return this 17 | } 18 | 19 | MetricsAssertion haveCapturedLatency(String metricsName) { 20 | assert meterRegistry.timer(metricsName).count() == 1 21 | return this 22 | } 23 | 24 | MetricsAssertion haveCapturedRate(String metricsName) { 25 | assert meterRegistry.counter(metricsName).count() == 1 26 | return this 27 | } 28 | 29 | MetricsAssertion haveCapturedNothing() { 30 | assert meterRegistry.meters.isEmpty() 31 | return this 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /charon-test/src/main/groovy/com/github/mkopylec/charon/test/assertions/ResponseAssertion.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test.assertions 2 | 3 | import org.springframework.http.HttpStatus 4 | import org.springframework.http.ResponseEntity 5 | 6 | import static org.apache.commons.lang3.StringUtils.isEmpty 7 | 8 | class ResponseAssertion { 9 | 10 | private ResponseEntity response 11 | 12 | protected ResponseAssertion(ResponseEntity response) { 13 | assert response != null 14 | this.response = response 15 | } 16 | 17 | ResponseAssertion bodyContains(String bodyPart) { 18 | assert response.body != null 19 | assert response.body.toString().contains(bodyPart) 20 | return this 21 | } 22 | 23 | ResponseAssertion hasBody(String body) { 24 | assert response.body == body 25 | return this 26 | } 27 | 28 | ResponseAssertion hasNoBody() { 29 | assert isEmpty(response.body as CharSequence) 30 | return this 31 | } 32 | 33 | ResponseAssertion hasStatus(HttpStatus status) { 34 | assert response.statusCode == status 35 | return this 36 | } 37 | 38 | ResponseAssertion containsHeaders(Map headers) { 39 | headers.each { k, v -> assert response.headers.get(k).join(', ') == v } 40 | return this 41 | } 42 | 43 | ResponseAssertion notContainsHeaders(String... headers) { 44 | headers.each { name -> assert !response.headers.containsKey(name) } 45 | return this 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /charon-test/src/main/groovy/com/github/mkopylec/charon/test/specification/BasicSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test.specification 2 | 3 | import com.github.mkopylec.charon.test.stubs.OutgoingServer 4 | import com.github.mkopylec.charon.test.utils.HttpClient 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import org.springframework.boot.test.context.SpringBootTest 7 | import org.springframework.test.context.TestPropertySource 8 | import spock.lang.Specification 9 | 10 | import static com.github.mkopylec.charon.test.stubs.OutgoingServersStubs.outgoingServers 11 | import static com.github.mkopylec.charon.test.utils.MeterRegistryProvider.clearMetrics 12 | import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT 13 | 14 | @SpringBootTest(webEnvironment = RANDOM_PORT) 15 | @TestPropertySource(properties = ['default-charon-configuration = false']) 16 | abstract class BasicSpec extends Specification { 17 | 18 | protected static OutgoingServer localhost8080 = new OutgoingServer(8080) 19 | protected static OutgoingServer localhost8081 = new OutgoingServer(8081) 20 | 21 | @Autowired 22 | protected HttpClient http 23 | 24 | void cleanup() { 25 | outgoingServers(localhost8080, localhost8081).clearStubs() 26 | clearMetrics() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /charon-test/src/main/groovy/com/github/mkopylec/charon/test/specification/CustomLoadBalancerBasicSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test.specification 2 | 3 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThatServers 4 | import static com.github.mkopylec.charon.test.stubs.OutgoingServersStubs.outgoingServers 5 | import static org.springframework.http.HttpMethod.GET 6 | import static org.springframework.http.HttpStatus.OK 7 | 8 | abstract class CustomLoadBalancerBasicSpec extends BasicSpec { 9 | 10 | def "Should forward request using custom load balancer"() { 11 | given: 12 | outgoingServers(localhost8080) 13 | .stubResponse(OK) 14 | when: 15 | http.sendRequest(GET, '/custom/load/balancer') 16 | 17 | then: 18 | assertThatServers(localhost8080) 19 | .haveReceivedRequest(GET, '/custom/load/balancer') 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /charon-test/src/main/groovy/com/github/mkopylec/charon/test/specification/DefaultCharonConfigurationBasicSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test.specification 2 | 3 | import org.springframework.test.context.TestPropertySource 4 | 5 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThat 6 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThatServers 7 | import static org.springframework.http.HttpMethod.GET 8 | import static org.springframework.http.HttpStatus.OK 9 | 10 | @TestPropertySource(properties = ['default-charon-configuration: true']) 11 | abstract class DefaultCharonConfigurationBasicSpec extends BasicSpec { 12 | 13 | def "Should handle all requests locally when no custom Charon configuration is provided"() { 14 | when: 15 | def response = http.sendRequest(GET, '/default/charon/configuration') 16 | 17 | then: 18 | assertThat(response) 19 | .hasStatus(OK) 20 | .hasBody('default charon configuration response') 21 | assertThatServers(localhost8080, localhost8081) 22 | .haveNotReceivedRequest() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /charon-test/src/main/groovy/com/github/mkopylec/charon/test/specification/MappingResolvingBasicSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test.specification 2 | 3 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThat 4 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThatServers 5 | import static org.springframework.http.HttpMethod.GET 6 | import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR 7 | import static org.springframework.http.HttpStatus.OK 8 | 9 | abstract class MappingResolvingBasicSpec extends BasicSpec { 10 | 11 | def "Should handle request locally when no request mapping could be found"() { 12 | when: 13 | def response = http.sendRequest(GET, '/no/mapping/found') 14 | 15 | then: 16 | assertThat(response) 17 | .hasStatus(OK) 18 | .hasBody('no mapping found response') 19 | assertThatServers(localhost8080, localhost8081) 20 | .haveNotReceivedRequest() 21 | } 22 | 23 | def "Should fail to forward request when multiple request mappings are found"() { 24 | when: 25 | def response = http.sendRequest(GET, '/multiple/mappings/found') 26 | 27 | then: 28 | assertThat(response) 29 | .hasStatus(INTERNAL_SERVER_ERROR) 30 | .bodyContains("More than one request mapping matches /multiple/mappings/found incoming request. Matching mappings are 'multiple mappings found 1', 'multiple mappings found 2'") 31 | assertThatServers(localhost8080, localhost8081) 32 | .haveNotReceivedRequest() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /charon-test/src/main/groovy/com/github/mkopylec/charon/test/specification/RateLimitingBasicSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test.specification 2 | 3 | import org.springframework.test.annotation.DirtiesContext 4 | 5 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThat 6 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThatMetrics 7 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThatServers 8 | import static com.github.mkopylec.charon.test.stubs.OutgoingServersStubs.outgoingServers 9 | import static org.springframework.http.HttpMethod.GET 10 | import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR 11 | import static org.springframework.http.HttpStatus.OK 12 | 13 | abstract class RateLimitingBasicSpec extends BasicSpec { 14 | 15 | @DirtiesContext 16 | def "Should limit request forwarding rate when proper interceptor is set"() { 17 | when: 18 | http.sendRequest(GET, '/rate/limiting') 19 | def response = http.sendRequest(GET, '/rate/limiting') 20 | 21 | then: 22 | assertThat(response) 23 | .hasStatus(INTERNAL_SERVER_ERROR) 24 | .bodyContains("RateLimiter 'rate limiting' does not permit further calls") 25 | assertThatServers(localhost8080, localhost8081) 26 | .haveReceivedRequest(GET, '/rate/limiting') 27 | assertThatMetrics() 28 | .haveCaptured('charon.rate limiting.rate-limiting.available-permissions') 29 | .haveCaptured('charon.rate limiting.rate-limiting.waiting-threads') 30 | } 31 | 32 | void setup() { 33 | outgoingServers(localhost8080, localhost8081) 34 | .stubResponse(OK) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /charon-test/src/main/groovy/com/github/mkopylec/charon/test/specification/RequestBodyRewritingBasicSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test.specification 2 | 3 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThat 4 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThatServers 5 | import static com.github.mkopylec.charon.test.stubs.OutgoingServersStubs.outgoingServers 6 | import static org.springframework.http.HttpMethod.POST 7 | import static org.springframework.http.HttpStatus.OK 8 | 9 | abstract class RequestBodyRewritingBasicSpec extends BasicSpec { 10 | 11 | def "Should rewrite forwarded request body using custom interceptor"() { 12 | given: 13 | outgoingServers(localhost8080, localhost8081) 14 | .stubResponse(OK) 15 | 16 | when: 17 | def response = http.sendRequest(POST, '/request/body/rewriting', originalBody) 18 | 19 | then: 20 | assertThat(response) 21 | .hasStatus(OK) 22 | assertThatServers(localhost8080, localhost8081) 23 | .haveReceivedRequest(POST, '/request/body/rewriting', ['Content-Length': '22'], 'Rewritten request body') 24 | 25 | where: 26 | originalBody << ['', 'original body'] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /charon-test/src/main/groovy/com/github/mkopylec/charon/test/specification/RequestHostHeaderRewritingBasicSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test.specification 2 | 3 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThat 4 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThatServers 5 | import static com.github.mkopylec.charon.test.stubs.OutgoingServersStubs.outgoingServers 6 | import static org.springframework.http.HttpMethod.GET 7 | import static org.springframework.http.HttpStatus.OK 8 | 9 | abstract class RequestHostHeaderRewritingBasicSpec extends BasicSpec { 10 | 11 | def "Should rewrite request 'Host' header when proper interceptor is set"() { 12 | when: 13 | def response = http.sendRequest(GET, '/request/host/header', ['Host': 'example.com']) 14 | 15 | then: 16 | assertThat(response) 17 | .hasStatus(OK) 18 | assertThatServers(localhost8080) 19 | .haveReceivedRequest(GET, '/request/host/header', ['Host': 'localhost:8080']) 20 | } 21 | 22 | def "Should not rewrite request 'Host' header by default"() { 23 | when: 24 | def response = http.sendRequest(GET, '/default', ['Host': 'example.com']) 25 | 26 | then: 27 | assertThat(response) 28 | .hasStatus(OK) 29 | assertThatServers(localhost8080, localhost8081) 30 | .haveReceivedRequest(GET, '/default', ['Host': 'example.com']) 31 | } 32 | 33 | void setup() { 34 | outgoingServers(localhost8080, localhost8081) 35 | .stubResponse(OK) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /charon-test/src/main/groovy/com/github/mkopylec/charon/test/specification/ResponseBodyRewritingBasicSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test.specification 2 | 3 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThat 4 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThatServers 5 | import static com.github.mkopylec.charon.test.stubs.OutgoingServersStubs.outgoingServers 6 | import static org.springframework.http.HttpMethod.GET 7 | import static org.springframework.http.HttpStatus.OK 8 | 9 | abstract class ResponseBodyRewritingBasicSpec extends BasicSpec { 10 | 11 | def "Should rewrite forwarded response body using custom interceptor"() { 12 | given: 13 | outgoingServers(localhost8080, localhost8081) 14 | .stubResponse(OK, originalBody) 15 | 16 | when: 17 | def response = http.sendRequest(GET, '/response/body/rewriting') 18 | 19 | then: 20 | assertThat(response) 21 | .hasBody('Rewritten response body') 22 | .containsHeaders(['Content-Length': '23']) 23 | assertThatServers(localhost8080, localhost8081) 24 | .haveReceivedRequest(GET, '/response/body/rewriting') 25 | 26 | where: 27 | originalBody << ['', 'original body'] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /charon-test/src/main/groovy/com/github/mkopylec/charon/test/specification/TimeoutBasicSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test.specification 2 | 3 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThat 4 | import static com.github.mkopylec.charon.test.assertions.Assertions.assertThatServers 5 | import static com.github.mkopylec.charon.test.stubs.OutgoingServersStubs.outgoingServers 6 | import static com.github.mkopylec.charon.test.stubs.ResponseDelay.TIMED_OUT 7 | import static org.springframework.http.HttpMethod.GET 8 | import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR 9 | import static org.springframework.http.HttpStatus.OK 10 | 11 | abstract class TimeoutBasicSpec extends BasicSpec { 12 | 13 | def "Should fail to forward request when outgoing server responds too long"() { 14 | given: 15 | outgoingServers(localhost8080, localhost8081) 16 | .stubResponse(OK, TIMED_OUT) 17 | 18 | when: 19 | def response = http.sendRequest(GET, '/timeout') 20 | 21 | then: 22 | assertThat(response) 23 | .hasStatus(INTERNAL_SERVER_ERROR) 24 | assertThatServers(localhost8080, localhost8081) 25 | .haveReceivedRequest(GET, '/timeout') 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /charon-test/src/main/groovy/com/github/mkopylec/charon/test/stubs/OutgoingServersStubs.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test.stubs 2 | 3 | import org.springframework.http.HttpStatus 4 | 5 | import static com.github.mkopylec.charon.test.stubs.ResponseDelay.REAL 6 | 7 | class OutgoingServersStubs { 8 | 9 | private List outgoingServers 10 | 11 | private OutgoingServersStubs(OutgoingServer... outgoingServers) { 12 | this.outgoingServers = outgoingServers 13 | } 14 | 15 | static OutgoingServersStubs outgoingServers(OutgoingServer... outgoingServers) { 16 | return new OutgoingServersStubs(outgoingServers) 17 | } 18 | 19 | void stubResponse(HttpStatus status) { 20 | outgoingServers.each { it.stubResponse(status, [:], null, REAL, 1) } 21 | } 22 | 23 | void stubResponse(HttpStatus status, int times) { 24 | outgoingServers.each { it.stubResponse(status, [:], null, REAL, times) } 25 | } 26 | 27 | void stubResponse(HttpStatus status, String body) { 28 | outgoingServers.each { it.stubResponse(status, [:], body, REAL, 1) } 29 | } 30 | 31 | void stubResponse(HttpStatus status, ResponseDelay delay) { 32 | outgoingServers.each { it.stubResponse(status, [:], null, delay, 1) } 33 | } 34 | 35 | void stubResponse(HttpStatus status, String body, int times) { 36 | outgoingServers.each { it.stubResponse(status, [:], body, REAL, times) } 37 | } 38 | 39 | void stubResponse(HttpStatus status, Map headers) { 40 | outgoingServers.each { it.stubResponse(status, headers, null, REAL, 1) } 41 | } 42 | 43 | void clearStubs() { 44 | outgoingServers.each { it.clear() } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /charon-test/src/main/groovy/com/github/mkopylec/charon/test/stubs/ResponseDelay.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test.stubs 2 | 3 | enum ResponseDelay { 4 | 5 | REAL(0), 6 | TIMED_OUT(1) 7 | 8 | private int seconds 9 | 10 | ResponseDelay(int seconds) { 11 | this.seconds = seconds 12 | } 13 | 14 | protected int getSeconds() { 15 | return seconds 16 | } 17 | } -------------------------------------------------------------------------------- /charon-test/src/main/groovy/com/github/mkopylec/charon/test/utils/MeterRegistryProvider.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test.utils 2 | 3 | import io.micrometer.core.instrument.MeterRegistry 4 | import io.micrometer.core.instrument.simple.SimpleMeterRegistry 5 | 6 | class MeterRegistryProvider { 7 | 8 | private static MeterRegistry meterRegistry = new SimpleMeterRegistry() 9 | 10 | static MeterRegistry meterRegistry() { 11 | return meterRegistry 12 | } 13 | 14 | static void clearMetrics() { 15 | meterRegistry.clear() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /charon-test/src/main/java/com/github/mkopylec/charon/test/CommonExceptionThrower.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorType; 4 | import org.slf4j.Logger; 5 | 6 | abstract class CommonExceptionThrower { 7 | 8 | private Logger log; 9 | 10 | CommonExceptionThrower(Logger log) { 11 | this.log = log; 12 | } 13 | 14 | public RequestForwardingInterceptorType getType() { 15 | return new RequestForwardingInterceptorType(201); 16 | } 17 | 18 | void logStart(String mappingName) { 19 | log.trace("[Start] Exception throwing for '{}' request mapping", mappingName); 20 | } 21 | 22 | RuntimeException createFailure() { 23 | return new RuntimeException("Error forwarding request"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /charon-test/src/main/java/com/github/mkopylec/charon/test/CommonRequestBodyRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorType; 4 | import org.slf4j.Logger; 5 | 6 | abstract class CommonRequestBodyRewriter { 7 | 8 | private Logger log; 9 | 10 | CommonRequestBodyRewriter(Logger log) { 11 | this.log = log; 12 | } 13 | 14 | public RequestForwardingInterceptorType getType() { 15 | return new RequestForwardingInterceptorType(666); 16 | } 17 | 18 | void logStart(String mappingName) { 19 | log.trace("[Start] Request body rewriting for '{}' request mapping", mappingName); 20 | } 21 | 22 | void logEnd(String mappingName) { 23 | log.trace("[End] Request body rewriting for '{}' request mapping", mappingName); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /charon-test/src/main/java/com/github/mkopylec/charon/test/CommonResponseBodyRewriter.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import com.github.mkopylec.charon.forwarding.interceptors.RequestForwardingInterceptorType; 4 | import org.slf4j.Logger; 5 | 6 | abstract class CommonResponseBodyRewriter { 7 | 8 | private Logger log; 9 | 10 | CommonResponseBodyRewriter(Logger log) { 11 | this.log = log; 12 | } 13 | 14 | public RequestForwardingInterceptorType getType() { 15 | return new RequestForwardingInterceptorType(667); 16 | } 17 | 18 | void logStart(String mappingName) { 19 | log.trace("[Start] Response body rewriting for '{}' request mapping", mappingName); 20 | } 21 | 22 | void logEnd(String mappingName) { 23 | log.trace("[End] Response body rewriting for '{}' request mapping", mappingName); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /charon-test/src/main/java/com/github/mkopylec/charon/test/LocalEndpoint.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | @RestController 7 | class LocalEndpoint { 8 | 9 | @GetMapping("/no/mapping/found") 10 | String handleNoMappingFound() { 11 | return "no mapping found response"; 12 | } 13 | 14 | @GetMapping("/default/charon/configuration") 15 | String handleDefaultCharonConfiguration() { 16 | return "default charon configuration response"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /charon-test/src/main/java/com/github/mkopylec/charon/test/ReverseProxyApplication.java: -------------------------------------------------------------------------------- 1 | package com.github.mkopylec.charon.test; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | 5 | import static org.springframework.boot.SpringApplication.run; 6 | 7 | @SpringBootApplication 8 | class ReverseProxyApplication { 9 | 10 | public static void main(String[] args) { 11 | run(ReverseProxyApplication.class, args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkopylec/charon-spring-boot-starter/194084171e63c69e77a6b2950910246b0ac80dba/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /release: -------------------------------------------------------------------------------- 1 | while getopts v: opts; do 2 | case ${opts} in 3 | v) VERSION=${OPTARG} ;; 4 | esac 5 | done 6 | 7 | if [ -z "$VERSION" ]; then 8 | echo "Set release version using -v option" 9 | exit 1 10 | else 11 | ./gradlew clean test release -Prelease.forceVersion=$VERSION && ./gradlew publishToSonatype -PsignArtifacts closeAndReleaseStagingRepositories 12 | fi 13 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'charon-spring-boot-starter' 2 | include 'charon-common' 3 | include 'charon-test' 4 | include 'charon-spring-webmvc' 5 | include 'charon-spring-webflux' 6 | --------------------------------------------------------------------------------