1a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath/* 2a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Copyright (C) 2006 The Android Open Source Project 3a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * 4a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Licensed under the Apache License, Version 2.0 (the "License"); 5a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * you may not use this file except in compliance with the License. 6a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * You may obtain a copy of the License at 7a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * 8a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * http://www.apache.org/licenses/LICENSE-2.0 9a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * 10a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Unless required by applicable law or agreed to in writing, software 11a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * distributed under the License is distributed on an "AS IS" BASIS, 12a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * See the License for the specific language governing permissions and 14a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * limitations under the License. 15a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 16a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 17a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathpackage android.net.http; 18a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 193e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamathimport android.net.compatibility.WebAddress; 20a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport android.webkit.CookieManager; 21a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 22a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport org.apache.commons.codec.binary.Base64; 23a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 24a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport java.io.InputStream; 25a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport java.lang.Math; 26a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport java.security.MessageDigest; 27a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport java.security.NoSuchAlgorithmException; 28a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport java.util.HashMap; 29a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport java.util.Map; 30a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport java.util.Random; 31a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 32a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath/** 33a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * RequestHandle: handles a request session that may include multiple 34a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * redirects, HTTP authentication requests, etc. 35a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 36a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathpublic class RequestHandle { 37a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 38a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private String mUrl; 39a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private WebAddress mUri; 40a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private String mMethod; 41a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private Map<String, String> mHeaders; 42a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private RequestQueue mRequestQueue; 43a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private Request mRequest; 44a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private InputStream mBodyProvider; 45a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private int mBodyLength; 46a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private int mRedirectCount = 0; 47a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // Used only with synchronous requests. 48a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private Connection mConnection; 49a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 50a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private final static String AUTHORIZATION_HEADER = "Authorization"; 51a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private final static String PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization"; 52a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 53a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public final static int MAX_REDIRECT_COUNT = 16; 54a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 55a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 56a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Creates a new request session. 57a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 58a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public RequestHandle(RequestQueue requestQueue, String url, WebAddress uri, 59a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String method, Map<String, String> headers, 60a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath InputStream bodyProvider, int bodyLength, Request request) { 61a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 62a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (headers == null) { 63a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath headers = new HashMap<String, String>(); 64a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 65a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mHeaders = headers; 66a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mBodyProvider = bodyProvider; 67a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mBodyLength = bodyLength; 68a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mMethod = method == null? "GET" : method; 69a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 70a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mUrl = url; 71a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mUri = uri; 72a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 73a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mRequestQueue = requestQueue; 74a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 75a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mRequest = request; 76a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 77a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 78a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 79a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Creates a new request session with a given Connection. This connection 80a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * is used during a synchronous load to handle this request. 81a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 82a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public RequestHandle(RequestQueue requestQueue, String url, WebAddress uri, 83a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String method, Map<String, String> headers, 84a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath InputStream bodyProvider, int bodyLength, Request request, 85a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath Connection conn) { 86a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath this(requestQueue, url, uri, method, headers, bodyProvider, bodyLength, 87a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath request); 88a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mConnection = conn; 89a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 90a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 91a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 92a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Cancels this request 93a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 94a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public void cancel() { 95a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (mRequest != null) { 96a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mRequest.cancel(); 97a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 98a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 99a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 100a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 101a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Pauses the loading of this request. For example, called from the WebCore thread 102a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * when the plugin can take no more data. 103a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 104a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public void pauseRequest(boolean pause) { 105a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (mRequest != null) { 106a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mRequest.setLoadingPaused(pause); 107a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 108a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 109a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 110a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 111a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Handles SSL error(s) on the way down from the user (the user 112a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * has already provided their feedback). 113a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 114a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public void handleSslErrorResponse(boolean proceed) { 115a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (mRequest != null) { 116a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mRequest.handleSslErrorResponse(proceed); 117a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 118a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 119a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 120a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 121a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @return true if we've hit the max redirect count 122a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 123a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public boolean isRedirectMax() { 124a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return mRedirectCount >= MAX_REDIRECT_COUNT; 125a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 126a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 127a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public int getRedirectCount() { 128a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return mRedirectCount; 129a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 130a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 131a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public void setRedirectCount(int count) { 132a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mRedirectCount = count; 133a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 134a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 135a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 136a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Create and queue a redirect request. 137a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * 138a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @param redirectTo URL to redirect to 139a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @param statusCode HTTP status code returned from original request 140a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @param cacheHeaders Cache header for redirect URL 141a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @return true if setup succeeds, false otherwise (redirect loop 142a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * count exceeded, body provider unable to rewind on 307 redirect) 143a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 144a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public boolean setupRedirect(String redirectTo, int statusCode, 145a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath Map<String, String> cacheHeaders) { 146a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (HttpLog.LOGV) { 147a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath HttpLog.v("RequestHandle.setupRedirect(): redirectCount " + 148a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mRedirectCount); 149a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 150a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 151a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // be careful and remove authentication headers, if any 152a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mHeaders.remove(AUTHORIZATION_HEADER); 153a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mHeaders.remove(PROXY_AUTHORIZATION_HEADER); 154a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 155a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (++mRedirectCount == MAX_REDIRECT_COUNT) { 156a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // Way too many redirects -- fail out 157a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (HttpLog.LOGV) HttpLog.v( 158a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath "RequestHandle.setupRedirect(): too many redirects " + 159a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mRequest); 160a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mRequest.error(EventHandler.ERROR_REDIRECT_LOOP, 1613e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath "The page contains too many server redirects."); 162a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return false; 163a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 164a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 165a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (mUrl.startsWith("https:") && redirectTo.startsWith("http:")) { 166a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // implement http://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html#sec15.1.3 167a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (HttpLog.LOGV) { 168a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath HttpLog.v("blowing away the referer on an https -> http redirect"); 169a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 170a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mHeaders.remove("Referer"); 171a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 172a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 173a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mUrl = redirectTo; 174a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath try { 175a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mUri = new WebAddress(mUrl); 1763e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath } catch (IllegalArgumentException e) { 177a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath e.printStackTrace(); 178a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 179a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 180a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // update the "Cookie" header based on the redirected url 181a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mHeaders.remove("Cookie"); 1823e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath String cookie = null; 1833e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath if (mUri != null) { 1843e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath cookie = CookieManager.getInstance().getCookie(mUri.toString()); 1853e387462b084cf0c62e89c21cfd071df50163e39Narayan Kamath } 186a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (cookie != null && cookie.length() > 0) { 187a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mHeaders.put("Cookie", cookie); 188a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 189a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 190a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if ((statusCode == 302 || statusCode == 303) && mMethod.equals("POST")) { 191a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (HttpLog.LOGV) { 192a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath HttpLog.v("replacing POST with GET on redirect to " + redirectTo); 193a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 194a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mMethod = "GET"; 195a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 196a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /* Only repost content on a 307. If 307, reset the body 197a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath provider so we can replay the body */ 198a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (statusCode == 307) { 199a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath try { 200a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (mBodyProvider != null) mBodyProvider.reset(); 201a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } catch (java.io.IOException ex) { 202a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (HttpLog.LOGV) { 203a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath HttpLog.v("setupRedirect() failed to reset body provider"); 204a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 205a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return false; 206a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 207a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 208a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } else { 209a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mHeaders.remove("Content-Type"); 210a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mBodyProvider = null; 211a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 212a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 213a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // Update the cache headers for this URL 214a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mHeaders.putAll(cacheHeaders); 215a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 216a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath createAndQueueNewRequest(); 217a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return true; 218a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 219a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 220a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 221a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Create and queue an HTTP authentication-response (basic) request. 222a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 223a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public void setupBasicAuthResponse(boolean isProxy, String username, String password) { 224a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String response = computeBasicAuthResponse(username, password); 225a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (HttpLog.LOGV) { 226a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath HttpLog.v("setupBasicAuthResponse(): response: " + response); 227a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 228a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mHeaders.put(authorizationHeader(isProxy), "Basic " + response); 229a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath setupAuthResponse(); 230a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 231a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 232a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 233a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Create and queue an HTTP authentication-response (digest) request. 234a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 235a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public void setupDigestAuthResponse(boolean isProxy, 236a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String username, 237a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String password, 238a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String realm, 239a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String nonce, 240a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String QOP, 241a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String algorithm, 242a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String opaque) { 243a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 244a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String response = computeDigestAuthResponse( 245a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath username, password, realm, nonce, QOP, algorithm, opaque); 246a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (HttpLog.LOGV) { 247a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath HttpLog.v("setupDigestAuthResponse(): response: " + response); 248a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 249a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mHeaders.put(authorizationHeader(isProxy), "Digest " + response); 250a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath setupAuthResponse(); 251a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 252a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 253a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private void setupAuthResponse() { 254a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath try { 255a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (mBodyProvider != null) mBodyProvider.reset(); 256a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } catch (java.io.IOException ex) { 257a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (HttpLog.LOGV) { 258a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath HttpLog.v("setupAuthResponse() failed to reset body provider"); 259a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 260a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 261a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath createAndQueueNewRequest(); 262a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 263a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 264a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 265a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @return HTTP request method (GET, PUT, etc). 266a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 267a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public String getMethod() { 268a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return mMethod; 269a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 270a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 271a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 272a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @return Basic-scheme authentication response: BASE64(username:password). 273a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 274a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public static String computeBasicAuthResponse(String username, String password) { 275a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (username == null) { 276a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath throw new NullPointerException("username == null"); 277a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 278a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 279a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (password == null) { 280a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath throw new NullPointerException("password == null"); 281a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 282a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 283a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // encode username:password to base64 284a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return new String(Base64.encodeBase64((username + ':' + password).getBytes())); 285a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 286a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 287a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public void waitUntilComplete() { 288a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mRequest.waitUntilComplete(); 289a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 290a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 291a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public void processRequest() { 292a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (mConnection != null) { 293a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mConnection.processRequests(mRequest); 294a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 295a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 296a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 297a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 298a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @return Digest-scheme authentication response. 299a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 300a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private String computeDigestAuthResponse(String username, 301a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String password, 302a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String realm, 303a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String nonce, 304a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String QOP, 305a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String algorithm, 306a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String opaque) { 307a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 308a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (username == null) { 309a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath throw new NullPointerException("username == null"); 310a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 311a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 312a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (password == null) { 313a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath throw new NullPointerException("password == null"); 314a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 315a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 316a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (realm == null) { 317a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath throw new NullPointerException("realm == null"); 318a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 319a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 320a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String A1 = username + ":" + realm + ":" + password; 321a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String A2 = mMethod + ":" + mUrl; 322a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 323a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // because we do not preemptively send authorization headers, nc is always 1 324a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String nc = "00000001"; 325a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String cnonce = computeCnonce(); 326a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String digest = computeDigest(A1, A2, nonce, QOP, nc, cnonce); 327a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 328a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String response = ""; 329a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath response += "username=" + doubleQuote(username) + ", "; 330a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath response += "realm=" + doubleQuote(realm) + ", "; 331a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath response += "nonce=" + doubleQuote(nonce) + ", "; 332a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath response += "uri=" + doubleQuote(mUrl) + ", "; 333a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath response += "response=" + doubleQuote(digest) ; 334a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 335a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (opaque != null) { 336a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath response += ", opaque=" + doubleQuote(opaque); 337a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 338a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 339a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (algorithm != null) { 340a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath response += ", algorithm=" + algorithm; 341a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 342a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 343a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (QOP != null) { 344a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath response += ", qop=" + QOP + ", nc=" + nc + ", cnonce=" + doubleQuote(cnonce); 345a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 346a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 347a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return response; 348a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 349a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 350a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 351a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @return The right authorization header (dependeing on whether it is a proxy or not). 352a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 353a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public static String authorizationHeader(boolean isProxy) { 354a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (!isProxy) { 355a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return AUTHORIZATION_HEADER; 356a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } else { 357a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return PROXY_AUTHORIZATION_HEADER; 358a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 359a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 360a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 361a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 362a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @return Double-quoted MD5 digest. 363a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 364a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private String computeDigest( 365a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String A1, String A2, String nonce, String QOP, String nc, String cnonce) { 366a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (HttpLog.LOGV) { 367a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath HttpLog.v("computeDigest(): QOP: " + QOP); 368a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 369a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 370a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (QOP == null) { 371a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return KD(H(A1), nonce + ":" + H(A2)); 372a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } else { 373a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (QOP.equalsIgnoreCase("auth")) { 374a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return KD(H(A1), nonce + ":" + nc + ":" + cnonce + ":" + QOP + ":" + H(A2)); 375a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 376a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 377a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 378a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return null; 379a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 380a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 381a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 382a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @return MD5 hash of concat(secret, ":", data). 383a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 384a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private String KD(String secret, String data) { 385a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return H(secret + ":" + data); 386a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 387a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 388a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 389a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @return MD5 hash of param. 390a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 391a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private String H(String param) { 392a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (param != null) { 393a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath try { 394a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath MessageDigest md5 = MessageDigest.getInstance("MD5"); 395a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 396a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath byte[] d = md5.digest(param.getBytes()); 397a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (d != null) { 398a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return bufferToHex(d); 399a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 400a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } catch (NoSuchAlgorithmException e) { 401a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath throw new RuntimeException(e); 402a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 403a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 404a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 405a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return null; 406a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 407a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 408a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 409a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @return HEX buffer representation. 410a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 411a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private String bufferToHex(byte[] buffer) { 412a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath final char hexChars[] = 413a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; 414a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 415a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (buffer != null) { 416a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath int length = buffer.length; 417a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (length > 0) { 418a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath StringBuilder hex = new StringBuilder(2 * length); 419a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 420a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath for (int i = 0; i < length; ++i) { 421a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath byte l = (byte) (buffer[i] & 0x0F); 422a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath byte h = (byte)((buffer[i] & 0xF0) >> 4); 423a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 424a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath hex.append(hexChars[h]); 425a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath hex.append(hexChars[l]); 426a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 427a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 428a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return hex.toString(); 429a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } else { 430a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return ""; 431a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 432a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 433a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 434a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return null; 435a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 436a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 437a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 438a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Computes a random cnonce value based on the current time. 439a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 440a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private String computeCnonce() { 441a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath Random rand = new Random(); 442a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath int nextInt = rand.nextInt(); 443a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath nextInt = (nextInt == Integer.MIN_VALUE) ? 444a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath Integer.MAX_VALUE : Math.abs(nextInt); 445a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return Integer.toString(nextInt, 16); 446a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 447a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 448a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 449a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * "Double-quotes" the argument. 450a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 451a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private String doubleQuote(String param) { 452a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (param != null) { 453a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return "\"" + param + "\""; 454a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 455a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 456a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return null; 457a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 458a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 459a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 460a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Creates and queues new request. 461a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 462a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private void createAndQueueNewRequest() { 463a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // mConnection is non-null if and only if the requests are synchronous. 464a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (mConnection != null) { 465a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath RequestHandle newHandle = mRequestQueue.queueSynchronousRequest( 466a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mUrl, mUri, mMethod, mHeaders, mRequest.mEventHandler, 467a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mBodyProvider, mBodyLength); 468a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mRequest = newHandle.mRequest; 469a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mConnection = newHandle.mConnection; 470a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath newHandle.processRequest(); 471a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return; 472a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 473a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mRequest = mRequestQueue.queueRequest( 474a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mUrl, mUri, mMethod, mHeaders, mRequest.mEventHandler, 475a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mBodyProvider, 476a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mBodyLength).mRequest; 477a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 478a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath} 479