Implemented a typed Authorization header
An Authorization header contains a Scheme. If you have no real scheme, you can use String as your scheme (Authorization<String>). This includes the `Basic` scheme built-in.
This commit is contained in:
committed by
Sean McArthur
parent
7478f3dfd6
commit
91a8e03e7a
180
src/header/common/authorization.rs
Normal file
180
src/header/common/authorization.rs
Normal file
@@ -0,0 +1,180 @@
|
||||
use std::fmt::{mod, Show};
|
||||
use std::from_str::FromStr;
|
||||
use std::str::from_utf8;
|
||||
use serialize::base64::{ToBase64, FromBase64, Standard, Config};
|
||||
use header::{Header, HeaderFormat};
|
||||
|
||||
/// The `Authorization` header field.
|
||||
#[deriving(Clone, PartialEq, Show)]
|
||||
pub struct Authorization<S: Scheme>(pub S);
|
||||
|
||||
impl<S: Scheme> Header for Authorization<S> {
|
||||
fn header_name(_: Option<Authorization<S>>) -> &'static str {
|
||||
"Authorization"
|
||||
}
|
||||
|
||||
fn parse_header(raw: &[Vec<u8>]) -> Option<Authorization<S>> {
|
||||
if raw.len() == 1 {
|
||||
match (from_utf8(unsafe { raw[].unsafe_get(0)[] }), Scheme::scheme(None::<S>)) {
|
||||
(Some(header), Some(scheme))
|
||||
if header.starts_with(scheme) && header.len() > scheme.len() + 1 => {
|
||||
from_str::<S>(header[scheme.len() + 1..]).map(|s| Authorization(s))
|
||||
},
|
||||
(Some(header), None) => from_str::<S>(header).map(|s| Authorization(s)),
|
||||
_ => None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Scheme> HeaderFormat for Authorization<S> {
|
||||
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match Scheme::scheme(None::<S>) {
|
||||
Some(scheme) => try!(write!(fmt, "{} ", scheme)),
|
||||
None => ()
|
||||
};
|
||||
self.0.fmt_scheme(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
/// An Authorization scheme to be used in the header.
|
||||
pub trait Scheme: FromStr + Send + Sync {
|
||||
/// An optional Scheme name.
|
||||
///
|
||||
/// For example, `Basic asdf` has the name `Basic`. The Option<Self> is
|
||||
/// just a marker that can be removed once UFCS is completed.
|
||||
fn scheme(Option<Self>) -> Option<&'static str>;
|
||||
/// Format the Scheme data into a header value.
|
||||
fn fmt_scheme(&self, &mut fmt::Formatter) -> fmt::Result;
|
||||
}
|
||||
|
||||
impl Scheme for String {
|
||||
fn scheme(_: Option<String>) -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
|
||||
fn fmt_scheme(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Credential holder for Basic Authentication
|
||||
#[deriving(Clone, PartialEq, Show)]
|
||||
pub struct Basic {
|
||||
/// The username as a possibly empty string
|
||||
pub username: String,
|
||||
/// The password. `None` if the `:` delimiter character was not
|
||||
/// part of the parsed input.
|
||||
pub password: Option<String>
|
||||
}
|
||||
|
||||
impl Scheme for Basic {
|
||||
fn scheme(_: Option<Basic>) -> Option<&'static str> {
|
||||
Some("Basic")
|
||||
}
|
||||
|
||||
fn fmt_scheme(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
//FIXME: serialize::base64 could use some Show implementation, so
|
||||
//that we don't have to allocate a new string here just to write it
|
||||
//to the formatter.
|
||||
let mut text = self.username.clone();
|
||||
text.push(':');
|
||||
if let Some(ref pass) = self.password {
|
||||
text.push_str(pass[]);
|
||||
}
|
||||
text.as_bytes().to_base64(Config {
|
||||
char_set: Standard,
|
||||
pad: true,
|
||||
line_length: None
|
||||
}).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Basic {
|
||||
fn from_str(s: &str) -> Option<Basic> {
|
||||
match s.from_base64() {
|
||||
Ok(decoded) => match String::from_utf8(decoded) {
|
||||
Ok(text) => {
|
||||
let mut parts = text[].split(':');
|
||||
let user = match parts.next() {
|
||||
Some(part) => part.into_string(),
|
||||
None => return None
|
||||
};
|
||||
let password = match parts.next() {
|
||||
Some(part) => Some(part.into_string()),
|
||||
None => None
|
||||
};
|
||||
Some(Basic {
|
||||
username: user,
|
||||
password: password
|
||||
})
|
||||
},
|
||||
Err(e) => {
|
||||
debug!("Basic::from_utf8 error={}", e);
|
||||
None
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
debug!("Basic::from_base64 error={}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::MemReader;
|
||||
use super::{Authorization, Basic};
|
||||
use super::super::super::{Headers};
|
||||
|
||||
fn mem(s: &str) -> MemReader {
|
||||
MemReader::new(s.as_bytes().to_vec())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_raw_auth() {
|
||||
let mut headers = Headers::new();
|
||||
headers.set(Authorization("foo bar baz".into_string()));
|
||||
assert_eq!(headers.to_string(), "Authorization: foo bar baz\r\n".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_raw_auth_parse() {
|
||||
let headers = Headers::from_raw(&mut mem("Authorization: foo bar baz\r\n\r\n")).unwrap();
|
||||
assert_eq!(headers.get::<Authorization<String>>().unwrap().0[], "foo bar baz");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_auth() {
|
||||
let mut headers = Headers::new();
|
||||
headers.set(Authorization(Basic { username: "Aladdin".into_string(), password: Some("open sesame".into_string()) }));
|
||||
assert_eq!(headers.to_string(), "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\r\n".into_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_auth_no_password() {
|
||||
let mut headers = Headers::new();
|
||||
headers.set(Authorization(Basic { username: "Aladdin".to_string(), password: None }));
|
||||
assert_eq!(headers.to_string(), "Authorization: Basic QWxhZGRpbjo=\r\n".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_auth_parse() {
|
||||
let headers = Headers::from_raw(&mut mem("Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\r\n\r\n")).unwrap();
|
||||
let auth = headers.get::<Authorization<Basic>>().unwrap();
|
||||
assert_eq!(auth.0.username[], "Aladdin");
|
||||
assert_eq!(auth.0.password, Some("open sesame".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_auth_parse_no_password() {
|
||||
let headers = Headers::from_raw(&mut mem("Authorization: Basic QWxhZGRpbjo=\r\n\r\n")).unwrap();
|
||||
let auth = headers.get::<Authorization<Basic>>().unwrap();
|
||||
assert_eq!(auth.0.username.as_slice(), "Aladdin");
|
||||
assert_eq!(auth.0.password, Some("".to_string()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
//! is used, such as `ContentType(pub Mime)`.
|
||||
|
||||
pub use self::accept::Accept;
|
||||
pub use self::authorization::Authorization;
|
||||
pub use self::connection::Connection;
|
||||
pub use self::content_length::ContentLength;
|
||||
pub use self::content_type::ContentType;
|
||||
@@ -56,8 +57,8 @@ pub mod user_agent;
|
||||
/// Exposes the Location header.
|
||||
pub mod location;
|
||||
|
||||
pub mod util;
|
||||
|
||||
/// Exposes the Authorization header.
|
||||
pub mod authorization;
|
||||
|
||||
fn from_comma_delimited<T: FromStr>(raw: &[Vec<u8>]) -> Option<Vec<T>> {
|
||||
if raw.len() != 1 {
|
||||
@@ -85,3 +86,4 @@ fn fmt_comma_delimited<T: Show>(fmt: &mut fmt::Formatter, parts: &[T]) -> fmt::R
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub mod util;
|
||||
|
||||
@@ -9,7 +9,7 @@ pub fn from_one_raw_str<T: FromStr>(raw: &[Vec<u8>]) -> Option<T> {
|
||||
return None;
|
||||
}
|
||||
// we JUST checked that raw.len() == 1, so raw[0] WILL exist.
|
||||
match from_utf8(unsafe { raw.as_slice().unsafe_get(0).as_slice() }) {
|
||||
match from_utf8(unsafe { raw[].unsafe_get(0)[] }) {
|
||||
Some(s) => FromStr::from_str(s),
|
||||
None => None
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![feature(macro_rules, phase, default_type_params, if_let, slicing_syntax)]
|
||||
#![feature(macro_rules, phase, default_type_params, if_let, slicing_syntax,
|
||||
tuple_indexing)]
|
||||
#![deny(missing_docs)]
|
||||
#![deny(warnings)]
|
||||
#![experimental]
|
||||
@@ -125,6 +126,7 @@
|
||||
//! implement `Reader` and can be read to get the data out of a `Response`.
|
||||
//!
|
||||
|
||||
extern crate serialize;
|
||||
extern crate time;
|
||||
extern crate url;
|
||||
extern crate openssl;
|
||||
|
||||
Reference in New Issue
Block a user