diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java index 9b66b8d6b4d..58f6fe33288 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java @@ -35,6 +35,7 @@ import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; +import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -103,7 +104,7 @@ public abstract class HttpConnection implements Connection URI uri = request.getURI(); - if (proxy != null && !HttpClient.isSchemeSecure(request.getScheme()) && uri != null) + if (proxy instanceof HttpProxy && !HttpClient.isSchemeSecure(request.getScheme()) && uri != null) { path = uri.toString(); request.path(path); diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/Socks4ProxyTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/Socks4ProxyTest.java index 478af71206e..97ee60bb8d0 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/Socks4ProxyTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/Socks4ProxyTest.java @@ -26,8 +26,6 @@ import java.nio.charset.StandardCharsets; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import org.eclipse.jetty.client.api.Response; -import org.eclipse.jetty.client.api.Result; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -69,17 +67,16 @@ public class Socks4ProxyTest byte ip4 = 13; String serverHost = ip1 + "." + ip2 + "." + ip3 + "." + ip4; int serverPort = proxyPort + 1; // Any port will do + String method = "GET"; + String path = "/path"; client.newRequest(serverHost, serverPort) - .path("/path") + .method(method) + .path(path) .timeout(5, TimeUnit.SECONDS) - .send(new Response.CompleteListener() + .send(result -> { - @Override - public void onComplete(Result result) - { - if (result.isSucceeded()) - latch.countDown(); - } + if (result.isSucceeded()) + latch.countDown(); }); SocketChannel channel = server.accept(); @@ -100,11 +97,11 @@ public class Socks4ProxyTest // Socks4 response. channel.write(ByteBuffer.wrap(new byte[]{0, 0x5A, 0, 0, 0, 0, 0, 0})); - buffer = ByteBuffer.allocate(3); + buffer = ByteBuffer.allocate(method.length() + 1 + path.length()); read = channel.read(buffer); - Assert.assertEquals(3, read); + Assert.assertEquals(buffer.capacity(), read); buffer.flip(); - Assert.assertEquals("GET", StandardCharsets.UTF_8.decode(buffer).toString()); + Assert.assertEquals(method + " " + path, StandardCharsets.UTF_8.decode(buffer).toString()); // Response String response = "" + @@ -129,19 +126,17 @@ public class Socks4ProxyTest String serverHost = "127.0.0.13"; // Test expects an IP address. int serverPort = proxyPort + 1; // Any port will do + String method = "GET"; client.newRequest(serverHost, serverPort) + .method(method) .path("/path") .timeout(5, TimeUnit.SECONDS) - .send(new Response.CompleteListener() + .send(result -> { - @Override - public void onComplete(Result result) - { - if (result.isSucceeded()) - latch.countDown(); - else - result.getFailure().printStackTrace(); - } + if (result.isSucceeded()) + latch.countDown(); + else + result.getFailure().printStackTrace(); }); SocketChannel channel = server.accept(); @@ -161,11 +156,11 @@ public class Socks4ProxyTest channel.write(ByteBuffer.wrap(chunk2)); - buffer = ByteBuffer.allocate(3); + buffer = ByteBuffer.allocate(method.length()); read = channel.read(buffer); - Assert.assertEquals(3, read); + Assert.assertEquals(buffer.capacity(), read); buffer.flip(); - Assert.assertEquals("GET", StandardCharsets.UTF_8.decode(buffer).toString()); + Assert.assertEquals(method, StandardCharsets.UTF_8.decode(buffer).toString()); // Response String response = "" + diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpSenderOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpSenderOverHTTP2.java index b13e68e0971..0f660ba1835 100644 --- a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpSenderOverHTTP2.java +++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpSenderOverHTTP2.java @@ -18,6 +18,8 @@ package org.eclipse.jetty.http2.client.http; +import java.net.URI; + import org.eclipse.jetty.client.HttpContent; import org.eclipse.jetty.client.HttpExchange; import org.eclipse.jetty.client.HttpSender; @@ -47,8 +49,9 @@ public class HttpSenderOverHTTP2 extends HttpSender @Override protected void sendHeaders(HttpExchange exchange, final HttpContent content, final Callback callback) { - final Request request = exchange.getRequest(); - HttpURI uri = new HttpURI(request.getScheme(), request.getHost(), request.getPort(), request.getPath(), null, request.getQuery(), null); + Request request = exchange.getRequest(); + String path = relativize(request.getPath()); + HttpURI uri = new HttpURI(request.getScheme(), request.getHost(), request.getPort(), path, null, request.getQuery(), null); MetaData.Request metaData = new MetaData.Request(request.getMethod(), uri, HttpVersion.HTTP_2, request.getHeaders()); HeadersFrame headersFrame = new HeadersFrame(metaData, null, !content.hasContent()); HttpChannelOverHTTP2 channel = getHttpChannel(); @@ -84,6 +87,24 @@ public class HttpSenderOverHTTP2 extends HttpSender channel.getSession().newStream(headersFrame, promise, channel.getStreamListener()); } + private String relativize(String path) + { + try + { + String result = path; + URI uri = URI.create(result); + if (uri.isAbsolute()) + result = uri.getPath(); + return result.isEmpty() ? "/" : result; + } + catch (Throwable x) + { + if (LOG.isDebugEnabled()) + LOG.debug("Could not relativize " + path); + return path; + } + } + @Override protected void sendContent(HttpExchange exchange, HttpContent content, Callback callback) { diff --git a/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2Test.java b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2Test.java index 2882c2b7cf7..590fd62fbc4 100644 --- a/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2Test.java +++ b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2Test.java @@ -33,6 +33,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpProxy; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpMethod; @@ -260,6 +261,58 @@ public class HttpClientTransportOverHTTP2Test extends AbstractTest Assert.assertEquals(lastStream.get(), stream.getId()); } + @Test + public void testAbsoluteFormTarget() throws Exception + { + String path = "/path"; + String query = "a=b"; + start(new AbstractHandler() + { + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + baseRequest.setHandled(true); + Assert.assertEquals(path, request.getRequestURI()); + Assert.assertEquals(query, request.getQueryString()); + } + }); + + ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) + .path("http://localhost:" + connector.getLocalPort() + path + "?" + query) + .timeout(5, TimeUnit.SECONDS) + .send(); + + Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); + } + + @Test + public void testRequestViaForwardHttpProxy() throws Exception + { + String path = "/path"; + String query = "a=b"; + start(new AbstractHandler() + { + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + baseRequest.setHandled(true); + Assert.assertEquals(path, request.getRequestURI()); + Assert.assertEquals(query, request.getQueryString()); + } + }); + + int proxyPort = connector.getLocalPort(); + client.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort)); + + int serverPort = proxyPort + 1; // Any port will do, just not the same as the proxy. + ContentResponse response = client.newRequest("localhost", serverPort) + .path(path + "?" + query) + .timeout(5, TimeUnit.SECONDS) + .send(); + + Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); + } + @Ignore @Test public void testExternalServer() throws Exception diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java index ee98d2179eb..a9e054c079d 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java @@ -21,11 +21,13 @@ package org.eclipse.jetty.http2.server; import java.io.IOException; import java.nio.ByteBuffer; +import org.eclipse.jetty.http.BadMessageException; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpGenerator; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.http2.IStream; @@ -70,67 +72,93 @@ public class HttpChannelOverHTTP2 extends HttpChannel public Runnable onRequest(HeadersFrame frame) { - MetaData.Request request = (MetaData.Request)frame.getMetaData(); - HttpFields fields = request.getFields(); - - // HTTP/2 sends the Host header as the :authority - // pseudo-header, so we need to synthesize a Host header. - if (!fields.contains(HttpHeader.HOST)) + try { - String authority = request.getURI().getAuthority(); - if (authority != null) + MetaData.Request request = (MetaData.Request)frame.getMetaData(); + HttpFields fields = request.getFields(); + + // HTTP/2 sends the Host header as the :authority + // pseudo-header, so we need to synthesize a Host header. + if (!fields.contains(HttpHeader.HOST)) { - // Lower-case to be consistent with other HTTP/2 headers. - fields.put("host", authority); + String authority = request.getURI().getAuthority(); + if (authority != null) + { + // Lower-case to be consistent with other HTTP/2 headers. + fields.put("host", authority); + } } + + _expect100Continue = fields.contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()); + + HttpFields response = getResponse().getHttpFields(); + if (getHttpConfiguration().getSendServerVersion()) + response.add(SERVER_VERSION); + if (getHttpConfiguration().getSendXPoweredBy()) + response.add(POWERED_BY); + + onRequest(request); + + boolean endStream = frame.isEndStream(); + if (endStream) + onRequestComplete(); + + _delayedUntilContent = getHttpConfiguration().isDelayDispatchUntilContent() && + !endStream && !_expect100Continue; + + if (LOG.isDebugEnabled()) + { + Stream stream = getStream(); + LOG.debug("HTTP2 Request #{}/{}, delayed={}:{}{} {} {}{}{}", + stream.getId(), Integer.toHexString(stream.getSession().hashCode()), + _delayedUntilContent, System.lineSeparator(), + request.getMethod(), request.getURI(), request.getVersion(), + System.lineSeparator(), fields); + } + + return _delayedUntilContent ? null : this; } - - _expect100Continue = fields.contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()); - - HttpFields response = getResponse().getHttpFields(); - if (getHttpConfiguration().getSendServerVersion()) - response.add(SERVER_VERSION); - if (getHttpConfiguration().getSendXPoweredBy()) - response.add(POWERED_BY); - - onRequest(request); - - boolean endStream = frame.isEndStream(); - if (endStream) - onRequestComplete(); - - _delayedUntilContent = getHttpConfiguration().isDelayDispatchUntilContent() && - !endStream && !_expect100Continue; - - if (LOG.isDebugEnabled()) + catch (BadMessageException x) { - Stream stream = getStream(); - LOG.debug("HTTP2 Request #{}/{}, delayed={}:{}{} {} {}{}{}", - stream.getId(), Integer.toHexString(stream.getSession().hashCode()), - _delayedUntilContent, System.lineSeparator(), - request.getMethod(), request.getURI(), request.getVersion(), - System.lineSeparator(), fields); + onBadMessage(x.getCode(), x.getReason()); + return null; + } + catch (Throwable x) + { + onBadMessage(HttpStatus.INTERNAL_SERVER_ERROR_500, null); + return null; } - - return _delayedUntilContent ? null : this; } public Runnable onPushRequest(MetaData.Request request) { - onRequest(request); - getRequest().setAttribute("org.eclipse.jetty.pushed", Boolean.TRUE); - onRequestComplete(); - - if (LOG.isDebugEnabled()) + try { - Stream stream = getStream(); - LOG.debug("HTTP2 PUSH Request #{}/{}:{}{} {} {}{}{}", - stream.getId(), Integer.toHexString(stream.getSession().hashCode()), System.lineSeparator(), - request.getMethod(), request.getURI(), request.getVersion(), - System.lineSeparator(), request.getFields()); - } + onRequest(request); + getRequest().setAttribute("org.eclipse.jetty.pushed", Boolean.TRUE); + onRequestComplete(); - return this; + if (LOG.isDebugEnabled()) + { + Stream stream = getStream(); + LOG.debug("HTTP2 PUSH Request #{}/{}:{}{} {} {}{}{}", + stream.getId(), Integer.toHexString(stream.getSession().hashCode()), System.lineSeparator(), + request.getMethod(), request.getURI(), request.getVersion(), + System.lineSeparator(), request.getFields()); + } + + return this; + } + catch (BadMessageException x) + { + onBadMessage(x.getCode(), x.getReason()); + return null; + } + catch (Throwable x) + { + onBadMessage(HttpStatus.INTERNAL_SERVER_ERROR_500, null); + return null; + } } @Override