jetty-9 - HTTP client: implemented abort of request and responses.
This commit is contained in:
parent
b5a329ecaf
commit
5315a6dcff
|
@ -30,6 +30,7 @@ import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
import org.eclipse.jetty.client.api.Request;
|
import org.eclipse.jetty.client.api.Request;
|
||||||
import org.eclipse.jetty.client.api.Response;
|
import org.eclipse.jetty.client.api.Response;
|
||||||
import org.eclipse.jetty.client.api.Result;
|
import org.eclipse.jetty.client.api.Result;
|
||||||
|
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
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;
|
||||||
|
@ -66,31 +67,18 @@ public class AuthenticationProtocolHandler implements ProtocolHandler
|
||||||
return new AuthenticationListener();
|
return new AuthenticationListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AuthenticationListener extends Response.Listener.Adapter
|
private class AuthenticationListener extends BufferingResponseListener
|
||||||
{
|
{
|
||||||
private byte[] buffer = new byte[0];
|
private AuthenticationListener()
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onContent(Response response, ByteBuffer content)
|
|
||||||
{
|
{
|
||||||
if (buffer.length == maxContentLength)
|
super(maxContentLength);
|
||||||
return;
|
|
||||||
|
|
||||||
long newLength = buffer.length + content.remaining();
|
|
||||||
if (newLength > maxContentLength)
|
|
||||||
newLength = maxContentLength;
|
|
||||||
|
|
||||||
byte[] newBuffer = new byte[(int)newLength];
|
|
||||||
System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
|
|
||||||
content.get(newBuffer, buffer.length, content.remaining());
|
|
||||||
buffer = newBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
{
|
{
|
||||||
Request request = result.getRequest();
|
Request request = result.getRequest();
|
||||||
ContentResponse response = new HttpContentResponse(result.getResponse(), buffer);
|
ContentResponse response = new HttpContentResponse(result.getResponse(), getContent());
|
||||||
if (result.isFailed())
|
if (result.isFailed())
|
||||||
{
|
{
|
||||||
Throwable failure = result.getFailure();
|
Throwable failure = result.getFailure();
|
||||||
|
@ -136,7 +124,7 @@ public class AuthenticationProtocolHandler implements ProtocolHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
authnResult.apply(request);
|
authnResult.apply(request);
|
||||||
request.send(new Adapter()
|
request.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Response response)
|
public void onSuccess(Response response)
|
||||||
|
|
|
@ -250,6 +250,11 @@ public class HttpConnection extends AbstractConnection implements Connection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void abort(HttpResponse response)
|
||||||
|
{
|
||||||
|
receiver.fail(new HttpResponseException("Response aborted", response));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,7 +42,7 @@ public class HttpExchange
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
this.request = request;
|
this.request = request;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.response = new HttpResponse(listener);
|
this.response = new HttpResponse(this, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpConversation conversation()
|
public HttpConversation conversation()
|
||||||
|
@ -117,6 +117,12 @@ public class HttpExchange
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void abort()
|
||||||
|
{
|
||||||
|
LOG.debug("Aborting {}", response);
|
||||||
|
connection.abort(response);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
|
|
@ -31,15 +31,13 @@ import org.eclipse.jetty.client.api.ContentProvider;
|
||||||
import org.eclipse.jetty.client.api.ContentResponse;
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
import org.eclipse.jetty.client.api.Request;
|
import org.eclipse.jetty.client.api.Request;
|
||||||
import org.eclipse.jetty.client.api.Response;
|
import org.eclipse.jetty.client.api.Response;
|
||||||
import org.eclipse.jetty.client.api.Result;
|
import org.eclipse.jetty.client.util.BlockingResponseListener;
|
||||||
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
|
||||||
import org.eclipse.jetty.client.util.PathContentProvider;
|
import org.eclipse.jetty.client.util.PathContentProvider;
|
||||||
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.HttpMethod;
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
import org.eclipse.jetty.http.HttpVersion;
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
import org.eclipse.jetty.util.Fields;
|
import org.eclipse.jetty.util.Fields;
|
||||||
import org.eclipse.jetty.util.FutureCallback;
|
|
||||||
|
|
||||||
public class HttpRequest implements Request
|
public class HttpRequest implements Request
|
||||||
{
|
{
|
||||||
|
@ -59,6 +57,7 @@ public class HttpRequest implements Request
|
||||||
private Listener listener;
|
private Listener listener;
|
||||||
private ContentProvider content;
|
private ContentProvider content;
|
||||||
private boolean followRedirects = true;
|
private boolean followRedirects = true;
|
||||||
|
private volatile boolean aborted;
|
||||||
|
|
||||||
public HttpRequest(HttpClient client, URI uri)
|
public HttpRequest(HttpClient client, URI uri)
|
||||||
{
|
{
|
||||||
|
@ -296,22 +295,9 @@ public class HttpRequest implements Request
|
||||||
@Override
|
@Override
|
||||||
public Future<ContentResponse> send()
|
public Future<ContentResponse> send()
|
||||||
{
|
{
|
||||||
final FutureCallback<ContentResponse> callback = new FutureCallback<>();
|
BlockingResponseListener listener = new BlockingResponseListener();
|
||||||
BufferingResponseListener listener = new BufferingResponseListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onComplete(Result result)
|
|
||||||
{
|
|
||||||
super.onComplete(result);
|
|
||||||
HttpContentResponse contentResponse = new HttpContentResponse(result.getResponse(), content());
|
|
||||||
if (!result.isFailed())
|
|
||||||
callback.completed(contentResponse);
|
|
||||||
else
|
|
||||||
callback.failed(contentResponse, result.getFailure());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
send(listener);
|
send(listener);
|
||||||
return callback;
|
return listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -320,6 +306,18 @@ public class HttpRequest implements Request
|
||||||
client.send(this, listener);
|
client.send(this, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void abort()
|
||||||
|
{
|
||||||
|
aborted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean aborted()
|
||||||
|
{
|
||||||
|
return aborted;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2012 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.client;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
|
||||||
|
public class HttpRequestException extends Throwable
|
||||||
|
{
|
||||||
|
private final Request request;
|
||||||
|
|
||||||
|
public HttpRequestException(String message, Request request)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Request getRequest()
|
||||||
|
{
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,13 +25,15 @@ import org.eclipse.jetty.http.HttpVersion;
|
||||||
public class HttpResponse implements Response
|
public class HttpResponse implements Response
|
||||||
{
|
{
|
||||||
private final HttpFields headers = new HttpFields();
|
private final HttpFields headers = new HttpFields();
|
||||||
|
private final HttpExchange exchange;
|
||||||
private final Listener listener;
|
private final Listener listener;
|
||||||
private HttpVersion version;
|
private HttpVersion version;
|
||||||
private int status;
|
private int status;
|
||||||
private String reason;
|
private String reason;
|
||||||
|
|
||||||
public HttpResponse(Response.Listener listener)
|
public HttpResponse(HttpExchange exchange, Listener listener)
|
||||||
{
|
{
|
||||||
|
this.exchange = exchange;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +86,7 @@ public class HttpResponse implements Response
|
||||||
@Override
|
@Override
|
||||||
public void abort()
|
public void abort()
|
||||||
{
|
{
|
||||||
// request.abort();
|
exchange.abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -60,13 +60,21 @@ public class HttpSender
|
||||||
|
|
||||||
public void send(HttpExchange exchange)
|
public void send(HttpExchange exchange)
|
||||||
{
|
{
|
||||||
LOG.debug("Sending {}", exchange.request());
|
Request request = exchange.request();
|
||||||
requestNotifier.notifyBegin(exchange.request());
|
if (request.aborted())
|
||||||
ContentProvider content = exchange.request().content();
|
{
|
||||||
|
fail(new HttpRequestException("Request aborted", request));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG.debug("Sending {}", request);
|
||||||
|
requestNotifier.notifyBegin(request);
|
||||||
|
ContentProvider content = request.content();
|
||||||
this.contentLength = content == null ? -1 : content.length();
|
this.contentLength = content == null ? -1 : content.length();
|
||||||
this.contentChunks = content == null ? Collections.<ByteBuffer>emptyIterator() : content.iterator();
|
this.contentChunks = content == null ? Collections.<ByteBuffer>emptyIterator() : content.iterator();
|
||||||
send();
|
send();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void send()
|
private void send()
|
||||||
{
|
{
|
||||||
|
@ -121,6 +129,12 @@ public class HttpSender
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FLUSH:
|
case FLUSH:
|
||||||
|
{
|
||||||
|
if (request.aborted())
|
||||||
|
{
|
||||||
|
fail(new HttpRequestException("Request aborted", request));
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
StatefulExecutorCallback callback = new StatefulExecutorCallback(client.getExecutor())
|
StatefulExecutorCallback callback = new StatefulExecutorCallback(client.getExecutor())
|
||||||
{
|
{
|
||||||
|
@ -142,17 +156,12 @@ public class HttpSender
|
||||||
header = BufferUtil.EMPTY_BUFFER;
|
header = BufferUtil.EMPTY_BUFFER;
|
||||||
if (chunk == null)
|
if (chunk == null)
|
||||||
chunk = BufferUtil.EMPTY_BUFFER;
|
chunk = BufferUtil.EMPTY_BUFFER;
|
||||||
LOG.debug("Writing {} {} {}", header, chunk, content);
|
|
||||||
endPoint.write(null, callback, header, chunk, content);
|
endPoint.write(null, callback, header, chunk, content);
|
||||||
if (callback.pending())
|
if (callback.pending())
|
||||||
{
|
|
||||||
LOG.debug("Write incomplete {} {} {}", header, chunk, content);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (callback.completed())
|
if (callback.completed())
|
||||||
{
|
{
|
||||||
LOG.debug("Write complete {} {} {}", header, chunk, content);
|
|
||||||
if (!committed)
|
if (!committed)
|
||||||
committed(request);
|
committed(request);
|
||||||
|
|
||||||
|
@ -160,6 +169,7 @@ public class HttpSender
|
||||||
content = contentChunks.hasNext() ? contentChunks.next() : BufferUtil.EMPTY_BUFFER;
|
content = contentChunks.hasNext() ? contentChunks.next() : BufferUtil.EMPTY_BUFFER;
|
||||||
lastContent = !contentChunks.hasNext();
|
lastContent = !contentChunks.hasNext();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SHUTDOWN_OUT:
|
case SHUTDOWN_OUT:
|
||||||
|
|
|
@ -24,7 +24,7 @@ import org.eclipse.jetty.client.api.Result;
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.http.HttpMethod;
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
|
|
||||||
public class RedirectProtocolHandler extends Response.Listener.Adapter implements ProtocolHandler
|
public class RedirectProtocolHandler extends Response.Listener.Empty implements ProtocolHandler
|
||||||
{
|
{
|
||||||
private static final String ATTRIBUTE = RedirectProtocolHandler.class.getName() + ".redirect";
|
private static final String ATTRIBUTE = RedirectProtocolHandler.class.getName() + ".redirect";
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ public class RedirectProtocolHandler extends Response.Listener.Adapter implement
|
||||||
// Copy content
|
// Copy content
|
||||||
redirect.content(request.content());
|
redirect.content(request.content());
|
||||||
|
|
||||||
redirect.send(new Adapter());
|
redirect.send(new Response.Listener.Empty());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -230,6 +230,18 @@ public interface Request
|
||||||
*/
|
*/
|
||||||
void send(Response.Listener listener);
|
void send(Response.Listener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to abort the send of this request.
|
||||||
|
*
|
||||||
|
* @see #aborted()
|
||||||
|
*/
|
||||||
|
void abort();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether {@link #abort()} was called
|
||||||
|
*/
|
||||||
|
boolean aborted();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener for request events
|
* Listener for request events
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -51,7 +51,7 @@ public interface Response
|
||||||
|
|
||||||
public void onComplete(Result result);
|
public void onComplete(Result result);
|
||||||
|
|
||||||
public static class Adapter implements Listener
|
public static class Empty implements Listener
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onBegin(Response response)
|
public void onBegin(Response response)
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2012 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.client.util;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.HttpContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.Response;
|
||||||
|
import org.eclipse.jetty.client.api.Result;
|
||||||
|
|
||||||
|
public class BlockingResponseListener extends BufferingResponseListener implements Future<ContentResponse>
|
||||||
|
{
|
||||||
|
private final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
private ContentResponse response;
|
||||||
|
private Throwable failure;
|
||||||
|
private volatile boolean cancelled;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBegin(Response response)
|
||||||
|
{
|
||||||
|
super.onBegin(response);
|
||||||
|
if (cancelled)
|
||||||
|
response.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHeaders(Response response)
|
||||||
|
{
|
||||||
|
super.onHeaders(response);
|
||||||
|
if (cancelled)
|
||||||
|
response.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onContent(Response response, ByteBuffer content)
|
||||||
|
{
|
||||||
|
super.onContent(response, content);
|
||||||
|
if (cancelled)
|
||||||
|
response.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete(Result result)
|
||||||
|
{
|
||||||
|
super.onComplete(result);
|
||||||
|
response = new HttpContentResponse(result.getResponse(), getContent());
|
||||||
|
failure = result.getFailure();
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean cancel(boolean mayInterruptIfRunning)
|
||||||
|
{
|
||||||
|
cancelled = true;
|
||||||
|
return latch.getCount() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled()
|
||||||
|
{
|
||||||
|
return cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDone()
|
||||||
|
{
|
||||||
|
return latch.getCount() == 0 || isCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ContentResponse get() throws InterruptedException, ExecutionException
|
||||||
|
{
|
||||||
|
latch.await();
|
||||||
|
return result();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ContentResponse get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
|
||||||
|
{
|
||||||
|
boolean expired = !latch.await(timeout, unit);
|
||||||
|
if (expired)
|
||||||
|
throw new TimeoutException();
|
||||||
|
return result();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ContentResponse result() throws ExecutionException
|
||||||
|
{
|
||||||
|
if (isCancelled())
|
||||||
|
throw (CancellationException)new CancellationException().initCause(failure);
|
||||||
|
if (failure != null)
|
||||||
|
throw new ExecutionException(failure);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,38 +18,32 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.client.util;
|
package org.eclipse.jetty.client.util;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.UnsupportedCharsetException;
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.client.api.Response;
|
import org.eclipse.jetty.client.api.Response;
|
||||||
|
|
||||||
public class BufferingResponseListener extends Response.Listener.Adapter
|
public class BufferingResponseListener extends Response.Listener.Empty
|
||||||
{
|
{
|
||||||
private final CountDownLatch latch = new CountDownLatch(1);
|
private final int maxLength;
|
||||||
private final int maxCapacity;
|
private volatile byte[] buffer = new byte[0];
|
||||||
private Response response;
|
|
||||||
private Throwable failure;
|
|
||||||
private byte[] buffer = new byte[0];
|
|
||||||
|
|
||||||
public BufferingResponseListener()
|
public BufferingResponseListener()
|
||||||
{
|
{
|
||||||
this(16 * 1024 * 1024);
|
this(2 * 1024 * 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferingResponseListener(int maxCapacity)
|
public BufferingResponseListener(int maxLength)
|
||||||
{
|
{
|
||||||
this.maxCapacity = maxCapacity;
|
this.maxLength = maxLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onContent(Response response, ByteBuffer content)
|
public void onContent(Response response, ByteBuffer content)
|
||||||
{
|
{
|
||||||
long newLength = buffer.length + content.remaining();
|
long newLength = buffer.length + content.remaining();
|
||||||
if (newLength > maxCapacity)
|
if (newLength > maxLength)
|
||||||
throw new IllegalStateException("Buffering capacity exceeded");
|
throw new IllegalStateException("Buffering capacity exceeded");
|
||||||
|
|
||||||
byte[] newBuffer = new byte[(int)newLength];
|
byte[] newBuffer = new byte[(int)newLength];
|
||||||
|
@ -58,38 +52,20 @@ public class BufferingResponseListener extends Response.Listener.Adapter
|
||||||
buffer = newBuffer;
|
buffer = newBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public byte[] getContent()
|
||||||
public void onSuccess(Response response)
|
|
||||||
{
|
|
||||||
this.response = response;
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Response response, Throwable failure)
|
|
||||||
{
|
|
||||||
this.response = response;
|
|
||||||
this.failure = failure;
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Response await(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
|
|
||||||
{
|
|
||||||
boolean expired = !latch.await(timeout, unit);
|
|
||||||
if (failure != null)
|
|
||||||
throw new ExecutionException(failure);
|
|
||||||
if (expired)
|
|
||||||
throw new TimeoutException();
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] content()
|
|
||||||
{
|
{
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String contentAsString(String encoding)
|
public String getContent(String encoding)
|
||||||
{
|
{
|
||||||
return new String(content(), Charset.forName(encoding));
|
try
|
||||||
|
{
|
||||||
|
return new String(getContent(), encoding);
|
||||||
|
}
|
||||||
|
catch (UnsupportedEncodingException x)
|
||||||
|
{
|
||||||
|
throw new UnsupportedCharsetException(encoding);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.api.Response;
|
import org.eclipse.jetty.client.api.Response;
|
||||||
|
|
||||||
public class StreamingResponseListener extends Response.Listener.Adapter
|
public class StreamingResponseListener extends Response.Listener.Empty
|
||||||
{
|
{
|
||||||
public Response get(long timeout, TimeUnit seconds)
|
public Response get(long timeout, TimeUnit seconds)
|
||||||
{
|
{
|
||||||
|
|
|
@ -113,7 +113,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
||||||
|
|
||||||
// Request without Authentication causes a 401
|
// Request without Authentication causes a 401
|
||||||
Request request = client.newRequest("localhost", connector.getLocalPort()).path("/test");
|
Request request = client.newRequest("localhost", connector.getLocalPort()).path("/test");
|
||||||
ContentResponse response = request.send().get(555, TimeUnit.SECONDS);
|
ContentResponse response = request.send().get(5, TimeUnit.SECONDS);
|
||||||
Assert.assertNotNull(response);
|
Assert.assertNotNull(response);
|
||||||
Assert.assertEquals(401, response.status());
|
Assert.assertEquals(401, response.status());
|
||||||
Assert.assertEquals(1, requests.get());
|
Assert.assertEquals(1, requests.get());
|
||||||
|
@ -133,7 +133,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
||||||
client.getRequestListeners().add(requestListener);
|
client.getRequestListeners().add(requestListener);
|
||||||
|
|
||||||
// Request with authentication causes a 401 (no previous successful authentication) + 200
|
// Request with authentication causes a 401 (no previous successful authentication) + 200
|
||||||
response = request.send().get(555, TimeUnit.SECONDS);
|
response = request.send().get(5, TimeUnit.SECONDS);
|
||||||
Assert.assertNotNull(response);
|
Assert.assertNotNull(response);
|
||||||
Assert.assertEquals(200, response.status());
|
Assert.assertEquals(200, response.status());
|
||||||
Assert.assertEquals(2, requests.get());
|
Assert.assertEquals(2, requests.get());
|
||||||
|
@ -153,7 +153,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
||||||
// Further requests do not trigger 401 because there is a previous successful authentication
|
// Further requests do not trigger 401 because there is a previous successful authentication
|
||||||
// Remove existing header to be sure it's added by the implementation
|
// Remove existing header to be sure it's added by the implementation
|
||||||
request.header(HttpHeader.AUTHORIZATION.asString(), null);
|
request.header(HttpHeader.AUTHORIZATION.asString(), null);
|
||||||
response = request.send().get(555, TimeUnit.SECONDS);
|
response = request.send().get(5, TimeUnit.SECONDS);
|
||||||
Assert.assertNotNull(response);
|
Assert.assertNotNull(response);
|
||||||
Assert.assertEquals(200, response.status());
|
Assert.assertEquals(200, response.status());
|
||||||
Assert.assertEquals(1, requests.get());
|
Assert.assertEquals(1, requests.get());
|
||||||
|
|
|
@ -234,7 +234,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Response response)
|
public void onSuccess(Response response)
|
||||||
|
@ -253,7 +253,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Response response)
|
public void onSuccess(Response response)
|
||||||
|
@ -299,7 +299,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Response response, Throwable failure)
|
public void onFailure(Response response, Throwable failure)
|
||||||
|
@ -309,7 +309,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
});
|
});
|
||||||
|
|
||||||
client.newRequest("http://localhost:" + connector.getLocalPort())
|
client.newRequest("http://localhost:" + connector.getLocalPort())
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Response response)
|
public void onSuccess(Response response)
|
||||||
|
@ -354,7 +354,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Response response)
|
public void onSuccess(Response response)
|
||||||
|
@ -414,7 +414,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
return Arrays.asList(ByteBuffer.allocate(chunkSize), null).iterator();
|
return Arrays.asList(ByteBuffer.allocate(chunkSize), null).iterator();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
|
@ -444,7 +444,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
destination.getActiveConnections().peek().close();
|
destination.getActiveConnections().peek().close();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
|
|
|
@ -66,7 +66,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
successLatch.countDown();
|
successLatch.countDown();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onHeaders(Response response)
|
public void onHeaders(Response response)
|
||||||
|
@ -129,7 +129,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
{
|
{
|
||||||
failureLatch.countDown();
|
failureLatch.countDown();
|
||||||
}
|
}
|
||||||
}).send(new Response.Listener.Adapter()
|
}).send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
|
@ -181,7 +181,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
successLatch.countDown();
|
successLatch.countDown();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Response response)
|
public void onSuccess(Response response)
|
||||||
|
@ -234,7 +234,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
failureLatch.countDown();
|
failureLatch.countDown();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
|
@ -276,7 +276,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
client.newRequest(host, port)
|
client.newRequest(host, port)
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
|
@ -321,7 +321,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
client.newRequest(host, port)
|
client.newRequest(host, port)
|
||||||
.content(new ByteBufferContentProvider(ByteBuffer.allocate(16 * 1024 * 1024)))
|
.content(new ByteBufferContentProvider(ByteBuffer.allocate(16 * 1024 * 1024)))
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
|
|
|
@ -26,7 +26,7 @@ import java.util.concurrent.TimeoutException;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.api.Response;
|
import org.eclipse.jetty.client.api.Response;
|
||||||
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
import org.eclipse.jetty.client.util.BlockingResponseListener;
|
||||||
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.HttpVersion;
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
|
@ -66,6 +66,7 @@ public class HttpReceiverTest
|
||||||
HttpExchange exchange = new HttpExchange(conversation, connection, null, listener);
|
HttpExchange exchange = new HttpExchange(conversation, connection, null, listener);
|
||||||
conversation.exchanges().offer(exchange);
|
conversation.exchanges().offer(exchange);
|
||||||
connection.setExchange(exchange);
|
connection.setExchange(exchange);
|
||||||
|
exchange.requestComplete(true);
|
||||||
return exchange;
|
return exchange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +79,7 @@ public class HttpReceiverTest
|
||||||
"\r\n");
|
"\r\n");
|
||||||
final AtomicReference<Response> responseRef = new AtomicReference<>();
|
final AtomicReference<Response> responseRef = new AtomicReference<>();
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
HttpExchange exchange = newExchange(new Response.Listener.Adapter()
|
HttpExchange exchange = newExchange(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Response response)
|
public void onSuccess(Response response)
|
||||||
|
@ -110,11 +111,11 @@ public class HttpReceiverTest
|
||||||
"Content-length: " + content.length() + "\r\n" +
|
"Content-length: " + content.length() + "\r\n" +
|
||||||
"\r\n" +
|
"\r\n" +
|
||||||
content);
|
content);
|
||||||
BufferingResponseListener listener = new BufferingResponseListener();
|
BlockingResponseListener listener = new BlockingResponseListener();
|
||||||
HttpExchange exchange = newExchange(listener);
|
HttpExchange exchange = newExchange(listener);
|
||||||
exchange.receive();
|
exchange.receive();
|
||||||
|
|
||||||
Response response = listener.await(5, TimeUnit.SECONDS);
|
Response response = listener.get(5, TimeUnit.SECONDS);
|
||||||
Assert.assertNotNull(response);
|
Assert.assertNotNull(response);
|
||||||
Assert.assertEquals(200, response.status());
|
Assert.assertEquals(200, response.status());
|
||||||
Assert.assertEquals("OK", response.reason());
|
Assert.assertEquals("OK", response.reason());
|
||||||
|
@ -123,7 +124,7 @@ public class HttpReceiverTest
|
||||||
Assert.assertNotNull(headers);
|
Assert.assertNotNull(headers);
|
||||||
Assert.assertEquals(1, headers.size());
|
Assert.assertEquals(1, headers.size());
|
||||||
Assert.assertEquals(String.valueOf(content.length()), headers.get(HttpHeader.CONTENT_LENGTH));
|
Assert.assertEquals(String.valueOf(content.length()), headers.get(HttpHeader.CONTENT_LENGTH));
|
||||||
String received = listener.contentAsString("UTF-8");
|
String received = listener.getContent("UTF-8");
|
||||||
Assert.assertEquals(content, received);
|
Assert.assertEquals(content, received);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +138,7 @@ public class HttpReceiverTest
|
||||||
"Content-length: " + (content1.length() + content2.length()) + "\r\n" +
|
"Content-length: " + (content1.length() + content2.length()) + "\r\n" +
|
||||||
"\r\n" +
|
"\r\n" +
|
||||||
content1);
|
content1);
|
||||||
BufferingResponseListener listener = new BufferingResponseListener();
|
BlockingResponseListener listener = new BlockingResponseListener();
|
||||||
HttpExchange exchange = newExchange(listener);
|
HttpExchange exchange = newExchange(listener);
|
||||||
exchange.receive();
|
exchange.receive();
|
||||||
endPoint.setInputEOF();
|
endPoint.setInputEOF();
|
||||||
|
@ -145,7 +146,7 @@ public class HttpReceiverTest
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
listener.await(5, TimeUnit.SECONDS);
|
listener.get(5, TimeUnit.SECONDS);
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
}
|
}
|
||||||
catch (ExecutionException e)
|
catch (ExecutionException e)
|
||||||
|
@ -161,7 +162,7 @@ public class HttpReceiverTest
|
||||||
"HTTP/1.1 200 OK\r\n" +
|
"HTTP/1.1 200 OK\r\n" +
|
||||||
"Content-length: 1\r\n" +
|
"Content-length: 1\r\n" +
|
||||||
"\r\n");
|
"\r\n");
|
||||||
BufferingResponseListener listener = new BufferingResponseListener();
|
BlockingResponseListener listener = new BlockingResponseListener();
|
||||||
HttpExchange exchange = newExchange(listener);
|
HttpExchange exchange = newExchange(listener);
|
||||||
exchange.receive();
|
exchange.receive();
|
||||||
// Simulate an idle timeout
|
// Simulate an idle timeout
|
||||||
|
@ -169,7 +170,7 @@ public class HttpReceiverTest
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
listener.await(5, TimeUnit.SECONDS);
|
listener.get(5, TimeUnit.SECONDS);
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
}
|
}
|
||||||
catch (ExecutionException e)
|
catch (ExecutionException e)
|
||||||
|
@ -185,13 +186,13 @@ public class HttpReceiverTest
|
||||||
"HTTP/1.1 200 OK\r\n" +
|
"HTTP/1.1 200 OK\r\n" +
|
||||||
"Content-length: A\r\n" +
|
"Content-length: A\r\n" +
|
||||||
"\r\n");
|
"\r\n");
|
||||||
BufferingResponseListener listener = new BufferingResponseListener();
|
BlockingResponseListener listener = new BlockingResponseListener();
|
||||||
HttpExchange exchange = newExchange(listener);
|
HttpExchange exchange = newExchange(listener);
|
||||||
exchange.receive();
|
exchange.receive();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
listener.await(5, TimeUnit.SECONDS);
|
listener.get(5, TimeUnit.SECONDS);
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
}
|
}
|
||||||
catch (ExecutionException e)
|
catch (ExecutionException e)
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2012 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.client;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
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.util.ByteBufferContentProvider;
|
||||||
|
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
|
import org.eclipse.jetty.util.IO;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
public class HttpRequestAbortTest extends AbstractHttpClientServerTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
public void testAbortOnQueued() throws Exception
|
||||||
|
{
|
||||||
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
|
final AtomicBoolean begin = new AtomicBoolean();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.listener(new Request.Listener.Empty()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onQueued(Request request)
|
||||||
|
{
|
||||||
|
request.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBegin(Request request)
|
||||||
|
{
|
||||||
|
begin.set(true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.send().get(5, TimeUnit.SECONDS);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (ExecutionException x)
|
||||||
|
{
|
||||||
|
HttpRequestException xx = (HttpRequestException)x.getCause();
|
||||||
|
Request request = xx.getRequest();
|
||||||
|
Assert.assertNotNull(request);
|
||||||
|
Assert.assertFalse(begin.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAbortOnBegin() throws Exception
|
||||||
|
{
|
||||||
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
|
final AtomicBoolean headers = new AtomicBoolean();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.listener(new Request.Listener.Empty()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onBegin(Request request)
|
||||||
|
{
|
||||||
|
request.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHeaders(Request request)
|
||||||
|
{
|
||||||
|
headers.set(true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.send().get(5, TimeUnit.SECONDS);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (ExecutionException x)
|
||||||
|
{
|
||||||
|
HttpRequestException xx = (HttpRequestException)x.getCause();
|
||||||
|
Request request = xx.getRequest();
|
||||||
|
Assert.assertNotNull(request);
|
||||||
|
Assert.assertFalse(headers.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAbortOnHeaders() throws Exception
|
||||||
|
{
|
||||||
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
|
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.listener(new Request.Listener.Empty()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onHeaders(Request request)
|
||||||
|
{
|
||||||
|
// Too late to abort
|
||||||
|
request.abort();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.send().get(5, TimeUnit.SECONDS);
|
||||||
|
assertEquals(200, response.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAbortOnHeadersWithContent() throws Exception
|
||||||
|
{
|
||||||
|
final AtomicReference<IOException> failure = new AtomicReference<>();
|
||||||
|
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);
|
||||||
|
IO.copy(request.getInputStream(), response.getOutputStream());
|
||||||
|
}
|
||||||
|
catch (IOException x)
|
||||||
|
{
|
||||||
|
failure.set(x);
|
||||||
|
throw x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test can behave in 2 ways:
|
||||||
|
// A) if the request is failed before the request arrived, then we get an ExecutionException
|
||||||
|
// B) if the request is failed after the request arrived, then we get a 500
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.listener(new Request.Listener.Empty()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onHeaders(Request request)
|
||||||
|
{
|
||||||
|
request.abort();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.content(new ByteBufferContentProvider(ByteBuffer.wrap(new byte[]{0}), ByteBuffer.wrap(new byte[]{1}))
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public long length()
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.send().get(5, TimeUnit.SECONDS);
|
||||||
|
Assert.assertNotNull(failure.get());
|
||||||
|
assertEquals(500, response.status());
|
||||||
|
}
|
||||||
|
catch (ExecutionException x)
|
||||||
|
{
|
||||||
|
HttpRequestException xx = (HttpRequestException)x.getCause();
|
||||||
|
Request request = xx.getRequest();
|
||||||
|
Assert.assertNotNull(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2012 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.client;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.Response;
|
||||||
|
import org.eclipse.jetty.client.api.Result;
|
||||||
|
import org.eclipse.jetty.server.Request;
|
||||||
|
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class HttpResponseAbortTest extends AbstractHttpClientServerTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
public void testAbortOnBegin() throws Exception
|
||||||
|
{
|
||||||
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.send(new Response.Listener.Empty()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onBegin(Response response)
|
||||||
|
{
|
||||||
|
response.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete(Result result)
|
||||||
|
{
|
||||||
|
Assert.assertTrue(result.isFailed());
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAbortOnHeaders() throws Exception
|
||||||
|
{
|
||||||
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.send(new Response.Listener.Empty()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onHeaders(Response response)
|
||||||
|
{
|
||||||
|
response.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete(Result result)
|
||||||
|
{
|
||||||
|
Assert.assertTrue(result.isFailed());
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAbortOnContent() throws Exception
|
||||||
|
{
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
OutputStream output = response.getOutputStream();
|
||||||
|
output.write(1);
|
||||||
|
output.flush();
|
||||||
|
output.write(2);
|
||||||
|
output.flush();
|
||||||
|
}
|
||||||
|
catch (IOException ignored)
|
||||||
|
{
|
||||||
|
// The client may have already closed, and we'll get an exception here, but it's expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.send(new Response.Listener.Empty()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onContent(Response response, ByteBuffer content)
|
||||||
|
{
|
||||||
|
response.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete(Result result)
|
||||||
|
{
|
||||||
|
Assert.assertTrue(result.isFailed());
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Assert.assertTrue(latch.await(555, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = CancellationException.class)
|
||||||
|
public void testCancelFuture() throws Exception
|
||||||
|
{
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
final AtomicReference<Future<ContentResponse>> ref = new AtomicReference<>();
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
latch.await(5, TimeUnit.SECONDS);
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
ref.get().cancel(true);
|
||||||
|
OutputStream output = response.getOutputStream();
|
||||||
|
output.write(new byte[]{0, 1, 2, 3, 4, 5, 6, 7});
|
||||||
|
}
|
||||||
|
catch (InterruptedException x)
|
||||||
|
{
|
||||||
|
throw new InterruptedIOException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<ContentResponse> future = client.newRequest("localhost", connector.getLocalPort()).send();
|
||||||
|
ref.set(future);
|
||||||
|
latch.countDown();
|
||||||
|
|
||||||
|
future.get(5, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
|
@ -129,7 +129,7 @@ public class HttpSenderTest
|
||||||
failureLatch.countDown();
|
failureLatch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connection.send(request, new Response.Listener.Adapter()
|
connection.send(request, new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
|
@ -158,7 +158,7 @@ public class HttpSenderTest
|
||||||
failureLatch.countDown();
|
failureLatch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connection.send(request, new Response.Listener.Adapter()
|
connection.send(request, new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
|
|
|
@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.HttpClient;
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
import org.eclipse.jetty.client.util.BlockingResponseListener;
|
||||||
import org.eclipse.jetty.client.util.PathContentProvider;
|
import org.eclipse.jetty.client.util.PathContentProvider;
|
||||||
import org.eclipse.jetty.client.util.StreamingResponseListener;
|
import org.eclipse.jetty.client.util.StreamingResponseListener;
|
||||||
import org.eclipse.jetty.http.HttpCookie;
|
import org.eclipse.jetty.http.HttpCookie;
|
||||||
|
@ -82,7 +82,7 @@ public class Usage
|
||||||
HttpClient client = new HttpClient();
|
HttpClient client = new HttpClient();
|
||||||
final AtomicReference<Response> responseRef = new AtomicReference<>();
|
final AtomicReference<Response> responseRef = new AtomicReference<>();
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
client.newRequest("localhost", 8080).send(new Response.Listener.Adapter()
|
client.newRequest("localhost", 8080).send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Response response)
|
public void onSuccess(Response response)
|
||||||
|
@ -119,9 +119,9 @@ public class Usage
|
||||||
try (Connection connection = client.getDestination("http", "localhost", 8080).newConnection().get(5, TimeUnit.SECONDS))
|
try (Connection connection = client.getDestination("http", "localhost", 8080).newConnection().get(5, TimeUnit.SECONDS))
|
||||||
{
|
{
|
||||||
Request request = client.newRequest("localhost", 8080);
|
Request request = client.newRequest("localhost", 8080);
|
||||||
BufferingResponseListener listener = new BufferingResponseListener();
|
BlockingResponseListener listener = new BlockingResponseListener();
|
||||||
connection.send(request, listener);
|
connection.send(request, listener);
|
||||||
Response response = listener.await(5, TimeUnit.SECONDS);
|
Response response = listener.get(5, TimeUnit.SECONDS);
|
||||||
Assert.assertNotNull(response);
|
Assert.assertNotNull(response);
|
||||||
Assert.assertEquals(200, response.status());
|
Assert.assertEquals(200, response.status());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1111,6 +1111,11 @@ public class HttpParser
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CLOSED:
|
||||||
|
{
|
||||||
|
BufferUtil.clear(buffer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue