281 lines
8.8 KiB
Java
281 lines
8.8 KiB
Java
|
/*
|
||
|
* Copyright (C) 2006 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
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;
|
||
|
|
||
|
/**
|
||
|
* HTTP authentication handler: local handler that takes care
|
||
|
* of HTTP authentication requests. This class is passed as a
|
||
|
* parameter to BrowserCallback.displayHttpAuthDialog and is
|
||
|
* meant to receive the user's response.
|
||
|
*/
|
||
|
public class HttpAuthHandler extends Handler {
|
||
|
/* It is important that the handler is in Network, because
|
||
|
* we want to share it accross multiple loaders and windows
|
||
|
* (like our subwindow and the main window).
|
||
|
*/
|
||
|
|
||
|
private static final String LOGTAG = "network";
|
||
|
|
||
|
/**
|
||
|
* Network.
|
||
|
*/
|
||
|
private Network mNetwork;
|
||
|
|
||
|
/**
|
||
|
* Loader queue.
|
||
|
*/
|
||
|
private LinkedList<LoadListener> mLoaderQueue;
|
||
|
|
||
|
|
||
|
// Message id for handling the user response
|
||
|
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
|
||
|
*
|
||
|
* @param network The parent network object
|
||
|
*/
|
||
|
/* package */ HttpAuthHandler(Network network) {
|
||
|
mNetwork = network;
|
||
|
mLoaderQueue = new LinkedList<LoadListener>();
|
||
|
}
|
||
|
|
||
|
|
||
|
@Override
|
||
|
public void handleMessage(Message msg) {
|
||
|
LoadListener loader = null;
|
||
|
synchronized (mLoaderQueue) {
|
||
|
loader = mLoaderQueue.poll();
|
||
|
}
|
||
|
assert(loader.isSynchronous() == false);
|
||
|
|
||
|
switch (msg.what) {
|
||
|
case AUTH_PROCEED:
|
||
|
String username = msg.getData().getString("username");
|
||
|
String password = msg.getData().getString("password");
|
||
|
|
||
|
loader.handleAuthResponse(username, password);
|
||
|
break;
|
||
|
|
||
|
case AUTH_CANCEL:
|
||
|
loader.handleAuthResponse(null, null);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return True if we can use user credentials on record
|
||
|
* (ie, if we did not fail trying to use them last time)
|
||
|
*/
|
||
|
public boolean useHttpAuthUsernamePassword() {
|
||
|
LoadListener loader = null;
|
||
|
synchronized (mLoaderQueue) {
|
||
|
loader = mLoaderQueue.peek();
|
||
|
}
|
||
|
if (loader != null) {
|
||
|
return !loader.authCredentialsInvalid();
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Enqueues the loader, if the loader is the only element
|
||
|
* in the queue, starts processing the loader
|
||
|
*
|
||
|
* @param loader The loader that resulted in this http
|
||
|
* 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) {
|
||
|
mLoaderQueue.offer(loader);
|
||
|
processNext =
|
||
|
(mLoaderQueue.size() == 1);
|
||
|
}
|
||
|
|
||
|
if (processNext) {
|
||
|
processNextLoader();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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)
|
||
|
*/
|
||
|
private void processNextLoader() {
|
||
|
LoadListener loader = null;
|
||
|
synchronized (mLoaderQueue) {
|
||
|
loader = mLoaderQueue.peek();
|
||
|
}
|
||
|
if (loader != null) {
|
||
|
synchronized (mRequestInFlightLock) {
|
||
|
assert(mRequestInFlight == false);
|
||
|
mRequestInFlight = true;
|
||
|
}
|
||
|
|
||
|
CallbackProxy proxy = loader.getFrame().getCallbackProxy();
|
||
|
|
||
|
String hostname = loader.proxyAuthenticate() ?
|
||
|
mNetwork.getProxyHostname() : loader.host();
|
||
|
|
||
|
String realm = loader.realm();
|
||
|
|
||
|
proxy.onReceivedHttpAuthRequest(this, hostname, realm);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Informs the WebView of a new set of credentials.
|
||
|
* @hide Pending API council review
|
||
|
*/
|
||
|
public static void onReceivedCredentials(LoadListener loader,
|
||
|
String host, String realm, String username, String password) {
|
||
|
CallbackProxy proxy = loader.getFrame().getCallbackProxy();
|
||
|
proxy.onReceivedHttpAuthCredentials(host, realm, username, password);
|
||
|
}
|
||
|
}
|