Auto merge of #10328 - DDEFISHER:master, r=jdm

401 authorization UI then restart request/save successful auth creds

Step 7 of the NCSU student project Implement HTTP authorization UI

> make an authorization UI appear when a 401 HTTP response is received (StatusCode::Unauthorized) - in load in http_loader.rs, right before trying to process an HTTP redirection, use the new tinyfiledialogs library to make two prompts appear (username and password), then restart the request with the new authorization value present applied. If an authorization value was present and the response is successful, add the credentials to the authorization cache.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/10328)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-04-15 08:01:23 +05:30
commit 7bd2381518
7 changed files with 282 additions and 61 deletions

View file

@ -32,6 +32,9 @@ git = "https://github.com/servo/ipc-channel"
[dependencies.webrender_traits]
git = "https://github.com/servo/webrender_traits"
[dependencies.tinyfiledialogs]
git = "https://github.com/jdm/tinyfiledialogs"
[dependencies]
cookie = "0.2"
flate2 = "0.2.0"

View file

@ -41,6 +41,7 @@ use std::sync::mpsc::Sender;
use std::sync::{Arc, RwLock};
use time;
use time::Tm;
use tinyfiledialogs;
use url::Url;
use util::resource_files::resources_dir_path;
use util::thread::spawn_named;
@ -150,8 +151,10 @@ fn load_for_consumer(load_data: LoadData,
let factory = NetworkHttpRequestFactory {
connector: connector,
};
let ui_provider = TFDProvider;
let context = load_data.context.clone();
match load::<WrappedHttpRequest>(load_data, &http_state,
match load::<WrappedHttpRequest, TFDProvider>(load_data, &ui_provider, &http_state,
devtools_chan, &factory,
user_agent, &cancel_listener) {
Err(LoadError::UnsupportedScheme(url)) => {
@ -694,13 +697,27 @@ pub fn obtain_response<A>(request_factory: &HttpRequestFactory<R=A>,
Ok(response)
}
pub fn load<A>(load_data: LoadData,
pub trait UIProvider {
fn input_username_and_password(&self) -> (Option<String>, Option<String>);
}
impl UIProvider for TFDProvider {
fn input_username_and_password(&self) -> (Option<String>, Option<String>) {
(tinyfiledialogs::input_box("Enter username", "Username:", ""),
tinyfiledialogs::input_box("Enter password", "Password:", ""))
}
}
struct TFDProvider;
pub fn load<A, B>(load_data: LoadData,
ui_provider: &B,
http_state: &HttpState,
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
request_factory: &HttpRequestFactory<R=A>,
user_agent: String,
cancel_listener: &CancellationListener)
-> Result<StreamedResponse<A::R>, LoadError> where A: HttpRequest + 'static {
-> Result<StreamedResponse<A::R>, LoadError> where A: HttpRequest + 'static, B: UIProvider {
// FIXME: At the time of writing this FIXME, servo didn't have any central
// location for configuration. If you're reading this and such a
// repository DOES exist, please update this constant to use it.
@ -711,6 +728,8 @@ pub fn load<A>(load_data: LoadData,
let mut redirected_to = HashSet::new();
let mut method = load_data.method.clone();
let mut new_auth_header: Option<Authorization<Basic>> = None;
if cancel_listener.is_cancelled() {
return Err(LoadError::Cancelled(doc_url, "load cancelled".to_owned()));
}
@ -765,12 +784,43 @@ pub fn load<A>(load_data: LoadData,
&user_agent, &http_state.cookie_jar,
&http_state.auth_cache, &load_data);
//if there is a new auth header then set the request headers with it
if let Some(ref auth_header) = new_auth_header {
request_headers.set(auth_header.clone());
}
let response = try!(obtain_response(request_factory, &doc_url, &method, &request_headers,
&cancel_listener, &load_data.data, &load_data.method,
&load_data.pipeline_id, iters, &devtools_chan, &request_id));
process_response_headers(&response, &doc_url, &http_state.cookie_jar, &http_state.hsts_list, &load_data);
//if response status is unauthorized then prompt user for username and password
if response.status() == StatusCode::Unauthorized {
let (username_option, password_option) = ui_provider.input_username_and_password();
match username_option {
Some(name) => {
new_auth_header = Some(Authorization(Basic { username: name, password: password_option }));
continue;
},
None => {},
}
}
new_auth_header = None;
if let Some(auth_header) = request_headers.get::<Authorization<Basic>>() {
if response.status().class() == StatusClass::Success {
let auth_entry = AuthCacheEntry {
user_name: auth_header.username.to_owned(),
password: auth_header.password.to_owned().unwrap(),
};
http_state.auth_cache.write().unwrap().insert(doc_url.clone(), auth_entry);
}
}
// --- Loop if there's a redirect
if response.status().class() == StatusClass::Redirection {
if let Some(&Location(ref new_url)) = response.headers().get::<Location>() {

View file

@ -29,6 +29,7 @@ extern crate openssl;
extern crate rustc_serialize;
extern crate threadpool;
extern crate time;
extern crate tinyfiledialogs;
extern crate unicase;
extern crate url;
extern crate util;

View file

@ -1286,6 +1286,7 @@ dependencies = [
"rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"threadpool 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
"tinyfiledialogs 0.1.0 (git+https://github.com/jdm/tinyfiledialogs)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
"util 0.0.1",
@ -2090,6 +2091,14 @@ dependencies = [
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tinyfiledialogs"
version = "0.1.0"
source = "git+https://github.com/jdm/tinyfiledialogs#2d2285985db1168da4d516000f24842aba46fd94"
dependencies = [
"libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "traitobject"
version = "0.0.1"