Merged branch 'jetty-9.3.x' into 'master'.
This commit is contained in:
commit
68a465d7a3
|
@ -35,6 +35,7 @@ import org.eclipse.jetty.http.HttpField;
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||||
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
import org.eclipse.jetty.http.HttpVersion;
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
@ -103,7 +104,7 @@ public abstract class HttpConnection implements Connection
|
||||||
|
|
||||||
URI uri = request.getURI();
|
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();
|
path = uri.toString();
|
||||||
request.path(path);
|
request.path(path);
|
||||||
|
|
|
@ -26,8 +26,6 @@ import java.nio.charset.StandardCharsets;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
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.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -69,17 +67,16 @@ public class Socks4ProxyTest
|
||||||
byte ip4 = 13;
|
byte ip4 = 13;
|
||||||
String serverHost = ip1 + "." + ip2 + "." + ip3 + "." + ip4;
|
String serverHost = ip1 + "." + ip2 + "." + ip3 + "." + ip4;
|
||||||
int serverPort = proxyPort + 1; // Any port will do
|
int serverPort = proxyPort + 1; // Any port will do
|
||||||
|
String method = "GET";
|
||||||
|
String path = "/path";
|
||||||
client.newRequest(serverHost, serverPort)
|
client.newRequest(serverHost, serverPort)
|
||||||
.path("/path")
|
.method(method)
|
||||||
|
.path(path)
|
||||||
.timeout(5, TimeUnit.SECONDS)
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
.send(new Response.CompleteListener()
|
.send(result ->
|
||||||
{
|
{
|
||||||
@Override
|
if (result.isSucceeded())
|
||||||
public void onComplete(Result result)
|
latch.countDown();
|
||||||
{
|
|
||||||
if (result.isSucceeded())
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
SocketChannel channel = server.accept();
|
SocketChannel channel = server.accept();
|
||||||
|
@ -100,11 +97,11 @@ public class Socks4ProxyTest
|
||||||
// Socks4 response.
|
// Socks4 response.
|
||||||
channel.write(ByteBuffer.wrap(new byte[]{0, 0x5A, 0, 0, 0, 0, 0, 0}));
|
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);
|
read = channel.read(buffer);
|
||||||
Assert.assertEquals(3, read);
|
Assert.assertEquals(buffer.capacity(), read);
|
||||||
buffer.flip();
|
buffer.flip();
|
||||||
Assert.assertEquals("GET", StandardCharsets.UTF_8.decode(buffer).toString());
|
Assert.assertEquals(method + " " + path, StandardCharsets.UTF_8.decode(buffer).toString());
|
||||||
|
|
||||||
// Response
|
// Response
|
||||||
String response = "" +
|
String response = "" +
|
||||||
|
@ -129,19 +126,17 @@ public class Socks4ProxyTest
|
||||||
|
|
||||||
String serverHost = "127.0.0.13"; // Test expects an IP address.
|
String serverHost = "127.0.0.13"; // Test expects an IP address.
|
||||||
int serverPort = proxyPort + 1; // Any port will do
|
int serverPort = proxyPort + 1; // Any port will do
|
||||||
|
String method = "GET";
|
||||||
client.newRequest(serverHost, serverPort)
|
client.newRequest(serverHost, serverPort)
|
||||||
|
.method(method)
|
||||||
.path("/path")
|
.path("/path")
|
||||||
.timeout(5, TimeUnit.SECONDS)
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
.send(new Response.CompleteListener()
|
.send(result ->
|
||||||
{
|
{
|
||||||
@Override
|
if (result.isSucceeded())
|
||||||
public void onComplete(Result result)
|
latch.countDown();
|
||||||
{
|
else
|
||||||
if (result.isSucceeded())
|
result.getFailure().printStackTrace();
|
||||||
latch.countDown();
|
|
||||||
else
|
|
||||||
result.getFailure().printStackTrace();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
SocketChannel channel = server.accept();
|
SocketChannel channel = server.accept();
|
||||||
|
@ -161,11 +156,11 @@ public class Socks4ProxyTest
|
||||||
|
|
||||||
channel.write(ByteBuffer.wrap(chunk2));
|
channel.write(ByteBuffer.wrap(chunk2));
|
||||||
|
|
||||||
buffer = ByteBuffer.allocate(3);
|
buffer = ByteBuffer.allocate(method.length());
|
||||||
read = channel.read(buffer);
|
read = channel.read(buffer);
|
||||||
Assert.assertEquals(3, read);
|
Assert.assertEquals(buffer.capacity(), read);
|
||||||
buffer.flip();
|
buffer.flip();
|
||||||
Assert.assertEquals("GET", StandardCharsets.UTF_8.decode(buffer).toString());
|
Assert.assertEquals(method, StandardCharsets.UTF_8.decode(buffer).toString());
|
||||||
|
|
||||||
// Response
|
// Response
|
||||||
String response = "" +
|
String response = "" +
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.http2.client.http;
|
package org.eclipse.jetty.http2.client.http;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.HttpContent;
|
import org.eclipse.jetty.client.HttpContent;
|
||||||
import org.eclipse.jetty.client.HttpExchange;
|
import org.eclipse.jetty.client.HttpExchange;
|
||||||
import org.eclipse.jetty.client.HttpSender;
|
import org.eclipse.jetty.client.HttpSender;
|
||||||
|
@ -47,8 +49,9 @@ public class HttpSenderOverHTTP2 extends HttpSender
|
||||||
@Override
|
@Override
|
||||||
protected void sendHeaders(HttpExchange exchange, final HttpContent content, final Callback callback)
|
protected void sendHeaders(HttpExchange exchange, final HttpContent content, final Callback callback)
|
||||||
{
|
{
|
||||||
final Request request = exchange.getRequest();
|
Request request = exchange.getRequest();
|
||||||
HttpURI uri = new HttpURI(request.getScheme(), request.getHost(), request.getPort(), request.getPath(), null, request.getQuery(), null);
|
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());
|
MetaData.Request metaData = new MetaData.Request(request.getMethod(), uri, HttpVersion.HTTP_2, request.getHeaders());
|
||||||
HeadersFrame headersFrame = new HeadersFrame(metaData, null, !content.hasContent());
|
HeadersFrame headersFrame = new HeadersFrame(metaData, null, !content.hasContent());
|
||||||
HttpChannelOverHTTP2 channel = getHttpChannel();
|
HttpChannelOverHTTP2 channel = getHttpChannel();
|
||||||
|
@ -84,6 +87,24 @@ public class HttpSenderOverHTTP2 extends HttpSender
|
||||||
channel.getSession().newStream(headersFrame, promise, channel.getStreamListener());
|
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
|
@Override
|
||||||
protected void sendContent(HttpExchange exchange, HttpContent content, Callback callback)
|
protected void sendContent(HttpExchange exchange, HttpContent content, Callback callback)
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,6 +33,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.HttpClient;
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.eclipse.jetty.client.HttpProxy;
|
||||||
import org.eclipse.jetty.client.api.ContentResponse;
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.http.HttpMethod;
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
|
@ -260,6 +261,58 @@ public class HttpClientTransportOverHTTP2Test extends AbstractTest
|
||||||
Assert.assertEquals(lastStream.get(), stream.getId());
|
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
|
@Ignore
|
||||||
@Test
|
@Test
|
||||||
public void testExternalServer() throws Exception
|
public void testExternalServer() throws Exception
|
||||||
|
|
|
@ -21,11 +21,13 @@ package org.eclipse.jetty.http2.server;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.BadMessageException;
|
||||||
import org.eclipse.jetty.http.HttpField;
|
import org.eclipse.jetty.http.HttpField;
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.http.HttpGenerator;
|
import org.eclipse.jetty.http.HttpGenerator;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||||
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.http.MetaData;
|
import org.eclipse.jetty.http.MetaData;
|
||||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||||
import org.eclipse.jetty.http2.IStream;
|
import org.eclipse.jetty.http2.IStream;
|
||||||
|
@ -70,67 +72,93 @@ public class HttpChannelOverHTTP2 extends HttpChannel
|
||||||
|
|
||||||
public Runnable onRequest(HeadersFrame frame)
|
public Runnable onRequest(HeadersFrame frame)
|
||||||
{
|
{
|
||||||
MetaData.Request request = (MetaData.Request)frame.getMetaData();
|
try
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
String authority = request.getURI().getAuthority();
|
MetaData.Request request = (MetaData.Request)frame.getMetaData();
|
||||||
if (authority != null)
|
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.
|
String authority = request.getURI().getAuthority();
|
||||||
fields.put("host", authority);
|
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;
|
||||||
}
|
}
|
||||||
|
catch (BadMessageException x)
|
||||||
_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();
|
onBadMessage(x.getCode(), x.getReason());
|
||||||
LOG.debug("HTTP2 Request #{}/{}, delayed={}:{}{} {} {}{}{}",
|
return null;
|
||||||
stream.getId(), Integer.toHexString(stream.getSession().hashCode()),
|
}
|
||||||
_delayedUntilContent, System.lineSeparator(),
|
catch (Throwable x)
|
||||||
request.getMethod(), request.getURI(), request.getVersion(),
|
{
|
||||||
System.lineSeparator(), fields);
|
onBadMessage(HttpStatus.INTERNAL_SERVER_ERROR_500, null);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _delayedUntilContent ? null : this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Runnable onPushRequest(MetaData.Request request)
|
public Runnable onPushRequest(MetaData.Request request)
|
||||||
{
|
{
|
||||||
onRequest(request);
|
try
|
||||||
getRequest().setAttribute("org.eclipse.jetty.pushed", Boolean.TRUE);
|
|
||||||
onRequestComplete();
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
{
|
{
|
||||||
Stream stream = getStream();
|
onRequest(request);
|
||||||
LOG.debug("HTTP2 PUSH Request #{}/{}:{}{} {} {}{}{}",
|
getRequest().setAttribute("org.eclipse.jetty.pushed", Boolean.TRUE);
|
||||||
stream.getId(), Integer.toHexString(stream.getSession().hashCode()), System.lineSeparator(),
|
onRequestComplete();
|
||||||
request.getMethod(), request.getURI(), request.getVersion(),
|
|
||||||
System.lineSeparator(), request.getFields());
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue