mirror of
https://github.com/jetty/jetty.project.git
synced 2025-03-02 20:09:21 +00:00
Moved HttpClientTransport SPDY implementation in its own module under the SPDY project.
This commit is contained in:
parent
af06b25538
commit
beee37e96a
101
jetty-spdy/spdy-http-client-transport/pom.xml
Normal file
101
jetty-spdy/spdy-http-client-transport/pom.xml
Normal file
@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.spdy</groupId>
|
||||
<artifactId>spdy-parent</artifactId>
|
||||
<version>9.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spdy-http-client-transport</artifactId>
|
||||
<name>Jetty :: SPDY :: HTTP Client Transport</name>
|
||||
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.client.http</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.mortbay.jetty.npn</groupId>
|
||||
<artifactId>npn-boot</artifactId>
|
||||
<version>${npn.version}</version>
|
||||
<type>jar</type>
|
||||
<overWrite>false</overWrite>
|
||||
<outputDirectory>${project.build.directory}/npn</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<argLine>-Xbootclasspath/p:${project.build.directory}/npn/npn-boot-${npn.version}.jar</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>manifest</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Export-Package>org.eclipse.jetty.spdy.client.http;version="9.1"</Export-Package>
|
||||
<Import-Package>!org.eclipse.jetty.npn,org.eclipse.jetty.*;version="[9.0,10.0)",*</Import-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.spdy</groupId>
|
||||
<artifactId>spdy-client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.spdy</groupId>
|
||||
<artifactId>spdy-http-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.spdy</groupId>
|
||||
<artifactId>spdy-http-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -16,7 +16,7 @@
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.client.spdy;
|
||||
package org.eclipse.jetty.spdy.client.http;
|
||||
|
||||
import org.eclipse.jetty.client.HttpChannel;
|
||||
import org.eclipse.jetty.client.HttpDestination;
|
@ -16,7 +16,7 @@
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.client.spdy;
|
||||
package org.eclipse.jetty.spdy.client.http;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
|
@ -16,7 +16,7 @@
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.client.spdy;
|
||||
package org.eclipse.jetty.spdy.client.http;
|
||||
|
||||
import org.eclipse.jetty.client.HttpChannel;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
@ -16,7 +16,7 @@
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.client.spdy;
|
||||
package org.eclipse.jetty.spdy.client.http;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@ -30,7 +30,7 @@ import org.eclipse.jetty.util.Promise;
|
||||
public class HttpDestinationOverSPDY extends HttpDestination implements Promise<Connection>
|
||||
{
|
||||
private final AtomicReference<ConnectState> connect = new AtomicReference<>(ConnectState.DISCONNECTED);
|
||||
private volatile HttpConnectionOverSPDY connection;
|
||||
private HttpConnectionOverSPDY connection;
|
||||
|
||||
public HttpDestinationOverSPDY(HttpClient client, String scheme, String host, int port)
|
||||
{
|
||||
@ -48,7 +48,7 @@ public class HttpDestinationOverSPDY extends HttpDestination implements Promise<
|
||||
case DISCONNECTED:
|
||||
{
|
||||
if (!connect.compareAndSet(current, ConnectState.CONNECTING))
|
||||
continue;
|
||||
break;
|
||||
newConnection(this);
|
||||
return;
|
||||
}
|
||||
@ -59,10 +59,9 @@ public class HttpDestinationOverSPDY extends HttpDestination implements Promise<
|
||||
}
|
||||
case CONNECTED:
|
||||
{
|
||||
HttpConnectionOverSPDY connection = this.connection;
|
||||
if (connection != null)
|
||||
process(connection, false);
|
||||
break;
|
||||
if (process(connection, false))
|
||||
break;
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
@ -75,14 +74,14 @@ public class HttpDestinationOverSPDY extends HttpDestination implements Promise<
|
||||
@Override
|
||||
public void succeeded(Connection result)
|
||||
{
|
||||
HttpConnectionOverSPDY connection = this.connection = (HttpConnectionOverSPDY)result;
|
||||
if (connect.compareAndSet(ConnectState.CONNECTING, ConnectState.CONNECTED))
|
||||
{
|
||||
HttpConnectionOverSPDY connection = this.connection = (HttpConnectionOverSPDY)result;
|
||||
process(connection, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.close();
|
||||
connection.close();
|
||||
failed(new IllegalStateException());
|
||||
}
|
||||
}
|
||||
@ -93,39 +92,40 @@ public class HttpDestinationOverSPDY extends HttpDestination implements Promise<
|
||||
connect.set(ConnectState.DISCONNECTED);
|
||||
}
|
||||
|
||||
private void process(final HttpConnectionOverSPDY connection, boolean dispatch)
|
||||
private boolean process(final HttpConnectionOverSPDY connection, boolean dispatch)
|
||||
{
|
||||
HttpClient client = getHttpClient();
|
||||
final HttpExchange exchange = getHttpExchanges().poll();
|
||||
LOG.debug("Processing exchange {} on connection {}", exchange, connection);
|
||||
if (exchange != null)
|
||||
if (exchange == null)
|
||||
return false;
|
||||
|
||||
final Request request = exchange.getRequest();
|
||||
Throwable cause = request.getAbortCause();
|
||||
if (cause != null)
|
||||
{
|
||||
final Request request = exchange.getRequest();
|
||||
Throwable cause = request.getAbortCause();
|
||||
if (cause != null)
|
||||
LOG.debug("Abort before processing {}: {}", exchange, cause);
|
||||
abort(exchange, cause);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dispatch)
|
||||
{
|
||||
abort(exchange, cause);
|
||||
LOG.debug("Aborted before processing {}: {}", exchange, cause);
|
||||
client.getExecutor().execute(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
connection.send(exchange);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dispatch)
|
||||
{
|
||||
client.getExecutor().execute(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
connection.send(exchange);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
connection.send(exchange);
|
||||
}
|
||||
connection.send(exchange);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private enum ConnectState
|
@ -16,7 +16,7 @@
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.client.spdy;
|
||||
package org.eclipse.jetty.spdy.client.http;
|
||||
|
||||
import org.eclipse.jetty.client.HttpExchange;
|
||||
import org.eclipse.jetty.client.HttpReceiver;
|
||||
@ -32,6 +32,7 @@ import org.eclipse.jetty.spdy.api.RstInfo;
|
||||
import org.eclipse.jetty.spdy.api.Stream;
|
||||
import org.eclipse.jetty.spdy.api.StreamFrameListener;
|
||||
import org.eclipse.jetty.spdy.api.StreamStatus;
|
||||
import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.Fields;
|
||||
|
||||
@ -55,37 +56,52 @@ public class HttpReceiverOverSPDY extends HttpReceiver implements StreamFrameLis
|
||||
if (exchange == null)
|
||||
return;
|
||||
|
||||
HttpResponse response = exchange.getResponse();
|
||||
|
||||
Fields fields = replyInfo.getHeaders();
|
||||
// TODO: use HTTPSPDYHeader enum
|
||||
HttpVersion version = HttpVersion.fromString(fields.get(":version").value());
|
||||
response.version(version);
|
||||
Integer status = fields.get(":status").valueAsInt();
|
||||
response.status(status);
|
||||
response.reason(HttpStatus.getMessage(status));
|
||||
|
||||
onResponseBegin(exchange);
|
||||
|
||||
for (Fields.Field field : fields)
|
||||
try
|
||||
{
|
||||
// TODO: handle multiple values properly
|
||||
// TODO: skip special headers
|
||||
HttpField httpField = new HttpField(field.name(), field.value());
|
||||
onResponseHeader(exchange, httpField);
|
||||
HttpResponse response = exchange.getResponse();
|
||||
|
||||
Fields fields = replyInfo.getHeaders();
|
||||
short spdy = stream.getSession().getVersion();
|
||||
HttpVersion version = HttpVersion.fromString(fields.get(HTTPSPDYHeader.VERSION.name(spdy)).value());
|
||||
response.version(version);
|
||||
String[] status = fields.get(HTTPSPDYHeader.STATUS.name(spdy)).value().split(" ", 2);
|
||||
|
||||
Integer code = Integer.parseInt(status[0]);
|
||||
response.status(code);
|
||||
String reason = status.length < 2 ? HttpStatus.getMessage(code) : status[1];
|
||||
response.reason(reason);
|
||||
|
||||
if (responseBegin(exchange))
|
||||
{
|
||||
for (Fields.Field field : fields)
|
||||
{
|
||||
String name = field.name();
|
||||
if (HTTPSPDYHeader.from(spdy, name) != null)
|
||||
continue;
|
||||
// TODO: handle multiple values properly
|
||||
HttpField httpField = new HttpField(name, field.value());
|
||||
responseHeader(exchange, httpField);
|
||||
}
|
||||
|
||||
if (responseHeaders(exchange))
|
||||
{
|
||||
if (replyInfo.isClose())
|
||||
{
|
||||
responseSuccess(exchange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onResponseHeaders(exchange);
|
||||
|
||||
if (replyInfo.isClose())
|
||||
catch (Exception x)
|
||||
{
|
||||
onResponseSuccess(exchange);
|
||||
responseFailure(x);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamFrameListener onPush(Stream stream, PushInfo pushInfo)
|
||||
{
|
||||
// SPDY push not supported
|
||||
getHttpChannel().getSession().rst(new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM), new Callback.Adapter());
|
||||
return null;
|
||||
}
|
||||
@ -103,14 +119,24 @@ public class HttpReceiverOverSPDY extends HttpReceiver implements StreamFrameLis
|
||||
if (exchange == null)
|
||||
return;
|
||||
|
||||
int length = dataInfo.length();
|
||||
// TODO: avoid data copy here
|
||||
onResponseContent(exchange, dataInfo.asByteBuffer(false));
|
||||
dataInfo.consume(length);
|
||||
|
||||
if (dataInfo.isClose())
|
||||
try
|
||||
{
|
||||
onResponseSuccess(exchange);
|
||||
int length = dataInfo.length();
|
||||
// TODO: avoid data copy here
|
||||
boolean process = responseContent(exchange, dataInfo.asByteBuffer(false));
|
||||
dataInfo.consume(length);
|
||||
|
||||
if (process)
|
||||
{
|
||||
if (dataInfo.isClose())
|
||||
{
|
||||
responseSuccess(exchange);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
responseFailure(x);
|
||||
}
|
||||
}
|
||||
}
|
@ -16,17 +16,18 @@
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.client.spdy;
|
||||
package org.eclipse.jetty.spdy.client.http;
|
||||
|
||||
import org.eclipse.jetty.client.HttpContent;
|
||||
import org.eclipse.jetty.client.HttpExchange;
|
||||
import org.eclipse.jetty.client.HttpSender;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.Stream;
|
||||
import org.eclipse.jetty.spdy.api.SynInfo;
|
||||
import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.Fields;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
|
||||
@ -46,14 +47,33 @@ public class HttpSenderOverSPDY extends HttpSender
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendHeaders(HttpExchange exchange, final HttpContent content)
|
||||
protected void sendHeaders(HttpExchange exchange, final HttpContent content, final Callback callback)
|
||||
{
|
||||
final Request request = exchange.getRequest();
|
||||
|
||||
short spdyVersion = getHttpChannel().getSession().getVersion();
|
||||
Fields fields = new Fields();
|
||||
HttpFields headers = request.getHeaders();
|
||||
for (HttpField header : headers)
|
||||
fields.add(header.getName(), header.getValue());
|
||||
HttpField hostHeader = null;
|
||||
for (HttpField header : request.getHeaders())
|
||||
{
|
||||
String name = header.getName();
|
||||
// The host header needs a special treatment
|
||||
if (HTTPSPDYHeader.from(spdyVersion, name) != HTTPSPDYHeader.HOST)
|
||||
fields.add(name, header.getValue());
|
||||
else
|
||||
hostHeader = header;
|
||||
}
|
||||
|
||||
// Add special SPDY headers
|
||||
fields.put(HTTPSPDYHeader.METHOD.name(spdyVersion), request.getMethod().asString());
|
||||
String path = request.getPath();
|
||||
String query = request.getQuery();
|
||||
if (query != null)
|
||||
path += "?" + query;
|
||||
fields.put(HTTPSPDYHeader.URI.name(spdyVersion), path);
|
||||
fields.put(HTTPSPDYHeader.VERSION.name(spdyVersion), request.getVersion().asString());
|
||||
if (hostHeader != null)
|
||||
fields.put(HTTPSPDYHeader.HOST.name(spdyVersion), hostHeader.getValue());
|
||||
|
||||
SynInfo synInfo = new SynInfo(fields, !content.hasContent());
|
||||
getHttpChannel().getSession().syn(synInfo, getHttpChannel().getHttpReceiver(), new Promise<Stream>()
|
||||
@ -63,23 +83,29 @@ public class HttpSenderOverSPDY extends HttpSender
|
||||
{
|
||||
if (content.hasContent())
|
||||
HttpSenderOverSPDY.this.stream = stream;
|
||||
content.succeeded();
|
||||
callback.succeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable failure)
|
||||
{
|
||||
content.failed(failure);
|
||||
callback.failed(failure);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendContent(HttpExchange exchange, HttpContent content)
|
||||
protected void sendContent(HttpExchange exchange, HttpContent content, Callback callback)
|
||||
{
|
||||
assert stream != null;
|
||||
ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(content.getByteBuffer(), content.isLast());
|
||||
stream.data(dataInfo, content);
|
||||
if (content.isConsumed())
|
||||
{
|
||||
callback.succeeded();
|
||||
}
|
||||
else
|
||||
{
|
||||
ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(content.getByteBuffer(), content.isLast());
|
||||
stream.data(dataInfo, callback);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
@ -0,0 +1,82 @@
|
||||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.spdy.client.http;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.NetworkConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
import org.eclipse.jetty.spdy.client.SPDYClient;
|
||||
import org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory;
|
||||
import org.eclipse.jetty.spdy.server.http.PushStrategy;
|
||||
import org.eclipse.jetty.toolchain.test.TestTracker;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
|
||||
public abstract class AbstractHttpClientServerTest
|
||||
{
|
||||
@Rule
|
||||
public final TestTracker tracker = new TestTracker();
|
||||
|
||||
protected Server server;
|
||||
protected NetworkConnector connector;
|
||||
protected SPDYClient.Factory factory;
|
||||
protected HttpClient client;
|
||||
protected String scheme = HttpScheme.HTTP.asString();
|
||||
|
||||
public void start(Handler handler) throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
|
||||
short version = SPDY.V3;
|
||||
|
||||
HTTPSPDYServerConnectionFactory spdyConnectionFactory = new HTTPSPDYServerConnectionFactory(version, new HttpConfiguration(), new PushStrategy.None());
|
||||
connector = new ServerConnector(server, spdyConnectionFactory);
|
||||
|
||||
server.addConnector(connector);
|
||||
server.setHandler(handler);
|
||||
server.start();
|
||||
|
||||
QueuedThreadPool executor = new QueuedThreadPool();
|
||||
executor.setName(executor.getName() + "-client");
|
||||
|
||||
factory = new SPDYClient.Factory(executor);
|
||||
factory.start();
|
||||
|
||||
client = new HttpClient(new HttpClientTransportOverSPDY(factory.newSPDYClient(version)), null);
|
||||
client.setExecutor(executor);
|
||||
client.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void dispose() throws Exception
|
||||
{
|
||||
if (client != null)
|
||||
client.stop();
|
||||
if (factory != null)
|
||||
factory.stop();
|
||||
if (server != null)
|
||||
server.stop();
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.spdy.client.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
|
||||
public class EmptyServerHandler extends AbstractHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
}
|
@ -0,0 +1,422 @@
|
||||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.spdy.client.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.util.BytesContentProvider;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.toolchain.test.annotation.Slow;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HttpClientTest extends AbstractHttpClientServerTest
|
||||
{
|
||||
@Test
|
||||
public void test_GET_ResponseWithoutContent() throws Exception
|
||||
{
|
||||
start(new EmptyServerHandler());
|
||||
|
||||
Response response = client.GET(scheme + "://localhost:" + connector.getLocalPort());
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_GET_ResponseWithContent() throws Exception
|
||||
{
|
||||
final byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7};
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
response.getOutputStream().write(data);
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
|
||||
ContentResponse response = client.GET(scheme + "://localhost:" + connector.getLocalPort());
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
byte[] content = response.getContent();
|
||||
Assert.assertArrayEquals(data, content);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_GET_WithParameters_ResponseWithContent() throws Exception
|
||||
{
|
||||
final String paramName1 = "a";
|
||||
final String paramName2 = "b";
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
ServletOutputStream output = response.getOutputStream();
|
||||
String paramValue1 = request.getParameter(paramName1);
|
||||
output.write(paramValue1.getBytes("UTF-8"));
|
||||
String paramValue2 = request.getParameter(paramName2);
|
||||
Assert.assertEquals("", paramValue2);
|
||||
output.write("empty".getBytes("UTF-8"));
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
|
||||
String value1 = "\u20AC";
|
||||
String paramValue1 = URLEncoder.encode(value1, "UTF-8");
|
||||
String query = paramName1 + "=" + paramValue1 + "&" + paramName2;
|
||||
ContentResponse response = client.GET(scheme + "://localhost:" + connector.getLocalPort() + "/?" + query);
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
String content = new String(response.getContent(), "UTF-8");
|
||||
Assert.assertEquals(value1 + "empty", content);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_GET_WithParametersMultiValued_ResponseWithContent() throws Exception
|
||||
{
|
||||
final String paramName1 = "a";
|
||||
final String paramName2 = "b";
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
ServletOutputStream output = response.getOutputStream();
|
||||
String[] paramValues1 = request.getParameterValues(paramName1);
|
||||
for (String paramValue : paramValues1)
|
||||
output.write(paramValue.getBytes("UTF-8"));
|
||||
String paramValue2 = request.getParameter(paramName2);
|
||||
output.write(paramValue2.getBytes("UTF-8"));
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
|
||||
String value11 = "\u20AC";
|
||||
String value12 = "\u20AA";
|
||||
String value2 = "&";
|
||||
String paramValue11 = URLEncoder.encode(value11, "UTF-8");
|
||||
String paramValue12 = URLEncoder.encode(value12, "UTF-8");
|
||||
String paramValue2 = URLEncoder.encode(value2, "UTF-8");
|
||||
String query = paramName1 + "=" + paramValue11 + "&" + paramName1 + "=" + paramValue12 + "&" + paramName2 + "=" + paramValue2;
|
||||
ContentResponse response = client.GET(scheme + "://localhost:" + connector.getLocalPort() + "/?" + query);
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
String content = new String(response.getContent(), "UTF-8");
|
||||
Assert.assertEquals(value11 + value12 + value2, content);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_POST_WithParameters() throws Exception
|
||||
{
|
||||
final String paramName = "a";
|
||||
final String paramValue = "\u20AC";
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
String value = request.getParameter(paramName);
|
||||
if (paramValue.equals(value))
|
||||
{
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setContentType("text/plain");
|
||||
response.getOutputStream().print(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort())
|
||||
.param(paramName, paramValue)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
Assert.assertEquals(paramValue, new String(response.getContent(), "UTF-8"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_PUT_WithParameters() throws Exception
|
||||
{
|
||||
final String paramName = "a";
|
||||
final String paramValue = "\u20AC";
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
String value = request.getParameter(paramName);
|
||||
if (paramValue.equals(value))
|
||||
{
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setContentType("text/plain");
|
||||
response.getOutputStream().print(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort() + "/path?" + paramName + "=" + paramValue);
|
||||
ContentResponse response = client.newRequest(uri)
|
||||
.method(HttpMethod.PUT)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
Assert.assertEquals(paramValue, new String(response.getContent(), "UTF-8"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_POST_WithParameters_WithContent() throws Exception
|
||||
{
|
||||
final byte[] content = {0, 1, 2, 3};
|
||||
final String paramName = "a";
|
||||
final String paramValue = "\u20AC";
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
String value = request.getParameter(paramName);
|
||||
if (paramValue.equals(value))
|
||||
{
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setContentType("application/octet-stream");
|
||||
response.getOutputStream().write(content);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort() + "/?b=1")
|
||||
.param(paramName, paramValue)
|
||||
.content(new BytesContentProvider(content))
|
||||
.timeout(555, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
Assert.assertArrayEquals(content, response.getContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_POST_WithContent_NotifiesRequestContentListener() throws Exception
|
||||
{
|
||||
final byte[] content = {0, 1, 2, 3};
|
||||
start(new EmptyServerHandler());
|
||||
|
||||
ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort())
|
||||
.onRequestContent(new Request.ContentListener()
|
||||
{
|
||||
@Override
|
||||
public void onContent(Request request, ByteBuffer buffer)
|
||||
{
|
||||
byte[] bytes = new byte[buffer.remaining()];
|
||||
buffer.get(bytes);
|
||||
if (!Arrays.equals(content, bytes))
|
||||
request.abort(new Exception());
|
||||
}
|
||||
})
|
||||
.content(new BytesContentProvider(content))
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_POST_WithContent_TracksProgress() throws Exception
|
||||
{
|
||||
start(new EmptyServerHandler());
|
||||
|
||||
final AtomicInteger progress = new AtomicInteger();
|
||||
ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort())
|
||||
.onRequestContent(new Request.ContentListener()
|
||||
{
|
||||
@Override
|
||||
public void onContent(Request request, ByteBuffer buffer)
|
||||
{
|
||||
byte[] bytes = new byte[buffer.remaining()];
|
||||
Assert.assertEquals(1, bytes.length);
|
||||
buffer.get(bytes);
|
||||
Assert.assertEquals(bytes[0], progress.getAndIncrement());
|
||||
}
|
||||
})
|
||||
.content(new BytesContentProvider(new byte[]{0}, new byte[]{1}, new byte[]{2}, new byte[]{3}, new byte[]{4}))
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
Assert.assertEquals(5, progress.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_GZIP_ContentEncoding() throws Exception
|
||||
{
|
||||
final byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
response.setHeader("Content-Encoding", "gzip");
|
||||
GZIPOutputStream gzipOutput = new GZIPOutputStream(response.getOutputStream());
|
||||
gzipOutput.write(data);
|
||||
gzipOutput.finish();
|
||||
}
|
||||
});
|
||||
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
Assert.assertArrayEquals(data, response.getContent());
|
||||
}
|
||||
|
||||
@Slow
|
||||
@Test
|
||||
public void test_Request_IdleTimeout() throws Exception
|
||||
{
|
||||
final long idleTimeout = 1000;
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
try
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
throw new ServletException(x);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
final String host = "localhost";
|
||||
final int port = connector.getLocalPort();
|
||||
try
|
||||
{
|
||||
client.newRequest(host, port)
|
||||
.scheme(scheme)
|
||||
.idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
|
||||
.timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
|
||||
.send();
|
||||
Assert.fail();
|
||||
}
|
||||
catch (ExecutionException expected)
|
||||
{
|
||||
Assert.assertTrue(expected.getCause() instanceof TimeoutException);
|
||||
}
|
||||
|
||||
// Make another request without specifying the idle timeout, should not fail
|
||||
ContentResponse response = client.newRequest(host, port)
|
||||
.scheme(scheme)
|
||||
.timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendToIPv6Address() throws Exception
|
||||
{
|
||||
start(new EmptyServerHandler());
|
||||
|
||||
ContentResponse response = client.newRequest("[::1]", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_HEAD_With_ResponseContentLength() throws Exception
|
||||
{
|
||||
final int length = 1024;
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
response.getOutputStream().write(new byte[length]);
|
||||
}
|
||||
});
|
||||
|
||||
// HEAD requests receive a Content-Length header, but do not
|
||||
// receive the content so they must handle this case properly
|
||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.method(HttpMethod.HEAD)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
Assert.assertEquals(0, response.getContent().length);
|
||||
|
||||
// Perform a normal GET request to be sure the content is now read
|
||||
response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
Assert.assertEquals(length, response.getContent().length);
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
#org.eclipse.jetty.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.client.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.spdy.LEVEL=DEBUG
|
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user