Merge from the pain train
[official-gcc.git] / libjava / gnu / java / net / protocol / http / HTTPConnection.java
bloba3f14b51012ff572d0e493381b1702dd9545e923
1 /* HTTPConnection.java --
2 Copyright (C) 2004 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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 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.java.net.protocol.http.event.ConnectionEvent;
43 import gnu.java.net.protocol.http.event.ConnectionListener;
44 import gnu.java.net.protocol.http.event.RequestEvent;
45 import gnu.java.net.protocol.http.event.RequestListener;
46 import gnu.java.net.EmptyX509TrustManager;
48 import java.io.BufferedInputStream;
49 import java.io.BufferedOutputStream;
50 import java.io.InputStream;
51 import java.io.IOException;
52 import java.io.OutputStream;
53 import java.net.InetSocketAddress;
54 import java.net.Socket;
55 import java.security.GeneralSecurityException;
56 import java.util.ArrayList;
57 import java.util.HashMap;
58 import java.util.Iterator;
59 import java.util.List;
60 import java.util.Map;
61 import javax.net.ssl.HandshakeCompletedListener;
62 import javax.net.ssl.SSLContext;
63 import javax.net.ssl.SSLSocket;
64 import javax.net.ssl.SSLSocketFactory;
65 import javax.net.ssl.TrustManager;
67 /**
68 * A connection to an HTTP server.
70 * @author Chris Burdess (dog@gnu.org)
72 public class HTTPConnection
75 /**
76 * The default HTTP port.
78 public static final int HTTP_PORT = 80;
80 /**
81 * The default HTTPS port.
83 public static final int HTTPS_PORT = 443;
85 private static final String userAgent = initUserAgent();
87 private static String initUserAgent()
89 try
91 StringBuffer buf = new StringBuffer("libgcj");
92 buf.append(" (");
93 buf.append(System.getProperty("os.name"));
94 buf.append("; ");
95 buf.append(System.getProperty("os.arch"));
96 buf.append("; ");
97 buf.append(System.getProperty("user.language"));
98 buf.append(")");
99 return buf.toString();
101 catch (SecurityException e)
103 return "inetlib/1.1";
108 * The host name of the server to connect to.
110 protected final String hostname;
113 * The port to connect to.
115 protected final int port;
118 * Whether the connection should use transport level security (HTTPS).
120 protected final boolean secure;
123 * The connection timeout for connecting the underlying socket.
125 protected final int connectionTimeout;
128 * The read timeout for reads on the underlying socket.
130 protected final int timeout;
133 * The host name of the proxy to connect to.
135 protected String proxyHostname;
138 * The port on the proxy to connect to.
140 protected int proxyPort;
143 * The major version of HTTP supported by this client.
145 protected int majorVersion;
148 * The minor version of HTTP supported by this client.
150 protected int minorVersion;
152 private final List connectionListeners;
153 private final List requestListeners;
154 private final List handshakeCompletedListeners;
157 * The socket this connection communicates on.
159 protected Socket socket;
162 * The SSL socket factory to use.
164 private SSLSocketFactory sslSocketFactory;
167 * The socket input stream.
169 protected InputStream in;
172 * The socket output stream.
174 protected OutputStream out;
177 * Nonce values seen by this connection.
179 private Map nonceCounts;
182 * The cookie manager for this connection.
184 protected CookieManager cookieManager;
187 * Creates a new HTTP connection.
188 * @param hostname the name of the host to connect to
190 public HTTPConnection(String hostname)
192 this(hostname, HTTP_PORT, false, 0, 0);
196 * Creates a new HTTP or HTTPS connection.
197 * @param hostname the name of the host to connect to
198 * @param secure whether to use a secure connection
200 public HTTPConnection(String hostname, boolean secure)
202 this(hostname, secure ? HTTPS_PORT : HTTP_PORT, secure, 0, 0);
206 * Creates a new HTTP or HTTPS connection on the specified port.
207 * @param hostname the name of the host to connect to
208 * @param secure whether to use a secure connection
209 * @param connectionTimeout the connection timeout
210 * @param timeout the socket read timeout
212 public HTTPConnection(String hostname, boolean secure,
213 int connectionTimeout, int timeout)
215 this(hostname, secure ? HTTPS_PORT : HTTP_PORT, secure,
216 connectionTimeout, timeout);
220 * Creates a new HTTP connection on the specified port.
221 * @param hostname the name of the host to connect to
222 * @param port the port on the host to connect to
224 public HTTPConnection(String hostname, int port)
226 this(hostname, port, false, 0, 0);
230 * Creates a new HTTP or HTTPS connection on the specified port.
231 * @param hostname the name of the host to connect to
232 * @param port the port on the host to connect to
233 * @param secure whether to use a secure connection
235 public HTTPConnection(String hostname, int port, boolean secure)
237 this(hostname, port, secure, 0, 0);
241 * Creates a new HTTP or HTTPS connection on the specified port.
242 * @param hostname the name of the host to connect to
243 * @param port the port on the host to connect to
244 * @param secure whether to use a secure connection
245 * @param connectionTimeout the connection timeout
246 * @param timeout the socket read timeout
248 public HTTPConnection(String hostname, int port, boolean secure,
249 int connectionTimeout, int timeout)
251 this.hostname = hostname;
252 this.port = port;
253 this.secure = secure;
254 this.connectionTimeout = connectionTimeout;
255 this.timeout = timeout;
256 majorVersion = minorVersion = 1;
257 connectionListeners = new ArrayList(4);
258 requestListeners = new ArrayList(4);
259 handshakeCompletedListeners = new ArrayList(2);
263 * Returns the name of the host to connect to.
265 public String getHostName()
267 return hostname;
271 * Returns the port on the host to connect to.
273 public int getPort()
275 return port;
279 * Indicates whether to use a secure connection or not.
281 public boolean isSecure()
283 return secure;
287 * Returns the HTTP version string supported by this connection.
288 * @see #version
290 public String getVersion()
292 return "HTTP/" + majorVersion + '.' + minorVersion;
296 * Sets the HTTP version supported by this connection.
297 * @param majorVersion the major version
298 * @param minorVersion the minor version
300 public void setVersion(int majorVersion, int minorVersion)
302 if (majorVersion != 1)
304 throw new IllegalArgumentException("major version not supported: " +
305 majorVersion);
307 if (minorVersion < 0 || minorVersion > 1)
309 throw new IllegalArgumentException("minor version not supported: " +
310 minorVersion);
312 this.majorVersion = majorVersion;
313 this.minorVersion = minorVersion;
317 * Directs this connection to use the specified proxy.
318 * @param hostname the proxy host name
319 * @param port the port on the proxy to connect to
321 public void setProxy(String hostname, int port)
323 proxyHostname = hostname;
324 proxyPort = port;
328 * Indicates whether this connection is using an HTTP proxy.
330 public boolean isUsingProxy()
332 return (proxyHostname != null && proxyPort > 0);
336 * Sets the cookie manager to use for this connection.
337 * @param cookieManager the cookie manager
339 public void setCookieManager(CookieManager cookieManager)
341 this.cookieManager = cookieManager;
345 * Returns the cookie manager in use for this connection.
347 public CookieManager getCookieManager()
349 return cookieManager;
353 * Creates a new request using this connection.
354 * @param method the HTTP method to invoke
355 * @param path the URI-escaped RFC2396 <code>abs_path</code> with
356 * optional query part
358 public Request newRequest(String method, String path)
360 if (method == null || method.length() == 0)
362 throw new IllegalArgumentException("method must have non-zero length");
364 if (path == null || path.length() == 0)
366 path = "/";
368 Request ret = new Request(this, method, path);
369 if ((secure && port != HTTPS_PORT) ||
370 (!secure && port != HTTP_PORT))
372 ret.setHeader("Host", hostname + ":" + port);
374 else
376 ret.setHeader("Host", hostname);
378 ret.setHeader("User-Agent", userAgent);
379 ret.setHeader("Connection", "keep-alive");
380 ret.setHeader("Accept-Encoding",
381 "chunked;q=1.0, gzip;q=0.9, deflate;q=0.8, " +
382 "identity;q=0.6, *;q=0");
383 if (cookieManager != null)
385 Cookie[] cookies = cookieManager.getCookies(hostname, secure, path);
386 if (cookies != null && cookies.length > 0)
388 StringBuffer buf = new StringBuffer();
389 buf.append("$Version=1");
390 for (int i = 0; i < cookies.length; i++)
392 buf.append(',');
393 buf.append(' ');
394 buf.append(cookies[i].toString());
396 ret.setHeader("Cookie", buf.toString());
399 fireRequestEvent(RequestEvent.REQUEST_CREATED, ret);
400 return ret;
404 * Closes this connection.
406 public void close()
407 throws IOException
411 closeConnection();
413 finally
415 fireConnectionEvent(ConnectionEvent.CONNECTION_CLOSED);
420 * Retrieves the socket associated with this connection.
421 * This creates the socket if necessary.
423 protected Socket getSocket()
424 throws IOException
426 if (socket == null)
428 String connectHostname = hostname;
429 int connectPort = port;
430 if (isUsingProxy())
432 connectHostname = proxyHostname;
433 connectPort = proxyPort;
435 socket = new Socket();
436 InetSocketAddress address =
437 new InetSocketAddress(connectHostname, connectPort);
438 if (connectionTimeout > 0)
440 socket.connect(address, connectionTimeout);
442 else
444 socket.connect(address);
446 if (timeout > 0)
448 socket.setSoTimeout(timeout);
450 if (secure)
454 SSLSocketFactory factory = getSSLSocketFactory();
455 SSLSocket ss =
456 (SSLSocket) factory.createSocket(socket, connectHostname,
457 connectPort, true);
458 String[] protocols = { "TLSv1", "SSLv3" };
459 ss.setEnabledProtocols(protocols);
460 ss.setUseClientMode(true);
461 synchronized (handshakeCompletedListeners)
463 if (!handshakeCompletedListeners.isEmpty())
465 for (Iterator i =
466 handshakeCompletedListeners.iterator();
467 i.hasNext(); )
469 HandshakeCompletedListener l =
470 (HandshakeCompletedListener) i.next();
471 ss.addHandshakeCompletedListener(l);
475 ss.startHandshake();
476 socket = ss;
478 catch (GeneralSecurityException e)
480 throw new IOException(e.getMessage());
483 in = socket.getInputStream();
484 in = new BufferedInputStream(in);
485 out = socket.getOutputStream();
486 out = new BufferedOutputStream(out);
488 return socket;
491 SSLSocketFactory getSSLSocketFactory()
492 throws GeneralSecurityException
494 if (sslSocketFactory == null)
496 TrustManager tm = new EmptyX509TrustManager();
497 SSLContext context = SSLContext.getInstance("SSL");
498 TrustManager[] trust = new TrustManager[] { tm };
499 context.init(null, trust, null);
500 sslSocketFactory = context.getSocketFactory();
502 return sslSocketFactory;
505 void setSSLSocketFactory(SSLSocketFactory factory)
507 sslSocketFactory = factory;
510 protected InputStream getInputStream()
511 throws IOException
513 if (socket == null)
515 getSocket();
517 return in;
520 protected OutputStream getOutputStream()
521 throws IOException
523 if (socket == null)
525 getSocket();
527 return out;
531 * Closes the underlying socket, if any.
533 protected void closeConnection()
534 throws IOException
536 if (socket != null)
540 socket.close();
542 finally
544 socket = null;
550 * Returns a URI representing the connection.
551 * This does not include any request path component.
553 protected String getURI()
555 StringBuffer buf = new StringBuffer();
556 buf.append(secure ? "https://" : "http://");
557 buf.append(hostname);
558 if (secure)
560 if (port != HTTPConnection.HTTPS_PORT)
562 buf.append(':');
563 buf.append(port);
566 else
568 if (port != HTTPConnection.HTTP_PORT)
570 buf.append(':');
571 buf.append(port);
574 return buf.toString();
578 * Get the number of times the specified nonce has been seen by this
579 * connection.
581 int getNonceCount(String nonce)
583 if (nonceCounts == null)
585 return 0;
587 return((Integer) nonceCounts.get(nonce)).intValue();
591 * Increment the number of times the specified nonce has been seen.
593 void incrementNonce(String nonce)
595 int current = getNonceCount(nonce);
596 if (nonceCounts == null)
598 nonceCounts = new HashMap();
600 nonceCounts.put(nonce, new Integer(current + 1));
603 // -- Events --
605 public void addConnectionListener(ConnectionListener l)
607 synchronized (connectionListeners)
609 connectionListeners.add(l);
613 public void removeConnectionListener(ConnectionListener l)
615 synchronized (connectionListeners)
617 connectionListeners.remove(l);
621 protected void fireConnectionEvent(int type)
623 ConnectionEvent event = new ConnectionEvent(this, type);
624 ConnectionListener[] l = null;
625 synchronized (connectionListeners)
627 l = new ConnectionListener[connectionListeners.size()];
628 connectionListeners.toArray(l);
630 for (int i = 0; i < l.length; i++)
632 switch (type)
634 case ConnectionEvent.CONNECTION_CLOSED:
635 l[i].connectionClosed(event);
636 break;
641 public void addRequestListener(RequestListener l)
643 synchronized (requestListeners)
645 requestListeners.add(l);
649 public void removeRequestListener(RequestListener l)
651 synchronized (requestListeners)
653 requestListeners.remove(l);
657 protected void fireRequestEvent(int type, Request request)
659 RequestEvent event = new RequestEvent(this, type, request);
660 RequestListener[] l = null;
661 synchronized (requestListeners)
663 l = new RequestListener[requestListeners.size()];
664 requestListeners.toArray(l);
666 for (int i = 0; i < l.length; i++)
668 switch (type)
670 case RequestEvent.REQUEST_CREATED:
671 l[i].requestCreated(event);
672 break;
673 case RequestEvent.REQUEST_SENDING:
674 l[i].requestSent(event);
675 break;
676 case RequestEvent.REQUEST_SENT:
677 l[i].requestSent(event);
678 break;
683 void addHandshakeCompletedListener(HandshakeCompletedListener l)
685 synchronized (handshakeCompletedListeners)
687 handshakeCompletedListeners.add(l);
690 void removeHandshakeCompletedListener(HandshakeCompletedListener l)
692 synchronized (handshakeCompletedListeners)
694 handshakeCompletedListeners.remove(l);