jetty-9 - HTTP client: Implemented basic authentication.
This commit is contained in:
parent
f3edbf9594
commit
60a0b8ac8d
|
@ -0,0 +1,152 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jetty.client.api.Authentication;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
|
||||
public class AuthenticationProtocolHandler extends Response.Listener.Adapter implements ProtocolHandler
|
||||
{
|
||||
private static final Pattern WWW_AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]+)\"(\\s*,\\s*)?(.*)", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private final ResponseNotifier notifier = new ResponseNotifier();
|
||||
private final HttpClient client;
|
||||
|
||||
public AuthenticationProtocolHandler(HttpClient client)
|
||||
{
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(Request request, Response response)
|
||||
{
|
||||
return response.status() == 401;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response.Listener getResponseListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
if (!result.isFailed())
|
||||
{
|
||||
List<WWWAuthenticate> wwwAuthenticates = parseWWWAuthenticate(result.getResponse());
|
||||
if (wwwAuthenticates.isEmpty())
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
else
|
||||
{
|
||||
Request request = result.getRequest();
|
||||
final String uri = request.uri();
|
||||
Authentication authentication = null;
|
||||
for (WWWAuthenticate wwwAuthenticate : wwwAuthenticates)
|
||||
{
|
||||
authentication = client.getAuthenticationStore().findAuthentication(wwwAuthenticate.type, uri, wwwAuthenticate.realm);
|
||||
if (authentication != null)
|
||||
break;
|
||||
}
|
||||
if (authentication != null)
|
||||
{
|
||||
final Authentication authn = authentication;
|
||||
authn.authenticate(request);
|
||||
request.send(new Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
if (!result.isFailed())
|
||||
{
|
||||
Authentication.Result authnResult = new Authentication.Result(uri, authn);
|
||||
client.getAuthenticationStore().addAuthenticationResult(authnResult);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
noAuthentication(request, result.getResponse());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<WWWAuthenticate> parseWWWAuthenticate(Response response)
|
||||
{
|
||||
List<WWWAuthenticate> result = new ArrayList<>();
|
||||
List<String> values = Collections.list(response.headers().getValues(HttpHeader.WWW_AUTHENTICATE.asString()));
|
||||
for (String value : values)
|
||||
{
|
||||
Matcher matcher = WWW_AUTHENTICATE_PATTERN.matcher(value);
|
||||
if (matcher.matches())
|
||||
{
|
||||
String type = matcher.group(1);
|
||||
String realm = matcher.group(2);
|
||||
String params = matcher.group(4);
|
||||
WWWAuthenticate wwwAuthenticate = new WWWAuthenticate(type, realm, params);
|
||||
result.add(wwwAuthenticate);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void noAuthentication(Request request, Response response)
|
||||
{
|
||||
HttpConversation conversation = client.getConversation(request);
|
||||
Response.Listener listener = conversation.exchanges().peekFirst().listener();
|
||||
notifier.notifyBegin(listener, response);
|
||||
notifier.notifyHeaders(listener, response);
|
||||
notifier.notifySuccess(listener, response);
|
||||
// TODO: this call here is horrid, but needed... but here it is too late for the exchange
|
||||
// TODO: to figure out that the conversation is finished, so we need to manually do it here, no matter what.
|
||||
// TODO: However, we also need to make sure that requests are not resent with the same ID
|
||||
// TODO: because here the connection has already been returned to the pool, so the "new" request may see
|
||||
// TODO: the same conversation but it's not really the case.
|
||||
// TODO: perhaps the factory for requests should be the conversation ?
|
||||
conversation.complete();
|
||||
notifier.notifyComplete(listener, new Result(request, response));
|
||||
}
|
||||
|
||||
private class WWWAuthenticate
|
||||
{
|
||||
private final String type;
|
||||
private final String realm;
|
||||
private final String params;
|
||||
|
||||
public WWWAuthenticate(String type, String realm, String params)
|
||||
{
|
||||
this.type = type;
|
||||
this.realm = realm;
|
||||
this.params = params;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.eclipse.jetty.client.api.Authentication;
|
||||
import org.eclipse.jetty.client.api.AuthenticationStore;
|
||||
|
||||
public class HttpAuthenticationStore implements AuthenticationStore
|
||||
{
|
||||
private final List<Authentication> authentications = new CopyOnWriteArrayList<>();
|
||||
private final Map<String, Authentication> results = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void addAuthentication(Authentication authentication)
|
||||
{
|
||||
authentications.add(authentication);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAuthentication(Authentication authentication)
|
||||
{
|
||||
authentications.remove(authentication);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication findAuthentication(String type, String uri, String realm)
|
||||
{
|
||||
for (Authentication authentication : authentications)
|
||||
{
|
||||
if (authentication.matches(type, uri, realm))
|
||||
return authentication;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAuthenticationResult(Authentication.Result result)
|
||||
{
|
||||
results.put(result.getURI(), result.getAuthentication());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAuthenticationResults()
|
||||
{
|
||||
results.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication findAuthenticationResult(String uri)
|
||||
{
|
||||
// TODO: I should match the longest URI
|
||||
for (Map.Entry<String, Authentication> entry : results.entrySet())
|
||||
{
|
||||
if (uri.startsWith(entry.getKey()))
|
||||
return entry.getValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -35,6 +35,7 @@ import java.util.concurrent.Executor;
|
|||
import java.util.concurrent.Future;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import org.eclipse.jetty.client.api.AuthenticationStore;
|
||||
import org.eclipse.jetty.client.api.Connection;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.CookieStore;
|
||||
|
@ -75,12 +76,12 @@ import org.eclipse.jetty.util.thread.TimerScheduler;
|
|||
*
|
||||
* // Building a request with a timeout
|
||||
* HTTPClient client = new HTTPClient();
|
||||
* Response response = client.newRequest("localhost:8080").send().get(5, TimeUnit.SECONDS);
|
||||
* Response response = client.newRequest("http://localhost:8080").send().get(5, TimeUnit.SECONDS);
|
||||
* int status = response.status();
|
||||
*
|
||||
* // Asynchronously
|
||||
* HTTPClient client = new HTTPClient();
|
||||
* client.newRequest("localhost:8080").send(new Response.Listener.Adapter()
|
||||
* client.newRequest("http://localhost:8080").send(new Response.Listener.Adapter()
|
||||
* {
|
||||
* @Override
|
||||
* public void onSuccess(Response response)
|
||||
|
@ -97,7 +98,9 @@ public class HttpClient extends AggregateLifeCycle
|
|||
private final ConcurrentMap<String, HttpDestination> destinations = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<Long, HttpConversation> conversations = new ConcurrentHashMap<>();
|
||||
private final List<ProtocolHandler> handlers = new CopyOnWriteArrayList<>();
|
||||
private final List<Request.Listener> requestListeners = new CopyOnWriteArrayList<>();
|
||||
private final CookieStore cookieStore = new HttpCookieStore();
|
||||
private final AuthenticationStore authenticationStore = new HttpAuthenticationStore();
|
||||
private volatile Executor executor;
|
||||
private volatile ByteBufferPool byteBufferPool;
|
||||
private volatile Scheduler scheduler;
|
||||
|
@ -114,6 +117,16 @@ public class HttpClient extends AggregateLifeCycle
|
|||
private volatile SocketAddress bindAddress;
|
||||
private volatile long idleTimeout;
|
||||
|
||||
public HttpClient()
|
||||
{
|
||||
this(null);
|
||||
}
|
||||
|
||||
public HttpClient(Executor executor)
|
||||
{
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public ByteBufferPool getByteBufferPool()
|
||||
{
|
||||
return byteBufferPool;
|
||||
|
@ -143,6 +156,7 @@ public class HttpClient extends AggregateLifeCycle
|
|||
addBean(selectorManager);
|
||||
|
||||
handlers.add(new RedirectProtocolHandler(this));
|
||||
handlers.add(new AuthenticationProtocolHandler(this));
|
||||
|
||||
super.doStart();
|
||||
|
||||
|
@ -171,11 +185,21 @@ public class HttpClient extends AggregateLifeCycle
|
|||
LOG.info("Stopped {}", this);
|
||||
}
|
||||
|
||||
public List<Request.Listener> getRequestListeners()
|
||||
{
|
||||
return requestListeners;
|
||||
}
|
||||
|
||||
public CookieStore getCookieStore()
|
||||
{
|
||||
return cookieStore;
|
||||
}
|
||||
|
||||
public AuthenticationStore getAuthenticationStore()
|
||||
{
|
||||
return authenticationStore;
|
||||
}
|
||||
|
||||
public long getIdleTimeout()
|
||||
{
|
||||
return idleTimeout;
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jetty.client.api.Authentication;
|
||||
import org.eclipse.jetty.client.api.Connection;
|
||||
import org.eclipse.jetty.client.api.ContentProvider;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
|
@ -97,7 +99,7 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
HttpConversation conversation = client.getConversation(request);
|
||||
HttpExchange exchange = new HttpExchange(conversation, this, request, listener);
|
||||
setExchange(exchange);
|
||||
conversation.add(exchange);
|
||||
conversation.exchanges().offer(exchange);
|
||||
conversation.listener(listener);
|
||||
sender.send(exchange);
|
||||
}
|
||||
|
@ -158,6 +160,11 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
if (cookieString != null)
|
||||
request.header(HttpHeader.COOKIE.asString(), cookieString.toString());
|
||||
|
||||
// Authorization
|
||||
Authentication authentication = client.getAuthenticationStore().findAuthenticationResult(request.uri());
|
||||
if (authentication != null)
|
||||
authentication.authenticate(request);
|
||||
|
||||
// TODO: decoder headers
|
||||
|
||||
// If we are HTTP 1.1, add the Host header
|
||||
|
@ -210,7 +217,17 @@ public class HttpConnection extends AbstractConnection implements Connection
|
|||
LOG.debug("{} disassociated from {}", exchange, this);
|
||||
if (success)
|
||||
{
|
||||
destination.release(this);
|
||||
HttpFields responseHeaders = exchange.response().headers();
|
||||
Collection<String> values = responseHeaders.getValuesCollection(HttpHeader.CONNECTION.asString());
|
||||
if (values != null && values.contains("close"))
|
||||
{
|
||||
destination.remove(this);
|
||||
close();
|
||||
}
|
||||
else
|
||||
{
|
||||
destination.release(this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
package org.eclipse.jetty.client;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.util.Attributes;
|
||||
|
@ -31,10 +31,11 @@ import org.eclipse.jetty.util.Attributes;
|
|||
public class HttpConversation implements Attributes
|
||||
{
|
||||
private final Map<String, Object> attributes = new ConcurrentHashMap<>();
|
||||
private final Queue<HttpExchange> exchanges = new ConcurrentLinkedQueue<>();
|
||||
private final Deque<HttpExchange> exchanges = new ConcurrentLinkedDeque<>();
|
||||
private final HttpClient client;
|
||||
private final long id;
|
||||
private volatile Response.Listener listener;
|
||||
private volatile HttpExchange last;
|
||||
|
||||
public HttpConversation(HttpClient client, long id)
|
||||
{
|
||||
|
@ -47,6 +48,11 @@ public class HttpConversation implements Attributes
|
|||
return id;
|
||||
}
|
||||
|
||||
public Deque<HttpExchange> exchanges()
|
||||
{
|
||||
return exchanges;
|
||||
}
|
||||
|
||||
public Response.Listener listener()
|
||||
{
|
||||
return listener;
|
||||
|
@ -57,14 +63,16 @@ public class HttpConversation implements Attributes
|
|||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void add(HttpExchange exchange)
|
||||
public HttpExchange last()
|
||||
{
|
||||
exchanges.offer(exchange);
|
||||
return last;
|
||||
}
|
||||
|
||||
public HttpExchange first()
|
||||
public void last(HttpExchange exchange)
|
||||
{
|
||||
return exchanges.peek();
|
||||
if (last == null)
|
||||
|
||||
last = exchange;
|
||||
}
|
||||
|
||||
public void complete()
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.eclipse.jetty.client.api.Connection;
|
|||
import org.eclipse.jetty.client.api.Destination;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.FutureCallback;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
@ -40,6 +41,7 @@ public class HttpDestination implements Destination, AutoCloseable
|
|||
private static final Logger LOG = Log.getLogger(HttpDestination.class);
|
||||
|
||||
private final AtomicInteger connectionCount = new AtomicInteger();
|
||||
private final ResponseNotifier responseNotifier = new ResponseNotifier();
|
||||
private final HttpClient client;
|
||||
private final String scheme;
|
||||
private final String host;
|
||||
|
@ -47,6 +49,7 @@ public class HttpDestination implements Destination, AutoCloseable
|
|||
private final Queue<RequestPair> requests;
|
||||
private final BlockingQueue<Connection> idleConnections;
|
||||
private final BlockingQueue<Connection> activeConnections;
|
||||
private final RequestNotifier requestNotifier;
|
||||
|
||||
public HttpDestination(HttpClient client, String scheme, String host, int port)
|
||||
{
|
||||
|
@ -57,6 +60,7 @@ public class HttpDestination implements Destination, AutoCloseable
|
|||
this.requests = new ArrayBlockingQueue<>(client.getMaxQueueSizePerAddress());
|
||||
this.idleConnections = new ArrayBlockingQueue<>(client.getMaxConnectionsPerAddress());
|
||||
this.activeConnections = new ArrayBlockingQueue<>(client.getMaxConnectionsPerAddress());
|
||||
this.requestNotifier = new RequestNotifier(client);
|
||||
}
|
||||
|
||||
protected BlockingQueue<Connection> getIdleConnections()
|
||||
|
@ -109,7 +113,7 @@ public class HttpDestination implements Destination, AutoCloseable
|
|||
else
|
||||
{
|
||||
LOG.debug("Queued {}", request);
|
||||
notifyRequestQueued(request);
|
||||
requestNotifier.notifyQueued(request);
|
||||
Connection connection = acquire();
|
||||
if (connection != null)
|
||||
process(connection, false);
|
||||
|
@ -126,47 +130,6 @@ public class HttpDestination implements Destination, AutoCloseable
|
|||
}
|
||||
}
|
||||
|
||||
private void notifyRequestQueued(Request request)
|
||||
{
|
||||
Request.Listener listener = request.listener();
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onQueued(request);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyRequestFailure(Request request, Throwable failure)
|
||||
{
|
||||
Request.Listener listener = request.listener();
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onFailure(request, failure);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyResponseFailure(Response.Listener listener, Throwable failure)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onFailure(null, failure);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
public Future<Connection> newConnection()
|
||||
{
|
||||
FutureCallback<Connection> result = new FutureCallback<>();
|
||||
|
@ -223,8 +186,8 @@ public class HttpDestination implements Destination, AutoCloseable
|
|||
RequestPair pair = requests.poll();
|
||||
if (pair != null)
|
||||
{
|
||||
notifyRequestFailure(pair.request, x);
|
||||
notifyResponseFailure(pair.listener, x);
|
||||
requestNotifier.notifyFailure(pair.request, x);
|
||||
responseNotifier.notifyComplete(pair.listener, new Result(pair.request, x, null));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -251,7 +214,7 @@ public class HttpDestination implements Destination, AutoCloseable
|
|||
if (requestPair == null)
|
||||
{
|
||||
LOG.debug("{} idle", connection);
|
||||
idleConnections.offer(connection);
|
||||
idleConnections.offer(connection); // TODO: check return value ?
|
||||
if (!client.isRunning())
|
||||
{
|
||||
LOG.debug("{} is stopping", client);
|
||||
|
@ -262,7 +225,7 @@ public class HttpDestination implements Destination, AutoCloseable
|
|||
else
|
||||
{
|
||||
LOG.debug("{} active", connection);
|
||||
activeConnections.offer(connection);
|
||||
activeConnections.offer(connection); // TODO: check return value ?
|
||||
if (dispatch)
|
||||
{
|
||||
client.getExecutor().execute(new Runnable()
|
||||
|
@ -330,8 +293,8 @@ public class HttpDestination implements Destination, AutoCloseable
|
|||
RequestPair pair;
|
||||
while ((pair = requests.poll()) != null)
|
||||
{
|
||||
notifyRequestFailure(pair.request, failure);
|
||||
notifyResponseFailure(pair.listener, failure);
|
||||
requestNotifier.notifyFailure(pair.request, failure);
|
||||
responseNotifier.notifyComplete(pair.listener, new Result(pair.request, failure, null));
|
||||
}
|
||||
|
||||
connectionCount.set(0);
|
||||
|
|
|
@ -108,7 +108,7 @@ public class HttpExchange
|
|||
{
|
||||
LOG.debug("{} complete", this);
|
||||
// Request and response completed
|
||||
if (conversation().listener() == conversation.first().listener())
|
||||
if (this == conversation.last())
|
||||
conversation.complete();
|
||||
int success = 0b1111;
|
||||
connection.complete(this, status == success);
|
||||
|
|
|
@ -42,6 +42,7 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
private static final Logger LOG = Log.getLogger(HttpReceiver.class);
|
||||
|
||||
private final HttpParser parser = new HttpParser(this);
|
||||
private final ResponseNotifier notifier = new ResponseNotifier();
|
||||
private final HttpConnection connection;
|
||||
private volatile boolean failed;
|
||||
|
||||
|
@ -104,15 +105,29 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
response.version(version).status(status).reason(reason);
|
||||
|
||||
// Probe the protocol handlers
|
||||
Response.Listener currentListener = exchange.listener();
|
||||
Response.Listener initialListener = conversation.exchanges().peekFirst().listener();
|
||||
HttpClient client = connection.getHttpClient();
|
||||
Response.Listener listener = client.lookup(exchange.request(), response);
|
||||
if (listener == null)
|
||||
listener = conversation.first().listener();
|
||||
conversation.listener(listener);
|
||||
Response.Listener handlerListener = client.lookup(exchange.request(), response);
|
||||
if (handlerListener == null)
|
||||
{
|
||||
conversation.last(exchange);
|
||||
if (currentListener == initialListener)
|
||||
conversation.listener(initialListener);
|
||||
else
|
||||
conversation.listener(new MultipleResponseListener(currentListener, initialListener));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentListener == initialListener)
|
||||
conversation.listener(handlerListener);
|
||||
else
|
||||
conversation.listener(new MultipleResponseListener(currentListener, handlerListener));
|
||||
}
|
||||
|
||||
LOG.debug("Receiving {}", response);
|
||||
|
||||
notifyBegin(listener, response);
|
||||
notifier.notifyBegin(conversation.listener(), response);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -150,7 +165,7 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
HttpConversation conversation = exchange.conversation();
|
||||
HttpResponse response = exchange.response();
|
||||
LOG.debug("Headers {}", response);
|
||||
notifyHeaders(conversation.listener(), response);
|
||||
notifier.notifyHeaders(conversation.listener(), response);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -161,7 +176,7 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
HttpConversation conversation = exchange.conversation();
|
||||
HttpResponse response = exchange.response();
|
||||
LOG.debug("Content {}: {} bytes", response, buffer.remaining());
|
||||
notifyContent(conversation.listener(), response, buffer);
|
||||
notifier.notifyContent(conversation.listener(), response, buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -186,11 +201,11 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
boolean exchangeComplete = exchange.responseComplete(true);
|
||||
|
||||
HttpConversation conversation = exchange.conversation();
|
||||
notifySuccess(conversation.listener(), response);
|
||||
notifier.notifySuccess(conversation.listener(), response);
|
||||
if (exchangeComplete)
|
||||
{
|
||||
Result result = new Result(exchange.request(), exchange.response());
|
||||
notifyComplete(conversation.listener(), result);
|
||||
Result result = new Result(exchange.request(), response);
|
||||
notifier.notifyComplete(conversation.listener(), result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,11 +228,11 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
boolean exchangeComplete = exchange.responseComplete(false);
|
||||
|
||||
HttpConversation conversation = exchange.conversation();
|
||||
notifyFailure(conversation.listener(), response, failure);
|
||||
notifier.notifyFailure(conversation.listener(), response, failure);
|
||||
if (exchangeComplete)
|
||||
{
|
||||
Result result = new Result(exchange.request(), response, failure);
|
||||
notifyComplete(conversation.listener(), result);
|
||||
notifier.notifyComplete(conversation.listener(), result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,86 +252,73 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
|
|||
fail(new HttpResponseException("HTTP protocol violation: bad response", response));
|
||||
}
|
||||
|
||||
private void notifyBegin(Response.Listener listener, Response response)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onBegin(response);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyHeaders(Response.Listener listener, Response response)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onHeaders(response);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyContent(Response.Listener listener, Response response, ByteBuffer buffer)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onContent(response, buffer);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifySuccess(Response.Listener listener, Response response)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onSuccess(response);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyFailure(Response.Listener listener, Response response, Throwable failure)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onFailure(response, failure);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyComplete(Response.Listener listener, Result result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onComplete(result);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
public void idleTimeout()
|
||||
{
|
||||
fail(new TimeoutException());
|
||||
}
|
||||
|
||||
private class MultipleResponseListener implements Response.Listener
|
||||
{
|
||||
private final ResponseNotifier notifier = new ResponseNotifier();
|
||||
private final Response.Listener[] listeners;
|
||||
|
||||
private MultipleResponseListener(Response.Listener... listeners)
|
||||
{
|
||||
this.listeners = listeners;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBegin(Response response)
|
||||
{
|
||||
for (Response.Listener listener : listeners)
|
||||
{
|
||||
notifier.notifyBegin(listener, response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeaders(Response response)
|
||||
{
|
||||
for (Response.Listener listener : listeners)
|
||||
{
|
||||
notifier.notifyHeaders(listener, response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContent(Response response, ByteBuffer content)
|
||||
{
|
||||
for (Response.Listener listener : listeners)
|
||||
{
|
||||
notifier.notifyContent(listener, response, content);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Response response)
|
||||
{
|
||||
for (Response.Listener listener : listeners)
|
||||
{
|
||||
notifier.notifySuccess(listener, response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Response response, Throwable failure)
|
||||
{
|
||||
for (Response.Listener listener : listeners)
|
||||
{
|
||||
notifier.notifyFailure(listener, response, failure);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
for (Response.Listener listener : listeners)
|
||||
{
|
||||
notifier.notifyComplete(listener, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,6 +154,18 @@ public class HttpRequest implements Request
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uri()
|
||||
{
|
||||
String scheme = scheme();
|
||||
String result = scheme + "://" + host();
|
||||
int port = port();
|
||||
result += "http".equals(scheme) && port != 80 ? ":" + port : "";
|
||||
result += "https".equals(scheme) && port != 443 ? ":" + port : "";
|
||||
result += path();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpVersion version()
|
||||
{
|
||||
|
@ -203,13 +215,6 @@ public class HttpRequest implements Request
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request cookie(String name, String value)
|
||||
{
|
||||
// TODO: cookie are handled via CookieStore, not sure this method is useful
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpFields headers()
|
||||
{
|
||||
|
|
|
@ -27,7 +27,6 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||
|
||||
import org.eclipse.jetty.client.api.ContentProvider;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.http.HttpGenerator;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
|
@ -43,7 +42,9 @@ public class HttpSender
|
|||
private static final Logger LOG = Log.getLogger(HttpSender.class);
|
||||
|
||||
private final HttpGenerator generator = new HttpGenerator();
|
||||
private final ResponseNotifier responseNotifier = new ResponseNotifier();
|
||||
private final HttpConnection connection;
|
||||
private final RequestNotifier requestNotifier;
|
||||
private long contentLength;
|
||||
private Iterator<ByteBuffer> contentChunks;
|
||||
private ByteBuffer header;
|
||||
|
@ -54,12 +55,13 @@ public class HttpSender
|
|||
public HttpSender(HttpConnection connection)
|
||||
{
|
||||
this.connection = connection;
|
||||
this.requestNotifier = new RequestNotifier(connection.getHttpClient());
|
||||
}
|
||||
|
||||
public void send(HttpExchange exchange)
|
||||
{
|
||||
LOG.debug("Sending {}", exchange.request());
|
||||
notifyRequestBegin(exchange.request());
|
||||
requestNotifier.notifyBegin(exchange.request());
|
||||
ContentProvider content = exchange.request().content();
|
||||
this.contentLength = content == null ? -1 : content.length();
|
||||
this.contentChunks = content == null ? Collections.<ByteBuffer>emptyIterator() : content.iterator();
|
||||
|
@ -192,7 +194,7 @@ public class HttpSender
|
|||
{
|
||||
LOG.debug("Committed {}", request);
|
||||
committed = true;
|
||||
notifyRequestHeaders(request);
|
||||
requestNotifier.notifyHeaders(request);
|
||||
}
|
||||
|
||||
protected void success()
|
||||
|
@ -205,16 +207,17 @@ public class HttpSender
|
|||
HttpExchange exchange = connection.getExchange();
|
||||
Request request = exchange.request();
|
||||
LOG.debug("Sent {}", request);
|
||||
|
||||
boolean exchangeCompleted = exchange.requestComplete(true);
|
||||
|
||||
// It is important to notify *after* we reset because
|
||||
// the notification may trigger another request/response
|
||||
notifyRequestSuccess(request);
|
||||
requestNotifier.notifySuccess(request);
|
||||
if (exchangeCompleted)
|
||||
{
|
||||
HttpConversation conversation = exchange.conversation();
|
||||
Result result = new Result(request, exchange.response());
|
||||
notifyComplete(conversation.listener(), result);
|
||||
responseNotifier.notifyComplete(conversation.listener(), result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,16 +235,17 @@ public class HttpSender
|
|||
HttpExchange exchange = connection.getExchange();
|
||||
Request request = exchange.request();
|
||||
LOG.debug("Failed {}", request);
|
||||
|
||||
boolean exchangeCompleted = exchange.requestComplete(false);
|
||||
if (!exchangeCompleted && !committed)
|
||||
exchangeCompleted = exchange.responseComplete(false);
|
||||
|
||||
notifyRequestFailure(request, failure);
|
||||
requestNotifier.notifyFailure(request, failure);
|
||||
if (exchangeCompleted)
|
||||
{
|
||||
HttpConversation conversation = exchange.conversation();
|
||||
Result result = new Result(request, failure, exchange.response());
|
||||
notifyComplete(conversation.listener(), result);
|
||||
responseNotifier.notifyComplete(conversation.listener(), result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -260,75 +264,6 @@ public class HttpSender
|
|||
}
|
||||
}
|
||||
|
||||
private void notifyRequestBegin(Request request)
|
||||
{
|
||||
Request.Listener listener = request.listener();
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onBegin(request);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyRequestHeaders(Request request)
|
||||
{
|
||||
Request.Listener listener = request.listener();
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onHeaders(request);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyRequestSuccess(Request request)
|
||||
{
|
||||
Request.Listener listener = request.listener();
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onSuccess(request);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyRequestFailure(Request request, Throwable failure)
|
||||
{
|
||||
Request.Listener listener = request.listener();
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onFailure(request, failure);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyComplete(Response.Listener listener, Result result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onComplete(result);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
private static abstract class StatefulExecutorCallback implements Callback<Void>, Runnable
|
||||
{
|
||||
private final AtomicReference<State> state = new AtomicReference<>(State.INCOMPLETE);
|
||||
|
|
|
@ -28,6 +28,7 @@ public class RedirectProtocolHandler extends Response.Listener.Adapter implement
|
|||
{
|
||||
private static final String ATTRIBUTE = RedirectProtocolHandler.class.getName() + ".redirect";
|
||||
|
||||
private final ResponseNotifier notifier = new ResponseNotifier();
|
||||
private final HttpClient client;
|
||||
|
||||
public RedirectProtocolHandler(HttpClient client)
|
||||
|
@ -113,6 +114,8 @@ public class RedirectProtocolHandler extends Response.Listener.Adapter implement
|
|||
++redirects;
|
||||
conversation.setAttribute(ATTRIBUTE, redirects);
|
||||
|
||||
// TODO: no, reuse the same request object, just have a setter for the URI
|
||||
|
||||
Request redirect = client.newRequest(request.id(), location);
|
||||
|
||||
// Use given method
|
||||
|
@ -138,10 +141,9 @@ public class RedirectProtocolHandler extends Response.Listener.Adapter implement
|
|||
Request request = result.getRequest();
|
||||
Response response = result.getResponse();
|
||||
HttpConversation conversation = client.getConversation(request);
|
||||
Response.Listener listener = conversation.first().listener();
|
||||
Response.Listener listener = conversation.exchanges().peekFirst().listener();
|
||||
// TODO: should we reply all event, or just the failure ?
|
||||
// TODO: wrap these into try/catch as usual
|
||||
listener.onFailure(response, failure);
|
||||
listener.onComplete(new Result(request, response, failure));
|
||||
notifier.notifyFailure(listener, response, failure);
|
||||
notifier.notifyComplete(listener, new Result(request, response, failure));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class RequestNotifier
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ResponseNotifier.class);
|
||||
|
||||
private final HttpClient client;
|
||||
|
||||
public RequestNotifier(HttpClient client)
|
||||
{
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public void notifyQueued(Request request)
|
||||
{
|
||||
notifyQueued(request.listener(), request);
|
||||
for (Request.Listener listener : client.getRequestListeners())
|
||||
notifyQueued(listener, request);
|
||||
}
|
||||
|
||||
private void notifyQueued(Request.Listener listener, Request request)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onQueued(request);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyBegin(Request request)
|
||||
{
|
||||
notifyBegin(request.listener(), request);
|
||||
for (Request.Listener listener : client.getRequestListeners())
|
||||
notifyBegin(listener, request);
|
||||
}
|
||||
|
||||
private void notifyBegin(Request.Listener listener, Request request)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onBegin(request);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyHeaders(Request request)
|
||||
{
|
||||
notifyHeaders(request.listener(), request);
|
||||
for (Request.Listener listener : client.getRequestListeners())
|
||||
notifyHeaders(listener, request);
|
||||
}
|
||||
|
||||
private void notifyHeaders(Request.Listener listener, Request request)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onHeaders(request);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifySuccess(Request request)
|
||||
{
|
||||
notifySuccess(request.listener(), request);
|
||||
for (Request.Listener listener : client.getRequestListeners())
|
||||
notifySuccess(listener, request);
|
||||
}
|
||||
|
||||
private void notifySuccess(Request.Listener listener, Request request)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onSuccess(request);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyFailure(Request request, Throwable failure)
|
||||
{
|
||||
notifyFailure(request.listener(), request, failure);
|
||||
for (Request.Listener listener : client.getRequestListeners())
|
||||
notifyFailure(listener, request, failure);
|
||||
}
|
||||
|
||||
private void notifyFailure(Request.Listener listener, Request request, Throwable failure)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onFailure(request, failure);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class ResponseNotifier
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ResponseNotifier.class);
|
||||
|
||||
public void notifyBegin(Response.Listener listener, Response response)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onBegin(response);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyHeaders(Response.Listener listener, Response response)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onHeaders(response);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyContent(Response.Listener listener, Response response, ByteBuffer buffer)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onContent(response, buffer);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifySuccess(Response.Listener listener, Response response)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onSuccess(response);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyFailure(Response.Listener listener, Response response, Throwable failure)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onFailure(response, failure);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyComplete(Response.Listener listener, Result result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onComplete(result);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.api;
|
||||
|
||||
public interface Authentication
|
||||
{
|
||||
boolean matches(String type, String uri, String realm);
|
||||
|
||||
void authenticate(Request request);
|
||||
|
||||
public static class Result
|
||||
{
|
||||
private final String uri;
|
||||
private final Authentication authentication;
|
||||
|
||||
public Result(String uri, Authentication authentication)
|
||||
{
|
||||
this.uri = uri;
|
||||
this.authentication = authentication;
|
||||
}
|
||||
|
||||
public String getURI()
|
||||
{
|
||||
return uri;
|
||||
}
|
||||
|
||||
public Authentication getAuthentication()
|
||||
{
|
||||
return authentication;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.api;
|
||||
|
||||
public interface AuthenticationStore
|
||||
{
|
||||
public void addAuthentication(Authentication authentication);
|
||||
|
||||
public void removeAuthentication(Authentication authentication);
|
||||
|
||||
public Authentication findAuthentication(String type, String uri, String realm);
|
||||
|
||||
public void addAuthenticationResult(Authentication.Result result);
|
||||
|
||||
public void removeAuthenticationResults();
|
||||
|
||||
public Authentication findAuthenticationResult(String uri);
|
||||
}
|
|
@ -47,6 +47,8 @@ public interface Request
|
|||
|
||||
Request path(String path);
|
||||
|
||||
String uri();
|
||||
|
||||
HttpVersion version();
|
||||
|
||||
Request version(HttpVersion version);
|
||||
|
@ -59,9 +61,6 @@ public interface Request
|
|||
|
||||
Request header(String name, String value);
|
||||
|
||||
// TODO: keep or remove this method ?
|
||||
Request cookie(String name, String value);
|
||||
|
||||
ContentProvider content();
|
||||
|
||||
Request content(ContentProvider buffer);
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
|
||||
import org.eclipse.jetty.client.api.Authentication;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.util.B64Code;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
|
||||
public class BasicAuthentication implements Authentication
|
||||
{
|
||||
private final String uri;
|
||||
private final String realm;
|
||||
private final String user;
|
||||
private final String password;
|
||||
|
||||
public BasicAuthentication(String uri, String realm, String user, String password)
|
||||
{
|
||||
this.uri = uri;
|
||||
this.realm = realm;
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String type, String uri, String realm)
|
||||
{
|
||||
if (!"basic".equalsIgnoreCase(type))
|
||||
return false;
|
||||
|
||||
if (!uri.startsWith(this.uri))
|
||||
return false;
|
||||
|
||||
return this.realm.equals(realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authenticate(Request request)
|
||||
{
|
||||
String encoding = StringUtil.__ISO_8859_1;
|
||||
try
|
||||
{
|
||||
String value = "Basic " + B64Code.encode(user + ":" + password, encoding);
|
||||
request.header(HttpHeader.AUTHORIZATION.asString(), value);
|
||||
}
|
||||
catch (UnsupportedEncodingException x)
|
||||
{
|
||||
throw new UnsupportedCharsetException(encoding);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ import org.eclipse.jetty.server.Handler;
|
|||
import org.eclipse.jetty.server.NetworkConnector;
|
||||
import org.eclipse.jetty.server.SelectChannelConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.rules.TestWatchman;
|
||||
|
@ -54,7 +55,9 @@ public class AbstractHttpClientServerTest
|
|||
server.setHandler(handler);
|
||||
server.start();
|
||||
|
||||
client = new HttpClient();
|
||||
QueuedThreadPool executor = new QueuedThreadPool();
|
||||
executor.setName(executor.getName() + "-client");
|
||||
client = new HttpClient(executor);
|
||||
client.start();
|
||||
}
|
||||
|
||||
|
|
|
@ -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.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.client.api.AuthenticationStore;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.util.BasicAuthentication;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.B64Code;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
||||
{
|
||||
@Test
|
||||
public void test_BasicAuthentication_WithChallenge() throws Exception
|
||||
{
|
||||
start(new BasicAuthenticationHandler());
|
||||
|
||||
AuthenticationStore authenticationStore = client.getAuthenticationStore();
|
||||
String realm = "test";
|
||||
|
||||
final AtomicInteger requests = new AtomicInteger();
|
||||
Request.Listener.Adapter requestListener = new Request.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Request request)
|
||||
{
|
||||
requests.incrementAndGet();
|
||||
}
|
||||
};
|
||||
client.getRequestListeners().add(requestListener);
|
||||
|
||||
// Request without Authentication causes a 401
|
||||
Request request = client.newRequest("localhost", connector.getLocalPort())
|
||||
.path("/test")
|
||||
.param("type", "Basic")
|
||||
.param("realm", realm);
|
||||
ContentResponse response = request.send().get(5, TimeUnit.SECONDS);
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(401, response.status());
|
||||
Assert.assertEquals(1, requests.get());
|
||||
client.getRequestListeners().remove(requestListener);
|
||||
requests.set(0);
|
||||
|
||||
String user = "jetty";
|
||||
String password = "rocks";
|
||||
authenticationStore.addAuthentication(new BasicAuthentication("http://localhost:" + connector.getLocalPort(), realm, user, password));
|
||||
|
||||
requestListener = new Request.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Request request)
|
||||
{
|
||||
requests.incrementAndGet();
|
||||
}
|
||||
};
|
||||
client.getRequestListeners().add(requestListener);
|
||||
|
||||
// Request with authentication causes a 401 (no previous successful authentication) + 200
|
||||
request.param("user", user).param("password", password);
|
||||
response = request.send().get(5, TimeUnit.SECONDS);
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.status());
|
||||
Assert.assertEquals(2, requests.get());
|
||||
client.getRequestListeners().remove(requestListener);
|
||||
requests.set(0);
|
||||
|
||||
requestListener = new Request.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Request request)
|
||||
{
|
||||
requests.incrementAndGet();
|
||||
}
|
||||
};
|
||||
client.getRequestListeners().add(requestListener);
|
||||
|
||||
// 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
|
||||
request.header(HttpHeader.AUTHORIZATION.asString(), null);
|
||||
response = request.send().get(555, TimeUnit.SECONDS);
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.status());
|
||||
Assert.assertEquals(1, requests.get());
|
||||
client.getRequestListeners().remove(requestListener);
|
||||
requests.set(0);
|
||||
}
|
||||
|
||||
private class BasicAuthenticationHandler extends AbstractHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
try
|
||||
{
|
||||
String type = request.getParameter("type");
|
||||
String authorization = request.getHeader(HttpHeader.AUTHORIZATION.asString());
|
||||
if (authorization == null)
|
||||
{
|
||||
String realm = request.getParameter("realm");
|
||||
response.setStatus(401);
|
||||
switch (type)
|
||||
{
|
||||
case "Basic":
|
||||
{
|
||||
response.setHeader("WWW-Authenticate", "Basic realm=\"" + realm + "\"");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case "Basic":
|
||||
{
|
||||
String user = request.getParameter("user");
|
||||
String password = request.getParameter("password");
|
||||
String expected = "Basic " + B64Code.encode(user + ":" + password);
|
||||
if (!expected.equals(authorization))
|
||||
throw new IOException(expected + " != " + authorization);
|
||||
IO.copy(request.getInputStream(), response.getOutputStream());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -122,7 +122,7 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
.method(HttpMethod.POST)
|
||||
.path("/307/localhost/done")
|
||||
.content(new ByteBufferContentProvider(ByteBuffer.wrap(data)))
|
||||
.send().get(500, TimeUnit.SECONDS);
|
||||
.send().get(5, TimeUnit.SECONDS);
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.status());
|
||||
Assert.assertFalse(response.headers().containsKey(HttpHeader.LOCATION.asString()));
|
||||
|
@ -151,6 +151,17 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_303_WithConnectionClose_WithBigRequest() throws Exception
|
||||
{
|
||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||
.path("/303/localhost/done?close=true")
|
||||
.send().get(5, TimeUnit.SECONDS);
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(200, response.status());
|
||||
Assert.assertFalse(response.headers().containsKey(HttpHeader.LOCATION.asString()));
|
||||
}
|
||||
|
||||
private class RedirectHandler extends AbstractHandler
|
||||
{
|
||||
@Override
|
||||
|
@ -159,10 +170,16 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
try
|
||||
{
|
||||
String[] paths = target.split("/", 4);
|
||||
|
||||
int status = Integer.parseInt(paths[1]);
|
||||
response.setStatus(status);
|
||||
|
||||
String host = paths[2];
|
||||
response.setHeader("Location", request.getScheme() + "://" + host + ":" + request.getServerPort() + "/" + paths[3]);
|
||||
|
||||
String close = request.getParameter("close");
|
||||
if (Boolean.parseBoolean(close))
|
||||
response.setHeader("Connection", "close");
|
||||
}
|
||||
catch (NumberFormatException x)
|
||||
{
|
||||
|
|
|
@ -18,15 +18,22 @@
|
|||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.client.api.Connection;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.util.ByteBufferContentProvider;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -49,7 +56,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
Assert.assertEquals(0, activeConnections.size());
|
||||
|
||||
final CountDownLatch headersLatch = new CountDownLatch(1);
|
||||
final CountDownLatch successLatch = new CountDownLatch(2);
|
||||
final CountDownLatch successLatch = new CountDownLatch(3);
|
||||
client.newRequest(host, port)
|
||||
.listener(new Request.Listener.Adapter()
|
||||
{
|
||||
|
@ -74,6 +81,13 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
{
|
||||
successLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertFalse(result.isFailed());
|
||||
successLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS));
|
||||
|
@ -135,7 +149,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void test_BadRequest_ReturnsConnection() throws Exception
|
||||
public void test_BadRequest_RemovesConnection() throws Exception
|
||||
{
|
||||
start(new EmptyHandler());
|
||||
|
||||
|
@ -150,7 +164,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
final BlockingQueue<Connection> activeConnections = destination.getActiveConnections();
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
|
||||
final CountDownLatch successLatch = new CountDownLatch(2);
|
||||
final CountDownLatch successLatch = new CountDownLatch(3);
|
||||
client.newRequest(host, port)
|
||||
.listener(new Request.Listener.Adapter()
|
||||
{
|
||||
|
@ -172,13 +186,23 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
public void onSuccess(Response response)
|
||||
{
|
||||
Assert.assertEquals(400, response.status());
|
||||
// 400 response also come with a Connection: close,
|
||||
// so the connection is closed and removed
|
||||
successLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertFalse(result.isFailed());
|
||||
successLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
Assert.assertEquals(1, idleConnections.size());
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
}
|
||||
|
||||
|
@ -213,8 +237,9 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
.send(new Response.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onFailure(Response response, Throwable failure)
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertTrue(result.isFailed());
|
||||
failureLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -224,4 +249,92 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
|||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ResponseWithConnectionCloseHeader_RemovesConnection() throws Exception
|
||||
{
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
response.setHeader("Connection", "close");
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
|
||||
String scheme = "http";
|
||||
String host = "localhost";
|
||||
int port = connector.getLocalPort();
|
||||
HttpDestination destination = (HttpDestination)client.getDestination(scheme, host, port);
|
||||
|
||||
final BlockingQueue<Connection> idleConnections = destination.getIdleConnections();
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
|
||||
final BlockingQueue<Connection> activeConnections = destination.getActiveConnections();
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest(host, port)
|
||||
.send(new Response.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertFalse(result.isFailed());
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_BigRequestContent_ResponseWithConnectionCloseHeader_RemovesConnection() throws Exception
|
||||
{
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
response.setHeader("Connection", "close");
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
|
||||
String scheme = "http";
|
||||
String host = "localhost";
|
||||
int port = connector.getLocalPort();
|
||||
HttpDestination destination = (HttpDestination)client.getDestination(scheme, host, port);
|
||||
|
||||
final BlockingQueue<Connection> idleConnections = destination.getIdleConnections();
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
|
||||
final BlockingQueue<Connection> activeConnections = destination.getActiveConnections();
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest(host, port)
|
||||
.content(new ByteBufferContentProvider(ByteBuffer.allocate(16 * 1024 * 1024)))
|
||||
.send(new Response.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
Assert.assertEquals(0, idleConnections.size());
|
||||
Assert.assertEquals(0, activeConnections.size());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ public class HttpReceiverTest
|
|||
protected HttpExchange newExchange(Response.Listener listener)
|
||||
{
|
||||
HttpExchange exchange = new HttpExchange(conversation, connection, null, listener);
|
||||
conversation.add(exchange);
|
||||
conversation.exchanges().offer(exchange);
|
||||
connection.setExchange(exchange);
|
||||
return exchange;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.eclipse.jetty.client.HttpClient;
|
|||
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
||||
import org.eclipse.jetty.client.util.PathContentProvider;
|
||||
import org.eclipse.jetty.client.util.StreamingResponseListener;
|
||||
import org.eclipse.jetty.http.HttpCookie;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.junit.Assert;
|
||||
|
@ -67,7 +68,6 @@ public class Usage
|
|||
.param("a", "b")
|
||||
.header("X-Header", "Y-value")
|
||||
.agent("Jetty HTTP Client")
|
||||
.cookie("cookie1", "value1")
|
||||
.decoder(null)
|
||||
.content(null)
|
||||
.idleTimeout(5000L);
|
||||
|
@ -140,7 +140,8 @@ public class Usage
|
|||
public void testCookie() throws Exception
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
Response response = client.newRequest("localhost", 8080).cookie("key", "value").send().get();
|
||||
client.getCookieStore().addCookie(client.getDestination("http", "host", 8080), new HttpCookie("name", "value"));
|
||||
Response response = client.newRequest("host", 8080).send().get();
|
||||
Assert.assertEquals(200, response.status());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue