440106 - Improve ProtocolHandler APIs.

Introduced ProtocolHandlers, the container for ProtocolHandler
instances, which now have also a name.
This commit is contained in:
Simone Bordet 2015-03-31 19:17:02 +02:00
parent 6263972d1e
commit 48dd4d8b56
9 changed files with 183 additions and 32 deletions

View File

@ -27,19 +27,27 @@ import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
/**
* <p>A protocol handler that handles the 100 response code.</p>
*/
public class ContinueProtocolHandler implements ProtocolHandler
{
public static final String NAME = "continue";
private static final String ATTRIBUTE = ContinueProtocolHandler.class.getName() + ".100continue";
private final HttpClient client;
private final ResponseNotifier notifier;
public ContinueProtocolHandler(HttpClient client)
public ContinueProtocolHandler()
{
this.client = client;
this.notifier = new ResponseNotifier();
}
@Override
public String getName()
{
return NAME;
}
@Override
public boolean accept(Request request, Response response)
{

View File

@ -109,7 +109,7 @@ public class HttpClient extends ContainerLifeCycle
private static final Logger LOG = Log.getLogger(HttpClient.class);
private final ConcurrentMap<Origin, HttpDestination> destinations = new ConcurrentHashMap<>();
private final List<ProtocolHandler> handlers = new ArrayList<>();
private final ProtocolHandlers handlers = new ProtocolHandlers();
private final List<Request.Listener> requestListeners = new ArrayList<>();
private final AuthenticationStore authenticationStore = new HttpAuthenticationStore();
private final Set<ContentDecoder.Factory> decoderFactories = new ContentDecoderFactorySet();
@ -210,10 +210,10 @@ public class HttpClient extends ContainerLifeCycle
resolver = new SocketAddressResolver(executor, scheduler, getAddressResolutionTimeout());
handlers.add(new ContinueProtocolHandler(this));
handlers.add(new RedirectProtocolHandler(this));
handlers.add(new WWWAuthenticationProtocolHandler(this));
handlers.add(new ProxyAuthenticationProtocolHandler(this));
handlers.put(new ContinueProtocolHandler());
handlers.put(new RedirectProtocolHandler(this));
handlers.put(new WWWAuthenticationProtocolHandler(this));
handlers.put(new ProxyAuthenticationProtocolHandler(this));
decoderFactories.add(new GZIPContentDecoder.Factory());
@ -547,22 +547,14 @@ public class HttpClient extends ContainerLifeCycle
return new HttpConversation();
}
protected List<ProtocolHandler> getProtocolHandlers()
public ProtocolHandlers getProtocolHandlers()
{
return handlers;
}
protected ProtocolHandler findProtocolHandler(Request request, Response response)
{
// Optimized to avoid allocations of iterator instances
List<ProtocolHandler> protocolHandlers = getProtocolHandlers();
for (int i = 0; i < protocolHandlers.size(); ++i)
{
ProtocolHandler handler = protocolHandlers.get(i);
if (handler.accept(request, response))
return handler;
}
return null;
return handlers.find(request, response);
}
/**

View File

@ -21,9 +21,38 @@ package org.eclipse.jetty.client;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
/**
* <p>A protocol handler performs HTTP protocol operations on
* behalf of the application, typically like a browser would.</p>
* <p>A typical example is handling HTTP redirects. {@link HttpClient}
* could just return the redirect response to the application,
* but the application would have to implement the redirect
* functionality (while browsers do this automatically).</p>
*/
public interface ProtocolHandler
{
/**
* @return a unique name among protocol handlers
*/
public String getName();
/**
* <p>Inspects the given {@code request} and {@code response}
* to detect whether this protocol handler should handle them.</p>
* <p>For example, a redirect protocol handler can inspect the
* response code and return true if it is a redirect response code.</p>
* <p>This method is being called just after the response line has
* been parsed, and before the response headers are available.</p>
*
* @param request the request to accept
* @param response the response to accept
* @return true if this protocol handler can handle the given request and response
*/
public boolean accept(Request request, Response response);
/**
* @return a response listener that will handle the request and response
* on behalf of the application.
*/
public Response.Listener getResponseListener();
}

View File

@ -0,0 +1,94 @@
//
// ========================================================================
// Copyright (c) 1995-2015 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.LinkedHashMap;
import java.util.Map;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
/**
* <p>A container for {@link ProtocolHandler}s accessible from {@link HttpClient#getProtocolHandlers()}.</p>
*/
public class ProtocolHandlers
{
private final Map<String, ProtocolHandler> handlers = new LinkedHashMap<>();
protected ProtocolHandlers()
{
}
/**
* <p>Stores the given {@code protocolHandler} in this container.</p>
* <p>If a protocol handler with the same name exists, it is
* replaced by the given one, and the existing returned.</p>
*
* @param protocolHandler the protocol handler to store
* @return the existing protocol handler with the same name,
* or null if no protocol handler with that name was already stored
* @see #remove(String)
*/
public ProtocolHandler put(ProtocolHandler protocolHandler)
{
return handlers.put(protocolHandler.getName(), protocolHandler);
}
/**
* <p>Removes the protocol handler with the given name.</p>
*
* @param name the name of the protocol handler to remove
* @return the removed protocol handler, or null if no
* protocol handler with that name was already stored
* @see #put(ProtocolHandler)
* @see #clear()
*/
public ProtocolHandler remove(String name)
{
return handlers.remove(name);
}
/**
* <p>Removes all protocol handlers from this container.</p>
*/
public void clear()
{
handlers.clear();
}
/**
* <p>Finds the first protocol handler that
* {@link ProtocolHandler#accept(Request, Response) accepts}
* the given request and response.</p>
*
* @param request the request to accept
* @param response the response to accept
* @return the protocol handler that accepted the request and response,
* or null if none of the protocol handlers accepted the request and response
*/
public ProtocolHandler find(Request request, Response response)
{
for (ProtocolHandler handler : handlers.values())
{
if (handler.accept(request, response))
return handler;
}
return null;
}
}

View File

@ -25,8 +25,16 @@ import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
/**
* <p>A protocol handler that handles the 401 response code
* in association with the {@code Proxy-Authenticate} header.</p>
*
* @see WWWAuthenticationProtocolHandler
*/
public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHandler
{
public static final String NAME = "proxy-authenticate";
public ProxyAuthenticationProtocolHandler(HttpClient client)
{
this(client, DEFAULT_MAX_CONTENT_LENGTH);
@ -37,6 +45,12 @@ public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHa
super(client, maxContentLength);
}
@Override
public String getName()
{
return NAME;
}
@Override
public boolean accept(Request request, Response response)
{

View File

@ -22,8 +22,13 @@ import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
/**
* <p>A protocol handler that handles redirect status codes 301, 302, 303, 307 and 308.</p>
*/
public class RedirectProtocolHandler extends Response.Listener.Adapter implements ProtocolHandler
{
public static final String NAME = "redirect";
private final HttpRedirector redirector;
public RedirectProtocolHandler(HttpClient client)
@ -31,6 +36,12 @@ public class RedirectProtocolHandler extends Response.Listener.Adapter implement
redirector = new HttpRedirector(client);
}
@Override
public String getName()
{
return NAME;
}
@Override
public boolean accept(Request request, Response response)
{

View File

@ -25,8 +25,16 @@ import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
/**
* <p>A protocol handler that handles the 401 response code
* in association with the {@code WWW-Authenticate} header.</p>
*
* @see ProxyAuthenticationProtocolHandler
*/
public class WWWAuthenticationProtocolHandler extends AuthenticationProtocolHandler
{
public static final String NAME = "www-authenticate";
public WWWAuthenticationProtocolHandler(HttpClient client)
{
this(client, DEFAULT_MAX_CONTENT_LENGTH);
@ -37,6 +45,12 @@ public class WWWAuthenticationProtocolHandler extends AuthenticationProtocolHand
super(client, maxContentLength);
}
@Override
public String getName()
{
return NAME;
}
@Override
public boolean accept(Request request, Response response)
{

View File

@ -27,11 +27,8 @@ import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
@ -405,7 +402,7 @@ public class HttpClientContinueTest extends AbstractHttpClientServerTest
});
client.getProtocolHandlers().clear();
client.getProtocolHandlers().add(new ContinueProtocolHandler(client)
client.getProtocolHandlers().put(new ContinueProtocolHandler()
{
@Override
public Response.Listener getResponseListener()
@ -609,14 +606,7 @@ public class HttpClientContinueTest extends AbstractHttpClientServerTest
final DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.wrap(chunk1));
List<ProtocolHandler> protocolHandlers = client.getProtocolHandlers();
for (Iterator<ProtocolHandler> iterator = protocolHandlers.iterator(); iterator.hasNext();)
{
ProtocolHandler protocolHandler = iterator.next();
if (protocolHandler instanceof ContinueProtocolHandler)
iterator.remove();
}
protocolHandlers.add(new ContinueProtocolHandler(client)
client.getProtocolHandlers().put(new ContinueProtocolHandler()
{
@Override
public Response.Listener getResponseListener()

View File

@ -25,7 +25,6 @@ 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;
@ -528,7 +527,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
final AtomicBoolean aborted = new AtomicBoolean();
final CountDownLatch latch = new CountDownLatch(1);
client.getProtocolHandlers().clear();
client.getProtocolHandlers().add(new RedirectProtocolHandler(client)
client.getProtocolHandlers().put(new RedirectProtocolHandler(client)
{
@Override
public void onComplete(Result result)