Intermediate commit.

This commit is contained in:
Simone Bordet 2012-07-18 17:38:05 +02:00
parent 7ff3fe3b53
commit 70915bb870
14 changed files with 704 additions and 33 deletions

View File

@ -125,5 +125,11 @@
<version>1.7.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.2.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -13,10 +13,21 @@
package org.eclipse.jetty.client;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.client.api.Address;
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.io.SelectorManager;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.Name;
import org.eclipse.jetty.util.component.AggregateLifeCycle;
/**
@ -35,12 +46,12 @@ import org.eclipse.jetty.util.component.AggregateLifeCycle;
*
* // Using the builder with a timeout
* HTTPClient client = new HTTPClient();
* Response response = client.builder("localhost:8080").uri("/").build().send().get(5, TimeUnit.SECONDS);
* Response response = client.builder("localhost:8080").path("/").build().send().get(5, TimeUnit.SECONDS);
* int status = response.getStatus();
*
* // Asynchronously
* HTTPClient client = new HTTPClient();
* client.builder("localhost:8080").uri("/").build().send(new Response.Listener.Adapter()
* client.builder("localhost:8080").path("/").build().send(new Response.Listener.Adapter()
* {
* &#64;Override
* public void onComplete(Response response)
@ -52,14 +63,64 @@ import org.eclipse.jetty.util.component.AggregateLifeCycle;
*/
public class HTTPClient extends AggregateLifeCycle
{
private final ConcurrentMap<Address, Destination> destinations = new ConcurrentHashMap<>();
private volatile String agent = "Jetty/" + Jetty.VERSION;
private volatile boolean followRedirects;
private Executor executor;
private int maxConnectionsPerAddress = Integer.MAX_VALUE;
@Override
protected void doStart() throws Exception
{
super.doStart();
}
@Override
protected void doStop() throws Exception
{
super.doStop();
}
public Future<Response> GET(String absoluteURL)
{
return null;
try
{
return GET(new URI(absoluteURL));
}
catch (URISyntaxException x)
{
throw new IllegalArgumentException(x);
}
}
public Future<Response> GET(URI uri)
{
boolean secure = false;
String scheme = uri.getScheme();
if ("https".equals(scheme))
secure = true;
else if (!"http".equals(scheme))
throw new IllegalArgumentException("Invalid scheme " + scheme);
return builder(uri.getHost() + ":" + uri.getPort())
.method("GET")
.secure(secure)
.path(uri.getPath())
// Add decoder, cookies, agent, default headers, etc.
.agent(getUserAgent())
.followRedirects(isFollowRedirects())
.build()
.send();
}
public Request.Builder builder(String hostAndPort)
{
return null;
return builder(Address.from(hostAndPort));
}
private Request.Builder builder(Address address)
{
return new StandardRequest(this, address);
}
public Request.Builder builder(Request prototype)
@ -67,22 +128,80 @@ public class HTTPClient extends AggregateLifeCycle
return null;
}
public Destination getDestination(String hostAndPort)
public Destination getDestination(Address address)
{
return null;
Destination destination = destinations.get(address);
if (destination == null)
{
destination = new StandardDestination(this, address);
Destination existing = destinations.putIfAbsent(address, destination);
if (existing != null)
destination = existing;
}
return destination;
}
public String getUserAgent()
{
return agent;
}
public void setUserAgent(String agent)
{
this.agent = agent;
}
public boolean isFollowRedirects()
{
return followRedirects;
}
public void setFollowRedirects(boolean follow)
{
this.followRedirects = follow;
}
public interface Destination
public void join()
{
Connection newConnection();
}
public interface Connection extends AutoCloseable
public void join(long timeout, TimeUnit unit)
{
Future<Response> send(Request request, Response.Listener listener);
}
public Future<Response> send(Request request, Response.Listener listener)
{
return getDestination(request.address()).send(request, listener);
}
public Executor getExecutor()
{
return executor;
}
public int getMaxConnectionsPerAddress()
{
return maxConnectionsPerAddress;
}
public void setMaxConnectionsPerAddress(int maxConnectionsPerAddress)
{
this.maxConnectionsPerAddress = maxConnectionsPerAddress;
}
protected class ClientSelectorManager extends SelectorManager
{
public ClientSelectorManager()
{
this(1);
}
public ClientSelectorManager(int selectors)
{
super(selectors);
}
}
}

View File

@ -0,0 +1,63 @@
//========================================================================
//Copyright 2012-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.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.client.api.Address;
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;
public class StandardDestination implements Destination
{
private final HTTPClient client;
private final Address address;
private final Queue<Connection> connections;
public StandardDestination(HTTPClient client, Address address)
{
this.client = client;
this.address = address;
this.connections = new ArrayBlockingQueue<>(client.getMaxConnectionsPerAddress());
}
@Override
public Connection connect(long timeout, TimeUnit unit)
{
return null;
}
@Override
public Future<Response> send(Request request, Response.Listener listener)
{
if (!address.equals(request.address()))
throw new IllegalArgumentException("Invalid request address " + request.address() + " for destination " + this);
return getConnection().send(request, listener);
}
protected Connection getConnection()
{
Connection connection = connections.poll();
if (connection == null)
{
client.
}
return connection;
}
}

View File

@ -0,0 +1,142 @@
//========================================================================
//Copyright 2012-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.File;
import java.util.concurrent.Future;
import org.eclipse.jetty.client.api.Address;
import org.eclipse.jetty.client.api.Authentication;
import org.eclipse.jetty.client.api.ContentDecoder;
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.util.FutureCallback;
public class StandardRequest implements Request, Request.Builder
{
private final HTTPClient client;
private final Address address;
private boolean secure;
private String method;
private String path;
private String agent;
private Response response;
public StandardRequest(HTTPClient client, Address address)
{
this.client = client;
this.address = address;
}
@Override
public Request.Builder secure(boolean secure)
{
this.secure = secure;
return this;
}
@Override
public Request.Builder method(String method)
{
this.method = method;
return this;
}
@Override
public Request.Builder path(String path)
{
this.path = path;
return this;
}
@Override
public Request.Builder agent(String userAgent)
{
this.agent = userAgent;
return this;
}
@Override
public Request.Builder header(String name, String value)
{
return this;
}
@Override
public Request.Builder listener(Request.Listener listener)
{
return this;
}
@Override
public Request.Builder file(File file)
{
return this;
}
@Override
public Request.Builder content(ContentProvider buffer)
{
return this;
}
@Override
public Request.Builder decoder(ContentDecoder decoder)
{
return this;
}
@Override
public Request.Builder param(String name, String value)
{
return this;
}
@Override
public Request.Builder cookie(String key, String value)
{
return this;
}
@Override
public Request.Builder authentication(Authentication authentication)
{
return this;
}
@Override
public Request.Builder followRedirects(boolean follow)
{
return this;
}
@Override
public Request build()
{
return this;
}
@Override
public Future<Response> send()
{
return send(null);
}
@Override
public Future<Response> send(final Response.Listener listener)
{
return client.send(this, listener);
}
}

View File

@ -0,0 +1,70 @@
//========================================================================
//Copyright 2012-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.InputStream;
import org.eclipse.jetty.client.api.ContentProvider;
import org.eclipse.jetty.client.api.Headers;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.util.FutureCallback;
public class StandardResponse extends FutureCallback<Response> implements Response
{
private final Request request;
private final Listener listener;
public StandardResponse(Request request, Response.Listener listener)
{
this.request = request;
this.listener = listener;
}
@Override
public int getStatus()
{
return 0;
}
@Override
public Headers getHeaders()
{
return null;
}
@Override
public Request getRequest()
{
return request;
}
@Override
public ContentProvider content()
{
return null;
}
@Override
public InputStream contentAsStream()
{
return null;
}
@Override
public void abort()
{
request.abort();
}
}

View File

@ -0,0 +1,91 @@
// ========================================================================
// Copyright (c) 2008-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;
import java.net.InetSocketAddress;
/**
* @version $Revision: 4135 $ $Date: 2008-12-02 11:57:07 +0100 (Tue, 02 Dec 2008) $
*/
public class Address
{
private final String host;
private final int port;
public static Address from(String hostAndPort)
{
String host;
int port;
int colon = hostAndPort.indexOf(':');
if (colon >= 0)
{
host = hostAndPort.substring(0, colon);
port = Integer.parseInt(hostAndPort.substring(colon + 1));
}
else
{
host = hostAndPort;
port = 0;
}
return new Address(host, port);
}
public Address(String host, int port)
{
if (host == null)
throw new IllegalArgumentException("Host is null");
this.host = host.trim();
this.port = port;
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Address that = (Address)obj;
if (!host.equals(that.host)) return false;
return port == that.port;
}
@Override
public int hashCode()
{
int result = host.hashCode();
result = 31 * result + port;
return result;
}
public String getHost()
{
return host;
}
public int getPort()
{
return port;
}
public InetSocketAddress toSocketAddress()
{
return new InetSocketAddress(getHost(), getPort());
}
@Override
public String toString()
{
return host + ":" + port;
}
}

View File

@ -0,0 +1,21 @@
//========================================================================
//Copyright 2012-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;
import java.util.concurrent.Future;
public interface Connection extends AutoCloseable
{
Future<Response> send(Request request, Response.Listener listener);
}

View File

@ -0,0 +1,24 @@
//========================================================================
//Copyright 2012-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;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public interface Destination
{
Connection connect(long timeout, TimeUnit unit);
Future<Response> send(Request request, Response.Listener listener);
}

View File

@ -22,16 +22,20 @@ public interface Request
Future<Response> send(Response.Listener listener);
Address address();
void abort();
/**
* <p>A builder for requests</p>.
*/
public interface Builder
{
Builder scheme(String scheme);
Builder secure(boolean secure);
Builder method(String method);
Builder uri(String uri);
Builder path(String path);
Builder header(String name, String value);
@ -49,6 +53,8 @@ public interface Request
Builder authentication(Authentication authentication);
Builder agent(String userAgent);
Builder followRedirects(boolean follow);
Request build();

View File

@ -0,0 +1,78 @@
//========================================================================
//Copyright 2012-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 javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import junit.framework.Assert;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.SelectChannelConnector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.junit.After;
import org.junit.Test;
public class HTTPClientTest
{
private Server server;
private HTTPClient client;
private Connector.NetConnector connector;
public void start(Handler handler) throws Exception
{
server = new Server();
connector = new SelectChannelConnector();
server.addConnector(connector);
server.setHandler(handler);
server.start();
client = new HTTPClient();
client.start();
}
@After
public void destroy() throws Exception
{
client.stop();
client.join(5, TimeUnit.SECONDS);
server.stop();
server.join();
}
@Test
public void testGETNoResponseContent() throws Exception
{
start(new AbstractHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
}
});
Response response = client.GET("http://localhost:" + connector.getLocalPort()).get(5, TimeUnit.SECONDS);
Assert.assertNotNull(response);
Assert.assertEquals(200, response.getStatus());
}
}

View File

@ -0,0 +1,27 @@
//========================================================================
//Copyright 2012-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;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.junit.Test;
public class ApacheUsage
{
@Test
public void test()
{
HttpClient client = new DefaultHttpClient();
}
}

View File

@ -48,7 +48,7 @@ public class Usage
HTTPClient client = new HTTPClient();
// Address must be provided, it's the only thing non defaultable
Request.Builder builder = client.builder("localhost:8080");
Future<Response> responseFuture = builder.method("GET").uri("/").header("Origin", "localhost").build().send();
Future<Response> responseFuture = builder.method("GET").path("/").header("Origin", "localhost").build().send();
responseFuture.get();
}
@ -57,7 +57,7 @@ public class Usage
public void testSimpleAsyncGET() throws Exception
{
HTTPClient client = new HTTPClient();
client.builder("localhost:8080").method("GET").uri("/").header("Origin", "localhost").build().send(new Response.Listener.Adapter()
client.builder("localhost:8080").method("GET").path("/").header("Origin", "localhost").build().send(new Response.Listener.Adapter()
{
@Override
public void onEnd(Response response)
@ -72,7 +72,7 @@ public class Usage
HTTPClient client = new HTTPClient();
Response response = client.builder("localhost:8080")
.method("GET")
.uri("/")
.path("/")
.listener(new Request.Listener.Adapter()
{
@Override
@ -94,10 +94,10 @@ public class Usage
public void testRequestWithExplicitConnectionControl() throws Exception
{
HTTPClient client = new HTTPClient();
try (HTTPClient.Connection connection = client.getDestination("localhost:8080").newConnection())
try (Connection connection = client.getDestination(Address.from("localhost:8080")).connect(5, TimeUnit.SECONDS))
{
Request.Builder builder = client.builder("localhost:8080");
Request request = builder.method("GET").uri("/").header("Origin", "localhost").build();
Request request = builder.method("GET").path("/").header("Origin", "localhost").build();
Future<Response> response = connection.send(request, new Response.Listener.Adapter());
response.get().getStatus();
@ -109,7 +109,7 @@ public class Usage
{
HTTPClient client = new HTTPClient();
Response response = client.builder("localhost:8080")
.method("GET").uri("/").file(new File("")).build().send().get();
.method("GET").path("/").file(new File("")).build().send().get();
response.getStatus();
}

View File

@ -4,7 +4,7 @@
// 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
// 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
@ -28,6 +28,7 @@ import org.eclipse.jetty.server.Connector.NetConnector;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.TypeUtil;
@ -54,17 +55,6 @@ public class Server extends HandlerWrapper implements Attributes
{
private static final Logger LOG = Log.getLogger(Server.class);
private static final String __version;
static
{
if (Server.class.getPackage()!=null &&
"Eclipse.org - Jetty".equals(Server.class.getPackage().getImplementationVendor()) &&
Server.class.getPackage().getImplementationVersion()!=null)
__version=Server.class.getPackage().getImplementationVersion();
else
__version=System.getProperty("jetty.version","9.0.y.z-SNAPSHOT");
}
private final Container _container=new Container();
private final AttributesMap _attributes = new AttributesMap();
private ThreadPool _threadPool;
@ -117,7 +107,7 @@ public class Server extends HandlerWrapper implements Attributes
/* ------------------------------------------------------------ */
public static String getVersion()
{
return __version;
return Jetty.VERSION;
}
/* ------------------------------------------------------------ */
@ -255,8 +245,8 @@ public class Server extends HandlerWrapper implements Attributes
if (getStopAtShutdown())
ShutdownThread.register(this);
LOG.info("jetty-"+__version);
HttpGenerator.setServerVersion(__version);
LOG.info("jetty-"+getVersion());
HttpGenerator.setServerVersion(getVersion());
MultiException mex=new MultiException();
if (_threadPool==null)

View File

@ -0,0 +1,34 @@
//========================================================================
//Copyright 2012-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.util;
public class Jetty
{
public static final String VERSION;
static
{
Package pkg = Jetty.class.getPackage();
if (pkg != null &&
"Eclipse.org - Jetty".equals(pkg.getImplementationVendor()) &&
pkg.getImplementationVersion() != null)
VERSION = pkg.getImplementationVersion();
else
VERSION = System.getProperty("jetty.version", "9.0.z-SNAPSHOT");
}
private Jetty()
{
}
}