2 * Copyright (C) 2006 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package android.webkit;
19 import android.os.Bundle;
20 import android.os.Handler;
21 import android.os.Message;
22 import android.util.Log;
24 import java.util.ListIterator;
25 import java.util.LinkedList;
28 * HTTP authentication handler: local handler that takes care
29 * of HTTP authentication requests. This class is passed as a
30 * parameter to BrowserCallback.displayHttpAuthDialog and is
31 * meant to receive the user's response.
33 public class HttpAuthHandler extends Handler {
34 /* It is important that the handler is in Network, because
35 * we want to share it accross multiple loaders and windows
36 * (like our subwindow and the main window).
39 private static final String LOGTAG = "network";
44 private Network mNetwork;
49 private LinkedList<LoadListener> mLoaderQueue;
52 // Message id for handling the user response
53 private static final int AUTH_PROCEED = 100;
54 private static final int AUTH_CANCEL = 200;
56 // Use to synchronize when making synchronous calls to
57 // onReceivedHttpAuthRequest(). We can't use a single Boolean object for
58 // both the lock and the state, because Boolean is immutable.
59 Object mRequestInFlightLock = new Object();
60 boolean mRequestInFlight;
65 * Creates a new HTTP authentication handler with an empty
68 * @param network The parent network object
70 /* package */ HttpAuthHandler(Network network) {
72 mLoaderQueue = new LinkedList<LoadListener>();
77 public void handleMessage(Message msg) {
78 LoadListener loader = null;
79 synchronized (mLoaderQueue) {
80 loader = mLoaderQueue.poll();
82 assert(loader.isSynchronous() == false);
86 String username = msg.getData().getString("username");
87 String password = msg.getData().getString("password");
89 loader.handleAuthResponse(username, password);
93 loader.handleAuthResponse(null, null);
101 * Helper method used to unblock handleAuthRequest(), which in the case of a
102 * synchronous request will wait for proxy.onReceivedHttpAuthRequest() to
103 * call back to either proceed() or cancel().
105 * @param username The username to use for authentication
106 * @param password The password to use for authentication
107 * @return True if the request is synchronous and handleAuthRequest() has
110 private boolean handleResponseForSynchronousRequest(String username, String password) {
111 LoadListener loader = null;
112 synchronized (mLoaderQueue) {
113 loader = mLoaderQueue.peek();
115 if (loader.isSynchronous()) {
116 mUsername = username;
117 mPassword = password;
123 private void signalRequestComplete() {
124 synchronized (mRequestInFlightLock) {
125 assert(mRequestInFlight);
126 mRequestInFlight = false;
127 mRequestInFlightLock.notify();
132 * Proceed with the authorization with the given credentials
134 * May be called on the UI thread, rather than the WebCore thread.
136 * @param username The username to use for authentication
137 * @param password The password to use for authentication
139 public void proceed(String username, String password) {
140 if (handleResponseForSynchronousRequest(username, password)) {
141 signalRequestComplete();
144 Message msg = obtainMessage(AUTH_PROCEED);
145 msg.getData().putString("username", username);
146 msg.getData().putString("password", password);
148 signalRequestComplete();
152 * Cancel the authorization request
154 * May be called on the UI thread, rather than the WebCore thread.
157 public void cancel() {
158 if (handleResponseForSynchronousRequest(null, null)) {
159 signalRequestComplete();
162 sendMessage(obtainMessage(AUTH_CANCEL));
163 signalRequestComplete();
167 * @return True if we can use user credentials on record
168 * (ie, if we did not fail trying to use them last time)
170 public boolean useHttpAuthUsernamePassword() {
171 LoadListener loader = null;
172 synchronized (mLoaderQueue) {
173 loader = mLoaderQueue.peek();
175 if (loader != null) {
176 return !loader.authCredentialsInvalid();
183 * Enqueues the loader, if the loader is the only element
184 * in the queue, starts processing the loader
186 * @param loader The loader that resulted in this http
187 * authentication request
189 /* package */ void handleAuthRequest(LoadListener loader) {
190 // The call to proxy.onReceivedHttpAuthRequest() may be asynchronous. If
191 // the request is synchronous, we must block here until we have a
193 if (loader.isSynchronous()) {
194 // If there's a request in flight, wait for it to complete. The
195 // response will queue a message on this thread.
196 waitForRequestToComplete();
197 // Make a request to the proxy for this request, jumping the queue.
198 // We use the queue so that the loader is present in
199 // useHttpAuthUsernamePassword().
200 synchronized (mLoaderQueue) {
201 mLoaderQueue.addFirst(loader);
204 // Wait for this request to complete.
205 waitForRequestToComplete();
206 // Pop the loader from the queue.
207 synchronized (mLoaderQueue) {
208 assert(mLoaderQueue.peek() == loader);
212 loader.handleAuthResponse(mUsername, mPassword);
213 // The message queued by the response from the last asynchronous
214 // request, if present, will start the next request.
218 boolean processNext = false;
220 synchronized (mLoaderQueue) {
221 mLoaderQueue.offer(loader);
223 (mLoaderQueue.size() == 1);
232 * Wait for the request in flight, if any, to complete
234 private void waitForRequestToComplete() {
235 synchronized (mRequestInFlightLock) {
236 while (mRequestInFlight) {
238 mRequestInFlightLock.wait();
239 } catch(InterruptedException e) {
240 Log.e(LOGTAG, "Interrupted while waiting for request to complete");
247 * Process the next loader in the queue (helper method)
249 private void processNextLoader() {
250 LoadListener loader = null;
251 synchronized (mLoaderQueue) {
252 loader = mLoaderQueue.peek();
254 if (loader != null) {
255 synchronized (mRequestInFlightLock) {
256 assert(mRequestInFlight == false);
257 mRequestInFlight = true;
260 CallbackProxy proxy = loader.getFrame().getCallbackProxy();
262 String hostname = loader.proxyAuthenticate() ?
263 mNetwork.getProxyHostname() : loader.host();
265 String realm = loader.realm();
267 proxy.onReceivedHttpAuthRequest(this, hostname, realm);
272 * Informs the WebView of a new set of credentials.
273 * @hide Pending API council review
275 public static void onReceivedCredentials(LoadListener loader,
276 String host, String realm, String username, String password) {
277 CallbackProxy proxy = loader.getFrame().getCallbackProxy();
278 proxy.onReceivedHttpAuthCredentials(host, realm, username, password);