mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			915 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			915 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* Request.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 gnu.java.net.protocol.http.event.RequestEvent;
 | |
| import gnu.java.net.BASE64;
 | |
| import gnu.java.net.LineInputStream;
 | |
| 
 | |
| import java.io.InputStream;
 | |
| import java.io.IOException;
 | |
| import java.io.OutputStream;
 | |
| import java.net.ProtocolException;
 | |
| import java.security.MessageDigest;
 | |
| import java.security.NoSuchAlgorithmException;
 | |
| import java.text.DateFormat;
 | |
| import java.text.ParseException;
 | |
| import java.util.Calendar;
 | |
| import java.util.Date;
 | |
| import java.util.HashMap;
 | |
| import java.util.Iterator;
 | |
| import java.util.Map;
 | |
| import java.util.Properties;
 | |
| import java.util.zip.GZIPInputStream;
 | |
| import java.util.zip.InflaterInputStream;
 | |
| 
 | |
| /**
 | |
|  * A single HTTP request.
 | |
|  *
 | |
|  * @author Chris Burdess (dog@gnu.org)
 | |
|  */
 | |
| public class Request
 | |
| {
 | |
| 
 | |
|   /**
 | |
|    * The connection context in which this request is invoked.
 | |
|    */
 | |
|   protected final HTTPConnection connection;
 | |
| 
 | |
|   /**
 | |
|    * The HTTP method to invoke.
 | |
|    */
 | |
|   protected final String method;
 | |
| 
 | |
|   /**
 | |
|    * The path identifying the resource.
 | |
|    * This string must conform to the abs_path definition given in RFC2396,
 | |
|    * with an optional "?query" part, and must be URI-escaped by the caller.
 | |
|    */
 | |
|   protected final String path;
 | |
| 
 | |
|   /**
 | |
|    * The headers in this request.
 | |
|    */
 | |
|   protected final Headers requestHeaders;
 | |
| 
 | |
|   /**
 | |
|    * The request body provider.
 | |
|    */
 | |
|   protected RequestBodyWriter requestBodyWriter;
 | |
| 
 | |
|   /**
 | |
|    * Request body negotiation threshold for 100-continue expectations.
 | |
|    */
 | |
|   protected int requestBodyNegotiationThreshold;
 | |
| 
 | |
|   /**
 | |
|    * The response body reader.
 | |
|    */
 | |
|   protected ResponseBodyReader responseBodyReader;
 | |
| 
 | |
|   /**
 | |
|    * Map of response header handlers.
 | |
|    */
 | |
|   protected Map responseHeaderHandlers;
 | |
| 
 | |
|   /**
 | |
|    * The authenticator.
 | |
|    */
 | |
|   protected Authenticator authenticator;
 | |
| 
 | |
|   /**
 | |
|    * Whether this request has been dispatched yet.
 | |
|    */
 | |
|   private boolean dispatched;
 | |
| 
 | |
|   /**
 | |
|    * Constructor for a new request.
 | |
|    * @param connection the connection context
 | |
|    * @param method the HTTP method
 | |
|    * @param path the resource path including query part
 | |
|    */
 | |
|   protected Request(HTTPConnection connection, String method,
 | |
|                     String path)
 | |
|   {
 | |
|     this.connection = connection;
 | |
|     this.method = method;
 | |
|     this.path = path;
 | |
|     requestHeaders = new Headers();
 | |
|     responseHeaderHandlers = new HashMap();
 | |
|     requestBodyNegotiationThreshold = 4096;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the connection associated with this request.
 | |
|    * @see #connection
 | |
|    */
 | |
|   public HTTPConnection getConnection()
 | |
|   {
 | |
|     return connection;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the HTTP method to invoke.
 | |
|    * @see #method
 | |
|    */
 | |
|   public String getMethod()
 | |
|   {
 | |
|     return method;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the resource path.
 | |
|    * @see #path
 | |
|    */
 | |
|   public String getPath()
 | |
|   {
 | |
|     return path;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the full request-URI represented by this request, as specified
 | |
|    * by HTTP/1.1.
 | |
|    */
 | |
|   public String getRequestURI()
 | |
|   {
 | |
|     return connection.getURI() + path;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the headers in this request.
 | |
|    */
 | |
|   public Headers getHeaders()
 | |
|   {
 | |
|     return requestHeaders;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the value of the specified header in this request.
 | |
|    * @param name the header name
 | |
|    */
 | |
|   public String getHeader(String name)
 | |
|   {
 | |
|     return requestHeaders.getValue(name);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the value of the specified header in this request as an integer.
 | |
|    * @param name the header name
 | |
|    */
 | |
|   public int getIntHeader(String name)
 | |
|   {
 | |
|     return requestHeaders.getIntValue(name);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the value of the specified header in this request as a date.
 | |
|    * @param name the header name
 | |
|    */
 | |
|   public Date getDateHeader(String name)
 | |
|   {
 | |
|     return requestHeaders.getDateValue(name);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Sets the specified header in this request.
 | |
|    * @param name the header name
 | |
|    * @param value the header value
 | |
|    */
 | |
|   public void setHeader(String name, String value)
 | |
|   {
 | |
|     requestHeaders.put(name, value);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Convenience method to set the entire request body.
 | |
|    * @param requestBody the request body content
 | |
|    */
 | |
|   public void setRequestBody(byte[] requestBody)
 | |
|   {
 | |
|     setRequestBodyWriter(new ByteArrayRequestBodyWriter(requestBody));
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Sets the request body provider.
 | |
|    * @param requestBodyWriter the handler used to obtain the request body
 | |
|    */
 | |
|   public void setRequestBodyWriter(RequestBodyWriter requestBodyWriter)
 | |
|   {
 | |
|     this.requestBodyWriter = requestBodyWriter;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Sets the response body reader.
 | |
|    * @param responseBodyReader the handler to receive notifications of
 | |
|    * response body content
 | |
|    */
 | |
|   public void setResponseBodyReader(ResponseBodyReader responseBodyReader)
 | |
|   {
 | |
|     this.responseBodyReader = responseBodyReader;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Sets a callback handler to be invoked for the specified header name.
 | |
|    * @param name the header name
 | |
|    * @param handler the handler to receive the value for the header
 | |
|    */
 | |
|   public void setResponseHeaderHandler(String name,
 | |
|                                        ResponseHeaderHandler handler)
 | |
|   {
 | |
|     responseHeaderHandlers.put(name, handler);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Sets an authenticator that can be used to handle authentication
 | |
|    * automatically.
 | |
|    * @param authenticator the authenticator
 | |
|    */
 | |
|   public void setAuthenticator(Authenticator authenticator)
 | |
|   {
 | |
|     this.authenticator = authenticator;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Sets the request body negotiation threshold.
 | |
|    * If this is set, it determines the maximum size that the request body
 | |
|    * may be before body negotiation occurs(via the
 | |
|    * <code>100-continue</code> expectation). This ensures that a large
 | |
|    * request body is not sent when the server wouldn't have accepted it
 | |
|    * anyway.
 | |
|    * @param threshold the body negotiation threshold, or <=0 to disable
 | |
|    * request body negotation entirely
 | |
|    */
 | |
|   public void setRequestBodyNegotiationThreshold(int threshold)
 | |
|   {
 | |
|     requestBodyNegotiationThreshold = threshold;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Dispatches this request.
 | |
|    * A request can only be dispatched once; calling this method a second
 | |
|    * time results in a protocol exception.
 | |
|    * @exception IOException if an I/O error occurred
 | |
|    * @return an HTTP response object representing the result of the operation
 | |
|    */
 | |
|   public Response dispatch()
 | |
|     throws IOException
 | |
|   {
 | |
|     if (dispatched)
 | |
|       {
 | |
|         throw new ProtocolException("request already dispatched");
 | |
|       }
 | |
|     final String CRLF = "\r\n";
 | |
|     final String HEADER_SEP = ": ";
 | |
|     final String US_ASCII = "US-ASCII";
 | |
|     final String version = connection.getVersion();
 | |
|     Response response;
 | |
|     int contentLength = -1;
 | |
|     boolean retry = false;
 | |
|     int attempts = 0;
 | |
|     boolean expectingContinue = false;
 | |
|     if (requestBodyWriter != null)
 | |
|       {
 | |
|         contentLength = requestBodyWriter.getContentLength();
 | |
|         if (contentLength > requestBodyNegotiationThreshold)
 | |
|           {
 | |
|             expectingContinue = true;
 | |
|             setHeader("Expect", "100-continue");
 | |
|           }
 | |
|         else
 | |
|           {
 | |
|             setHeader("Content-Length", Integer.toString(contentLength));
 | |
|           }
 | |
|       }
 | |
|     
 | |
|     try
 | |
|       {
 | |
|         // Loop while authentication fails or continue
 | |
|         do
 | |
|           {
 | |
|             retry = false;
 | |
|             // Send request
 | |
|             connection.fireRequestEvent(RequestEvent.REQUEST_SENDING, this);
 | |
|             
 | |
|             // Get socket output and input streams
 | |
|             OutputStream out = connection.getOutputStream();
 | |
|             LineInputStream in =
 | |
|               new LineInputStream(connection.getInputStream());
 | |
|             // Request line
 | |
|             String requestUri = path;
 | |
|             if (connection.isUsingProxy() &&
 | |
|                 !"*".equals(requestUri) &&
 | |
|                 !"CONNECT".equals(method))
 | |
|               {
 | |
|                 requestUri = getRequestURI();
 | |
|               }
 | |
|             String line = method + ' ' + requestUri + ' ' + version + CRLF;
 | |
|             out.write(line.getBytes(US_ASCII));
 | |
|             // Request headers
 | |
|             for (Iterator i = requestHeaders.keySet().iterator();
 | |
|                  i.hasNext(); )
 | |
|               {
 | |
|                 String name =(String) i.next();
 | |
|                 String value =(String) requestHeaders.get(name);
 | |
|                 line = name + HEADER_SEP + value + CRLF;
 | |
|                 out.write(line.getBytes(US_ASCII));
 | |
|               }
 | |
|             out.write(CRLF.getBytes(US_ASCII));
 | |
|             // Request body
 | |
|             if (requestBodyWriter != null && !expectingContinue)
 | |
|               {
 | |
|                 byte[] buffer = new byte[4096];
 | |
|                 int len;
 | |
|                 int count = 0;
 | |
|                 
 | |
|                 requestBodyWriter.reset();
 | |
|                 do
 | |
|                   {
 | |
|                     len = requestBodyWriter.write(buffer);
 | |
|                     if (len > 0)
 | |
|                       {
 | |
|                         out.write(buffer, 0, len);
 | |
|                       }
 | |
|                     count += len;
 | |
|                   }
 | |
|                 while (len > -1 && count < contentLength);
 | |
|                 out.write(CRLF.getBytes(US_ASCII));
 | |
|               }
 | |
|             out.flush();
 | |
|             // Sent event
 | |
|             connection.fireRequestEvent(RequestEvent.REQUEST_SENT, this);
 | |
|             // Get response
 | |
|             response = readResponse(in);
 | |
|             int sc = response.getCode();
 | |
|             if (sc == 401 && authenticator != null)
 | |
|               {
 | |
|                 if (authenticate(response, attempts++))
 | |
|                   {
 | |
|                     retry = true;
 | |
|                   }
 | |
|               }
 | |
|             else if (sc == 100 && expectingContinue)
 | |
|               {
 | |
|                 requestHeaders.remove("Expect");
 | |
|                 setHeader("Content-Length", Integer.toString(contentLength));
 | |
|                 expectingContinue = false;
 | |
|                 retry = true;
 | |
|               }
 | |
|           }
 | |
|         while (retry);
 | |
|       }
 | |
|     catch (IOException e)
 | |
|       {
 | |
|         connection.close();
 | |
|         throw e;
 | |
|       }
 | |
|     return response;
 | |
|   }
 | |
|     
 | |
|   Response readResponse(LineInputStream in)
 | |
|     throws IOException
 | |
|   {
 | |
|     String line;
 | |
|     int len;
 | |
|     
 | |
|     // Read response status line
 | |
|     line = in.readLine();
 | |
|     if (line == null)
 | |
|       {
 | |
|         throw new ProtocolException("Peer closed connection");
 | |
|       }
 | |
|     if (!line.startsWith("HTTP/"))
 | |
|       {
 | |
|         throw new ProtocolException(line);
 | |
|       }
 | |
|     len = line.length();
 | |
|     int start = 5, end = 6;
 | |
|     while (line.charAt(end) != '.')
 | |
|       {
 | |
|         end++;
 | |
|       }
 | |
|     int majorVersion = Integer.parseInt(line.substring(start, end));
 | |
|     start = end + 1;
 | |
|     end = start + 1;
 | |
|     while (line.charAt(end) != ' ')
 | |
|       {
 | |
|         end++;
 | |
|       }
 | |
|     int minorVersion = Integer.parseInt(line.substring(start, end));
 | |
|     start = end + 1;
 | |
|     end = start + 3;
 | |
|     int code = Integer.parseInt(line.substring(start, end));
 | |
|     String message = line.substring(end + 1, len - 1);
 | |
|     // Read response headers
 | |
|     Headers responseHeaders = new Headers();
 | |
|     responseHeaders.parse(in);
 | |
|     notifyHeaderHandlers(responseHeaders);
 | |
|     // Construct response
 | |
|     int codeClass = code / 100;
 | |
|     Response ret = new Response(majorVersion, minorVersion, code,
 | |
|                                 codeClass, message, responseHeaders);
 | |
|     switch (code)
 | |
|       {
 | |
|       case 204:
 | |
|       case 205:
 | |
|         break;
 | |
|       default:
 | |
|         // Does response body reader want body?
 | |
|         boolean notify = (responseBodyReader != null);
 | |
|         if (notify)
 | |
|           {
 | |
|             if (!responseBodyReader.accept(this, ret))
 | |
|               {
 | |
|                 notify = false;
 | |
|               }
 | |
|           }
 | |
|         readResponseBody(ret, in, notify);
 | |
|       }
 | |
|     return ret;
 | |
|   }
 | |
| 
 | |
|   void notifyHeaderHandlers(Headers headers)
 | |
|   {
 | |
|     for (Iterator i = headers.entrySet().iterator(); i.hasNext(); )
 | |
|       {
 | |
|         Map.Entry entry = (Map.Entry) i.next();
 | |
|         String name =(String) entry.getKey();
 | |
|         // Handle Set-Cookie
 | |
|         if ("Set-Cookie".equalsIgnoreCase(name))
 | |
|           {
 | |
|             String value = (String) entry.getValue();
 | |
|             handleSetCookie(value);
 | |
|           }
 | |
|         ResponseHeaderHandler handler =
 | |
|           (ResponseHeaderHandler) responseHeaderHandlers.get(name);
 | |
|         if (handler != null)
 | |
|           {
 | |
|             String value = (String) entry.getValue();
 | |
|             handler.setValue(value);
 | |
|           }
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   void readResponseBody(Response response, InputStream in,
 | |
|                         boolean notify)
 | |
|     throws IOException
 | |
|   {
 | |
|     byte[] buffer = new byte[4096];
 | |
|     int contentLength = -1;
 | |
|     Headers trailer = null;
 | |
|     
 | |
|     String transferCoding = response.getHeader("Transfer-Encoding");
 | |
|     if ("chunked".equalsIgnoreCase(transferCoding))
 | |
|       {
 | |
|         trailer = new Headers();
 | |
|         in = new ChunkedInputStream(in, trailer);
 | |
|       } 
 | |
|     else
 | |
|       {
 | |
|         contentLength = response.getIntHeader("Content-Length");
 | |
|       }
 | |
|     String contentCoding = response.getHeader("Content-Encoding");
 | |
|     if (contentCoding != null && !"identity".equals(contentCoding))
 | |
|       {
 | |
|         if ("gzip".equals(contentCoding))
 | |
|           {
 | |
|             in = new GZIPInputStream(in);
 | |
|           }
 | |
|         else if ("deflate".equals(contentCoding))
 | |
|           {
 | |
|             in = new InflaterInputStream(in);
 | |
|           }
 | |
|         else
 | |
|           {
 | |
|             throw new ProtocolException("Unsupported Content-Encoding: " +
 | |
|                                         contentCoding);
 | |
|           }
 | |
|       }
 | |
|     
 | |
|     // Persistent connections are the default in HTTP/1.1
 | |
|     boolean doClose = "close".equalsIgnoreCase(getHeader("Connection")) ||
 | |
|       "close".equalsIgnoreCase(response.getHeader("Connection")) ||
 | |
|       (connection.majorVersion == 1 && connection.minorVersion == 0) ||
 | |
|       (response.majorVersion == 1 && response.minorVersion == 0);
 | |
|     
 | |
|     int count = contentLength;
 | |
|     int len = (count > -1) ? count : buffer.length;
 | |
|     len = (len > buffer.length) ? buffer.length : len;
 | |
|     while (len > -1)
 | |
|       {
 | |
|         len = in.read(buffer, 0, len);
 | |
|         if (len < 0)
 | |
|           {
 | |
|             // EOF
 | |
|             connection.closeConnection();
 | |
|             break;
 | |
|           }
 | |
|         if (notify)
 | |
|           {
 | |
|             responseBodyReader.read(buffer, 0, len);
 | |
|           }
 | |
|         if (count > -1)
 | |
|           {
 | |
|             count -= len;
 | |
|             if (count < 1)
 | |
|               {
 | |
|                 if (doClose)
 | |
|                   {
 | |
|                     connection.closeConnection();
 | |
|                   }
 | |
|                 break;
 | |
|               }
 | |
|           }
 | |
|       }
 | |
|     if (notify)
 | |
|       {
 | |
|         responseBodyReader.close();
 | |
|       }
 | |
|     if (trailer != null)
 | |
|       {
 | |
|         response.getHeaders().putAll(trailer);
 | |
|         notifyHeaderHandlers(trailer);
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   boolean authenticate(Response response, int attempts)
 | |
|     throws IOException
 | |
|   {
 | |
|     String challenge = response.getHeader("WWW-Authenticate");
 | |
|     if (challenge == null)
 | |
|       {
 | |
|         challenge = response.getHeader("Proxy-Authenticate");
 | |
|       }
 | |
|     int si = challenge.indexOf(' ');
 | |
|     String scheme = (si == -1) ? challenge : challenge.substring(0, si);
 | |
|     if ("Basic".equalsIgnoreCase(scheme))
 | |
|       {
 | |
|         Properties params = parseAuthParams(challenge.substring(si + 1));
 | |
|         String realm = params.getProperty("realm");
 | |
|         Credentials creds = authenticator.getCredentials(realm, attempts);
 | |
|         String userPass = creds.getUsername() + ':' + creds.getPassword();
 | |
|         byte[] b_userPass = userPass.getBytes("US-ASCII");
 | |
|         byte[] b_encoded = BASE64.encode(b_userPass);
 | |
|         String authorization =
 | |
|           scheme + " " + new String(b_encoded, "US-ASCII");
 | |
|         setHeader("Authorization", authorization);
 | |
|         return true;
 | |
|       }
 | |
|     else if ("Digest".equalsIgnoreCase(scheme))
 | |
|       {
 | |
|         Properties params = parseAuthParams(challenge.substring(si + 1));
 | |
|         String realm = params.getProperty("realm");
 | |
|         String nonce = params.getProperty("nonce");
 | |
|         String qop = params.getProperty("qop");
 | |
|         String algorithm = params.getProperty("algorithm");
 | |
|         String digestUri = getRequestURI();
 | |
|         Credentials creds = authenticator.getCredentials(realm, attempts);
 | |
|         String username = creds.getUsername();
 | |
|         String password = creds.getPassword();
 | |
|         connection.incrementNonce(nonce);
 | |
|         try
 | |
|           {
 | |
|             MessageDigest md5 = MessageDigest.getInstance("MD5");
 | |
|             final byte[] COLON = { 0x3a };
 | |
|             
 | |
|             // Calculate H(A1)
 | |
|             md5.reset();
 | |
|             md5.update(username.getBytes("US-ASCII"));
 | |
|             md5.update(COLON);
 | |
|             md5.update(realm.getBytes("US-ASCII"));
 | |
|             md5.update(COLON);
 | |
|             md5.update(password.getBytes("US-ASCII"));
 | |
|             byte[] ha1 = md5.digest();
 | |
|             if ("md5-sess".equals(algorithm))
 | |
|               {
 | |
|                 byte[] cnonce = generateNonce();
 | |
|                 md5.reset();
 | |
|                 md5.update(ha1);
 | |
|                 md5.update(COLON);
 | |
|                 md5.update(nonce.getBytes("US-ASCII"));
 | |
|                 md5.update(COLON);
 | |
|                 md5.update(cnonce);
 | |
|                 ha1 = md5.digest();
 | |
|               }
 | |
|             String ha1Hex = toHexString(ha1);
 | |
|             
 | |
|             // Calculate H(A2)
 | |
|             md5.reset();
 | |
|             md5.update(method.getBytes("US-ASCII"));
 | |
|             md5.update(COLON);
 | |
|             md5.update(digestUri.getBytes("US-ASCII"));
 | |
|             if ("auth-int".equals(qop))
 | |
|               {
 | |
|                 byte[] hEntity = null; // TODO hash of entity body
 | |
|                 md5.update(COLON);
 | |
|                 md5.update(hEntity);
 | |
|               }
 | |
|             byte[] ha2 = md5.digest();
 | |
|             String ha2Hex = toHexString(ha2);
 | |
|             
 | |
|             // Calculate response
 | |
|             md5.reset();
 | |
|             md5.update(ha1Hex.getBytes("US-ASCII"));
 | |
|             md5.update(COLON);
 | |
|             md5.update(nonce.getBytes("US-ASCII"));
 | |
|             if ("auth".equals(qop) || "auth-int".equals(qop))
 | |
|               {
 | |
|                 String nc = getNonceCount(nonce);
 | |
|                 byte[] cnonce = generateNonce();
 | |
|                 md5.update(COLON);
 | |
|                 md5.update(nc.getBytes("US-ASCII"));
 | |
|                 md5.update(COLON);
 | |
|                 md5.update(cnonce);
 | |
|                 md5.update(COLON);
 | |
|                 md5.update(qop.getBytes("US-ASCII"));
 | |
|               }
 | |
|             md5.update(COLON);
 | |
|             md5.update(ha2Hex.getBytes("US-ASCII"));
 | |
|             String digestResponse = toHexString(md5.digest());
 | |
|             
 | |
|             String authorization = scheme + 
 | |
|               " username=\"" + username + "\"" +
 | |
|               " realm=\"" + realm + "\"" +
 | |
|               " nonce=\"" + nonce + "\"" +
 | |
|               " uri=\"" + digestUri + "\"" +
 | |
|               " response=\"" + digestResponse + "\"";
 | |
|             setHeader("Authorization", authorization);
 | |
|             return true;
 | |
|           }
 | |
|         catch (NoSuchAlgorithmException e)
 | |
|           {
 | |
|             return false;
 | |
|           }
 | |
|       }
 | |
|     // Scheme not recognised
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   Properties parseAuthParams(String text)
 | |
|   {
 | |
|     int len = text.length();
 | |
|     String key = null;
 | |
|     StringBuffer buf = new StringBuffer();
 | |
|     Properties ret = new Properties();
 | |
|     boolean inQuote = false;
 | |
|     for (int i = 0; i < len; i++)
 | |
|       {
 | |
|         char c = text.charAt(i);
 | |
|         if (c == '"')
 | |
|           {
 | |
|             inQuote = !inQuote;
 | |
|           }
 | |
|         else if (c == '=' && key == null)
 | |
|           {
 | |
|             key = buf.toString().trim();
 | |
|             buf.setLength(0);
 | |
|           }
 | |
|         else if (c == ' ' && !inQuote)
 | |
|           {
 | |
|             String value = unquote(buf.toString().trim());
 | |
|             ret.put(key, value);
 | |
|             key = null;
 | |
|             buf.setLength(0);
 | |
|           }
 | |
|         else if (c != ',' || (i <(len - 1) && text.charAt(i + 1) != ' '))
 | |
|           {   
 | |
|             buf.append(c);
 | |
|           }
 | |
|       }
 | |
|     if (key != null)
 | |
|       {
 | |
|         String value = unquote(buf.toString().trim());
 | |
|         ret.put(key, value);
 | |
|       }
 | |
|     return ret;
 | |
|   }
 | |
| 
 | |
|   String unquote(String text)
 | |
|   {
 | |
|     int len = text.length();
 | |
|     if (len > 0 && text.charAt(0) == '"' && text.charAt(len - 1) == '"')
 | |
|       {
 | |
|         return text.substring(1, len - 1);
 | |
|       }
 | |
|     return text;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the number of times the specified nonce value has been seen.
 | |
|    * This always returns an 8-byte 0-padded hexadecimal string.
 | |
|    */
 | |
|   String getNonceCount(String nonce)
 | |
|   {
 | |
|     int nc = connection.getNonceCount(nonce);
 | |
|     String hex = Integer.toHexString(nc);
 | |
|     StringBuffer buf = new StringBuffer();
 | |
|     for (int i = 8 - hex.length(); i > 0; i--)
 | |
|       {
 | |
|         buf.append('0');
 | |
|       }
 | |
|     buf.append(hex);
 | |
|     return buf.toString();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Client nonce value.
 | |
|    */
 | |
|   byte[] nonce;
 | |
| 
 | |
|   /**
 | |
|    * Generates a new client nonce value.
 | |
|    */
 | |
|   byte[] generateNonce()
 | |
|     throws IOException, NoSuchAlgorithmException
 | |
|   {
 | |
|     if (nonce == null)
 | |
|       {
 | |
|         long time = System.currentTimeMillis();
 | |
|         MessageDigest md5 = MessageDigest.getInstance("MD5");
 | |
|         md5.update(Long.toString(time).getBytes("US-ASCII"));
 | |
|         nonce = md5.digest();
 | |
|       }
 | |
|     return nonce;
 | |
|   }
 | |
| 
 | |
|   String toHexString(byte[] bytes)
 | |
|   {
 | |
|     char[] ret = new char[bytes.length * 2];
 | |
|     for (int i = 0, j = 0; i < bytes.length; i++)
 | |
|       {
 | |
|         int c =(int) bytes[i];
 | |
|         if (c < 0)
 | |
|           {
 | |
|             c += 0x100;
 | |
|           }
 | |
|         ret[j++] = Character.forDigit(c / 0x10, 0x10);
 | |
|         ret[j++] = Character.forDigit(c % 0x10, 0x10);
 | |
|       }
 | |
|     return new String(ret);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Parse the specified cookie list and notify the cookie manager.
 | |
|    */
 | |
|   void handleSetCookie(String text)
 | |
|   {
 | |
|     CookieManager cookieManager = connection.getCookieManager();
 | |
|     if (cookieManager == null)
 | |
|       {
 | |
|         return;
 | |
|       }
 | |
|     String name = null;
 | |
|     String value = null;
 | |
|     String comment = null;
 | |
|     String domain = connection.getHostName();
 | |
|     String path = this.path;
 | |
|     int lsi = path.lastIndexOf('/');
 | |
|     if (lsi != -1)
 | |
|       {
 | |
|         path = path.substring(0, lsi);
 | |
|       }
 | |
|     boolean secure = false;
 | |
|     Date expires = null;
 | |
| 
 | |
|     int len = text.length();
 | |
|     String attr = null;
 | |
|     StringBuffer buf = new StringBuffer();
 | |
|     boolean inQuote = false;
 | |
|     for (int i = 0; i <= len; i++)
 | |
|       {
 | |
|         char c =(i == len) ? '\u0000' : text.charAt(i);
 | |
|         if (c == '"')
 | |
|           {
 | |
|             inQuote = !inQuote;
 | |
|           }
 | |
|         else if (!inQuote)
 | |
|           {
 | |
|             if (c == '=' && attr == null)
 | |
|               {
 | |
|                 attr = buf.toString().trim();
 | |
|                 buf.setLength(0);
 | |
|               }
 | |
|             else if (c == ';' || i == len || c == ',')
 | |
|               {
 | |
|                 String val = unquote(buf.toString().trim());
 | |
|                 if (name == null)
 | |
|                   {
 | |
|                     name = attr;
 | |
|                     value = val;
 | |
|                   }
 | |
|                 else if ("Comment".equalsIgnoreCase(attr))
 | |
|                   {
 | |
|                     comment = val;
 | |
|                   }
 | |
|                 else if ("Domain".equalsIgnoreCase(attr))
 | |
|                   {
 | |
|                     domain = val;
 | |
|                   }
 | |
|                 else if ("Path".equalsIgnoreCase(attr))
 | |
|                   {
 | |
|                     path = val;
 | |
|                   }
 | |
|                 else if ("Secure".equalsIgnoreCase(val))
 | |
|                   {
 | |
|                     secure = true;
 | |
|                   }
 | |
|                 else if ("Max-Age".equalsIgnoreCase(attr))
 | |
|                   {
 | |
|                     int delta = Integer.parseInt(val);
 | |
|                     Calendar cal = Calendar.getInstance();
 | |
|                     cal.setTimeInMillis(System.currentTimeMillis());
 | |
|                     cal.add(Calendar.SECOND, delta);
 | |
|                     expires = cal.getTime();
 | |
|                   }
 | |
|                 else if ("Expires".equalsIgnoreCase(attr))
 | |
|                   {
 | |
|                     DateFormat dateFormat = new HTTPDateFormat();
 | |
|                     try
 | |
|                       {
 | |
|                         expires = dateFormat.parse(val);
 | |
|                       }
 | |
|                     catch (ParseException e)
 | |
|                       {
 | |
|                         // if this isn't a valid date, it may be that
 | |
|                         // the value was returned unquoted; in that case, we
 | |
|                         // want to continue buffering the value
 | |
|                         buf.append(c);
 | |
|                         continue;
 | |
|                       }
 | |
|                   }
 | |
|                 attr = null;
 | |
|                 buf.setLength(0);
 | |
|                 // case EOL
 | |
|                 if (i == len || c == ',')
 | |
|                   {
 | |
|                     Cookie cookie = new Cookie(name, value, comment, domain,
 | |
|                                                path, secure, expires);
 | |
|                     cookieManager.setCookie(cookie);
 | |
|                   }
 | |
|                 if (c == ',')
 | |
|                   {
 | |
|                     // Reset cookie fields
 | |
|                     name = null;
 | |
|                     value = null;
 | |
|                     comment = null;
 | |
|                     domain = connection.getHostName();
 | |
|                     path = this.path;
 | |
|                     if (lsi != -1)
 | |
|                       {
 | |
|                         path = path.substring(0, lsi);
 | |
|                       }
 | |
|                     secure = false;
 | |
|                     expires = null;
 | |
|                   }
 | |
|               }
 | |
|             else
 | |
|               {
 | |
|                 buf.append(c);
 | |
|               }
 | |
|           }
 | |
|         else
 | |
|           {
 | |
|             buf.append(c);
 | |
|           }
 | |
|       }
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 |