Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java')
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java546
1 files changed, 546 insertions, 0 deletions
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
new file mode 100644
index 0000000000..f717c474f0
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
@@ -0,0 +1,546 @@
+// ========================================================================
+// Copyright (c) 2006-2009 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.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+
+import org.eclipse.jetty.client.security.Authorization;
+import org.eclipse.jetty.client.security.SecurityListener;
+import org.eclipse.jetty.http.HttpCookie;
+import org.eclipse.jetty.http.HttpHeaders;
+import org.eclipse.jetty.http.PathMap;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.ByteArrayBuffer;
+import org.eclipse.jetty.util.log.Log;
+
+/**
+ *
+ *
+ */
+public class HttpDestination
+{
+ private ByteArrayBuffer _hostHeader;
+ private final Address _address;
+ private final LinkedList<HttpConnection> _connections = new LinkedList<HttpConnection>();
+ private final ArrayList<HttpConnection> _idle = new ArrayList<HttpConnection>();
+ private final HttpClient _client;
+ private final boolean _ssl;
+ private int _maxConnections;
+ private int _pendingConnections = 0;
+ private ArrayBlockingQueue<Object> _newQueue = new ArrayBlockingQueue<Object>(10, true);
+ private int _newConnection = 0;
+ private Address _proxy;
+ private Authorization _proxyAuthentication;
+ private PathMap _authorizations;
+ private List<HttpCookie> _cookies;
+
+ public void dump() throws IOException
+ {
+ synchronized (this)
+ {
+ System.err.println(this);
+ System.err.println("connections=" + _connections.size());
+ System.err.println("idle=" + _idle.size());
+ System.err.println("pending=" + _pendingConnections);
+ for (HttpConnection c : _connections)
+ {
+ if (!c.isIdle())
+ c.dump();
+ }
+ }
+ }
+
+ /* The queue of exchanged for this destination if connections are limited */
+ private LinkedList<HttpExchange> _queue = new LinkedList<HttpExchange>();
+
+ /* ------------------------------------------------------------ */
+ HttpDestination(HttpClient pool, Address address, boolean ssl, int maxConnections)
+ {
+ _client = pool;
+ _address = address;
+ _ssl = ssl;
+ _maxConnections = maxConnections;
+ String addressString = address.getHost();
+ if (address.getPort() != (_ssl ? 443 : 80)) addressString += ":" + address.getPort();
+ _hostHeader = new ByteArrayBuffer(addressString);
+ }
+
+ /* ------------------------------------------------------------ */
+ public Address getAddress()
+ {
+ return _address;
+ }
+
+ /* ------------------------------------------------------------ */
+ public Buffer getHostHeader()
+ {
+ return _hostHeader;
+ }
+
+ /* ------------------------------------------------------------ */
+ public HttpClient getHttpClient()
+ {
+ return _client;
+ }
+
+ /* ------------------------------------------------------------ */
+ public boolean isSecure()
+ {
+ return _ssl;
+ }
+
+ /* ------------------------------------------------------------ */
+ public void addAuthorization(String pathSpec, Authorization authorization)
+ {
+ synchronized (this)
+ {
+ if (_authorizations == null)
+ _authorizations = new PathMap();
+ _authorizations.put(pathSpec, authorization);
+ }
+
+ // TODO query and remove methods
+ }
+
+ /* ------------------------------------------------------------------------------- */
+ public void addCookie(HttpCookie cookie)
+ {
+ synchronized (this)
+ {
+ if (_cookies == null)
+ _cookies = new ArrayList<HttpCookie>();
+ _cookies.add(cookie);
+ }
+
+ // TODO query, remove and age methods
+ }
+
+ /* ------------------------------------------------------------------------------- */
+ /**
+ * Get a connection. We either get an idle connection if one is available, or
+ * we make a new connection, if we have not yet reached maxConnections. If we
+ * have reached maxConnections, we wait until the number reduces.
+ * @param timeout max time prepared to block waiting to be able to get a connection
+ * @return
+ * @throws IOException
+ */
+ private HttpConnection getConnection(long timeout) throws IOException
+ {
+ HttpConnection connection = null;
+
+ while ((connection == null) && (connection = getIdleConnection()) == null && timeout>0)
+ {
+ int totalConnections = 0;
+ boolean starting = false;
+ synchronized (this)
+ {
+ totalConnections = _connections.size() + _pendingConnections;
+ if (totalConnections < _maxConnections)
+ {
+ _newConnection++;
+ startNewConnection();
+ starting = true;
+ }
+ }
+
+ if (!starting)
+ {
+ try
+ {
+ Thread.currentThread().sleep(200);
+ timeout-=200;
+ }
+ catch (InterruptedException e)
+ {
+ Log.ignore(e);
+ }
+ }
+ else
+ {
+ try
+ {
+ Object o = _newQueue.take();
+ if (o instanceof HttpConnection)
+ {
+ connection = (HttpConnection)o;
+ }
+ else
+ throw (IOException)o;
+ }
+ catch (InterruptedException e)
+ {
+ Log.ignore(e);
+ }
+ }
+ }
+ return connection;
+ }
+
+ /* ------------------------------------------------------------------------------- */
+ public HttpConnection reserveConnection(long timeout) throws IOException
+ {
+ HttpConnection connection = getConnection(timeout);
+ if (connection != null)
+ connection.setReserved(true);
+ return connection;
+ }
+
+ /* ------------------------------------------------------------------------------- */
+ public HttpConnection getIdleConnection() throws IOException
+ {
+ synchronized (this)
+ {
+ long now = System.currentTimeMillis();
+ long idleTimeout = _client.getIdleTimeout();
+
+ // Find an idle connection
+ while (_idle.size() > 0)
+ {
+ HttpConnection connection = _idle.remove(_idle.size() - 1);
+ long last = connection.getLast();
+ if (connection.getEndPoint().isOpen() && (last == 0 || ((now - last) < idleTimeout)))
+ return connection;
+ else
+ {
+ _connections.remove(connection);
+ connection.getEndPoint().close();
+ }
+ }
+
+ return null;
+ }
+ }
+
+ /* ------------------------------------------------------------------------------- */
+ protected void startNewConnection()
+ {
+ try
+ {
+ synchronized (this)
+ {
+ _pendingConnections++;
+ }
+ _client._connector.startConnection(this);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ onConnectionFailed(e);
+ }
+ }
+
+ /* ------------------------------------------------------------------------------- */
+ public void onConnectionFailed(Throwable throwable)
+ {
+ Throwable connect_failure = null;
+
+ synchronized (this)
+ {
+ _pendingConnections--;
+ if (_newConnection > 0)
+ {
+ connect_failure = throwable;
+ _newConnection--;
+ }
+ else if (_queue.size() > 0)
+ {
+ HttpExchange ex = _queue.removeFirst();
+ ex.getEventListener().onConnectionFailed(throwable);
+ }
+ }
+
+ if (connect_failure != null)
+ {
+ try
+ {
+ _newQueue.put(connect_failure);
+ }
+ catch (InterruptedException e)
+ {
+ Log.ignore(e);
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------------------------- */
+ public void onException(Throwable throwable)
+ {
+ synchronized (this)
+ {
+ _pendingConnections--;
+ if (_queue.size() > 0)
+ {
+ HttpExchange ex = _queue.removeFirst();
+ ex.getEventListener().onException(throwable);
+ ex.setStatus(HttpExchange.STATUS_EXCEPTED);
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------------------------- */
+ public void onNewConnection(HttpConnection connection) throws IOException
+ {
+ HttpConnection q_connection = null;
+
+ synchronized (this)
+ {
+ _pendingConnections--;
+ _connections.add(connection);
+
+ if (_newConnection > 0)
+ {
+ q_connection = connection;
+ _newConnection--;
+ }
+ else if (_queue.size() == 0)
+ {
+ _idle.add(connection);
+ }
+ else
+ {
+ HttpExchange ex = _queue.removeFirst();
+ connection.send(ex);
+ }
+ }
+
+ if (q_connection != null)
+ {
+ try
+ {
+ _newQueue.put(q_connection);
+ }
+ catch (InterruptedException e)
+ {
+ Log.ignore(e);
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------------------------- */
+ public void returnConnection(HttpConnection connection, boolean close) throws IOException
+ {
+ if (connection.isReserved())
+ connection.setReserved(false);
+
+ if (close)
+ {
+ try
+ {
+ connection.close();
+ }
+ catch (IOException e)
+ {
+ Log.ignore(e);
+ }
+ }
+
+ if (!_client.isStarted())
+ return;
+
+ if (!close && connection.getEndPoint().isOpen())
+ {
+ synchronized (this)
+ {
+ if (_queue.size() == 0)
+ {
+ connection.setLast(System.currentTimeMillis());
+ _idle.add(connection);
+ }
+ else
+ {
+ HttpExchange ex = _queue.removeFirst();
+ connection.send(ex);
+ }
+ this.notifyAll();
+ }
+ }
+ else
+ {
+ synchronized (this)
+ {
+ _connections.remove(connection);
+ if (!_queue.isEmpty())
+ startNewConnection();
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ public void send(HttpExchange ex) throws IOException
+ {
+ LinkedList<String> listeners = _client.getRegisteredListeners();
+
+ if (listeners != null)
+ {
+ // Add registered listeners, fail if we can't load them
+ for (int i = listeners.size(); i > 0; --i)
+ {
+ String listenerClass = listeners.get(i - 1);
+
+ try
+ {
+ Class listener = Class.forName(listenerClass);
+ Constructor constructor = listener.getDeclaredConstructor(HttpDestination.class, HttpExchange.class);
+ HttpEventListener elistener = (HttpEventListener)constructor.newInstance(this, ex);
+ ex.setEventListener(elistener);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ throw new IOException("Unable to instantiate registered listener for destination: " + listenerClass);
+ }
+ }
+ }
+
+ // Security is supported by default and should be the first consulted
+ if (_client.hasRealms())
+ {
+ ex.setEventListener(new SecurityListener(this, ex));
+ }
+
+ doSend(ex);
+ }
+
+ /* ------------------------------------------------------------ */
+ public void resend(HttpExchange ex) throws IOException
+ {
+ ex.getEventListener().onRetry();
+ doSend(ex);
+ }
+
+ /* ------------------------------------------------------------ */
+ protected void doSend(HttpExchange ex) throws IOException
+ {
+ // add cookies
+ // TODO handle max-age etc.
+ if (_cookies != null)
+ {
+ StringBuilder buf = null;
+ for (HttpCookie cookie : _cookies)
+ {
+ if (buf == null)
+ buf = new StringBuilder();
+ else
+ buf.append("; ");
+ buf.append(cookie.getName()); // TODO quotes
+ buf.append("=");
+ buf.append(cookie.getValue()); // TODO quotes
+ }
+ if (buf != null)
+ ex.addRequestHeader(HttpHeaders.COOKIE, buf.toString());
+ }
+
+ // Add any known authorizations
+ if (_authorizations != null)
+ {
+ Authorization auth = (Authorization)_authorizations.match(ex.getURI());
+ if (auth != null)
+ ((Authorization)auth).setCredentials(ex);
+ }
+
+ synchronized (this)
+ {
+ //System.out.println( "Sending: " + ex.toString() );
+
+ HttpConnection connection = null;
+ if (_queue.size() > 0 || (connection = getIdleConnection()) == null || !connection.send(ex))
+ {
+ _queue.add(ex);
+ if (_connections.size() + _pendingConnections < _maxConnections)
+ {
+ startNewConnection();
+ }
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ public synchronized String toString()
+ {
+ return "HttpDestination@" + hashCode() + "//" + _address.getHost() + ":" + _address.getPort() + "(" + _connections.size() + "," + _idle.size() + "," + _queue.size() + ")";
+ }
+
+ /* ------------------------------------------------------------ */
+ public synchronized String toDetailString()
+ {
+ StringBuilder b = new StringBuilder();
+ b.append(toString());
+ b.append('\n');
+ synchronized (this)
+ {
+ for (HttpConnection connection : _connections)
+ {
+ if (connection._exchange != null)
+ {
+ b.append(connection.toDetailString());
+ if (_idle.contains(connection))
+ b.append(" IDLE");
+ b.append('\n');
+ }
+ }
+ }
+ b.append("--");
+ b.append('\n');
+
+ return b.toString();
+ }
+
+ /* ------------------------------------------------------------ */
+ public void setProxy(Address proxy)
+ {
+ _proxy = proxy;
+ }
+
+ /* ------------------------------------------------------------ */
+ public Address getProxy()
+ {
+ return _proxy;
+ }
+
+ /* ------------------------------------------------------------ */
+ public Authorization getProxyAuthentication()
+ {
+ return _proxyAuthentication;
+ }
+
+ /* ------------------------------------------------------------ */
+ public void setProxyAuthentication(Authorization authentication)
+ {
+ _proxyAuthentication = authentication;
+ }
+
+ /* ------------------------------------------------------------ */
+ public boolean isProxied()
+ {
+ return _proxy != null;
+ }
+
+ /* ------------------------------------------------------------ */
+ public void close() throws IOException
+ {
+ synchronized (this)
+ {
+ for (HttpConnection connection : _connections)
+ {
+ connection.close();
+ }
+ }
+ }
+
+}

Back to the top