Giter Site home page Giter Site logo

mkopylec / charon-spring-boot-starter Goto Github PK

View Code? Open in Web Editor NEW
235.0 14.0 53.0 1.34 MB

Reverse proxy implementation in form of a Spring Boot starter.

License: Apache License 2.0

Java 75.90% Groovy 24.10%
charon java reverse-proxy reverse proxy spring-boot webmvc webflux spring

charon-spring-boot-starter's Introduction

Charon Spring Boot Starter

Build Status Code Coverage Maven Central

Charon is a reverse proxy implementation. It automatically forwards HTTP requests from one HTTP server to another and sends back the received HTTP response to the client. There are some alternative reverse proxy implementations like Zuul or Smiley's HTTP Proxy Servlet. Zuul is highly bounded to Spring Cloud Netflix, Smiley's HTTP Proxy Servlet is a simple one, without advanced features. Charon is a universal Spring Boot tool. It already has a lot of features implemented and its architecture provides an easy way to add new ones.

Features

  • highly configurable and extensible
  • Spring WebMVC and WebFlux support
  • multiple request forwarding mappings
  • load balancing
  • flexible path rewriting
  • Resilience4j support
  • metrics based on Micrometer
  • asynchronous request forwarding
  • authentication
  • cookies rewriting
  • 'X-Forwarded' HTTP headers support
  • forwarding process intercepting
  • configurable HTTP client

Migrating from older versions to 4.x.x

Charon was completely rewritten, configuration via application.yml file is no longer available. Now Charon can be configured by in-code configuration. See the documentation for more details.

Documentation

Full documentation is located here.

License

Charon Spring Boot Starter is published under Apache License 2.0.

charon-spring-boot-starter's People

Contributors

cezarykluczynski avatar jecvdch avatar mkopylec avatar richardbradley avatar wieczorslawo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

charon-spring-boot-starter's Issues

Routing through a web proxy

One of our route needs to be routed through a webproxy (Squid like)
Is there a way to do it?
If not, I'll write some code and generate a PR

Release last version

I don't know how to ask that except by opening an issue:
When will you release next version with Host header #45 fixed?

Does it support path rewrite?

Does this library support path rewrite?

For example in spring cloud gateway, we are able to rewrite the path and use regular expressions

b.route("universal-annuities", r -> r.path( "some/path/**") .filters(f -> { f.rewritePath("/some/path/(?<segment>.*)", "/new/path/${segment}"); return f; }) .uri(destinationUri));

If not, any intent to add this to the library?

about performance ?

about performance

charon config

charon:
    filter-order: 100000 
    timeout:
        connect: 5000
        read: 5000
    retrying:
        max-attempts: 3 
    metrics:
        enabled: false 
        names-prefix: charon
        logging-reporter:
            enabled: false 
            reporting-interval-in-seconds: 60 
    tracing:
        enabled: false 
    mappings-update:
        enabled: false 
    mappings:
        -
            name: echo
            path: /api/echo/
            destinations: http://localhost:9092/api/echo/
            asynchronous: true 

echo server

@RestController
@RequestMapping("/api/echo") 
public class EchoController {

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String echo() {      
        return null;
    }

}

use ab test

ab -v 2 -n 5000 -c 20 http://localhost:8080/api/echo/hello

some error in console

17:06:50.759 ERROR com.github.mkopylec.charon.core.retry.LoggingListener - All 1 attempts to forward HTTP request using 'echo' mapping has failed
[WARNING]
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:9092/api/echo/hello": connection timed out: localhost/127.0.0.1:9092; nested exception is io.netty.channel.ConnectTimeoutException: connection timed out: localhost/127.0.0.1:9092
        at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:607)
        at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:572)
        at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:534)
        at com.github.mkopylec.charon.core.http.RequestForwarder.sendRequest(RequestForwarder.java:99)
        at com.github.mkopylec.charon.core.http.RequestForwarder.forwardHttpRequest(RequestForwarder.java:67)
        at com.github.mkopylec.charon.core.http.ReverseProxyFilter.lambda$null$3(ReverseProxyFilter.java:96)
        at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:263)
        at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:154)
        at com.github.mkopylec.charon.core.http.ReverseProxyFilter.lambda$doFilterInternal$4(ReverseProxyFilter.java:95)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
Caused by: io.netty.channel.ConnectTimeoutException: connection timed out: localhost/127.0.0.1:9092
        at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe$1.run(AbstractNioChannel.java:220)
        at io.netty.util.concurrent.PromiseTask$RunnableAdapter.call(PromiseTask.java:38)
        at io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:120)
        at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:358)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:374)
        at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112)
        at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
        ... 1 more
17:06:55.872 ERROR com.github.mkopylec.charon.core.retry.LoggingListener - All 1 attempts to forward HTTP request using 'echo' mapping has failed

when try to test 9092 , No error appears in the console.

ab -v 2 -n 5000 -c 20 http://localhost:9092/api/echo/hello

Is there XHR support available?

When working with Grafana behind Charon reverse proxy I found out, that some(many) Grafana parts/pages are not working. After investigation I found out, that XHR(ajax) calls are not proxied properly. Grafana is using Angular that communicates with server using ajax/xhr . These requests remain as pending in browser ... that means that request was send to charon, but there is no reply back. (see screenshot from browser)

image

In SpringBoot I can see this request beeing forwarded (see below) but somehow no response is sent back to browser:
c.g.m.charon.core.http.RequestForwarder : Forwarding: GET /grafana/api/dashboards/db/es-polna -> http://localhost:3000/api/dashboards/db/es-polna 200

Any idea what is wrong here?

Does it support path rewrite?

Does this library support path rewrite?

For example in spring cloud gateway, we are able to rewrite the path and use regular expressions.

    b.route("route",
        r -> r.path("some/path/**")
            .filters(f -> {
                f.rewritePath("/some/path/(?<segment>.*)", "/new/path/${segment}");
                return f;
            })
            .uri(destinationUri));

If not, any intent to add this to the library?

Turn off mappings update by default

Mappings updates triggered after errors only make sense if custom MappingsProvider is provided, because this is the only way the mappings can be changed during runtime.
Therefore by default they should be disabled.

Not working with HTTPS by default

When you try to proxy to HTTPS destination you get:

org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://e.video-cdn.net/video": null; nested exception is java.nio.channels.ClosedChannelException
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:666) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:628) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:590) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at com.github.mkopylec.charon.core.http.RequestForwarder.sendRequest(RequestForwarder.java:96) ~[charon-spring-boot-starter-2.0.1.jar:na]
	at com.github.mkopylec.charon.core.http.RequestForwarder.forwardHttpRequest(RequestForwarder.java:64) ~[charon-spring-boot-starter-2.0.1.jar:na]
	at com.github.mkopylec.charon.core.http.ReverseProxyFilter.lambda$forwardToDestination$2(ReverseProxyFilter.java:123) ~[charon-spring-boot-starter-2.0.1.jar:na]
	at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:286) ~[spring-retry-1.2.0.RELEASE.jar:na]
	at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:163) ~[spring-retry-1.2.0.RELEASE.jar:na]
	at com.github.mkopylec.charon.core.http.ReverseProxyFilter.forwardToDestination(ReverseProxyFilter.java:123) ~[charon-spring-boot-starter-2.0.1.jar:na]
	at com.github.mkopylec.charon.core.http.ReverseProxyFilter.doFilterInternal(ReverseProxyFilter.java:93) ~[charon-spring-boot-starter-2.0.1.jar:na]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:110) ~[spring-boot-actuator-1.5.3.RELEASE.jar:1.5.3.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177) ~[spring-security-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106) ~[spring-boot-actuator-1.5.3.RELEASE.jar:1.5.3.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.8.RELEASE.jar:4.3.8.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478) [tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80) [tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799) [tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861) [tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455) [tomcat-embed-core-8.5.14.jar:8.5.14]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.14.jar:8.5.14]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_60]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_60]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.14.jar:8.5.14]
	at java.lang.Thread.run(Thread.java:745) [na:1.8.0_60]
Caused by: java.nio.channels.ClosedChannelException: null

This happens because Netty is not configured with SSLContext. To fix that I have provided my own HttpClientProvider bean with overridden createHttpClient(MappingProperties):

    // Enable TLS protocols disabled by default.
    @Bean
    public HttpClientProvider charonHttpClientProvider(
            CharonProperties charonProperties) {
        return new HttpClientProvider(charonProperties) {

            @Override
            protected RestOperations createHttpClient(MappingProperties mapping) {
                SslContext sslContext;
                try {
                    sslContext = SslContextBuilder.forClient().build();
                } catch (SSLException e) {
                    throw new IllegalStateException("Failed to initialize SslContext for Charon");
                }

                Netty4ClientHttpRequestFactory requestFactory = new Netty4ClientHttpRequestFactory();

                requestFactory.setSslContext(sslContext);
                requestFactory.setConnectTimeout(mapping.getTimeout().getConnect());
                requestFactory.setReadTimeout(mapping.getTimeout().getRead());

                return new RestTemplate(requestFactory);
            }
        };
    }

But actually the only difference with the default one configured in Spring Boot CharonConfiguration could be put in one line:
requestFactory.setSslContext(SslContextBuilder.forClient().build());

I think this should be the default config (as HTTPS is actually pretty wide-spread protocol), or at least it should be configurable through properties without bean override.

No news from mkopylec?

Is mkopylec still maintaining charon?
If no, I'll fork it and use the fork.
Thanks to clarify.

Error creating bean with name 'charonReverseProxyFilterRegistrationBean'

When I tried to use charon sproing boot starter I have an error

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'charonReverseProxyFilterRegistrationBean' defined in class
..........................................
[org.springframework.web.client.RestOperations]: No qualifying bean of type [org.springframework.web.client.RestOperations] found for dependency: expected at least 1 bean which qual
ifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=charonRestOperations)}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type
[org.springframework.web.client.RestOperations] found for dependency: expected at least 1 bean

Runtime Exception "No qualifying bean of type 'org.springframework.core.task.TaskExecutor'"

The factory below will never produce an task executor object if isAsynchronousMappingPresent is false.

@Bean
    @ConditionalOnMissingBean
    public TaskExecutor charonTaskExecutor() {
        if (isAsynchronousMappingPresent()) {
            ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
            taskExecutor.setQueueCapacity(charon.getAsynchronousForwardingThreadPool().getQueueCapacity());
            taskExecutor.setCorePoolSize(charon.getAsynchronousForwardingThreadPool().getSize().getInitial());
            taskExecutor.setMaxPoolSize(charon.getAsynchronousForwardingThreadPool().getSize().getMaximum());
            return taskExecutor;
        }
        return null;
    }

Maybe just a problem with the spring-boot 2.x.x.

In my old migration hack you can see my workaround

For information: Spring uses the bean name as a default qualifier value. I think the qualifier annotation is superfluous, if you rename the parameters.

Host header is not rewritten

I am finding that Charon is forwarding my requests to the remote server with a Host header holding the old value (i.e. the host name of my local server). This is causing my requests to fail.

Is this a known issue?

I will attempt to isolate and create a PR in the next day or two, unless this is my mistake somehow?

My Spring config looks like this:

charon.mappings[0].name: wordpress
charon.mappings[0].path: /wp-content
charon.mappings[0].destinations: https://example.com/wp-content
charon.mappings[0].timeout.read: 10000

how to ignore SSL

PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Unable to start Charon due to problem with dual ServerProperties bean

We have an application embedding Spring Cloud Zuul (Intelligent microservices reverse proxy)
If we add Charon to this application, it'won't start anymore. It fail with the error:

`


APPLICATION FAILED TO START


Description:
Field server in com.github.mkopylec.charon.configuration.CharonConfiguration required a single bean, but 2 were found:
- serverProperties: defined by method 'serverProperties' in org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration
- server-org.springframework.boot.autoconfigure.web.ServerProperties: defined in null
`

We have workaround it by excluding CharonConfiguration
@SpringBootApplication(exclude = {CharonConfiguration.class})
And adding our copy-paste version, while removing ServerProperties here:
@EnableConfigurationProperties({CharonProperties.class})

multipart/form-data support ?

Has anyone reported or is there any solution to traffic multipart/form-data content-type?
When I submit this content-type, the following error is displayed: java.io.IOException: UT000036: Connection terminated parsing multipart data

This is because the file gets lost in the proxy.
Any solution? Can you help me with this?

Thanks

Consider renaming the starter

spring-projects/spring-boot#6117 requests to include this starter in the list of 3rd party starters so I had a quick look to it.

Could you please consider renaming this project into something less generic? A code name of some kind rather than "reverse-proxy" would be good. The positive side effect is that the root group for configuration key would be a single word (rather than reverse-proxy which is quite unusual).

Problem with j_security_check login to Tomcat

I have strange problem - it looks like Charon proxy does not pass login form data.

My charon version is:

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.10.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

My charon code is simple default Spring Boot starter without any interceptor:

package cz.tsp.kbe;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CharonTestApplication {

	public static void main(String[] args) {
		SpringApplication.run(CharonTestApplication.class, args);
	}
}

There is configuration - intention is to proxy http://localhost:8080/APG/ to http://172.24.36.70:58080/APG/
application.yml:

charon.mappings:
    -
        name: my mapping
        path: /APG
        destinations: http://172.24.36.70:58080
        strip-path: false

charon.tracing.enabled: 

Problem is with standard tomcat j_security_check form login, when login form data is sent via POST method.
I catched HTTP communication to target using wireshark.
When I access target server directly without charon proxy, the necessary form item j_username and j_password are sent and wireshart see it.
When I access server through proxy, form ites are missing and event ContentLength header is set to zero.

There is wireshark log, first POST is direct to target, second via charon proxy, (I am sending wrong password intentionally to prevent huge wireshark log by page load after login):

No.     Time           Source                Destination           Protocol Length Info
     22 9.423414       172.24.5.21           172.24.36.70          HTTP     668    POST /APG/j_security_check HTTP/1.1  (application/x-www-form-urlencoded)

Frame 22: 668 bytes on wire (5344 bits), 668 bytes captured (5344 bits) on interface 0
Ethernet II, Src: Flextron_c0:3a:51 (00:21:cc:c0:3a:51), Dst: Cisco_97:8b:8f (b0:fa:eb:97:8b:8f)
Internet Protocol Version 4, Src: 172.24.5.21, Dst: 172.24.36.70
Transmission Control Protocol, Src Port: 50328, Dst Port: 58080, Seq: 1, Ack: 1, Len: 614
Hypertext Transfer Protocol
    POST /APG/j_security_check HTTP/1.1\r\n
    Host: 172.24.36.70:58080\r\n
    Connection: keep-alive\r\n
    Content-Length: 37\r\n
        [Content length: 37]
    Accept: */*\r\n
    Origin: http://172.24.36.70:58080\r\n
    X-Requested-With: XMLHttpRequest\r\n
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\r\n
    Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n
    Referer: http://172.24.36.70:58080/APG/\r\n
    Accept-Encoding: gzip, deflate\r\n
    Accept-Language: cs,en;q=0.9,sk;q=0.8,en-US;q=0.7\r\n
    Cookie: tree_width=231; JSESSIONID=26FC6E139E445D3E5172606730BD40BC\r\n
        Cookie pair: tree_width=231
        Cookie pair: JSESSIONID=26FC6E139E445D3E5172606730BD40BC
    \r\n
    [Full request URI: http://172.24.36.70:58080/APG/j_security_check]
    [HTTP request 1/1]
    [Response in frame: 25]
    File Data: 37 bytes
HTML Form URL Encoded: application/x-www-form-urlencoded
    Form item: "j_username" = "adminx"
        Key: j_username
        Value: adminx
    Form item: "j_password" = "changeme"
        Key: j_password
        Value: changeme

No.     Time           Source                Destination           Protocol Length Info
     25 9.427447       172.24.36.70          172.24.5.21           HTTP     96     HTTP/1.1 200 OK  (application/json)

Frame 25: 96 bytes on wire (768 bits), 96 bytes captured (768 bits) on interface 0
Ethernet II, Src: Cisco_97:8b:8f (b0:fa:eb:97:8b:8f), Dst: Flextron_c0:3a:51 (00:21:cc:c0:3a:51)
Internet Protocol Version 4, Src: 172.24.36.70, Dst: 172.24.5.21
Transmission Control Protocol, Src Port: 58080, Dst Port: 50328, Seq: 150, Ack: 615, Len: 42
[2 Reassembled TCP Segments (191 bytes): #24(149), #25(42)]
Hypertext Transfer Protocol
    HTTP/1.1 200 OK\r\n
    Server: Apache-Coyote/1.1\r\n
    Content-Type: application/json;charset=UTF-8\r\n
    Content-Length: 42\r\n
        [Content length: 42]
    Date: Fri, 13 Apr 2018 15:42:54 GMT\r\n
    \r\n
    [HTTP response 1/1]
    [Time since request: 0.004033000 seconds]
    [Request in frame: 22]
    File Data: 42 bytes
JavaScript Object Notation: application/json
    Object
        Member Key: error
            String value: Invalid Login and/or Password
            Key: error

No.     Time           Source                Destination           Protocol Length Info
     36 13.476014      172.24.5.21           172.24.36.70          HTTP     801    POST /APG/j_security_check HTTP/1.1 

Frame 36: 801 bytes on wire (6408 bits), 801 bytes captured (6408 bits) on interface 0
Ethernet II, Src: Flextron_c0:3a:51 (00:21:cc:c0:3a:51), Dst: Cisco_97:8b:8f (b0:fa:eb:97:8b:8f)
Internet Protocol Version 4, Src: 172.24.5.21, Dst: 172.24.36.70
Transmission Control Protocol, Src Port: 50335, Dst Port: 58080, Seq: 1, Ack: 1, Len: 747
Hypertext Transfer Protocol
    POST /APG/j_security_check HTTP/1.1\r\n
    host: localhost:8080\r\n
    connection: keep-alive\r\n
    accept: */*\r\n
    origin: http://localhost:8080\r\n
    x-requested-with: XMLHttpRequest\r\n
    user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\r\n
    content-type: application/x-www-form-urlencoded; charset=UTF-8\r\n
    referer: http://localhost:8080/APG/\r\n
    accept-encoding: gzip, deflate, br\r\n
    accept-language: cs,en;q=0.9,sk;q=0.8,en-US;q=0.7\r\n
    cookie: JSESSIONID=ACFA48819DAC2EB3E7DE6F8C53CCE99C; JSESSIONID=2130E398133F87100F7428645AC1B981; oam.Flash.RENDERMAP.TOKEN=1cn10f4jnc\r\n
        Cookie pair: JSESSIONID=ACFA48819DAC2EB3E7DE6F8C53CCE99C
        Cookie pair: JSESSIONID=2130E398133F87100F7428645AC1B981
        Cookie pair: oam.Flash.RENDERMAP.TOKEN=1cn10f4jnc
    X-Forwarded-For: 0:0:0:0:0:0:0:1\r\n
    X-Forwarded-Proto: http\r\n
    X-Forwarded-Host: localhost\r\n
    X-Forwarded-Port: 8080\r\n
    Content-Length: 0\r\n
        [Content length: 0]
    \r\n
    [Full request URI: http://localhost:8080/APG/j_security_check]
    [HTTP request 1/1]
    [Response in frame: 39]

No.     Time           Source                Destination           Protocol Length Info
     39 13.478819      172.24.36.70          172.24.5.21           HTTP     96     HTTP/1.1 200 OK  (application/json)

Frame 39: 96 bytes on wire (768 bits), 96 bytes captured (768 bits) on interface 0
Ethernet II, Src: Cisco_97:8b:8f (b0:fa:eb:97:8b:8f), Dst: Flextron_c0:3a:51 (00:21:cc:c0:3a:51)
Internet Protocol Version 4, Src: 172.24.36.70, Dst: 172.24.5.21
Transmission Control Protocol, Src Port: 58080, Dst Port: 50335, Seq: 150, Ack: 748, Len: 42
[2 Reassembled TCP Segments (191 bytes): #38(149), #39(42)]
Hypertext Transfer Protocol
    HTTP/1.1 200 OK\r\n
    Server: Apache-Coyote/1.1\r\n
    Content-Type: application/json;charset=UTF-8\r\n
    Content-Length: 42\r\n
        [Content length: 42]
    Date: Fri, 13 Apr 2018 15:42:58 GMT\r\n
    \r\n
    [HTTP response 1/1]
    [Time since request: 0.002805000 seconds]
    [Request in frame: 36]
    File Data: 42 bytes
JavaScript Object Notation: application/json
    Object
        Member Key: error
            String value: Invalid Login and/or Password
            Key: error

As seen in log, POST via charon proxy missing completely form data.

I have checked (using Web Sniffer chrome extension) that the form data goes out from browser in both cases.

There is charon trace log:

2018-04-13 17:44:07.698  INFO 7460 --- [nio-8080-exec-2] c.g.m.c.c.trace.LoggingTraceInterceptor  : 
  Trace ID: 89a425df-3f84-4023-adc5-e1e0db9adae3
  Incoming HTTP request received:
    - method: POST
    - uri: /APG/j_security_check
    - headers: {host=[localhost:8080], connection=[keep-alive], content-length=[37], accept=[*/*], origin=[http://localhost:8080], x-requested-with=[XMLHttpRequest], user-agent=[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36], content-type=[application/x-www-form-urlencoded; charset=UTF-8], referer=[http://localhost:8080/APG/], accept-encoding=[gzip, deflate, br], accept-language=[cs,en;q=0.9,sk;q=0.8,en-US;q=0.7], cookie=[JSESSIONID=ACFA48819DAC2EB3E7DE6F8C53CCE99C; JSESSIONID=2130E398133F87100F7428645AC1B981; oam.Flash.RENDERMAP.TOKEN=1cn10f4jnc]}
2018-04-13 17:44:07.708  INFO 7460 --- [nio-8080-exec-2] c.g.m.c.c.trace.LoggingTraceInterceptor  : 
  Trace ID: 89a425df-3f84-4023-adc5-e1e0db9adae3
  Forwarding HTTP request started:
    - mapping name: W4N mapping
    - method: POST
    - uri: http://172.24.36.70:58080/APG/j_security_check
    - body: 
    - headers: {host=[localhost:8080], connection=[keep-alive], content-length=[37], accept=[*/*], origin=[http://localhost:8080], x-requested-with=[XMLHttpRequest], user-agent=[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36], content-type=[application/x-www-form-urlencoded; charset=UTF-8], referer=[http://localhost:8080/APG/], accept-encoding=[gzip, deflate, br], accept-language=[cs,en;q=0.9,sk;q=0.8,en-US;q=0.7], cookie=[JSESSIONID=ACFA48819DAC2EB3E7DE6F8C53CCE99C; JSESSIONID=2130E398133F87100F7428645AC1B981; oam.Flash.RENDERMAP.TOKEN=1cn10f4jnc], X-Forwarded-For=[0:0:0:0:0:0:0:1], X-Forwarded-Proto=[http], X-Forwarded-Host=[localhost], X-Forwarded-Port=[8080]}
2018-04-13 17:44:07.791  INFO 7460 --- [nio-8080-exec-2] c.g.m.charon.core.http.RequestForwarder  : Forwarding: POST /APG/j_security_check -> http://172.24.36.70:58080/APG/j_security_check 200
2018-04-13 17:44:07.792  INFO 7460 --- [nio-8080-exec-2] c.g.m.c.c.trace.LoggingTraceInterceptor  : 
  Trace ID: 89a425df-3f84-4023-adc5-e1e0db9adae3
  Forward HTTP response received:
    - status: 200
    - body: {"error":"Invalid Login and\/or Password"}
    - headers: {Server=[Apache-Coyote/1.1], Content-Type=[application/json;charset=UTF-8], Content-Length=[42], Date=[Fri, 13 Apr 2018 15:42:58 GMT]}

What I am doing wrong?

Log mapping name in retry logs

Change metrics-name property to name and pass it through retry context.
The result of the change will be remowing DEFAULT_METRICS_NAME as a metric name and use name property to name metrics. There also should be a one, configurable metrics name prefix, preferably "charon"

HTTPS not working by default

When I try to proxy to HTTPS destination I'm getting:

`2018.03.08;12:17:26 [http-nio-8043-exec-8] [rootId: parentId: eventId: ] INFO o.a.coyote.http11.Http11Processor java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens
at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:421)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:667)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:798)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1434)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

  • Error parsing HTTP request header
    Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
    `

Trying this from SoapUI with url https://something/something and forward to https://something/something

If I try with HTTP everything goes OK. Is this expected behaviour? Why is HTTPS not being recognized?

Thanks in advance

Async forward execution

Create an async config property in mapping.
When async is enabled charon does not wait for the destination to send response, the response is sent immidiatly after forwarding the request..

Bad chunked encoded request fail

We have an IIS server which respond a bad encoded chunked response (PDF data)
This server is proxified by Apache 2.4 and it works, if we get the PDF directly from the server (No RP), it works.

Charon fail at returning the data because the chunked response is not right according to the specification:
https://en.wikipedia.org/wiki/Chunked_transfer_encoding

The chunked response should end with an empty chunked (len=0), but our server doesn't do that. It send the real data in chunked (in fact only one BIG chunk) ans then ends the HTTP stream

We have made a change in Charon to support this, as a workaround. It can be found in the attached PR

What do you think?

Multipart file uploads supported?

I am trying to utilize your proxy implementation to add a security layer between my UI and multiple backend REST services. I have been able to handle all OPTIONS requests as well as all GET requests. I am wondering how to manage proxying a Multipart file upload POST request.

I have found that your implementation utilises the Netty4ClientHttpRequestFactory wich in its documentation indicates "this implementation consistently closes the HTTP connection on each request". So I have overridden the HttpClientProvider to utilize the HttpComponentsAsyncClientHttpRequestFactory instead to allow for keep alive connections.

We have an HAProxy instance between my implementation and the actual REST services. Becuase the security layer has its own DNS host name entry I have also overridden the ReverseProxyFilter.addForwardHeaders to set the HOST header correctly (from the mappingProperties) so that the host name HAPRoxy ACLs will function correctly.

The service I am proxying my requests to continues to return a 400 BAD Request response to my requests die to the body not containing the boundry.

is there special configuration required to handle mutlipart file uploads?

charon.mappings not getting updated when using MappingProvider

I'm just testing around with your library and realized that it seems not to work when using a MappingProvider

@Component
@EnableConfigurationProperties(CharonProperties::class, ServerProperties::class)
class ProxyMappingProvider(server: ServerProperties,
                           charon: CharonProperties,
                           mappingsCorrector: MappingsCorrector,
                           clientProvider: HttpClientProvider) :
        MappingsProvider(server, charon, mappingsCorrector, clientProvider) {

    override fun shouldUpdateMappings(p0: HttpServletRequest?): Boolean {
        return true
    }

    override fun retrieveMappings(): List<MappingProperties> {
        val mappingProperties = MappingProperties()
        mappingProperties.name = "test"
        mappingProperties.path = "test"
        mappingProperties.destinations = listOf("https://google.de")
        return listOf(mappingProperties)
    }
}

This doesn't work because after retrieving the new mappings httpClientProvider.updateHttpClients(); is called which iterates ofter the mappings in the CharonProperties. So before updating the HttpClients the CharonProperties need to be informed over the new settings. I've prepared a patch for this

Request body is always cleared

I failed to POST data on a controller in the same app hosting charon proxy.
The requestbody is eternally empty (but it works if I remove charon).

I think that body is read forn inputstream once in doFilterInternal
byte[] body = extractor.extractBody(request);
but since inputstream has already been read once, it can be reused later in another servlet filter or in the controller, especially if there is no mapping (responseEntity==null).

One solution would be to read body only if mapping exist.
Isn't it ?

Incompatibility with Spring Boot 2.0

I tried loading in the dependency for a Spring Boot 2.0 project, however they made some minor abstractions in the ServerProperties class, breaking this starter.

Currently you use this line of code in MappingsProvider and RequestForwarder:
return correctUri(server.getContextPath()) + mapping.getPath();
While Spring Boot 2 requires:
return correctUri(server.getServlet().getContextPath()) + mapping.getPath();
Can we support both versions?

Not removing servlet context from path

This is my configuration

spring:
  application:
    name: revproxy

server:
  servlet:
    context-path: /${spring.application.name}

logging:
  level:
    com.github.mkopylec.charon: DEBUG

charon:
  mappings:
    -
      name: http-bin-get
      strip-path: false
      path: /get
      destinations: http://httpbin.org

When I try http://127.0.0.1:8080/revproxy/get, I see the following log:

2018-11-20 17:26:05.958 DEBUG 49800 --- [nio-8080-exec-1] c.g.m.c.core.http.ReverseProxyFilter     : Incoming: GET /revproxy/get
2018-11-20 17:26:06.398 DEBUG 49800 --- [nio-8080-exec-1] c.g.m.charon.core.http.RequestForwarder  : Forwarding: GET /revproxy/get -> http://httpbin.org/revproxy/get 404
2018-11-20 17:26:06.399 DEBUG 49800 --- [nio-8080-exec-1] c.g.m.charon.core.retry.LoggingListener  : Attempt 1 to forward HTTP request using 'http-bin-get' mapping has succeeded

Would the correct assumption be that the application context would be removed from the path when sending the forward request? My understanding was that the final request URI would be http://httpbin.org/get.

Websockets

Web sockets appear to not work with this. I get a read timed out. Am I missing something, or are they not supported?

HTTPS required?

When I do this:

build.gradle

compile 'com.github.mkopylec:charon-spring-boot-starter:2.0.2'

application.properties:

charon.mappings[0].name: HTTP anyone
charon.mappings[0].path: /somepath
charon.mappings[0].destinations: http://localhost:8080/otherpath

I get this:

org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:8080/otherpath/api/": not an SSL/TLS record: 485454502f312e3120343030200d0a5472616e736665722d456e636f64696e673a206368756e6b65640d0a446174653a205468752c203237204a756c20323031372030393a31343a323420474d540d0a436f6e6e656374696f6e3a20636c6f73650d0a0d0a300d0a0d0a; nested exception is io.netty.handler.ssl.NotSslRecordException: not an SSL/TLS record: 485454502f312e3120343030200d0a5472616e736665722d456e636f64696e673a206368756e6b65640d0a446174653a205468752c203237204a756c20323031372030393a31343a323420474d540d0a436f6e6e656374696f6e3a20636c6f73650d0a0d0a300d0a0d0a
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:633) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:595) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:557) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
	at com.github.mkopylec.charon.core.http.RequestForwarder.sendRequest(RequestForwarder.java:96) ~[charon-spring-boot-starter-2.0.2.jar:na]
	at com.github.mkopylec.charon.core.http.RequestForwarder.forwardHttpRequest(RequestForwarder.java:64) ~[charon-spring-boot-starter-2.0.2.jar:na]
	at com.github.mkopylec.charon.core.http.ReverseProxyFilter.lambda$forwardToDestination$2(ReverseProxyFilter.java:123) ~[charon-spring-boot-starter-2.0.2.jar:na]
	at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:276) ~[spring-retry-1.1.4.RELEASE.jar:na]
	at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:157) ~[spring-retry-1.1.4.RELEASE.jar:na]
	at com.github.mkopylec.charon.core.http.ReverseProxyFilter.forwardToDestination(ReverseProxyFilter.java:123) ~[charon-spring-boot-starter-2.0.2.jar:na]
	at com.github.mkopylec.charon.core.http.ReverseProxyFilter.doFilterInternal(ReverseProxyFilter.java:93) ~[charon-spring-boot-starter-2.0.2.jar:na]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
[...]

To fix this I had to do this:

    // Disable TLS protocols required by default.
    @Bean
    public HttpClientProvider charonHttpClientProvider(
            CharonProperties charonProperties) {
        return new HttpClientProvider(charonProperties) {

            @Override
            protected RestOperations createHttpClient(MappingProperties mapping) {
                Netty4ClientHttpRequestFactory requestFactory = new Netty4ClientHttpRequestFactory();

                //requestFactory.setSslContext(sslContext); << I think not doing this is the key
                int time = mapping.getTimeout().getConnect();
                requestFactory.setConnectTimeout(time);
                requestFactory.setReadTimeout(mapping.getTimeout().getRead());

                return new RestTemplate(requestFactory);
            }
        };
    }

Is this how I have to do it? Or did I simply miss a property for that?

Logger / Interceptor

Another required feature would be to trace/log/intercept requests.
Such metricRegistry behaviour but a method retrieving headers and content (or just RequestEntity).

I think I can extend RequestDataExtractor, but not sure it is the better way.
What do you think ?

Allow manual mappings update

It would be great to have a manual entry to update mappings, not only when request failed.
In a case where MappingsProvider would be updated externally (updated properties file, or DB storage)

Anyway, nice project. I hope to see more features later ;)

`ForwardedRequestInterceptor` question

I'm looking at using this starter but I need to be able to enforce a valid Bearer token is in the Authorization header. If so I can call a custom service to validate the token. If it's not valid I want to stop the forward from happening. I realize I could simply write another filter and have it execute prior to the ReverseProxyFilter but the problem is I need to set certain headers with values that come from the result of the validation call. What's the best approach here? If I use a separate filter I end up calling the same service twice.

AuthenticationFilter -> AuthenticationService.validateToken
ReverseProxyFilter -> AuthenticationService.validateToken

But if I throw an exception inside of ForwardedRequestInterceptor that has @ResponseStatus(HttpStatus.UNAUTHORIZED) then the TraceFilter bombs and I get a 500 back.

o.s.c.sleuth.instrument.web.TraceFilter : Uncaught exception thrown

Disable Cookie Management in HttpClient provider

Don't stock the cookies that are sent between the client and the browser
This can cause a lot of problems around mixing Sessions between HTTP connections
This can lead to Session steal from a user to another!
Hint: Spring Cloud Hystrix also disables it by default

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.