diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java index c2e47c6e22..9b24ea4366 100644 --- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java +++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java @@ -17,7 +17,6 @@ package org.glassfish.jersey.netty.connector; import java.io.IOException; -import java.io.InputStream; import java.net.URI; import java.util.Iterator; import java.util.List; @@ -47,7 +46,6 @@ import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.timeout.IdleStateEvent; import org.glassfish.jersey.uri.internal.JerseyUriBuilder; @@ -145,7 +143,21 @@ protected void notifyResponse() { ClientRequest newReq = new ClientRequest(jerseyRequest); newReq.setUri(newUri); restrictRedirectRequest(newReq, cr); - connector.execute(newReq, redirectUriHistory, responseAvailable); + + final NettyConnector newConnector = new NettyConnector(newReq.getClient()); + newConnector.execute(newReq, redirectUriHistory, new CompletableFuture() { + @Override + public boolean complete(ClientResponse value) { + newConnector.close(); + return responseAvailable.complete(value); + } + + @Override + public boolean completeExceptionally(Throwable ex) { + newConnector.close(); + return responseAvailable.completeExceptionally(ex); + } + }); } } catch (IllegalArgumentException e) { responseAvailable.completeExceptionally( diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java index 4a4cb8603a..f242f10bc7 100644 --- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java +++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java @@ -418,7 +418,14 @@ protected void initChannel(SocketChannel ch) throws Exception { // host header - http 1.1 if (!nettyRequest.headers().contains(HttpHeaderNames.HOST)) { - nettyRequest.headers().add(HttpHeaderNames.HOST, jerseyRequest.getUri().getHost()); + int requestPort = jerseyRequest.getUri().getPort(); + final String hostHeader; + if (requestPort != 80 && requestPort != 443) { + hostHeader = jerseyRequest.getUri().getHost() + ":" + requestPort; + } else { + hostHeader = jerseyRequest.getUri().getHost(); + } + nettyRequest.headers().add(HttpHeaderNames.HOST, hostHeader); } } diff --git a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/FollowRedirectsTest.java b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/FollowRedirectsTest.java index 127668bea8..c1b2c9216c 100644 --- a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/FollowRedirectsTest.java +++ b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/FollowRedirectsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -24,16 +24,20 @@ import java.util.logging.Logger; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.ProcessingException; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Application; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.client.RequestEntityProcessing; import org.glassfish.jersey.logging.LoggingFeature; import org.glassfish.jersey.netty.connector.internal.RedirectException; import org.glassfish.jersey.server.ResourceConfig; @@ -60,6 +64,11 @@ public String get() { return "GET"; } + @POST + public String post() { + return "POST"; + } + @GET @Path("redirect") public Response redirect() { @@ -77,6 +86,12 @@ public Response loop() { public Response redirect2() { return Response.seeOther(URI.create(TEST_URL_REF.get() + "/redirect")).build(); } + + @POST + @Path("status307") + public Response status307() { + return Response.temporaryRedirect(URI.create(TEST_URL_REF.get())).build(); + } } @Override @@ -169,4 +184,15 @@ public void testRedirectNoLimitReached() { assertEquals(200, r.getStatus()); assertEquals("GET", r.readEntity(String.class)); } + + @Test + public void testRedirect307PostBuffered() { + try (Response response = target("test/status307") + .property(ClientProperties.FOLLOW_REDIRECTS, true) + .property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.BUFFERED) + .request().post(Entity.entity("Something", MediaType.TEXT_PLAIN_TYPE))) { + assertEquals(200, response.getStatus()); + assertEquals("POST", response.readEntity(String.class)); + } + } } diff --git a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HostHeaderTest.java b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HostHeaderTest.java new file mode 100644 index 0000000000..8d42c3699a --- /dev/null +++ b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HostHeaderTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.netty.connector; + +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; + +public class HostHeaderTest extends JerseyTest { + + private static final String HTTP_HEADER_NAME = "HTTP_PORT_INT"; + + @Path("/") + public static class HostHeaderTestEchoResource { + @GET + public String get(@Context HttpHeaders headers) { + String sPort = headers.getHeaderString(HTTP_HEADER_NAME); + String hostPort = headers.getHeaderString(HttpHeaders.HOST); + int indexColon = hostPort.indexOf(':'); + if (indexColon != -1) { + hostPort = hostPort.substring(indexColon + 1); + } + if (sPort.equals(hostPort.trim())) { + return GET.class.getName(); + } else { + return "Expected port " + sPort + " but found " + hostPort; + } + } + } + + @Override + protected Application configure() { + return new ResourceConfig(HostHeaderTestEchoResource.class); + } + + @Test + public void testHostHeaderAndPort() { + int port = getPort(); + ClientConfig config = new ClientConfig(); + config.connectorProvider(new NettyConnectorProvider()); + try (Response response = ClientBuilder.newClient(config).target(target().getUri()) + .request() + .header(HTTP_HEADER_NAME, port) + .get()) { + MatcherAssert.assertThat(response.getStatus(), Matchers.is(200)); + MatcherAssert.assertThat(response.readEntity(String.class), Matchers.is(GET.class.getName())); + } + } + +}