| /* |
| * Copyright (C) 2010 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; |
| |
| /** |
| * HttpAuthHandler implementation is used only by the Android Java HTTP stack. |
| * <p> |
| * This class is not needed when we're using the Chromium HTTP stack. |
| */ |
| class HttpAuthHandlerImpl extends HttpAuthHandler { |
| /* |
| * 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 */ HttpAuthHandlerImpl(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); |
| } |
| } |