Fix HttpAuthHandler for synchronous requests

When HttpAuthHandler queries the WebViewClient to obtain a username and
password, we need to make sure that this is done synchronously when the request
is synchronous.

Bug: 2511043
Change-Id: I9ff2156cfb3c81edaf4a50ec7094a00a8f8ff91f
This commit is contained in:
Steve Block
2010-03-12 18:49:48 +00:00
parent 9b63c8ee37
commit c877c6b87a

View File

@@ -19,6 +19,7 @@ package android.webkit;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import java.util.ListIterator;
import java.util.LinkedList;
@@ -52,6 +53,14 @@ public class HttpAuthHandler extends Handler {
private static final int AUTH_PROCEED = 100;
private static final int AUTH_CANCEL = 200;
// Use to synchronize when making synchronous calls to
// onReceivedHttpAuthRequest(). We can't use a single Boolean object for
// both the lock and the state, because Boolean is immutable.
Object mRequestInFlightLock = new Object();
boolean mRequestInFlight;
String mUsername;
String mPassword;
/**
* Creates a new HTTP authentication handler with an empty
* loader queue
@@ -70,6 +79,7 @@ public class HttpAuthHandler extends Handler {
synchronized (mLoaderQueue) {
loader = mLoaderQueue.poll();
}
assert(loader.isSynchronous() == false);
switch (msg.what) {
case AUTH_PROCEED:
@@ -87,25 +97,70 @@ public class HttpAuthHandler extends Handler {
processNextLoader();
}
/**
* Helper method used to unblock handleAuthRequest(), which in the case of a
* synchronous request will wait for proxy.onReceivedHttpAuthRequest() to
* call back to either proceed() or cancel().
*
* @param username The username to use for authentication
* @param password The password to use for authentication
* @return True if the request is synchronous and handleAuthRequest() has
* been unblocked
*/
private boolean handleResponseForSynchronousRequest(String username, String password) {
LoadListener loader = null;
synchronized (mLoaderQueue) {
loader = mLoaderQueue.peek();
}
if (loader.isSynchronous()) {
mUsername = username;
mPassword = password;
return true;
}
return false;
}
private void signalRequestComplete() {
synchronized (mRequestInFlightLock) {
assert(mRequestInFlight);
mRequestInFlight = false;
mRequestInFlightLock.notify();
}
}
/**
* Proceed with the authorization with the given credentials
*
* May be called on the UI thread, rather than the WebCore thread.
*
* @param username The username to use for authentication
* @param password The password to use for authentication
*/
public void proceed(String username, String password) {
if (handleResponseForSynchronousRequest(username, password)) {
signalRequestComplete();
return;
}
Message msg = obtainMessage(AUTH_PROCEED);
msg.getData().putString("username", username);
msg.getData().putString("password", password);
sendMessage(msg);
signalRequestComplete();
}
/**
* Cancel the authorization request
*
* May be called on the UI thread, rather than the WebCore thread.
*
*/
public void cancel() {
if (handleResponseForSynchronousRequest(null, null)) {
signalRequestComplete();
return;
}
sendMessage(obtainMessage(AUTH_CANCEL));
signalRequestComplete();
}
/**
@@ -132,6 +187,34 @@ public class HttpAuthHandler extends Handler {
* authentication request
*/
/* package */ void handleAuthRequest(LoadListener loader) {
// The call to proxy.onReceivedHttpAuthRequest() may be asynchronous. If
// the request is synchronous, we must block here until we have a
// response.
if (loader.isSynchronous()) {
// If there's a request in flight, wait for it to complete. The
// response will queue a message on this thread.
waitForRequestToComplete();
// Make a request to the proxy for this request, jumping the queue.
// We use the queue so that the loader is present in
// useHttpAuthUsernamePassword().
synchronized (mLoaderQueue) {
mLoaderQueue.addFirst(loader);
}
processNextLoader();
// Wait for this request to complete.
waitForRequestToComplete();
// Pop the loader from the queue.
synchronized (mLoaderQueue) {
assert(mLoaderQueue.peek() == loader);
mLoaderQueue.poll();
}
// Call back.
loader.handleAuthResponse(mUsername, mPassword);
// The message queued by the response from the last asynchronous
// request, if present, will start the next request.
return;
}
boolean processNext = false;
synchronized (mLoaderQueue) {
@@ -145,6 +228,21 @@ public class HttpAuthHandler extends Handler {
}
}
/**
* Wait for the request in flight, if any, to complete
*/
private void waitForRequestToComplete() {
synchronized (mRequestInFlightLock) {
while (mRequestInFlight) {
try {
mRequestInFlightLock.wait();
} catch(InterruptedException e) {
Log.e(LOGTAG, "Interrupted while waiting for request to complete");
}
}
}
}
/**
* Process the next loader in the queue (helper method)
*/
@@ -154,6 +252,11 @@ public class HttpAuthHandler extends Handler {
loader = mLoaderQueue.peek();
}
if (loader != null) {
synchronized (mRequestInFlightLock) {
assert(mRequestInFlight == false);
mRequestInFlight = true;
}
CallbackProxy proxy = loader.getFrame().getCallbackProxy();
String hostname = loader.proxyAuthenticate() ?