Imported GNU Classpath 0.19 + gcj-import-20051115.
[official-gcc.git] / libjava / classpath / gnu / java / net / protocol / http / HTTPConnection.java
blob573a7918d82de2c5f898a575acf1a81c4354c9d0
1 /* HTTPConnection.java --
2 Copyright (C) 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu.java.net.protocol.http;
41 import gnu.classpath.Configuration;
42 import gnu.classpath.SystemProperties;
43 import gnu.java.net.EmptyX509TrustManager;
45 import java.io.BufferedInputStream;
46 import java.io.BufferedOutputStream;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.io.OutputStream;
50 import java.net.InetSocketAddress;
51 import java.net.Socket;
52 import java.security.GeneralSecurityException;
53 import java.util.ArrayList;
54 import java.util.HashMap;
55 import java.util.Iterator;
56 import java.util.LinkedHashMap;
57 import java.util.List;
58 import java.util.Map;
60 import javax.net.ssl.HandshakeCompletedListener;
61 import javax.net.ssl.SSLContext;
62 import javax.net.ssl.SSLSocket;
63 import javax.net.ssl.SSLSocketFactory;
64 import javax.net.ssl.TrustManager;
66 /**
67 * A connection to an HTTP server.
69 * @author Chris Burdess (dog@gnu.org)
71 public class HTTPConnection
74 /**
75 * The default HTTP port.
77 public static final int HTTP_PORT = 80;
79 /**
80 * The default HTTPS port.
82 public static final int HTTPS_PORT = 443;
84 private static final String userAgent = SystemProperties.getProperty("http.agent");
86 /**
87 * The host name of the server to connect to.
89 protected final String hostname;
91 /**
92 * The port to connect to.
94 protected final int port;
96 /**
97 * Whether the connection should use transport level security (HTTPS).
99 protected final boolean secure;
102 * The connection timeout for connecting the underlying socket.
104 protected final int connectionTimeout;
107 * The read timeout for reads on the underlying socket.
109 protected final int timeout;
112 * The host name of the proxy to connect to.
114 protected String proxyHostname;
117 * The port on the proxy to connect to.
119 protected int proxyPort;
122 * The major version of HTTP supported by this client.
124 protected int majorVersion;
127 * The minor version of HTTP supported by this client.
129 protected int minorVersion;
131 private final List handshakeCompletedListeners;
134 * The socket this connection communicates on.
136 protected Socket socket;
139 * The SSL socket factory to use.
141 private SSLSocketFactory sslSocketFactory;
144 * The socket input stream.
146 protected InputStream in;
149 * The socket output stream.
151 protected OutputStream out;
154 * Nonce values seen by this connection.
156 private Map nonceCounts;
159 * The cookie manager for this connection.
161 protected CookieManager cookieManager;
165 * The pool that this connection is a member of (if any).
167 private LinkedHashMap pool;
170 * Creates a new HTTP connection.
171 * @param hostname the name of the host to connect to
173 public HTTPConnection(String hostname)
175 this(hostname, HTTP_PORT, false, 0, 0);
179 * Creates a new HTTP or HTTPS connection.
180 * @param hostname the name of the host to connect to
181 * @param secure whether to use a secure connection
183 public HTTPConnection(String hostname, boolean secure)
185 this(hostname, secure ? HTTPS_PORT : HTTP_PORT, secure, 0, 0);
189 * Creates a new HTTP or HTTPS connection on the specified port.
190 * @param hostname the name of the host to connect to
191 * @param secure whether to use a secure connection
192 * @param connectionTimeout the connection timeout
193 * @param timeout the socket read timeout
195 public HTTPConnection(String hostname, boolean secure,
196 int connectionTimeout, int timeout)
198 this(hostname, secure ? HTTPS_PORT : HTTP_PORT, secure,
199 connectionTimeout, timeout);
203 * Creates a new HTTP connection on the specified port.
204 * @param hostname the name of the host to connect to
205 * @param port the port on the host to connect to
207 public HTTPConnection(String hostname, int port)
209 this(hostname, port, false, 0, 0);
213 * Creates a new HTTP or HTTPS connection on the specified port.
214 * @param hostname the name of the host to connect to
215 * @param port the port on the host to connect to
216 * @param secure whether to use a secure connection
218 public HTTPConnection(String hostname, int port, boolean secure)
220 this(hostname, port, secure, 0, 0);
224 * Creates a new HTTP or HTTPS connection on the specified port.
225 * @param hostname the name of the host to connect to
226 * @param port the port on the host to connect to
227 * @param secure whether to use a secure connection
228 * @param connectionTimeout the connection timeout
229 * @param timeout the socket read timeout
231 public HTTPConnection(String hostname, int port, boolean secure,
232 int connectionTimeout, int timeout)
234 this.hostname = hostname;
235 this.port = port;
236 this.secure = secure;
237 this.connectionTimeout = connectionTimeout;
238 this.timeout = timeout;
239 majorVersion = minorVersion = 1;
240 handshakeCompletedListeners = new ArrayList(2);
244 * Returns the name of the host to connect to.
246 public String getHostName()
248 return hostname;
252 * Returns the port on the host to connect to.
254 public int getPort()
256 return port;
260 * Indicates whether to use a secure connection or not.
262 public boolean isSecure()
264 return secure;
268 * Returns the HTTP version string supported by this connection.
269 * @see #version
271 public String getVersion()
273 return "HTTP/" + majorVersion + '.' + minorVersion;
277 * Sets the HTTP version supported by this connection.
278 * @param majorVersion the major version
279 * @param minorVersion the minor version
281 public void setVersion(int majorVersion, int minorVersion)
283 if (majorVersion != 1)
285 throw new IllegalArgumentException("major version not supported: " +
286 majorVersion);
288 if (minorVersion < 0 || minorVersion > 1)
290 throw new IllegalArgumentException("minor version not supported: " +
291 minorVersion);
293 this.majorVersion = majorVersion;
294 this.minorVersion = minorVersion;
298 * Directs this connection to use the specified proxy.
299 * @param hostname the proxy host name
300 * @param port the port on the proxy to connect to
302 public void setProxy(String hostname, int port)
304 proxyHostname = hostname;
305 proxyPort = port;
309 * Indicates whether this connection is using an HTTP proxy.
311 public boolean isUsingProxy()
313 return (proxyHostname != null && proxyPort > 0);
317 * Sets the cookie manager to use for this connection.
318 * @param cookieManager the cookie manager
320 public void setCookieManager(CookieManager cookieManager)
322 this.cookieManager = cookieManager;
326 * Returns the cookie manager in use for this connection.
328 public CookieManager getCookieManager()
330 return cookieManager;
334 * The number of times this HTTPConnection has be used via keep-alive.
336 int useCount;
339 * Generates a key for connections in the connection pool.
341 * @param h the host name.
342 * @param p the port.
343 * @param sec true if using https.
345 * @return the key.
347 static Object getPoolKey(String h, int p, boolean sec)
349 StringBuilder buf = new StringBuilder(sec ? "https://" : "http://");
350 buf.append(h);
351 buf.append(':');
352 buf.append(p);
353 return buf.toString();
357 * Set the connection pool that this HTTPConnection is a member of.
358 * If left unset or set to null, it will not be a member of any pool
359 * and will not be a candidate for reuse.
361 * @param p the pool.
363 void setPool(LinkedHashMap p)
365 pool = p;
369 * Signal that this HTTPConnection is no longer needed and can be
370 * returned to the connection pool.
373 void release()
375 if (pool != null)
377 synchronized (pool)
379 useCount++;
380 Object key = HTTPConnection.getPoolKey(hostname, port, secure);
381 pool.put(key, this);
382 while (pool.size() >= HTTPURLConnection.maxConnections)
384 // maxConnections must always be >= 1
385 Object lru = pool.keySet().iterator().next();
386 HTTPConnection c = (HTTPConnection)pool.remove(lru);
389 c.closeConnection();
391 catch (IOException ioe)
393 // Ignore it. We are just cleaning up.
401 * Creates a new request using this connection.
402 * @param method the HTTP method to invoke
403 * @param path the URI-escaped RFC2396 <code>abs_path</code> with
404 * optional query part
406 public Request newRequest(String method, String path)
408 if (method == null || method.length() == 0)
410 throw new IllegalArgumentException("method must have non-zero length");
412 if (path == null || path.length() == 0)
414 path = "/";
416 Request ret = new Request(this, method, path);
417 if ((secure && port != HTTPS_PORT) ||
418 (!secure && port != HTTP_PORT))
420 ret.setHeader("Host", hostname + ":" + port);
422 else
424 ret.setHeader("Host", hostname);
426 ret.setHeader("User-Agent", userAgent);
427 ret.setHeader("Connection", "keep-alive");
428 ret.setHeader("Accept-Encoding",
429 "chunked;q=1.0, gzip;q=0.9, deflate;q=0.8, " +
430 "identity;q=0.6, *;q=0");
431 if (cookieManager != null)
433 Cookie[] cookies = cookieManager.getCookies(hostname, secure, path);
434 if (cookies != null && cookies.length > 0)
436 StringBuilder buf = new StringBuilder();
437 buf.append("$Version=1");
438 for (int i = 0; i < cookies.length; i++)
440 buf.append(',');
441 buf.append(' ');
442 buf.append(cookies[i].toString());
444 ret.setHeader("Cookie", buf.toString());
447 return ret;
451 * Closes this connection.
453 public void close()
454 throws IOException
456 closeConnection();
460 * Retrieves the socket associated with this connection.
461 * This creates the socket if necessary.
463 protected synchronized Socket getSocket()
464 throws IOException
466 if (socket == null)
468 String connectHostname = hostname;
469 int connectPort = port;
470 if (isUsingProxy())
472 connectHostname = proxyHostname;
473 connectPort = proxyPort;
475 socket = new Socket();
476 InetSocketAddress address =
477 new InetSocketAddress(connectHostname, connectPort);
478 if (connectionTimeout > 0)
480 socket.connect(address, connectionTimeout);
482 else
484 socket.connect(address);
486 if (timeout > 0)
488 socket.setSoTimeout(timeout);
490 if (secure)
494 SSLSocketFactory factory = getSSLSocketFactory();
495 SSLSocket ss =
496 (SSLSocket) factory.createSocket(socket, connectHostname,
497 connectPort, true);
498 String[] protocols = { "TLSv1", "SSLv3" };
499 ss.setEnabledProtocols(protocols);
500 ss.setUseClientMode(true);
501 synchronized (handshakeCompletedListeners)
503 if (!handshakeCompletedListeners.isEmpty())
505 for (Iterator i =
506 handshakeCompletedListeners.iterator();
507 i.hasNext(); )
509 HandshakeCompletedListener l =
510 (HandshakeCompletedListener) i.next();
511 ss.addHandshakeCompletedListener(l);
515 ss.startHandshake();
516 socket = ss;
518 catch (GeneralSecurityException e)
520 throw new IOException(e.getMessage());
523 in = socket.getInputStream();
524 in = new BufferedInputStream(in);
525 out = socket.getOutputStream();
526 out = new BufferedOutputStream(out);
528 return socket;
531 SSLSocketFactory getSSLSocketFactory()
532 throws GeneralSecurityException
534 if (sslSocketFactory == null)
536 TrustManager tm = new EmptyX509TrustManager();
537 SSLContext context = SSLContext.getInstance("SSL");
538 TrustManager[] trust = new TrustManager[] { tm };
539 context.init(null, trust, null);
540 sslSocketFactory = context.getSocketFactory();
542 return sslSocketFactory;
545 void setSSLSocketFactory(SSLSocketFactory factory)
547 sslSocketFactory = factory;
550 protected synchronized InputStream getInputStream()
551 throws IOException
553 if (socket == null)
555 getSocket();
557 return in;
560 protected synchronized OutputStream getOutputStream()
561 throws IOException
563 if (socket == null)
565 getSocket();
567 return out;
571 * Closes the underlying socket, if any.
573 protected synchronized void closeConnection()
574 throws IOException
576 if (socket != null)
580 socket.close();
582 finally
584 socket = null;
590 * Returns a URI representing the connection.
591 * This does not include any request path component.
593 protected String getURI()
595 StringBuilder buf = new StringBuilder();
596 buf.append(secure ? "https://" : "http://");
597 buf.append(hostname);
598 if (secure)
600 if (port != HTTPConnection.HTTPS_PORT)
602 buf.append(':');
603 buf.append(port);
606 else
608 if (port != HTTPConnection.HTTP_PORT)
610 buf.append(':');
611 buf.append(port);
614 return buf.toString();
618 * Get the number of times the specified nonce has been seen by this
619 * connection.
621 int getNonceCount(String nonce)
623 if (nonceCounts == null)
625 return 0;
627 return((Integer) nonceCounts.get(nonce)).intValue();
631 * Increment the number of times the specified nonce has been seen.
633 void incrementNonce(String nonce)
635 int current = getNonceCount(nonce);
636 if (nonceCounts == null)
638 nonceCounts = new HashMap();
640 nonceCounts.put(nonce, new Integer(current + 1));
643 // -- Events --
645 void addHandshakeCompletedListener(HandshakeCompletedListener l)
647 synchronized (handshakeCompletedListeners)
649 handshakeCompletedListeners.add(l);
652 void removeHandshakeCompletedListener(HandshakeCompletedListener l)
654 synchronized (handshakeCompletedListeners)
656 handshakeCompletedListeners.remove(l);