mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			650 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			650 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* HTTPURLConnection.java --
 | |
|    Copyright (C) 2004 Free Software Foundation, Inc.
 | |
| 
 | |
| This file is part of GNU Classpath.
 | |
| 
 | |
| GNU Classpath is free software; you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation; either version 2, or (at your option)
 | |
| any later version.
 | |
|  
 | |
| GNU Classpath is distributed in the hope that it will be useful, but
 | |
| WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
| General Public License for more details.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License
 | |
| along with GNU Classpath; see the file COPYING.  If not, write to the
 | |
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 | |
| 02111-1307 USA.
 | |
| 
 | |
| Linking this library statically or dynamically with other modules is
 | |
| making a combined work based on this library.  Thus, the terms and
 | |
| conditions of the GNU General Public License cover the whole
 | |
| combination.
 | |
| 
 | |
| As a special exception, the copyright holders of this library give you
 | |
| permission to link this library with independent modules to produce an
 | |
| executable, regardless of the license terms of these independent
 | |
| modules, and to copy and distribute the resulting executable under
 | |
| terms of your choice, provided that you also meet, for each linked
 | |
| independent module, the terms and conditions of the license of that
 | |
| module.  An independent module is a module which is not derived from
 | |
| or based on this library.  If you modify this library, you may extend
 | |
| this exception to your version of the library, but you are not
 | |
| obligated to do so.  If you do not wish to do so, delete this
 | |
| exception statement from your version. */
 | |
| 
 | |
| 
 | |
| package gnu.java.net.protocol.http;
 | |
| 
 | |
| import java.io.ByteArrayInputStream;
 | |
| import java.io.ByteArrayOutputStream;
 | |
| import java.io.InputStream;
 | |
| import java.io.IOException;
 | |
| import java.io.OutputStream;
 | |
| import java.net.ProtocolException;
 | |
| import java.net.URL;
 | |
| import java.security.AccessController;
 | |
| import java.security.PrivilegedAction;
 | |
| import java.security.cert.Certificate;
 | |
| import java.util.Date;
 | |
| import java.util.Collections;
 | |
| import java.util.Iterator;
 | |
| import java.util.LinkedHashMap;
 | |
| import java.util.Map;
 | |
| import javax.net.ssl.HandshakeCompletedEvent;
 | |
| import javax.net.ssl.HandshakeCompletedListener;
 | |
| import javax.net.ssl.HostnameVerifier;
 | |
| import javax.net.ssl.HttpsURLConnection;
 | |
| import javax.net.ssl.SSLPeerUnverifiedException;
 | |
| import javax.net.ssl.SSLSocketFactory;
 | |
| 
 | |
| /**
 | |
|  * A URLConnection that uses the HTTPConnection class.
 | |
|  *
 | |
|  * @author Chris Burdess (dog@gnu.org)
 | |
|  */
 | |
| public class HTTPURLConnection
 | |
|   extends HttpsURLConnection
 | |
|   implements HandshakeCompletedListener
 | |
| {
 | |
| 
 | |
|   /**
 | |
|    * Pool of reusable connections, used if keepAlive is true.
 | |
|    */
 | |
|   private static final Map connectionPool = new LinkedHashMap();
 | |
| 
 | |
|   /*
 | |
|    * The underlying connection.
 | |
|    */
 | |
|   private HTTPConnection connection;
 | |
| 
 | |
|   private String proxyHostname;
 | |
|   private int proxyPort;
 | |
|   private String agent;
 | |
|   private boolean keepAlive;
 | |
|   private int maxConnections;
 | |
| 
 | |
|   private Request request;
 | |
|   private Headers requestHeaders;
 | |
|   private ByteArrayOutputStream requestSink;
 | |
|   private boolean requestMethodSetExplicitly;
 | |
| 
 | |
|   private Response response;
 | |
|   private ByteArrayInputStream responseSink;
 | |
| 
 | |
|   private HandshakeCompletedEvent handshakeEvent;
 | |
| 
 | |
|   /**
 | |
|    * Constructor.
 | |
|    * @param url the URL
 | |
|    */
 | |
|   public HTTPURLConnection(URL url)
 | |
|     throws IOException
 | |
|   {
 | |
|     super(url);
 | |
|     requestHeaders = new Headers();
 | |
|     AccessController.doPrivileged(this.new GetHTTPPropertiesAction());
 | |
|   }
 | |
| 
 | |
|   class GetHTTPPropertiesAction
 | |
|     implements PrivilegedAction
 | |
|   {
 | |
| 
 | |
|     public Object run()
 | |
|     {
 | |
|       proxyHostname = System.getProperty("http.proxyHost");
 | |
|       if (proxyHostname != null && proxyHostname.length() > 0)
 | |
|         {
 | |
|           String port = System.getProperty("http.proxyPort");
 | |
|           if (port != null && port.length() > 0)
 | |
|             {
 | |
|               proxyPort = Integer.parseInt(port);
 | |
|             }
 | |
|           else
 | |
|             {
 | |
|               proxyHostname = null;
 | |
|               proxyPort = -1;
 | |
|             }
 | |
|         }
 | |
|       agent = System.getProperty("http.agent");
 | |
|       String ka = System.getProperty("http.keepAlive");
 | |
|       keepAlive = !(ka != null && "false".equals(ka));
 | |
|       String mc = System.getProperty("http.maxConnections");
 | |
|       maxConnections = (mc != null && mc.length() > 0) ?
 | |
|         Math.max(Integer.parseInt(mc), 1) : 5;
 | |
|       return null;
 | |
|     }
 | |
| 
 | |
|   }
 | |
| 
 | |
|   public void connect()
 | |
|     throws IOException
 | |
|   {
 | |
|     if (connected)
 | |
|       {
 | |
|         return;
 | |
|       }
 | |
|     String protocol = url.getProtocol();
 | |
|     boolean secure = "https".equals(protocol);
 | |
|     String host = url.getHost();
 | |
|     int port = url.getPort();
 | |
|     if (port < 0)
 | |
|       {
 | |
|         port = secure ? HTTPConnection.HTTPS_PORT :
 | |
|           HTTPConnection.HTTP_PORT;
 | |
|       }
 | |
|     String file = url.getFile();
 | |
|     String username = url.getUserInfo();
 | |
|     String password = null;
 | |
|     if (username != null)
 | |
|       {
 | |
|         int ci = username.indexOf(':');
 | |
|         if (ci != -1)
 | |
|           {
 | |
|             password = username.substring(ci + 1);
 | |
|             username = username.substring(0, ci);
 | |
|           }
 | |
|       }
 | |
|     final Credentials creds = (username == null) ? null :
 | |
|       new Credentials (username, password);
 | |
|     
 | |
|     boolean retry;
 | |
|     do
 | |
|       {
 | |
|         retry = false;
 | |
|         if (connection == null)
 | |
|           {
 | |
|             connection = getConnection(host, port, secure);
 | |
|             if (secure)
 | |
|               {
 | |
|                 SSLSocketFactory factory = getSSLSocketFactory();
 | |
|                 HostnameVerifier verifier = getHostnameVerifier();
 | |
|                 if (factory != null)
 | |
|                   {
 | |
|                     connection.setSSLSocketFactory(factory);
 | |
|                   }
 | |
|                 connection.addHandshakeCompletedListener(this);
 | |
|                 // TODO verifier
 | |
|               }
 | |
|           }
 | |
|         if (proxyHostname != null)
 | |
|           {
 | |
|             if (proxyPort < 0)
 | |
|               {
 | |
|                 proxyPort = secure ? HTTPConnection.HTTPS_PORT :
 | |
|                   HTTPConnection.HTTP_PORT;
 | |
|               }
 | |
|             connection.setProxy(proxyHostname, proxyPort);
 | |
|           }
 | |
|         request = connection.newRequest(method, file);
 | |
|         if (!keepAlive)
 | |
|           {
 | |
|             request.setHeader("Connection", "close");
 | |
|           }
 | |
|         if (agent != null)
 | |
|           {
 | |
|             request.setHeader("User-Agent", agent);
 | |
|           }
 | |
|         request.getHeaders().putAll(requestHeaders);
 | |
|         if (requestSink != null)
 | |
|           {
 | |
|             byte[] content = requestSink.toByteArray();
 | |
|             RequestBodyWriter writer = new ByteArrayRequestBodyWriter(content);
 | |
|             request.setRequestBodyWriter(writer);
 | |
|           }
 | |
|         ByteArrayResponseBodyReader reader = new ByteArrayResponseBodyReader();
 | |
|         request.setResponseBodyReader(reader);
 | |
|         if (creds != null)
 | |
|           {
 | |
|             request.setAuthenticator(new Authenticator() {
 | |
|               public Credentials getCredentials(String realm, int attempts)
 | |
|               {
 | |
|                 return (attempts < 2) ? creds : null;
 | |
|               }
 | |
|             });
 | |
|           }
 | |
|         response = request.dispatch();
 | |
|         if (response.getCodeClass() == 3 && getInstanceFollowRedirects())
 | |
|           {
 | |
|             // Follow redirect
 | |
|             String location = response.getHeader("Location");
 | |
|             String connectionUri = connection.getURI();
 | |
|             int start = connectionUri.length();
 | |
|             if (location.startsWith(connectionUri) &&
 | |
|                 location.charAt(start) == '/')
 | |
|               {
 | |
|                 file = location.substring(start);
 | |
|                 retry = true;
 | |
|               }
 | |
|             else if (location.startsWith("http:"))
 | |
|               {
 | |
|                 connection.close();
 | |
|                 connection = null;
 | |
|                 secure = false;
 | |
|                 start = 7;
 | |
|                 int end = location.indexOf('/', start);
 | |
|                 host = location.substring(start, end);
 | |
|                 int ci = host.lastIndexOf(':');
 | |
|                 if (ci != -1)
 | |
|                   {
 | |
|                     port = Integer.parseInt(host.substring (ci + 1));
 | |
|                     host = host.substring(0, ci);
 | |
|                   }
 | |
|                 else
 | |
|                   {
 | |
|                     port = HTTPConnection.HTTP_PORT;
 | |
|                   }
 | |
|                 file = location.substring(end);
 | |
|                 retry = true;
 | |
|               }
 | |
|             else if (location.startsWith("https:"))
 | |
|               {
 | |
|                 connection.close();
 | |
|                 connection = null;
 | |
|                 secure = true;
 | |
|                 start = 8;
 | |
|                 int end = location.indexOf('/', start);
 | |
|                 host = location.substring(start, end);
 | |
|                 int ci = host.lastIndexOf(':');
 | |
|                 if (ci != -1)
 | |
|                   {
 | |
|                     port = Integer.parseInt(host.substring (ci + 1));
 | |
|                     host = host.substring(0, ci);
 | |
|                   }
 | |
|                 else
 | |
|                   {
 | |
|                     port = HTTPConnection.HTTPS_PORT;
 | |
|                   }
 | |
|                 file = location.substring(end);
 | |
|                 retry = true;
 | |
|               }
 | |
|             // Otherwise this is not an HTTP redirect, can't follow
 | |
|           }
 | |
|         else
 | |
|           {
 | |
|             responseSink = new ByteArrayInputStream(reader.toByteArray ());
 | |
|           }
 | |
|       }
 | |
|     while (retry);
 | |
|     connected = true;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns a connection, from the pool if necessary.
 | |
|    */
 | |
|   HTTPConnection getConnection(String host, int port, boolean secure)
 | |
|     throws IOException
 | |
|   {
 | |
|     HTTPConnection connection;
 | |
|     if (keepAlive)
 | |
|       {
 | |
|         StringBuffer buf = new StringBuffer(secure ? "https://" : "http://");
 | |
|         buf.append(host);
 | |
|         buf.append(':');
 | |
|         buf.append(port);
 | |
|         String key = buf.toString();
 | |
|         synchronized (connectionPool)
 | |
|           {
 | |
|             connection = (HTTPConnection) connectionPool.get(key);
 | |
|             if (connection == null)
 | |
|               {
 | |
|                 connection = new HTTPConnection(host, port, secure);
 | |
|                 // Good housekeeping
 | |
|                 if (connectionPool.size() == maxConnections)
 | |
|                   {
 | |
|                     // maxConnections must always be >= 1
 | |
|                     Object lru = connectionPool.keySet().iterator().next();
 | |
|                     connectionPool.remove(lru);
 | |
|                   }
 | |
|                 connectionPool.put(key, connection);
 | |
|               }
 | |
|           }
 | |
|       }
 | |
|     else
 | |
|       {
 | |
|         connection = new HTTPConnection(host, port, secure);
 | |
|       }
 | |
|     return connection;
 | |
|   }
 | |
| 
 | |
|   public void disconnect()
 | |
|   {
 | |
|     if (connection != null)
 | |
|       {
 | |
|         try
 | |
|           {
 | |
|             connection.close();
 | |
|           }
 | |
|         catch (IOException e)
 | |
|           {
 | |
|           }
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   public boolean usingProxy()
 | |
|   {
 | |
|     return (proxyHostname != null);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Overrides the corresponding method in HttpURLConnection to permit
 | |
|    * arbitrary methods, as long as they're valid ASCII alphabetic
 | |
|    * characters. This is to permit WebDAV and other HTTP extensions to
 | |
|    * function.
 | |
|    * @param method the method
 | |
|    */
 | |
|   public void setRequestMethod(String method)
 | |
|     throws ProtocolException
 | |
|   {
 | |
|     if (connected)
 | |
|       {
 | |
|         throw new ProtocolException("Already connected");
 | |
|       }
 | |
|     // Validate
 | |
|     method = method.toUpperCase();
 | |
|     int len = method.length();
 | |
|     if (len == 0)
 | |
|       {
 | |
|         throw new ProtocolException("Empty method name");
 | |
|       }
 | |
|     for (int i = 0; i < len; i++)
 | |
|       {
 | |
|         char c = method.charAt(i);
 | |
|         if (c < 0x41 || c > 0x5a)
 | |
|           {
 | |
|             throw new ProtocolException("Illegal character '" + c +
 | |
|                                         "' at index " + i);
 | |
|           }
 | |
|       }
 | |
|     // OK
 | |
|     this.method = method;
 | |
|     requestMethodSetExplicitly = true;
 | |
|   }
 | |
| 
 | |
|   public String getRequestProperty(String key)
 | |
|   {
 | |
|     return requestHeaders.getValue(key);
 | |
|   }
 | |
| 
 | |
|   public Map getRequestProperties()
 | |
|   {
 | |
|     return requestHeaders;
 | |
|   }
 | |
| 
 | |
|   public void setRequestProperty(String key, String value)
 | |
|   {
 | |
|     requestHeaders.put(key, value);
 | |
|   }
 | |
| 
 | |
|   public void addRequestProperty(String key, String value)
 | |
|   {
 | |
|     String old = requestHeaders.getValue(key);
 | |
|     if (old == null)
 | |
|       {
 | |
|         requestHeaders.put(key, value);
 | |
|       }
 | |
|     else
 | |
|       {
 | |
|         requestHeaders.put(key, old + "," + value);
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   public OutputStream getOutputStream()
 | |
|     throws IOException
 | |
|   {
 | |
|     if (connected)
 | |
|       {
 | |
|         throw new ProtocolException("Already connected");
 | |
|       }
 | |
|     if (!doOutput)
 | |
|       {
 | |
|         throw new ProtocolException("doOutput is false");
 | |
|       }
 | |
|     else if (!requestMethodSetExplicitly)
 | |
|       {
 | |
|         /*
 | |
|          * Silently change the method to POST if no method was set
 | |
|          * explicitly. This is due to broken applications depending on this
 | |
|          * behaviour (Apache XMLRPC for one).
 | |
|          */
 | |
|         method = "POST";
 | |
|       }
 | |
|     if (requestSink == null)
 | |
|       {
 | |
|         requestSink = new ByteArrayOutputStream();
 | |
|       }
 | |
|     return requestSink;
 | |
|   }
 | |
|   
 | |
|   // -- Response --
 | |
|   
 | |
|   public InputStream getInputStream()
 | |
|     throws IOException
 | |
|   {
 | |
|     if (!connected)
 | |
|       {
 | |
|         connect();
 | |
|       }
 | |
|     if (!doInput)
 | |
|       {
 | |
|         throw new ProtocolException("doInput is false");
 | |
|       }
 | |
|     return responseSink;
 | |
|   }
 | |
| 
 | |
|   public Map getHeaderFields()
 | |
|   {
 | |
|     if (!connected)
 | |
|       {
 | |
|         try
 | |
|           {
 | |
|             connect();
 | |
|           }
 | |
|         catch (IOException e)
 | |
|           {
 | |
|             return null;
 | |
|           }
 | |
|       }
 | |
|     Map headers = response.getHeaders();
 | |
|     Map ret = new LinkedHashMap();
 | |
|     ret.put("", Collections.singletonList(getStatusLine(response)));
 | |
|     for (Iterator i = headers.entrySet().iterator(); i.hasNext(); )
 | |
|       {
 | |
|         Map.Entry entry = (Map.Entry) i.next();
 | |
|         String key = (String) entry.getKey();
 | |
|         String value = (String) entry.getValue();
 | |
|         ret.put(key, Collections.singletonList(value));
 | |
|       }
 | |
|     return ret;
 | |
|   }
 | |
| 
 | |
|   String getStatusLine(Response response)
 | |
|   {
 | |
|     return "HTTP/" + response.getMajorVersion() +
 | |
|       "." + response.getMinorVersion() +
 | |
|       " " + response.getCode() +
 | |
|       " " + response.getMessage();
 | |
|   }
 | |
|   
 | |
|   public String getHeaderField(int index)
 | |
|   {
 | |
|     if (!connected)
 | |
|       {
 | |
|         try
 | |
|           {
 | |
|             connect();
 | |
|           }
 | |
|         catch (IOException e)
 | |
|           {
 | |
|             return null;
 | |
|           }
 | |
|       }
 | |
|     if (index == 0)
 | |
|       {
 | |
|         return getStatusLine(response);
 | |
|       }
 | |
|     Iterator i = response.getHeaders().entrySet().iterator();
 | |
|     Map.Entry entry;
 | |
|     int count = 1;
 | |
|     do
 | |
|       {
 | |
|         if (!i.hasNext())
 | |
|           {
 | |
|             return null;
 | |
|           }
 | |
|         entry = (Map.Entry) i.next();
 | |
|         count++;
 | |
|       }
 | |
|     while (count <= index);
 | |
|     return (String) entry.getValue();
 | |
|   }
 | |
| 
 | |
|   public String getHeaderFieldKey(int index)
 | |
|   {
 | |
|     if (!connected)
 | |
|       {
 | |
|         try
 | |
|           {
 | |
|             connect();
 | |
|           }
 | |
|         catch (IOException e)
 | |
|           {
 | |
|             return null;
 | |
|           }
 | |
|       }
 | |
|     if (index == 0)
 | |
|       {
 | |
|         return null;
 | |
|       }
 | |
|     Iterator i = response.getHeaders().entrySet().iterator();
 | |
|     Map.Entry entry;
 | |
|     int count = 1;
 | |
|     do
 | |
|       {
 | |
|         entry = (Map.Entry) i.next();
 | |
|         count++;
 | |
|       }
 | |
|     while (count <= index);
 | |
|     return (String) entry.getKey();
 | |
|   }
 | |
| 
 | |
|   public String getHeaderField(String name)
 | |
|   {
 | |
|     if (!connected)
 | |
|       {
 | |
|         try
 | |
|           {
 | |
|             connect();
 | |
|           }
 | |
|         catch (IOException e)
 | |
|           {
 | |
|             return null;
 | |
|           }
 | |
|       }
 | |
|     return (String) response.getHeader(name);
 | |
|   }
 | |
| 
 | |
|   public long getHeaderFieldDate(String name, long def)
 | |
|   {
 | |
|     if (!connected)
 | |
|       {
 | |
|         try
 | |
|           {
 | |
|             connect();
 | |
|           }
 | |
|         catch (IOException e)
 | |
|           {
 | |
|             return def;
 | |
|           }
 | |
|       }
 | |
|     Date date = response.getDateHeader(name);
 | |
|     return (date == null) ? def : date.getTime();
 | |
|   }
 | |
| 
 | |
|   public String getContentType()
 | |
|   {
 | |
|     return getHeaderField("Content-Type");
 | |
|   }
 | |
| 
 | |
|   public int getResponseCode()
 | |
|     throws IOException
 | |
|   {
 | |
|     if (!connected)
 | |
|       {
 | |
|         connect();
 | |
|       }
 | |
|     return response.getCode();
 | |
|   }
 | |
| 
 | |
|   public String getResponseMessage()
 | |
|     throws IOException
 | |
|   {
 | |
|     if (!connected)
 | |
|       {
 | |
|         connect();
 | |
|       }
 | |
|     return response.getMessage();
 | |
|   }
 | |
| 
 | |
|   // -- HTTPS specific --
 | |
| 
 | |
|   public String getCipherSuite()
 | |
|   {
 | |
|     if (!connected)
 | |
|       {
 | |
|         throw new IllegalStateException("not connected");
 | |
|       }
 | |
|     return handshakeEvent.getCipherSuite();
 | |
|   }
 | |
|   
 | |
|   public Certificate[] getLocalCertificates()
 | |
|   {
 | |
|     if (!connected)
 | |
|       {
 | |
|         throw new IllegalStateException("not connected");
 | |
|       }
 | |
|     return handshakeEvent.getLocalCertificates();
 | |
|   }
 | |
| 
 | |
|   public Certificate[] getServerCertificates()
 | |
|     throws SSLPeerUnverifiedException
 | |
|   {
 | |
|     if (!connected)
 | |
|       {
 | |
|         throw new IllegalStateException("not connected");
 | |
|       }
 | |
|     return handshakeEvent.getPeerCertificates();
 | |
|   }
 | |
| 
 | |
|   // HandshakeCompletedListener
 | |
| 
 | |
|   public void handshakeCompleted(HandshakeCompletedEvent event)
 | |
|   {
 | |
|     handshakeEvent = event;
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 |