OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / core / java / android / webkit / HttpAuthHandler.java
1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package android.webkit;
18
19 import android.os.Bundle;
20 import android.os.Handler;
21 import android.os.Message;
22 import android.util.Log;
23
24 import java.util.ListIterator;
25 import java.util.LinkedList;
26
27 /**
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.
32  */
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).
37      */
38
39     private static final String LOGTAG = "network";
40
41     /**
42      * Network.
43      */
44     private Network mNetwork;
45
46     /**
47      * Loader queue.
48      */
49     private LinkedList<LoadListener> mLoaderQueue;
50
51
52     // Message id for handling the user response
53     private static final int AUTH_PROCEED = 100;
54     private static final int AUTH_CANCEL = 200;
55
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;
61     String mUsername;
62     String mPassword;
63
64     /**
65      * Creates a new HTTP authentication handler with an empty
66      * loader queue
67      *
68      * @param network The parent network object
69      */
70     /* package */ HttpAuthHandler(Network network) {
71         mNetwork = network;
72         mLoaderQueue = new LinkedList<LoadListener>();
73     }
74
75
76     @Override
77     public void handleMessage(Message msg) {
78         LoadListener loader = null;
79         synchronized (mLoaderQueue) {
80             loader = mLoaderQueue.poll();
81         }
82         assert(loader.isSynchronous() == false);
83
84         switch (msg.what) {
85             case AUTH_PROCEED:
86                 String username = msg.getData().getString("username");
87                 String password = msg.getData().getString("password");
88
89                 loader.handleAuthResponse(username, password);
90                 break;
91
92             case AUTH_CANCEL:
93                 loader.handleAuthResponse(null, null);
94                 break;
95         }
96
97         processNextLoader();
98     }
99
100     /**
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().
104      *
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
108      * been unblocked
109      */
110     private boolean handleResponseForSynchronousRequest(String username, String password) {
111         LoadListener loader = null;
112         synchronized (mLoaderQueue) {
113             loader = mLoaderQueue.peek();
114         }
115         if (loader.isSynchronous()) {
116             mUsername = username;
117             mPassword = password;
118             return true;
119         }
120         return false;
121     }
122
123     private void signalRequestComplete() {
124         synchronized (mRequestInFlightLock) {
125             assert(mRequestInFlight);
126             mRequestInFlight = false;
127             mRequestInFlightLock.notify();
128         }
129     }
130
131     /**
132      * Proceed with the authorization with the given credentials
133      *
134      * May be called on the UI thread, rather than the WebCore thread.
135      *
136      * @param username The username to use for authentication
137      * @param password The password to use for authentication
138      */
139     public void proceed(String username, String password) {
140         if (handleResponseForSynchronousRequest(username, password)) {
141             signalRequestComplete();
142             return;
143         }
144         Message msg = obtainMessage(AUTH_PROCEED);
145         msg.getData().putString("username", username);
146         msg.getData().putString("password", password);
147         sendMessage(msg);
148         signalRequestComplete();
149     }
150
151     /**
152      * Cancel the authorization request
153      *
154      * May be called on the UI thread, rather than the WebCore thread.
155      *
156      */
157     public void cancel() {
158         if (handleResponseForSynchronousRequest(null, null)) {
159             signalRequestComplete();
160             return;
161         }
162         sendMessage(obtainMessage(AUTH_CANCEL));
163         signalRequestComplete();
164     }
165
166     /**
167      * @return True if we can use user credentials on record
168      * (ie, if we did not fail trying to use them last time)
169      */
170     public boolean useHttpAuthUsernamePassword() {
171         LoadListener loader = null;
172         synchronized (mLoaderQueue) {
173             loader = mLoaderQueue.peek();
174         }
175         if (loader != null) {
176             return !loader.authCredentialsInvalid();
177         }
178
179         return false;
180     }
181
182     /**
183      * Enqueues the loader, if the loader is the only element
184      * in the queue, starts processing the loader
185      *
186      * @param loader The loader that resulted in this http
187      * authentication request
188      */
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
192         // response.
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);
202             }
203             processNextLoader();
204             // Wait for this request to complete.
205             waitForRequestToComplete();
206             // Pop the loader from the queue.
207             synchronized (mLoaderQueue) {
208                 assert(mLoaderQueue.peek() == loader);
209                 mLoaderQueue.poll();
210             }
211             // Call back.
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.
215             return;
216         }
217
218         boolean processNext = false;
219
220         synchronized (mLoaderQueue) {
221             mLoaderQueue.offer(loader);
222             processNext =
223                 (mLoaderQueue.size() == 1);
224         }
225
226         if (processNext) {
227             processNextLoader();
228         }
229     }
230
231     /**
232      * Wait for the request in flight, if any, to complete
233      */
234     private void waitForRequestToComplete() {
235         synchronized (mRequestInFlightLock) {
236             while (mRequestInFlight) {
237                 try {
238                     mRequestInFlightLock.wait();
239                 } catch(InterruptedException e) {
240                     Log.e(LOGTAG, "Interrupted while waiting for request to complete");
241                 }
242             }
243         }
244     }
245
246     /**
247      * Process the next loader in the queue (helper method)
248      */
249     private void processNextLoader() {
250         LoadListener loader = null;
251         synchronized (mLoaderQueue) {
252             loader = mLoaderQueue.peek();
253         }
254         if (loader != null) {
255             synchronized (mRequestInFlightLock) {
256                 assert(mRequestInFlight == false);
257                 mRequestInFlight = true;
258             }
259
260             CallbackProxy proxy = loader.getFrame().getCallbackProxy();
261
262             String hostname = loader.proxyAuthenticate() ?
263                 mNetwork.getProxyHostname() : loader.host();
264
265             String realm = loader.realm();
266
267             proxy.onReceivedHttpAuthRequest(this, hostname, realm);
268         }
269     }
270
271     /**
272      * Informs the WebView of a new set of credentials.
273      * @hide Pending API council review
274      */
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);
279     }
280 }