mkopylec / charon-spring-boot-starter Goto Github PK
View Code? Open in Web Editor NEWReverse proxy implementation in form of a Spring Boot starter.
License: Apache License 2.0
Reverse proxy implementation in form of a Spring Boot starter.
License: Apache License 2.0
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 ?
There is already a trace checkpoint on error in every retry but there is nothing to handle that the whole operation has failed.
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
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.
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
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?
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.
Create a configuration property responsible for naming the retryable exceptions.
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 ?
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?
Would you mind merging in branch SB 1.5 and release?
#66
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
.
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
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 ;)
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)
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?
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?
Just for curiosity, why the name Charon ?
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?
Is mkopylec still maintaining charon?
If no, I'll fork it and use the fork.
Thanks to clarify.
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
We would like to integrate Grafana using 'authproxy' feature with our SpringBoot app see https://grafana.com/blog/2015/12/07/grafana-authproxy-have-it-your-way/ ... to share authentication Grafana requires custom header (X-WEBAUTH-USER) to be forwarder with proxied request. Is there a way how to add custom header like this?
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
Capture trace data when a single try to forward request will fail.
Web sockets appear to not work with this. I get a read timed out. Am I missing something, or are they not supported?
I think they are not while the mappings are updated on error.
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
Hi,
Is it possible to update this project to spring-boot 2.0?
Here are my changes for spring-boot 2.0.1 The groovy test still needs to be corrected. Note: @qualified and name are not necessary if the factory method is named accordingly.
migrateToSpringBoot201.patch.txt
Thanks,
Torsten
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)
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
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})
Move retrying settings to mapping configuration.
Set the default number of max attempts to 1.
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).
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
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?
I don't know how to ask that except by opening an issue:
When will you release next version with Host header #45 fixed?
Trace ID is not visible in executors threads, because it is kept in ThreadLocal
field.
Don't know yet which strategy to choose:
Hi,
Thanks for this handy reverse proxy.
Is it possible to manipulate subsequent mappings from the configuration?
I need a way to put the port later.
Thanks,
Torstem
request to /path/* -> mapped to /path
request to /path/something/* -> mapped to /path/something
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
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?
Use some third party library.
Allow to create custom metric reporters.
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
@RestController
@RequestMapping("/api/echo")
public class EchoController {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String echo() {
return null;
}
}
ab -v 2 -n 5000 -c 20 http://localhost:8080/api/echo/hello
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
We need add a header in every response (this token takes data from response)
TraceInterceptor es read only... We need to be able to modify the response to the client
Expose configuration properties for thread executor that is used for aynchronous mappings handling.
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?
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..
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"
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.
Create an inner class in CharonProperties
instead of inline fields.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.