jetty-9 - HTTP client: implemented query params and connection failure handling.

This commit is contained in:
Simone Bordet 2012-09-06 13:24:38 +02:00
parent d8ed9116f1
commit 9c194ce2b5
16 changed files with 431 additions and 70 deletions

View File

@ -442,6 +442,13 @@ public class HttpClient extends AggregateLifeCycle
}
}
@Override
protected void connectionFailed(SocketChannel channel, Throwable ex, Object attachment)
{
ConnectionCallback callback = (ConnectionCallback)attachment;
callback.callback.failed(null, ex);
}
@Override
protected void execute(Runnable task)
{

View File

@ -108,7 +108,7 @@ public class HttpDestination implements Destination
else
{
LOG.debug("Queued {}", request);
notifyRequestQueued(request.listener(), request);
notifyRequestQueued(request);
Connection connection = acquire();
if (connection != null)
process(connection);
@ -125,8 +125,9 @@ public class HttpDestination implements Destination
}
}
private void notifyRequestQueued(Request.Listener listener, Request request)
private void notifyRequestQueued(Request request)
{
Request.Listener listener = request.listener();
try
{
if (listener != null)
@ -138,6 +139,33 @@ public class HttpDestination implements Destination
}
}
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<>();
@ -181,9 +209,23 @@ public class HttpDestination implements Destination
}
@Override
public void failed(Connection connection, Throwable x)
public void failed(Connection connection, final Throwable x)
{
// TODO: what here ?
LOG.debug("Connection failed {} for {}", x, this);
connectionCount.decrementAndGet();
client.getExecutor().execute(new Runnable()
{
@Override
public void run()
{
RequestPair pair = requests.poll();
if (pair != null)
{
notifyRequestFailure(pair.request, x);
notifyResponseFailure(pair.listener, x);
}
}
});
}
});
// Try again the idle connections

View File

@ -18,8 +18,10 @@
package org.eclipse.jetty.client;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.Map;
import java.net.URLDecoder;
import java.nio.charset.UnsupportedCharsetException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;
@ -33,6 +35,7 @@ import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.FutureCallback;
public class HttpRequest implements Request
@ -47,11 +50,11 @@ public class HttpRequest implements Request
private String path;
private HttpMethod method;
private HttpVersion version;
private String agent;
private long idleTimeout;
private Listener listener;
private ContentProvider content;
private final HttpFields headers = new HttpFields();
private final Fields params = new Fields();
public HttpRequest(HttpClient client, URI uri)
{
@ -66,7 +69,28 @@ public class HttpRequest implements Request
host = uri.getHost();
port = uri.getPort();
path(uri.getPath());
// TODO: query params
String query = uri.getRawQuery();
if (query != null)
{
for (String nameValue : query.split("&"))
{
String[] parts = nameValue.split("=");
param(parts[0], parts.length < 2 ? "" : urlDecode(parts[1]));
}
}
}
private String urlDecode(String value)
{
String charset = "UTF-8";
try
{
return URLDecoder.decode(value, charset);
}
catch (UnsupportedEncodingException x)
{
throw new UnsupportedCharsetException(charset);
}
}
@Override
@ -142,13 +166,14 @@ public class HttpRequest implements Request
@Override
public Request param(String name, String value)
{
params.add(name, value);
return this;
}
@Override
public Map<String, String> params()
public Fields params()
{
return null;
return params;
}
@Override

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.client;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.Iterator;
@ -33,6 +34,7 @@ import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -91,7 +93,27 @@ public class HttpSender
{
case NEED_INFO:
{
info = new HttpGenerator.RequestInfo(request.version(), request.headers(), contentLength, request.method().asString(), request.path());
String path = request.path();
Fields fields = request.params();
if (!fields.isEmpty())
{
path += "?";
for (Iterator<Fields.Field> fieldIterator = fields.iterator(); fieldIterator.hasNext();)
{
Fields.Field field = fieldIterator.next();
String[] values = field.values();
for (int i = 0; i < values.length; ++i)
{
if (i > 0)
path += "&";
path += field.name() + "=";
path += URLEncoder.encode(values[i], "UTF-8");
}
if (fieldIterator.hasNext())
path += "&";
}
}
info = new HttpGenerator.RequestInfo(request.version(), request.headers(), contentLength, request.method().asString(), path);
break;
}
case NEED_HEADER:

View File

@ -18,12 +18,12 @@
package org.eclipse.jetty.client.api;
import java.util.Map;
import java.util.concurrent.Future;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.util.Fields;
public interface Request
{
@ -49,7 +49,7 @@ public interface Request
Request version(HttpVersion version);
Map<String, String> params();
Fields params();
Request param(String name, String value);

View File

@ -1,3 +1,21 @@
//
// ========================================================================
// 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.server.Handler;

View File

@ -1,3 +1,21 @@
//
// ========================================================================
// 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;

View File

@ -1,22 +1,29 @@
//========================================================================
//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.
//========================================================================
//
// ========================================================================
// 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.net.URLEncoder;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -31,7 +38,27 @@ import org.junit.Test;
public class HttpClientTest extends AbstractHttpClientServerTest
{
@Test
public void test_GET_NoResponseContent() throws Exception
public void test_DestinationCount() throws Exception
{
start(new EmptyHandler());
String scheme = "http";
String host = "localhost";
int port = connector.getLocalPort();
client.GET(scheme + "://" + host + ":" + port).get(5, TimeUnit.SECONDS);
List<Destination> destinations = client.getDestinations();
Assert.assertNotNull(destinations);
Assert.assertEquals(1, destinations.size());
Destination destination = destinations.get(0);
Assert.assertNotNull(destination);
Assert.assertEquals(scheme, destination.scheme());
Assert.assertEquals(host, destination.host());
Assert.assertEquals(port, destination.port());
}
@Test
public void test_GET_ResponseWithContent() throws Exception
{
start(new EmptyHandler());
@ -42,7 +69,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
@Test
public void test_GET_ResponseContent() throws Exception
public void test_GET_ResponseWithoutContent() throws Exception
{
final byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7};
start(new AbstractHandler()
@ -64,16 +91,70 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
@Test
public void test_DestinationCount() throws Exception
public void test_GET_WithParameters_ResponseWithContent() throws Exception
{
start(new EmptyHandler());
final String paramName1 = "a";
final String paramName2 = "b";
start(new AbstractHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
response.setCharacterEncoding("UTF-8");
ServletOutputStream output = response.getOutputStream();
String paramValue1 = request.getParameter(paramName1);
output.write(paramValue1.getBytes("UTF-8"));
String paramValue2 = request.getParameter(paramName2);
Assert.assertEquals("", paramValue2);
output.write("empty".getBytes("UTF-8"));
baseRequest.setHandled(true);
}
});
client.GET("http://localhost:" + connector.getLocalPort()).get(5, TimeUnit.SECONDS);
String value1 = "\u20AC";
String paramValue1 = URLEncoder.encode(value1, "UTF-8");
String query = paramName1 + "=" + paramValue1 + "&" + paramName2;
ContentResponse response = client.GET("http://localhost:" + connector.getLocalPort() + "/?" + query).get(5, TimeUnit.SECONDS);
List<Destination> destinations = client.getDestinations();
Assert.assertNotNull(destinations);
Assert.assertEquals(1, destinations.size());
Destination destination = destinations.get(0);
Assert.assertNotNull(destination);
Assert.assertNotNull(response);
Assert.assertEquals(200, response.status());
String content = new String(response.content(), "UTF-8");
Assert.assertEquals(value1 + "empty", content);
}
@Test
public void test_GET_WithParametersMultiValued_ResponseWithContent() throws Exception
{
final String paramName1 = "a";
final String paramName2 = "b";
start(new AbstractHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
response.setCharacterEncoding("UTF-8");
ServletOutputStream output = response.getOutputStream();
String[] paramValues1 = request.getParameterValues(paramName1);
for (String paramValue : paramValues1)
output.write(paramValue.getBytes("UTF-8"));
String paramValue2 = request.getParameter(paramName2);
output.write(paramValue2.getBytes("UTF-8"));
baseRequest.setHandled(true);
}
});
String value11 = "\u20AC";
String value12 = "\u20AA";
String value2 = "&";
String paramValue11 = URLEncoder.encode(value11, "UTF-8");
String paramValue12 = URLEncoder.encode(value12, "UTF-8");
String paramValue2 = URLEncoder.encode(value2, "UTF-8");
String query = paramName1 + "=" + paramValue11 + "&" + paramName1 + "=" + paramValue12 + "&" + paramName2 + "=" + paramValue2;
ContentResponse response = client.GET("http://localhost:" + connector.getLocalPort() + "/?" + query).get(5, TimeUnit.SECONDS);
Assert.assertNotNull(response);
Assert.assertEquals(200, response.status());
String content = new String(response.content(), "UTF-8");
Assert.assertEquals(value11 + value12 + value2, content);
}
}

View File

@ -1,3 +1,21 @@
//
// ========================================================================
// 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.concurrent.BlockingQueue;
@ -161,4 +179,47 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
Assert.assertEquals(1, idleConnections.size());
Assert.assertEquals(0, activeConnections.size());
}
@Test
public void test_ConnectionFailure_RemovesConnection() throws Exception
{
start(new EmptyHandler());
String scheme = "http";
String host = "localhost";
int port = connector.getLocalPort();
HttpDestination destination = (HttpDestination)client.getDestination(scheme, host, port);
final BlockingQueue<Connection> idleConnections = destination.idleConnections();
Assert.assertEquals(0, idleConnections.size());
final BlockingQueue<Connection> activeConnections = destination.activeConnections();
Assert.assertEquals(0, activeConnections.size());
server.stop();
final CountDownLatch failureLatch = new CountDownLatch(2);
client.newRequest(host, port)
.listener(new Request.Listener.Adapter()
{
@Override
public void onFailure(Request request, Throwable failure)
{
failureLatch.countDown();
}
})
.send(new Response.Listener.Adapter()
{
@Override
public void onFailure(Response response, Throwable failure)
{
failureLatch.countDown();
}
});
Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS));
Assert.assertEquals(0, idleConnections.size());
Assert.assertEquals(0, activeConnections.size());
}
}

View File

@ -1,3 +1,21 @@
//
// ========================================================================
// 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.concurrent.CountDownLatch;

View File

@ -1,3 +1,21 @@
//
// ========================================================================
// 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.EOFException;

View File

@ -1,3 +1,21 @@
//
// ========================================================================
// 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.net.URI;

View File

@ -1,3 +1,21 @@
//
// ========================================================================
// 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;

View File

@ -1,15 +1,20 @@
//========================================================================
//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.
//========================================================================
//
// ========================================================================
// 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;

View File

@ -1,15 +1,20 @@
//========================================================================
//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.
//========================================================================
//
// ========================================================================
// 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;

View File

@ -1,15 +1,20 @@
//========================================================================
//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.
//========================================================================
//
// ========================================================================
// 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;