Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HttpClient does not work with a proxy configuration #2893

Closed
eau-de-la-seine opened this issue Sep 4, 2023 · 3 comments
Closed

HttpClient does not work with a proxy configuration #2893

eau-de-la-seine opened this issue Sep 4, 2023 · 3 comments
Assignees
Labels
status/invalid We don't feel this issue is valid

Comments

@eau-de-la-seine
Copy link

eau-de-la-seine commented Sep 4, 2023

Summary

I would like to configure an HttpClient instance so it can use my mocked target service and mocked proxy:

  • the spring cloud gateway application works perfectly fine when the HttpClient instance's proxy configuration is not involved
  • the tests success when calling the mocked target service and mocked proxy directly from a WebTestClient without the gateway
  • the tests fail when calling the mocked target service and mocked proxy through the gateway with a HttpClient instance that involves a proxy configuration

Similar issues: 617, in this issue it is advised to use hoverfly but I'm not able to use it

Expected Behavior

the gateway works when a proxy configuration is not set for the route, the mocked target service and proxy work (tested with WebTestClient)... so it should work through the gateway with the HttpClient instance set with a proxy configuration too but it does not :/

Actual Behavior

The HttpClient configuration class:

import io.netty.handler.logging.LogLevel;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.config.HttpClientProperties;
import org.springframework.cloud.gateway.filter.NettyRoutingFilter;
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.ServerWebExchange;
import reactor.netty.http.client.HttpClient;
import reactor.netty.transport.ProxyProvider;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static com.accor.wc.gateway.configuration.WcGatewayConfiguration.METADATA_PROXY_AGENT_HOST;
import static com.accor.wc.gateway.configuration.WcGatewayConfiguration.METADATA_PROXY_AGENT_PORT;

@Configuration
public class WcGatewayProxyAgentConfiguration extends NettyRoutingFilter {

    public WcGatewayProxyAgentConfiguration(HttpClient httpClient, ObjectProvider<List<HttpHeadersFilter>> headersFiltersProvider, HttpClientProperties properties) {
        super(httpClient, headersFiltersProvider, properties);
    }

    @Override
    protected HttpClient getHttpClient(Route route, ServerWebExchange exchange) {
        Map<String, Object> metadata = route.getMetadata();
        String proxyAgentHost = (String) metadata.get(METADATA_PROXY_AGENT_HOST);
        HttpClient httpClient = super.getHttpClient(route, exchange);

        // tests that do not involve the proxy work :)
        if (Objects.isNull(proxyAgentHost)) {
            return httpClient;
        }

        // problems start here :(
        Integer proxyAgentPort = (Integer) metadata.get(METADATA_PROXY_AGENT_PORT);
        return httpClient
                .wiretap("debugging: " + route.getId(), LogLevel.INFO) // For debugging
                .proxy(proxy -> proxy
                        .type(ProxyProvider.Proxy.HTTP)
                        // .address(new InetSocketAddress(proxyAgentHost, proxyAgentPort)) // Alternative syntax
                        .host(proxyAgentHost)
                        .port(proxyAgentPort)
                );
    }
}

The test class, there's two @Test, the malfunctioning test is disabled:

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpStatus;
import org.springframework.test.web.reactive.server.WebTestClient;

import static com.accor.wc.gateway.configuration.WcGatewayConfigurationInitDataBeanPostProcessor.PROXY_AGENT_PORT_NUMBER;
import static com.accor.wc.gateway.configuration.WcGatewayConfigurationInitDataBeanPostProcessor.TARGET_PORT_NUMBER;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;

@SpringBootTest(
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
class WcGatewayConfigurationTest {

    @Autowired
    private WebTestClient webTestClientWithGatewayAsEntrypoint;

    private final WireMockServer wireMockServer = new WireMockServer(
            WireMockConfiguration.wireMockConfig().port(TARGET_PORT_NUMBER));
    private final WireMockServer proxyAgentWireMockServer = new WireMockServer(
            WireMockConfiguration.wireMockConfig().port(PROXY_AGENT_PORT_NUMBER));

    @BeforeEach
    public void beforeEach() {
        wireMockServer.start();
        proxyAgentWireMockServer.start();
        wireMockServer.stubFor(
                get(urlEqualTo("/orders/MY-PMID"))
                        .willReturn(
                                aResponse()
                                        .withStatus(HttpStatus.OK.value())
                                        .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
                                        .withBody("[\"hello\"]")
                        ));
    }

    @AfterEach
    public void afterEach() {
        try {
            wireMockServer.stop();
        } finally {
            proxyAgentWireMockServer.stop();
        }
    }

    @Nested
    class ProxyAgentTest {
        @Nested
        class NominalCasesTest {
            /**
             * This test just proves that wiremock works for proxy-agent, but we don't passthrough the gateway
             */
            @Test
            public void nominal_case_without_gateway_as_entrypoint() {
                // GIVEN
                wireMockServer.stubFor(
                        get(urlEqualTo("/test-proxy-agent"))
                                .willReturn(
                                        aResponse()
                                                .withStatus(HttpStatus.OK.value())
                                                .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
                                                .withBody("[\"hello\"]")
                                ));

                proxyAgentWireMockServer.stubFor(
                        get(urlEqualTo("/test-proxy-agent"))
                                .willReturn(
                                        aResponse()
                                                .proxiedFrom(wireMockServer.baseUrl())
                                ));
                WebTestClient webTestClientThatDoesNotUseGatewayAsEntrypoint = WebTestClient
                        .bindToServer()
                        .baseUrl("http://localhost:" + PROXY_AGENT_PORT_NUMBER)
                        .build();

                // WHEN / THEN
                webTestClientThatDoesNotUseGatewayAsEntrypoint
                        .get().uri("/test-proxy-agent")
                        .header("apikey", "x123456")
                        .exchange()
                        .expectStatus().isOk()
                        .expectBody()
                        .json("[\"hello\"]");
            }

            @Disabled
            @Test
            public void nominal_case_with_gateway_as_entrypoint() {
                // GIVEN
                wireMockServer.stubFor(
                        get(urlEqualTo("/test-proxy-agent"))
                                .willReturn(
                                        aResponse()
                                                .withStatus(HttpStatus.OK.value())
                                                .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
                                                .withBody("[\"hello\"]")
                                ));

                proxyAgentWireMockServer.stubFor(
                        get(urlEqualTo("/test-proxy-agent"))
                                .willReturn(
                                        aResponse()
                                                .proxiedFrom("http://127.0.0.1:" + TARGET_PORT_NUMBER)
                                ));

                // WHEN / THEN
                webTestClientWithGatewayAsEntrypoint
                        .get().uri("/test-proxy-agent")
                        .header("apikey", "x123456")
                        .exchange()
                        .expectStatus().isOk()
                        .expectBody()
                        .json("[\"hello\"]");
            }
        }
    }
}

Tests output:

2023-09-04T19:40:57.827+02:00  INFO 141333 --- [           main] w.org.eclipse.jetty.server.Server        : Started @13503ms
2023-09-04T19:40:58.325+02:00  INFO 141333 --- [or-http-epoll-1] debugging: test-proxy-agent              : [aef4b4d3] REGISTERED
2023-09-04T19:40:58.327+02:00  INFO 141333 --- [or-http-epoll-1] debugging: test-proxy-agent              : [aef4b4d3] CONNECT: /127.0.0.1:65432
2023-09-04T19:40:58.330+02:00  INFO 141333 --- [or-http-epoll-1] debugging: test-proxy-agent              : [aef4b4d3, L:/127.0.0.1:44750 - R:127.0.0.1/127.0.0.1:62514] ACTIVE
2023-09-04T19:40:58.357+02:00  INFO 141333 --- [or-http-epoll-1] debugging: test-proxy-agent              : [aef4b4d3-1, L:/127.0.0.1:44750 - R:127.0.0.1/127.0.0.1:62514] WRITE: 372B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 47 45 54 20 2f 74 65 73 74 2d 70 72 6f 78 79 2d |GET /test-proxy-|
|00000010| 61 67 65 6e 74 20 48 54 54 50 2f 31 2e 31 0d 0a |agent HTTP/1.1..|
|00000020| 61 63 63 65 70 74 2d 65 6e 63 6f 64 69 6e 67 3a |accept-encoding:|
|00000030| 20 67 7a 69 70 0d 0a 75 73 65 72 2d 61 67 65 6e | gzip..user-agen|
|00000040| 74 3a 20 52 65 61 63 74 6f 72 4e 65 74 74 79 2f |t: ReactorNetty/|
|00000050| 31 2e 31 2e 31 30 0d 0a 61 63 63 65 70 74 3a 20 |1.1.10..accept: |
|00000060| 2a 2f 2a 0d 0a 57 65 62 54 65 73 74 43 6c 69 65 |*/*..WebTestClie|
|00000070| 6e 74 2d 52 65 71 75 65 73 74 2d 49 64 3a 20 31 |nt-Request-Id: 1|
|00000080| 0d 0a 61 70 69 6b 65 79 3a 20 78 31 32 33 34 35 |..apikey: x12345|
|00000090| 36 0d 0a 46 6f 72 77 61 72 64 65 64 3a 20 70 72 |6..Forwarded: pr|
|000000a0| 6f 74 6f 3d 68 74 74 70 3b 68 6f 73 74 3d 22 6c |oto=http;host="l|
|000000b0| 6f 63 61 6c 68 6f 73 74 3a 33 37 34 34 35 22 3b |ocalhost:37445";|
|000000c0| 66 6f 72 3d 22 31 32 37 2e 30 2e 30 2e 31 3a 34 |for="127.0.0.1:4|
|000000d0| 36 33 34 36 22 0d 0a 58 2d 46 6f 72 77 61 72 64 |6346"..X-Forward|
|000000e0| 65 64 2d 46 6f 72 3a 20 31 32 37 2e 30 2e 30 2e |ed-For: 127.0.0.|
|000000f0| 31 0d 0a 58 2d 46 6f 72 77 61 72 64 65 64 2d 50 |1..X-Forwarded-P|
|00000100| 72 6f 74 6f 3a 20 68 74 74 70 0d 0a 58 2d 46 6f |roto: http..X-Fo|
|00000110| 72 77 61 72 64 65 64 2d 50 6f 72 74 3a 20 33 37 |rwarded-Port: 37|
|00000120| 34 34 35 0d 0a 58 2d 46 6f 72 77 61 72 64 65 64 |445..X-Forwarded|
|00000130| 2d 48 6f 73 74 3a 20 6c 6f 63 61 6c 68 6f 73 74 |-Host: localhost|
|00000140| 3a 33 37 34 34 35 0d 0a 68 6f 73 74 3a 20 31 32 |:37445..host: 12|
|00000150| 37 2e 30 2e 30 2e 31 3a 36 35 34 33 32 0d 0a 63 |7.0.0.1:65432..c|
|00000160| 6f 6e 74 65 6e 74 2d 6c 65 6e 67 74 68 3a 20 30 |ontent-length: 0|
|00000170| 0d 0a 0d 0a                                     |....            |
+--------+-------------------------------------------------+----------------+
2023-09-04T19:40:58.366+02:00  INFO 141333 --- [or-http-epoll-1] debugging: test-proxy-agent              : [aef4b4d3-1, L:/127.0.0.1:44750 - R:127.0.0.1/127.0.0.1:62514] FLUSH
2023-09-04T19:40:58.423+02:00  WARN 141333 --- [or-http-epoll-1] r.netty.http.client.HttpClientConnect    : [aef4b4d3-1, L:/127.0.0.1:44750 - R:127.0.0.1/127.0.0.1:62514] The connection observed an error

io.netty.handler.proxy.HttpProxyHandler$HttpProxyConnectException: http, none, /127.0.0.1:62514 => /127.0.0.1:65432, status: 404 Not Found
	at io.netty.handler.proxy.HttpProxyHandler.handleResponse(HttpProxyHandler.java:200) ~[netty-handler-proxy-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.handler.proxy.ProxyHandler.channelRead(ProxyHandler.java:257) ~[netty-handler-proxy-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.handler.logging.LoggingHandler.channelRead(LoggingHandler.java:280) ~[netty-handler-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346) ~[netty-codec-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318) ~[netty-codec-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.handler.proxy.HttpProxyHandler$HttpClientCodecWrapper.channelRead(HttpProxyHandler.java:272) ~[netty-handler-proxy-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:800) ~[netty-transport-classes-epoll-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:509) ~[netty-transport-classes-epoll-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:407) ~[netty-transport-classes-epoll-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.97.Final.jar:4.1.97.Final]
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.97.Final.jar:4.1.97.Final]
	at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]

2023-09-04T19:40:58.461+02:00 ERROR 141333 --- [or-http-epoll-1] a.w.r.e.AbstractErrorWebExceptionHandler : [cd9f2d3d-1]  500 Server Error for HTTP GET "/test-proxy-agent"

io.netty.handler.proxy.HttpProxyHandler$HttpProxyConnectException: http, none, /127.0.0.1:62514 => /127.0.0.1:65432, status: 404 Not Found
	at io.netty.handler.proxy.HttpProxyHandler.handleResponse(HttpProxyHandler.java:200) ~[netty-handler-proxy-4.1.97.Final.jar:4.1.97.Final]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	*__checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ HTTP GET "/test-proxy-agent" [ExceptionHandlingWebHandler]
Original Stack Trace:
		at io.netty.handler.proxy.HttpProxyHandler.handleResponse(HttpProxyHandler.java:200) ~[netty-handler-proxy-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.handler.proxy.ProxyHandler.channelRead(ProxyHandler.java:257) ~[netty-handler-proxy-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.handler.logging.LoggingHandler.channelRead(LoggingHandler.java:280) ~[netty-handler-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346) ~[netty-codec-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318) ~[netty-codec-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.handler.proxy.HttpProxyHandler$HttpClientCodecWrapper.channelRead(HttpProxyHandler.java:272) ~[netty-handler-proxy-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:800) ~[netty-transport-classes-epoll-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:509) ~[netty-transport-classes-epoll-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:407) ~[netty-transport-classes-epoll-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.97.Final.jar:4.1.97.Final]
		at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]

2023-09-04T19:40:58.561+02:00  INFO 141333 --- [or-http-epoll-1] debugging: test-proxy-agent              : [aef4b4d3, L:/127.0.0.1:44750 - R:127.0.0.1/127.0.0.1:62514] EXCEPTION: io.netty.handler.proxy.HttpProxyHandler$HttpProxyConnectException: http, none, /127.0.0.1:62514 => /127.0.0.1:65432, status: 404 Not Found

io.netty.handler.proxy.HttpProxyHandler$HttpProxyConnectException: http, none, /127.0.0.1:62514 => /127.0.0.1:65432, status: 404 Not Found
	at io.netty.handler.proxy.HttpProxyHandler.handleResponse(HttpProxyHandler.java:200) ~[netty-handler-proxy-4.1.97.Final.jar:4.1.97.Final]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	*__checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ HTTP GET "/test-proxy-agent" [ExceptionHandlingWebHandler]
Original Stack Trace:
		at io.netty.handler.proxy.HttpProxyHandler.handleResponse(HttpProxyHandler.java:200) ~[netty-handler-proxy-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.handler.proxy.ProxyHandler.channelRead(ProxyHandler.java:257) ~[netty-handler-proxy-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.handler.logging.LoggingHandler.channelRead(LoggingHandler.java:280) ~[netty-handler-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346) ~[netty-codec-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318) ~[netty-codec-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.handler.proxy.HttpProxyHandler$HttpClientCodecWrapper.channelRead(HttpProxyHandler.java:272) ~[netty-handler-proxy-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:800) ~[netty-transport-classes-epoll-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:509) ~[netty-transport-classes-epoll-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:407) ~[netty-transport-classes-epoll-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.97.Final.jar:4.1.97.Final]
		at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.97.Final.jar:4.1.97.Final]
		at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]

2023-09-04T19:40:58.574+02:00  INFO 141333 --- [or-http-epoll-1] debugging: test-proxy-agent              : [aef4b4d3, L:/127.0.0.1:44750 ! R:127.0.0.1/127.0.0.1:62514] INACTIVE
2023-09-04T19:40:58.574+02:00  INFO 141333 --- [or-http-epoll-1] debugging: test-proxy-agent              : [aef4b4d3, L:/127.0.0.1:44750 ! R:127.0.0.1/127.0.0.1:62514] UNREGISTERED
2023-09-04T19:40:58.613+02:00 ERROR 141333 --- [           main] o.s.t.w.reactive.server.ExchangeResult   : Request details for assertion failure:

> GET http://localhost:37445/test-proxy-agent
> accept-encoding: [gzip]
> user-agent: [ReactorNetty/1.1.10]
> host: [localhost:37445]
> accept: [*/*]
> WebTestClient-Request-Id: [1]
> apikey: [x123456]

No content

< 500 INTERNAL_SERVER_ERROR Internal Server Error
< Content-Type: [application/json]
< Content-Length: [142]

{"timestamp":"2023-09-04T17:40:58.456+00:00","path":"/test-proxy-agent","status":500,"error":"Internal Server Error","requestId":"cd9f2d3d-1"}

I have tested a lot of things, you may have noticed that I use "127.0.0.1" instead of "localhost" in the disabled test, because when I initialize proxy() with "localhost" instead of "127.0.0.1" I get <unresolved> on the target service:

2023-09-04T19:46:13.851+02:00  INFO 142720 --- [           main] w.org.eclipse.jetty.server.Server        : Started @15360ms
2023-09-04T19:46:14.267+02:00  INFO 142720 --- [or-http-epoll-1] debugging: test-proxy-agent              : [b80ba0eb] REGISTERED
2023-09-04T19:46:14.268+02:00  INFO 142720 --- [or-http-epoll-1] debugging: test-proxy-agent              : [b80ba0eb] CONNECT: localhost/<unresolved>:65432
2023-09-04T19:46:14.272+02:00  INFO 142720 --- [or-http-epoll-1] debugging: test-proxy-agent              : [b80ba0eb, L:/127.0.0.1:49354 - R:localhost/127.0.0.1:62514] ACTIVE
2023-09-04T19:46:14.283+02:00  INFO 142720 --- [or-http-epoll-1] debugging: test-proxy-agent              : [b80ba0eb-1, L:/127.0.0.1:49354 - R:localhost/127.0.0.1:62514] WRITE: 372B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 47 45 54 20 2f 74 65 73 74 2d 70 72 6f 78 79 2d |GET /test-proxy-|
|00000010| 61 67 65 6e 74 20 48 54 54 50 2f 31 2e 31 0d 0a |agent HTTP/1.1..|
|00000020| 61 63 63 65 70 74 2d 65 6e 63 6f 64 69 6e 67 3a |accept-encoding:|
|00000030| 20 67 7a 69 70 0d 0a 75 73 65 72 2d 61 67 65 6e | gzip..user-agen|
|00000040| 74 3a 20 52 65 61 63 74 6f 72 4e 65 74 74 79 2f |t: ReactorNetty/|
|00000050| 31 2e 31 2e 31 30 0d 0a 61 63 63 65 70 74 3a 20 |1.1.10..accept: |
|00000060| 2a 2f 2a 0d 0a 57 65 62 54 65 73 74 43 6c 69 65 |*/*..WebTestClie|
|00000070| 6e 74 2d 52 65 71 75 65 73 74 2d 49 64 3a 20 31 |nt-Request-Id: 1|
|00000080| 0d 0a 61 70 69 6b 65 79 3a 20 78 31 32 33 34 35 |..apikey: x12345|
|00000090| 36 0d 0a 46 6f 72 77 61 72 64 65 64 3a 20 70 72 |6..Forwarded: pr|
|000000a0| 6f 74 6f 3d 68 74 74 70 3b 68 6f 73 74 3d 22 6c |oto=http;host="l|
|000000b0| 6f 63 61 6c 68 6f 73 74 3a 34 34 35 31 33 22 3b |ocalhost:44513";|
|000000c0| 66 6f 72 3d 22 31 32 37 2e 30 2e 30 2e 31 3a 33 |for="127.0.0.1:3|
|000000d0| 33 34 39 30 22 0d 0a 58 2d 46 6f 72 77 61 72 64 |3490"..X-Forward|
|000000e0| 65 64 2d 46 6f 72 3a 20 31 32 37 2e 30 2e 30 2e |ed-For: 127.0.0.|
|000000f0| 31 0d 0a 58 2d 46 6f 72 77 61 72 64 65 64 2d 50 |1..X-Forwarded-P|
|00000100| 72 6f 74 6f 3a 20 68 74 74 70 0d 0a 58 2d 46 6f |roto: http..X-Fo|
|00000110| 72 77 61 72 64 65 64 2d 50 6f 72 74 3a 20 34 34 |rwarded-Port: 44|
|00000120| 35 31 33 0d 0a 58 2d 46 6f 72 77 61 72 64 65 64 |513..X-Forwarded|
|00000130| 2d 48 6f 73 74 3a 20 6c 6f 63 61 6c 68 6f 73 74 |-Host: localhost|
|00000140| 3a 34 34 35 31 33 0d 0a 68 6f 73 74 3a 20 6c 6f |:44513..host: lo|
|00000150| 63 61 6c 68 6f 73 74 3a 36 35 34 33 32 0d 0a 63 |calhost:65432..c|
|00000160| 6f 6e 74 65 6e 74 2d 6c 65 6e 67 74 68 3a 20 30 |ontent-length: 0|
|00000170| 0d 0a 0d 0a                                     |....            |
+--------+-------------------------------------------------+----------------+
2023-09-04T19:46:14.289+02:00  INFO 142720 --- [or-http-epoll-1] debugging: test-proxy-agent              : [b80ba0eb-1, L:/127.0.0.1:49354 - R:localhost/127.0.0.1:62514] FLUSH
2023-09-04T19:46:14.358+02:00  WARN 142720 --- [or-http-epoll-1] r.netty.http.client.HttpClientConnect    : [b80ba0eb-1, L:/127.0.0.1:49354 - R:localhost/127.0.0.1:62514] The connection observed an error

io.netty.handler.proxy.HttpProxyHandler$HttpProxyConnectException: http, none, localhost/127.0.0.1:62514 => localhost/<unresolved>:65432, status: 404 Not Found

Possible Solution

No idea

Your Environment

  • Reactor version(s) used: 3.5.9 (imported by Spring)
  • JVM version (java -version): OpenJDK 17.0.1
  • OS and version (eg. uname -a): Linux Ubuntu 22.04 LTS
  • spring-cloud-starter-gateway, it's a Spring Boot Webflux application, version: 3.1.3
  • in the tests I use com.github.tomakehurst:wiremock-standalone:2.27.2, notice that simply importing io.specto:hoverfly-java:0.14.4 breaks the whole project
[ERROR]   WcGatewayApplicationTests.contextLoads » IllegalState Failed to load ApplicationContext for [ReactiveWebMergedContextConfiguration@21422231 testClass = com.accor.wc.gateway.WcGatewayApplicationTests, locations = [], classes = [com.accor.wc.gateway.WcGatewayApplication], contextInitializerClasses = [], activeProfiles = [], propertySourceLocations = [], propertySourceProperties = ["org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@7bd4937b, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@58d75e99, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@2805d709, org.springframework.boot.test.web.reactive.server.WebTestClientContextCustomizer@f0da945, org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizer@4c1909a3, org.springframework.boot.test.context.SpringBootTestAnnotation@60f582ee], contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]
@eau-de-la-seine eau-de-la-seine added status/need-triage A new issue that still need to be evaluated as a whole type/bug A general bug labels Sep 4, 2023
@violetagg violetagg self-assigned this Sep 5, 2023
@violetagg violetagg removed the status/need-triage A new issue that still need to be evaluated as a whole label Sep 5, 2023
@violetagg
Copy link
Member

violetagg commented Sep 5, 2023

@eau-de-la-seine I still believe that there is an issue with wiremock wiremock/wiremock#379 (comment)

In Reactor Netty we do use hoverfly for testing proxy, I don't know what might be the issue with your project.

@violetagg violetagg added the for/user-attention This issue needs user attention (feedback, rework, etc...) label Sep 5, 2023
@eau-de-la-seine
Copy link
Author

Hi @violetagg , thank you for your response. I think a miracle just happened, org.wiremock:wiremock-standalone:3.0.1 has been released 4 days ago, I have integrated it... and it just seems to... work, just like that O_O

I have created an issue at hoverfly

@violetagg
Copy link
Member

@eau-de-la-seine nice ... then let me close that

@violetagg violetagg added status/invalid We don't feel this issue is valid and removed type/bug A general bug for/user-attention This issue needs user attention (feedback, rework, etc...) labels Sep 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status/invalid We don't feel this issue is valid
Projects
None yet
Development

No branches or pull requests

2 participants