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