<commit_before>#![deny(warnings)]

//! A library to drive web browsers using the webdriver
//! interface.

use std::convert::From;
use std::io::Read;
use std::io;
use std::fmt::{self, Debug};

extern crate hyper;
use hyper::client::*;
use hyper::Url;

extern crate serde;
use serde::Serialize;
use serde::de::DeserializeOwned;

extern crate serde_json;
pub use serde_json::Value as JsonValue;

#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate log;

#[macro_use]
extern crate derive_builder;

extern crate rand;

pub mod messages;
use messages::*;

pub mod firefox;

#[derive(Debug)]
pub enum Error {
    FailedToLaunchDriver,
    InvalidUrl,
    ConnectionError,
    Io(io::Error),
    JsonDecodeError(serde_json::Error),
    WebDriverError(WebDriverError),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::FailedToLaunchDriver => write!(f, "Unable to start browser driver"),
            Error::InvalidUrl => write!(f, "Invalid URL"),
            Error::ConnectionError => write!(f, "Error connecting to browser"),
            Error::Io(ref err) => write!(f, "{}", err),
            Error::JsonDecodeError(ref s) => write!(f, "Received invalid response from browser: {}", s),
            Error::WebDriverError(ref err) => write!(f, "Error: {}", err.message),
        }
    }
}

impl From<hyper::Error> for Error {
    fn from(_: hyper::Error) -> Error {
        Error::ConnectionError
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<serde_json::Error> for Error {
    fn from(e: serde_json::Error) -> Error {
        Error::JsonDecodeError(e)
    }
}

pub trait Driver {
    /// The url used to connect to this driver
    fn url(&self) -> &str;
    /// Start a session for this driver
    fn session(self) -> Result<DriverSession, Error> where Self : Sized + 'static {
        DriverSession::create_session(Box::new(self))
    }
}

/// A driver using a pre-existing WebDriver HTTP URL.
#[derive(Builder)]
#[builder(field(private))]
pub struct HttpDriver {
    #[builder(setter(into))]
    url: String,
}

impl Driver for HttpDriver {
    fn url(&self) -> &str { &self.url }
}

/// Wrapper around the hyper Client, that handles Json encoding and URL construction
struct HttpClient {
    baseurl: Url,
    http: Client,
}

impl HttpClient {
    pub fn new(baseurl: Url) -> Self {
        HttpClient {
            baseurl: baseurl,
            http: Client::new(),
        }
    }

    pub fn decode<D: DeserializeOwned + Debug>(res: &mut Response) -> Result<D, Error> {
        let mut data = String::new();
        try!(res.read_to_string(&mut data));
        debug!("result status: {}\n\
                body: '{}'", res.status, data);

        if !res.status.is_success() {
            let err: Value<WebDriverError> = try!(serde_json::from_str(&data));
            trace!("deserialize error result: {:#?}", err);
            return Err(Error::WebDriverError(err.value));
        }
        let response = serde_json::from_str(&data);
        trace!("deserialize result: {:#?}", response);
        Ok(response?)
    }

    pub fn get<D: DeserializeOwned + Debug>(&self, path: &str) -> Result<D, Error> {
        let url = try!(self.baseurl.join(path)
                           .map_err(|_| Error::InvalidUrl));
        debug!("GET {}", url);
        let mut res = try!(self.http.get(url)
                            .send());
        Self::decode(&mut res)
    }

    pub fn delete<D: DeserializeOwned + Debug>(&self, path: &str) -> Result<D, Error> {
        let url = try!(self.baseurl.join(path)
                           .map_err(|_| Error::InvalidUrl));
        debug!("DELETE {}", url);
        let mut res = try!(self.http.delete(url)
                            .send());
        Self::decode(&mut res)
    }

    pub fn post<D: DeserializeOwned + Debug, E: Serialize>(&self, path: &str, body: &E) -> Result<D, Error> {
        let url = try!(self.baseurl.join(path)
                           .map_err(|_| Error::InvalidUrl));
        let body_str = try!(serde_json::to_string(body));
        debug!("POST url: {}\n\
                body: {}", url, body_str);
        let mut res = try!(self.http.post(url)
                            .body(&body_str)
                            .send());
        Self::decode(&mut res)
    }
}

/// A WebDriver session.
///
/// By default the session is removed on `Drop`
pub struct DriverSession {
    driver: Box<Driver>,
    client: HttpClient,
    session_id: String,
    drop_session: bool,
}

impl DriverSession {
    /// Create a new session with the driver.
    pub fn create_session(driver: Box<Driver>)
    -> Result<DriverSession, Error>
    {
        let baseurl = Url::parse(driver.url())
                          .map_err(|_| Error::InvalidUrl)?;
        let mut s = DriverSession {
            driver: driver,
            client: HttpClient::new(baseurl),
            session_id: String::new(),
            drop_session: true,
        };
        info!("Creating session at {}", s.client.baseurl);
        let sess = try!(s.new_session(&NewSessionCmd::new()));
        s.session_id = sess.sessionId;
        info!("Session {} created", s.session_id);
        Ok(s)
    }

    /// Use an existing session
    pub fn attach(url: &str, session_id: &str) -> Result<DriverSession, Error> {
        let driver = Box::new(HttpDriver {
            url: url.to_owned(),
        });
        let baseurl = try!(Url::parse(url).map_err(|_| Error::InvalidUrl));
        let s = DriverSession {
            driver: driver,
            client: HttpClient::new(baseurl),
            session_id: session_id.to_owned(),
            drop_session: true,
        };
        info!("Connecting to session at {} with id {}", url, session_id);

        // FIXME /status would be preferable here to test the connection, but
        // it does not seem to work for the current geckodriver

        // We can fetch any value for the session to verify it exists.
        // The page URL will work.
        let _ = s.get_current_url()?;

        info!("Connected to existing session {}", s.session_id);
        Ok(s)
    }

    pub fn session_id(&self) -> &str {
        &self.session_id
    }

    /// Whether to remove the session on Drop, the default is true
    pub fn drop_session(&mut self, drop: bool) {
        self.drop_session = drop;
    }

    /// Create a new webdriver session
    fn new_session(&mut self, params: &NewSessionCmd) -> Result<Session, Error> {
        let resp: Value<Session> = try!(self.client.post("/session", &params));
        Ok(resp.value)
    }

    /// Navigate to the given URL
    pub fn go(&self, url: &str) -> Result<(), Error> {
        let params = GoCmd { url: url.to_string() };
        let _: Empty = try!(self.client.post(&format!("/session/{}/url", &self.session_id), &params));
        Ok(())
    }

    pub fn get_current_url(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.client.get(&format!("/session/{}/url", self.session_id)));
        Ok(v.value)
    }

    pub fn back(&self) -> Result<(), Error> {
        let _: Empty = try!(self.client.post(&format!("/session/{}/back", self.session_id), &Empty {}));
        Ok(())
    }

    pub fn forward(&self) -> Result<(), Error> {
        let _: Empty = try!(self.client.post(&format!("/session/{}/forward", self.session_id), &Empty {}));
        Ok(())
    }

    pub fn refresh(&self) -> Result<(), Error> {
        let _: Empty = try!(self.client.post(&format!("/session/{}/refresh", self.session_id), &Empty {}));
        Ok(())
    }

    pub fn get_page_source(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.client.get(&format!("/session/{}/source", self.session_id)));
        Ok(v.value)
    }

    pub fn get_title(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.client.get(&format!("/session/{}/title", self.session_id)));
        Ok(v.value)
    }

    /// Get all cookies
    pub fn get_cookies(&self) -> Result<Vec<Cookie>, Error> {
        let v: Value<_> = try!(self.client.get(&format!("/session/{}/cookie", self.session_id)));
        Ok(v.value)
    }

    pub fn get_window_handle(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.client.get(&format!("/session/{}/window", self.session_id)));
        Ok(v.value)
    }

    pub fn switch_window(&mut self, handle: &str) -> Result<(), Error> {
        let _: Empty = try!(self.client.post(&format!("/session/{}/window", self.session_id), &SwitchWindowCmd::from(handle)));
        Ok(())
    }

    pub fn close_window(&mut self) -> Result<(), Error> {
        let _: Empty = try!(self.client.delete(&format!("/session/{}/window", self.session_id)));
        Ok(())
    }

    pub fn get_window_handles(&self) -> Result<Vec<String>, Error> {
        let v: Value<_> = try!(self.client.get(&format!("/session/{}/window/handles", self.session_id)));
        Ok(v.value)
    }

    pub fn find_element(&self, selector: &str, strategy: LocationStrategy) -> Result<Element, Error> {
        let cmd = FindElementCmd { using: strategy, value: selector};
        let v: Value<ElementReference> = try!(self.client.post(&format!("/session/{}/element", self.session_id), &cmd));
        Ok(Element::new(self, v.value.reference))
    }

    pub fn find_elements(&self, selector: &str, strategy: LocationStrategy) -> Result<Vec<Element>, Error> {
        let cmd = FindElementCmd { using: strategy, value: selector};
        let mut v: Value<Vec<ElementReference>> = try!(self.client.post(&format!("/session/{}/elements", self.session_id), &cmd));

        let mut elems = Vec::new();
        while let Some(er) = v.value.pop() {
            elems.push(Element::new(self, er.reference))
        }
        Ok(elems)
    }

    pub fn execute(&self, script: ExecuteCmd) -> Result<JsonValue, Error> {
        let v: Value<JsonValue> = try!(self.client.post(&format!("/session/{}/execute/sync", self.session_id), &script));
        Ok(v.value)
    }

    pub fn execute_async(&self, script: ExecuteCmd) -> Result<JsonValue, Error> {
        let v: Value<JsonValue> = try!(self.client.post(&format!("/session/{}/execute/async", self.session_id), &script));
        Ok(v.value)
    }

    pub fn switch_to_frame(&self, handle: JsonValue) -> Result<(), Error> {
        let _: Empty = try!(self.client.post(&format!("/session/{}/frame", self.session_id), &SwitchFrameCmd::from(handle)));
        Ok(())
    }
}

impl Drop for DriverSession {
    fn drop(&mut self) {
        if self.drop_session {
            let _: Result<Empty,_> = self.client.delete(&format!("/session/{}", self.session_id));
        }
    }
}

pub struct Element<'a> {
    session: &'a DriverSession,
    reference: String,
}

impl<'a> Element<'a> {
    fn new(s: &'a DriverSession, reference: String) -> Self {
        Element { session: s, reference: reference }
    }

    pub fn attribute(&self, name: &str) -> Result<String, Error> {
        let v: Value<_> = try!(self.session.client.get(&format!("/session/{}/element/{}/attribute/{}", self.session.session_id(), self.reference, name)));
        Ok(v.value)
    }

//    pub fn property(&self, name: &str) -> Result<String, Error> {
//        let v: Value<_> = try!(self.client.get(&format!("/session/{}/element/{}/property/{}", self.session_id, el.reference, name)));
//        Ok(v.value)
//    }

    pub fn css_value(&self, name: &str) -> Result<String, Error> {
        let v: Value<_> = try!(self.session.client.get(&format!("/session/{}/element/{}/css/{}", self.session.session_id(), self.reference, name)));
        Ok(v.value)
    }

    pub fn text(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.session.client.get(&format!("/session/{}/element/{}/text", self.session.session_id(), self.reference)));
        Ok(v.value)
    }

    /// Returns the tag name for this element
    pub fn name(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.session.client.get(&format!("/session/{}/element/{}/name", self.session.session_id(), self.reference)));
        Ok(v.value)
    }

    pub fn reference(&self) -> Result<JsonValue, Error> {
        serde_json::to_value(&ElementReference::from_str(&self.reference))
            .map_err(|err| Error::from(err))
    }

    /// Gets the `innerHTML` javascript attribute for this element. Some drivers can get
    /// this using regular attributes, in others it does not work. This method gets it
    /// executing a bit of javascript.
    pub fn inner_html(&self) -> Result<JsonValue, Error> {
        let script = ExecuteCmd {
            script: "return arguments[0].innerHTML;".to_owned(),
            args: vec![self.reference()?],
        };
        self.session.execute(script)
    }

    pub fn outer_html(&self) -> Result<JsonValue, Error> {
        let script = ExecuteCmd {
            script: "return arguments[0].outerHTML;".to_owned(),
            args: vec![self.reference()?],
        };
        self.session.execute(script)
    }
}

/// Switch the context of the current session to the given frame reference.
///
/// This structure implements Drop, and restores the session context
/// to the current top level window.
pub struct FrameContext<'a> {
    session: &'a DriverSession,
}

impl<'a> FrameContext<'a> {
    pub fn new(session: &'a DriverSession, frameref: JsonValue) -> Result<FrameContext<'a>, Error> {
        session.switch_to_frame(frameref)?;
        Ok(FrameContext { session: session })
    }
}

impl<'a> Drop for FrameContext<'a> {
    fn drop(&mut self) {
        let _ = self.session.switch_to_frame(JsonValue::Null);
    }
}
<commit_msg>Do not drop an invalid session<commit_after>#![deny(warnings)]

//! A library to drive web browsers using the webdriver
//! interface.

use std::convert::From;
use std::io::Read;
use std::io;
use std::fmt::{self, Debug};

extern crate hyper;
use hyper::client::*;
use hyper::Url;

extern crate serde;
use serde::Serialize;
use serde::de::DeserializeOwned;

extern crate serde_json;
pub use serde_json::Value as JsonValue;

#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate log;

#[macro_use]
extern crate derive_builder;

extern crate rand;

pub mod messages;
use messages::*;

pub mod firefox;

#[derive(Debug)]
pub enum Error {
    FailedToLaunchDriver,
    InvalidUrl,
    ConnectionError,
    Io(io::Error),
    JsonDecodeError(serde_json::Error),
    WebDriverError(WebDriverError),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::FailedToLaunchDriver => write!(f, "Unable to start browser driver"),
            Error::InvalidUrl => write!(f, "Invalid URL"),
            Error::ConnectionError => write!(f, "Error connecting to browser"),
            Error::Io(ref err) => write!(f, "{}", err),
            Error::JsonDecodeError(ref s) => write!(f, "Received invalid response from browser: {}", s),
            Error::WebDriverError(ref err) => write!(f, "Error: {}", err.message),
        }
    }
}

impl From<hyper::Error> for Error {
    fn from(_: hyper::Error) -> Error {
        Error::ConnectionError
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<serde_json::Error> for Error {
    fn from(e: serde_json::Error) -> Error {
        Error::JsonDecodeError(e)
    }
}

pub trait Driver {
    /// The url used to connect to this driver
    fn url(&self) -> &str;
    /// Start a session for this driver
    fn session(self) -> Result<DriverSession, Error> where Self : Sized + 'static {
        DriverSession::create_session(Box::new(self))
    }
}

/// A driver using a pre-existing WebDriver HTTP URL.
#[derive(Builder)]
#[builder(field(private))]
pub struct HttpDriver {
    #[builder(setter(into))]
    url: String,
}

impl Driver for HttpDriver {
    fn url(&self) -> &str { &self.url }
}

/// Wrapper around the hyper Client, that handles Json encoding and URL construction
struct HttpClient {
    baseurl: Url,
    http: Client,
}

impl HttpClient {
    pub fn new(baseurl: Url) -> Self {
        HttpClient {
            baseurl: baseurl,
            http: Client::new(),
        }
    }

    pub fn decode<D: DeserializeOwned + Debug>(res: &mut Response) -> Result<D, Error> {
        let mut data = String::new();
        try!(res.read_to_string(&mut data));
        debug!("result status: {}\n\
                body: '{}'", res.status, data);

        if !res.status.is_success() {
            let err: Value<WebDriverError> = try!(serde_json::from_str(&data));
            trace!("deserialize error result: {:#?}", err);
            return Err(Error::WebDriverError(err.value));
        }
        let response = serde_json::from_str(&data);
        trace!("deserialize result: {:#?}", response);
        Ok(response?)
    }

    pub fn get<D: DeserializeOwned + Debug>(&self, path: &str) -> Result<D, Error> {
        let url = try!(self.baseurl.join(path)
                           .map_err(|_| Error::InvalidUrl));
        debug!("GET {}", url);
        let mut res = try!(self.http.get(url)
                            .send());
        Self::decode(&mut res)
    }

    pub fn delete<D: DeserializeOwned + Debug>(&self, path: &str) -> Result<D, Error> {
        let url = try!(self.baseurl.join(path)
                           .map_err(|_| Error::InvalidUrl));
        debug!("DELETE {}", url);
        let mut res = try!(self.http.delete(url)
                            .send());
        Self::decode(&mut res)
    }

    pub fn post<D: DeserializeOwned + Debug, E: Serialize>(&self, path: &str, body: &E) -> Result<D, Error> {
        let url = try!(self.baseurl.join(path)
                           .map_err(|_| Error::InvalidUrl));
        let body_str = try!(serde_json::to_string(body));
        debug!("POST url: {}\n\
                body: {}", url, body_str);
        let mut res = try!(self.http.post(url)
                            .body(&body_str)
                            .send());
        Self::decode(&mut res)
    }
}

/// A WebDriver session.
///
/// By default the session is removed on `Drop`
pub struct DriverSession {
    driver: Box<Driver>,
    client: HttpClient,
    session_id: String,
    drop_session: bool,
}

impl DriverSession {
    /// Create a new session with the driver.
    pub fn create_session(driver: Box<Driver>)
    -> Result<DriverSession, Error>
    {
        let baseurl = Url::parse(driver.url())
                          .map_err(|_| Error::InvalidUrl)?;
        let client = HttpClient::new(baseurl);
        info!("Creating session at {}", client.baseurl);
        let sess = try!(Self::new_session(&client, &NewSessionCmd::new()));
        info!("Session {} created", sess.sessionId);
        Ok(DriverSession {
            driver: driver,
            client: client,
            session_id: sess.sessionId,
            drop_session: true,
        })
    }

    /// Use an existing session
    pub fn attach(url: &str, session_id: &str) -> Result<DriverSession, Error> {
        let driver = Box::new(HttpDriver {
            url: url.to_owned(),
        });
        let baseurl = try!(Url::parse(url).map_err(|_| Error::InvalidUrl));
        let s = DriverSession {
            driver: driver,
            client: HttpClient::new(baseurl),
            session_id: session_id.to_owned(),
            drop_session: true,
        };
        info!("Connecting to session at {} with id {}", url, session_id);

        // FIXME /status would be preferable here to test the connection, but
        // it does not seem to work for the current geckodriver

        // We can fetch any value for the session to verify it exists.
        // The page URL will work.
        let _ = s.get_current_url()?;

        info!("Connected to existing session {}", s.session_id);
        Ok(s)
    }

    pub fn session_id(&self) -> &str {
        &self.session_id
    }

    /// Whether to remove the session on Drop, the default is true
    pub fn drop_session(&mut self, drop: bool) {
        self.drop_session = drop;
    }

    /// Create a new webdriver session
    fn new_session(client: &HttpClient, params: &NewSessionCmd) -> Result<Session, Error> {
        let resp: Value<Session> = try!(client.post("/session", &params));
        Ok(resp.value)
    }

    /// Navigate to the given URL
    pub fn go(&self, url: &str) -> Result<(), Error> {
        let params = GoCmd { url: url.to_string() };
        let _: Empty = try!(self.client.post(&format!("/session/{}/url", &self.session_id), &params));
        Ok(())
    }

    pub fn get_current_url(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.client.get(&format!("/session/{}/url", self.session_id)));
        Ok(v.value)
    }

    pub fn back(&self) -> Result<(), Error> {
        let _: Empty = try!(self.client.post(&format!("/session/{}/back", self.session_id), &Empty {}));
        Ok(())
    }

    pub fn forward(&self) -> Result<(), Error> {
        let _: Empty = try!(self.client.post(&format!("/session/{}/forward", self.session_id), &Empty {}));
        Ok(())
    }

    pub fn refresh(&self) -> Result<(), Error> {
        let _: Empty = try!(self.client.post(&format!("/session/{}/refresh", self.session_id), &Empty {}));
        Ok(())
    }

    pub fn get_page_source(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.client.get(&format!("/session/{}/source", self.session_id)));
        Ok(v.value)
    }

    pub fn get_title(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.client.get(&format!("/session/{}/title", self.session_id)));
        Ok(v.value)
    }

    /// Get all cookies
    pub fn get_cookies(&self) -> Result<Vec<Cookie>, Error> {
        let v: Value<_> = try!(self.client.get(&format!("/session/{}/cookie", self.session_id)));
        Ok(v.value)
    }

    pub fn get_window_handle(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.client.get(&format!("/session/{}/window", self.session_id)));
        Ok(v.value)
    }

    pub fn switch_window(&mut self, handle: &str) -> Result<(), Error> {
        let _: Empty = try!(self.client.post(&format!("/session/{}/window", self.session_id), &SwitchWindowCmd::from(handle)));
        Ok(())
    }

    pub fn close_window(&mut self) -> Result<(), Error> {
        let _: Empty = try!(self.client.delete(&format!("/session/{}/window", self.session_id)));
        Ok(())
    }

    pub fn get_window_handles(&self) -> Result<Vec<String>, Error> {
        let v: Value<_> = try!(self.client.get(&format!("/session/{}/window/handles", self.session_id)));
        Ok(v.value)
    }

    pub fn find_element(&self, selector: &str, strategy: LocationStrategy) -> Result<Element, Error> {
        let cmd = FindElementCmd { using: strategy, value: selector};
        let v: Value<ElementReference> = try!(self.client.post(&format!("/session/{}/element", self.session_id), &cmd));
        Ok(Element::new(self, v.value.reference))
    }

    pub fn find_elements(&self, selector: &str, strategy: LocationStrategy) -> Result<Vec<Element>, Error> {
        let cmd = FindElementCmd { using: strategy, value: selector};
        let mut v: Value<Vec<ElementReference>> = try!(self.client.post(&format!("/session/{}/elements", self.session_id), &cmd));

        let mut elems = Vec::new();
        while let Some(er) = v.value.pop() {
            elems.push(Element::new(self, er.reference))
        }
        Ok(elems)
    }

    pub fn execute(&self, script: ExecuteCmd) -> Result<JsonValue, Error> {
        let v: Value<JsonValue> = try!(self.client.post(&format!("/session/{}/execute/sync", self.session_id), &script));
        Ok(v.value)
    }

    pub fn execute_async(&self, script: ExecuteCmd) -> Result<JsonValue, Error> {
        let v: Value<JsonValue> = try!(self.client.post(&format!("/session/{}/execute/async", self.session_id), &script));
        Ok(v.value)
    }

    pub fn switch_to_frame(&self, handle: JsonValue) -> Result<(), Error> {
        let _: Empty = try!(self.client.post(&format!("/session/{}/frame", self.session_id), &SwitchFrameCmd::from(handle)));
        Ok(())
    }
}

impl Drop for DriverSession {
    fn drop(&mut self) {
        if self.drop_session {
            let _: Result<Empty,_> = self.client.delete(&format!("/session/{}", self.session_id));
        }
    }
}

pub struct Element<'a> {
    session: &'a DriverSession,
    reference: String,
}

impl<'a> Element<'a> {
    fn new(s: &'a DriverSession, reference: String) -> Self {
        Element { session: s, reference: reference }
    }

    pub fn attribute(&self, name: &str) -> Result<String, Error> {
        let v: Value<_> = try!(self.session.client.get(&format!("/session/{}/element/{}/attribute/{}", self.session.session_id(), self.reference, name)));
        Ok(v.value)
    }

//    pub fn property(&self, name: &str) -> Result<String, Error> {
//        let v: Value<_> = try!(self.client.get(&format!("/session/{}/element/{}/property/{}", self.session_id, el.reference, name)));
//        Ok(v.value)
//    }

    pub fn css_value(&self, name: &str) -> Result<String, Error> {
        let v: Value<_> = try!(self.session.client.get(&format!("/session/{}/element/{}/css/{}", self.session.session_id(), self.reference, name)));
        Ok(v.value)
    }

    pub fn text(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.session.client.get(&format!("/session/{}/element/{}/text", self.session.session_id(), self.reference)));
        Ok(v.value)
    }

    /// Returns the tag name for this element
    pub fn name(&self) -> Result<String, Error> {
        let v: Value<_> = try!(self.session.client.get(&format!("/session/{}/element/{}/name", self.session.session_id(), self.reference)));
        Ok(v.value)
    }

    pub fn reference(&self) -> Result<JsonValue, Error> {
        serde_json::to_value(&ElementReference::from_str(&self.reference))
            .map_err(|err| Error::from(err))
    }

    /// Gets the `innerHTML` javascript attribute for this element. Some drivers can get
    /// this using regular attributes, in others it does not work. This method gets it
    /// executing a bit of javascript.
    pub fn inner_html(&self) -> Result<JsonValue, Error> {
        let script = ExecuteCmd {
            script: "return arguments[0].innerHTML;".to_owned(),
            args: vec![self.reference()?],
        };
        self.session.execute(script)
    }

    pub fn outer_html(&self) -> Result<JsonValue, Error> {
        let script = ExecuteCmd {
            script: "return arguments[0].outerHTML;".to_owned(),
            args: vec![self.reference()?],
        };
        self.session.execute(script)
    }
}

/// Switch the context of the current session to the given frame reference.
///
/// This structure implements Drop, and restores the session context
/// to the current top level window.
pub struct FrameContext<'a> {
    session: &'a DriverSession,
}

impl<'a> FrameContext<'a> {
    pub fn new(session: &'a DriverSession, frameref: JsonValue) -> Result<FrameContext<'a>, Error> {
        session.switch_to_frame(frameref)?;
        Ok(FrameContext { session: session })
    }
}

impl<'a> Drop for FrameContext<'a> {
    fn drop(&mut self) {
        let _ = self.session.switch_to_frame(JsonValue::Null);
    }
}
<|endoftext|>


<commit_before>#[test]
fn it_works() {
}
<commit_msg>Base types.<commit_after>use std::result::Result;

pub struct BytesConsumed(usize);
pub struct BytesProduced(usize);

pub enum DecodingError {
  InvalidFrame(BytesConsumed),
  IncompleteFrame
}

pub enum EncodingError {
  InvalidFrame,
  InsufficientBufferSize
}

pub struct DecodedFrame<F> {
  frame: F,
  bytes_consumed: BytesConsumed
}

pub struct EncodedFrame<F> {
  frame: F,
  bytes_produced: BytesProduced
}

pub type DecodingResult<F> = Result<Option<DecodedFrame<F>>, DecodingError>;
pub type EncodingResult<F> = Result<Option<EncodedFrame<F>>, EncodingError>;

pub trait Codec<F> {
  fn encode(&mut self, &mut [u8]) -> EncodingResult<F>;
  fn decode(&mut self, &[u8]) -> DecodingResult<F>;
}

#[test]
fn it_works() {
}
<|endoftext|>


<commit_before>#![feature(macro_rules)]
extern crate serialize;
extern crate chrono;

//use std::collections::HashMap;
use serialize::json;
use serialize::json::{
    Boolean,
    F64,
    I64,
    Json,
    List,
    Null,
    String,
    U64,
};
use std::io;
use chrono::NaiveDateTime;

#[deriving(Show, PartialEq)]
pub struct Event {
    client: Client,
    check: Check,
    occurences: Vec<i32>,
    action: String,
}

#[deriving(Show, PartialEq)]
pub struct Client {
    name: String,
    address: String,
    subscriptions: Vec<String>,
    timestamp: NaiveDateTime,
    additional: Json
}

#[deriving(Show, PartialEq)]
pub struct Check {
    name: String,
    issued: NaiveDateTime,
    output: String,
    status: i8,
    command: String,
    subscribers: Vec<String>,
    interval: i64,
    handler: Option<String>,
    handlers: Option<Vec<String>>,
    history: Vec<String>,
    flapping: bool,
    additional: Option<Json>
}

#[deriving(Show, PartialEq)]
pub enum SensuError {
    InitError(String),
    EventError(String)
}

pub type SensuResult<T> = Result<T, SensuError>;

/// Extract a generic JS object, given a type
/// Early return an error if it can't be extracted
macro_rules! jk(
    ($j:ident->$k:expr $p:ident) => (
        match $j.find($k) {
            Some(value) => match value {
                &$p(ref v) => v.clone(),
                _ => return Err(EventError(format!("Wrong type for '{}': {}", $k, value)))
            },
            None => return Err(EventError(format!("couldn't find {} in event", $k)))
        }
    )
)
/// Extract any kind of number as an i64
/// Early return an error if it can't be extracted
macro_rules! jki {
    ($event:ident, $field:expr) => {
        match $event.find($field) {
        Some(value) => match value {
            &I64(ref v) => v.clone(),
            &U64(ref v) => v.clone() as i64,
            &F64(ref v) => v.clone() as i64,
            _ => return Err(EventError(format!("Wrong type for '{}': {}", $field, value)))
        },
        None => return Err(EventError(format!("couldn't find '{}' in event", $field)))
    };
    }
}

pub fn read_stdin() -> SensuResult<String> {
    match io::stdin().read_to_end() {
        Ok(input) => Ok(input.to_string()),
        Err(e) => return Err(InitError(format!("couldn't read stdin: {}", e)))
    }
}

pub fn read_event(input: &str) -> SensuResult<(Client, Check)> {
    let event = match json::from_str(input) {
        Ok(v) => v,
        Err(e) => return Err(InitError(format!("couldn't parse json: {}", e)))
    };

    let client = try!(read_client(&event));
    let check = try!(read_check(&event));
    Ok((client, check))
}

/// Get a timestamp out and format it as a NaiveDateTime
fn pull_timestamp(event: &Json, field: &str) -> SensuResult<NaiveDateTime> {
    let ts = jki!(event, field);
    Ok(NaiveDateTime::from_num_seconds_from_unix_epoch(ts, 0))
}

fn extract_list_of_strings(list: &Json, field_name: &str) -> SensuResult<Vec<String>> {
    match *list {
        List(ref subs) => {
            let mut result = Vec::new();
            for sub in subs.iter() {
                match *sub {
                    String(ref s) => result.push(s.clone()),
                    _ => return Err(InitError(format!("Wrong type in {}: {}",
                                                      field_name, sub)))
                }
            }
            Ok(result)
        },
        _ => return Err(InitError(format!("{} is not a list ({})", field_name, list)))
    }
}

fn find_list_of_strings(obj: &Json, field_name: &str) -> SensuResult<Vec<String>> {
    let contained = obj.find(field_name);
    println!("{} is {}", field_name, contained)
    let result = match contained {
        Some(subs) => {
            try!(extract_list_of_strings(subs, field_name))
        },
        None => return Err(InitError(format!("Couldn't build {} for check", field_name)))
    };
    Ok(result)
}

fn read_client(input: &Json) -> SensuResult<Client> {
    let event = match input.find("client") {
        Some(client) => client,
        None => return Err(InitError("No client in event".into_string()))
    };

    let unverified_subs = jk!(event->"subscriptions" List);
    let mut subs: Vec<String> = Vec::new();
    for sub in unverified_subs.iter() {
        match *sub {
            json::String(ref s) => subs.push(s.clone()),
            _ => return Err(EventError(format!("Invalid subscription type {}", sub)))
        }
    }
    let ts = try!(pull_timestamp(event, "timestamp"));

    let client = Client {
        name: jk!(event->"name" String),
        address: jk!(event->"address" String),
        subscriptions: subs,
        timestamp: ts,
        additional: Null
    };
    Ok(client)
}

pub fn read_check(event: &Json) -> SensuResult<Check> {
    let check = match event.find("check") {
        Some(check) => check,
        None => return Err(InitError("No check in event".into_string()))
    };

    let subscribers = try!(find_list_of_strings(check, "subscribers"));

    Ok(Check {
        name: jk!(check->"name" String),
        issued: try!(pull_timestamp(check, "issued")),
        output: jk!(check->"output" String),
        status: jki!(check, "status") as i8,
        command: jk!(check->"command" String),
        subscribers: subscribers,
        interval: jki!(check, "interval"),
        handler: match check.find("handler") {
            Some(handler) => match *handler {
                String(ref h) => Some(h.clone()),
                _ => return Err(InitError(format!("Wrong type for handler ({})", handler)))
            },
            None => None
        },
        handlers: match check.find("handlers") {
            Some(handlers) => Some(try!(extract_list_of_strings(handlers, "handlers"))),
            None => None
        },
        history: try!(find_list_of_strings(check, "history")),
        flapping: jk!(check->"flapping" Boolean),
        additional: None
    })
}

#[cfg(test)]
mod build_objects {
    use serialize::json;
    use chrono::NaiveDateTime;
    use super::{read_client, read_check, Check};

    #[test]
    fn can_build_client() {
        let event = json::from_str(r#"{
            "client": {
                "name": "hello",
                "address": "192.168.1.1",
                "subscriptions": ["one", "two"],
                "timestamp": 127897
            }
        }"#).unwrap();
        let client = read_client(&event);
        match client {
            Ok(cl) => println!("Parsed: {}", cl),
            Err(e) => panic!("ERROR: {}", e)
        }
    }

    #[test]
    fn can_build_check() {
        let event = json::from_str(r#"{
            "check": {
                "name": "test-check",
                "issued": 1416069607,
                "output": "we have output",
                "status": 0,
                "command": "echo 'we have output'",
                "subscribers": ["examples", "tests"],
                "interval": 60,
                "handler": "default",
                "history": ["0", "0", "0"],
                "flapping": false,
                "additional": null
            }
        }"#).unwrap();
        match read_check(&event) {
            Ok(check) => {
                println!("Parsed: {}", check);
                assert_eq!(check, Check {
                    name: "test-check".into_string(),
                    issued: NaiveDateTime::from_num_seconds_from_unix_epoch(1416069607, 0),
                    output: "we have output".into_string(),
                    status: 0,
                    command: "echo 'we have output'".into_string(),
                    subscribers: vec!("examples".into_string(),
                                      "tests".into_string()),
                    interval: 60,
                    handler: Some("default".into_string()),
                    handlers: None,
                    history: Vec::from_fn(3, |_| "0".into_string()),
                    flapping: false,
                    additional: None });
            },
            Err(e) => panic!("ERROR: {}", e)
        }
    }

    #[test]
    fn can_build_check_with_handlers() {
        let event = json::from_str(r#"{
            "check": {
                "name": "test-check",
                "issued": 1416069607,
                "output": "we have output",
                "status": 0,
                "command": "echo 'we have output'",
                "subscribers": ["examples", "tests"],
                "interval": 60,
                "handlers": ["default", "pagerduty"],
                "history": ["0", "0", "0"],
                "flapping": false,
                "additional": null
            }
        }"#).unwrap();
        match read_check(&event) {
            Ok(cl) => println!("Parsed: {}", cl),
            Err(e) => panic!("ERROR: {}", e)
        }
    }
}
<commit_msg>Docs docs docs docs docs<commit_after>//! Iron Fan: strongly typed Sensu handlers
//!
//! The goal is to make it easy to transplant the majority of the
//! [`sensu-community-plugins`](https://github.com/sensu/sensu-community-plugins)
//! repo over to Rust, supporting the idioms provided there but strongly typed
//! and fast to execute, because your monitoring system shouldn't go down
//! because you mistyped `do_alret('critical')`.
//!
//! Expected use: write a bin that uses iron_fan::init(), which will populate
//! an `Event` and run filters on it:
//!
//! ```rust
//! extern crate iron_fan;
//!
//! fn main() {
//!     let event = try!(iron_fan::init());
//!     let settings = try!(iron_fan::settings());
//! }
//! ```
//!
//! This is my first serious experimentation with Rust, comments apprectiated.
//! Implementation issues that I would like to fix:
//!
//! * `Event`s, `Client`s and `Check`s are manually parsed, insted of being
//!   decoded in some way

#![feature(macro_rules)]
extern crate serialize;
extern crate chrono;

//use std::collections::HashMap;
use serialize::json;
use serialize::json::{
    Boolean,
    F64,
    I64,
    Json,
    List,
    Null,
    String,
    U64,
};
use std::io;
use chrono::NaiveDateTime;

#[deriving(Show, PartialEq)]
pub struct Event {
    client: Client,
    check: Check,
    occurences: Vec<i32>,
    action: String,
}

#[deriving(Show, PartialEq)]
pub struct Client {
    name: String,
    address: String,
    subscriptions: Vec<String>,
    timestamp: NaiveDateTime,
    additional: Json
}

#[deriving(Show, PartialEq)]
pub struct Check {
    name: String,
    issued: NaiveDateTime,
    output: String,
    status: i8,
    command: String,
    subscribers: Vec<String>,
    interval: i64,
    handler: Option<String>,
    handlers: Option<Vec<String>>,
    history: Vec<String>,
    flapping: bool,
    additional: Option<Json>
}

#[deriving(Show, PartialEq)]
pub enum SensuError {
    InitError(String),
    EventError(String)
}

pub type SensuResult<T> = Result<T, SensuError>;

/// Extract a generic JS object, given a type
/// Early return an error if it can't be extracted
macro_rules! jk(
    ($j:ident->$k:expr $p:ident) => (
        match $j.find($k) {
            Some(value) => match value {
                &$p(ref v) => v.clone(),
                _ => return Err(EventError(format!("Wrong type for '{}': {}", $k, value)))
            },
            None => return Err(EventError(format!("couldn't find {} in event", $k)))
        }
    )
)
/// Extract any kind of number as an i64
/// Early return an error if it can't be extracted
macro_rules! jki {
    ($event:ident, $field:expr) => {
        match $event.find($field) {
        Some(value) => match value {
            &I64(ref v) => v.clone(),
            &U64(ref v) => v.clone() as i64,
            &F64(ref v) => v.clone() as i64,
            _ => return Err(EventError(format!("Wrong type for '{}': {}", $field, value)))
        },
        None => return Err(EventError(format!("couldn't find '{}' in event", $field)))
    };
    }
}

pub fn read_stdin() -> SensuResult<String> {
    match io::stdin().read_to_end() {
        Ok(input) => Ok(input.to_string()),
        Err(e) => return Err(InitError(format!("couldn't read stdin: {}", e)))
    }
}

pub fn read_event(input: &str) -> SensuResult<(Client, Check)> {
    let event = match json::from_str(input) {
        Ok(v) => v,
        Err(e) => return Err(InitError(format!("couldn't parse json: {}", e)))
    };

    let client = try!(read_client(&event));
    let check = try!(read_check(&event));
    Ok((client, check))
}

/// Get a timestamp out and format it as a NaiveDateTime
fn pull_timestamp(event: &Json, field: &str) -> SensuResult<NaiveDateTime> {
    let ts = jki!(event, field);
    Ok(NaiveDateTime::from_num_seconds_from_unix_epoch(ts, 0))
}

fn extract_list_of_strings(list: &Json, field_name: &str) -> SensuResult<Vec<String>> {
    match *list {
        List(ref subs) => {
            let mut result = Vec::new();
            for sub in subs.iter() {
                match *sub {
                    String(ref s) => result.push(s.clone()),
                    _ => return Err(InitError(format!("Wrong type in {}: {}",
                                                      field_name, sub)))
                }
            }
            Ok(result)
        },
        _ => return Err(InitError(format!("{} is not a list ({})", field_name, list)))
    }
}

fn find_list_of_strings(obj: &Json, field_name: &str) -> SensuResult<Vec<String>> {
    let contained = obj.find(field_name);
    println!("{} is {}", field_name, contained)
    let result = match contained {
        Some(subs) => {
            try!(extract_list_of_strings(subs, field_name))
        },
        None => return Err(InitError(format!("Couldn't build {} for check", field_name)))
    };
    Ok(result)
}

fn read_client(input: &Json) -> SensuResult<Client> {
    let event = match input.find("client") {
        Some(client) => client,
        None => return Err(InitError("No client in event".into_string()))
    };

    let unverified_subs = jk!(event->"subscriptions" List);
    let mut subs: Vec<String> = Vec::new();
    for sub in unverified_subs.iter() {
        match *sub {
            json::String(ref s) => subs.push(s.clone()),
            _ => return Err(EventError(format!("Invalid subscription type {}", sub)))
        }
    }
    let ts = try!(pull_timestamp(event, "timestamp"));

    let client = Client {
        name: jk!(event->"name" String),
        address: jk!(event->"address" String),
        subscriptions: subs,
        timestamp: ts,
        additional: Null
    };
    Ok(client)
}

pub fn read_check(event: &Json) -> SensuResult<Check> {
    let check = match event.find("check") {
        Some(check) => check,
        None => return Err(InitError("No check in event".into_string()))
    };

    let subscribers = try!(find_list_of_strings(check, "subscribers"));

    Ok(Check {
        name: jk!(check->"name" String),
        issued: try!(pull_timestamp(check, "issued")),
        output: jk!(check->"output" String),
        status: jki!(check, "status") as i8,
        command: jk!(check->"command" String),
        subscribers: subscribers,
        interval: jki!(check, "interval"),
        handler: match check.find("handler") {
            Some(handler) => match *handler {
                String(ref h) => Some(h.clone()),
                _ => return Err(InitError(format!("Wrong type for handler ({})", handler)))
            },
            None => None
        },
        handlers: match check.find("handlers") {
            Some(handlers) => Some(try!(extract_list_of_strings(handlers, "handlers"))),
            None => None
        },
        history: try!(find_list_of_strings(check, "history")),
        flapping: jk!(check->"flapping" Boolean),
        additional: None
    })
}

#[cfg(test)]
mod build_objects {
    use serialize::json;
    use chrono::NaiveDateTime;
    use super::{read_client, read_check, Check};

    #[test]
    fn can_build_client() {
        let event = json::from_str(r#"{
            "client": {
                "name": "hello",
                "address": "192.168.1.1",
                "subscriptions": ["one", "two"],
                "timestamp": 127897
            }
        }"#).unwrap();
        let client = read_client(&event);
        match client {
            Ok(cl) => println!("Parsed: {}", cl),
            Err(e) => panic!("ERROR: {}", e)
        }
    }

    #[test]
    fn can_build_check() {
        let event = json::from_str(r#"{
            "check": {
                "name": "test-check",
                "issued": 1416069607,
                "output": "we have output",
                "status": 0,
                "command": "echo 'we have output'",
                "subscribers": ["examples", "tests"],
                "interval": 60,
                "handler": "default",
                "history": ["0", "0", "0"],
                "flapping": false,
                "additional": null
            }
        }"#).unwrap();
        match read_check(&event) {
            Ok(check) => {
                println!("Parsed: {}", check);
                assert_eq!(check, Check {
                    name: "test-check".into_string(),
                    issued: NaiveDateTime::from_num_seconds_from_unix_epoch(1416069607, 0),
                    output: "we have output".into_string(),
                    status: 0,
                    command: "echo 'we have output'".into_string(),
                    subscribers: vec!("examples".into_string(),
                                      "tests".into_string()),
                    interval: 60,
                    handler: Some("default".into_string()),
                    handlers: None,
                    history: Vec::from_fn(3, |_| "0".into_string()),
                    flapping: false,
                    additional: None });
            },
            Err(e) => panic!("ERROR: {}", e)
        }
    }

    #[test]
    fn can_build_check_with_handlers() {
        let event = json::from_str(r#"{
            "check": {
                "name": "test-check",
                "issued": 1416069607,
                "output": "we have output",
                "status": 0,
                "command": "echo 'we have output'",
                "subscribers": ["examples", "tests"],
                "interval": 60,
                "handlers": ["default", "pagerduty"],
                "history": ["0", "0", "0"],
                "flapping": false,
                "additional": null
            }
        }"#).unwrap();
        match read_check(&event) {
            Ok(cl) => println!("Parsed: {}", cl),
            Err(e) => panic!("ERROR: {}", e)
        }
    }
}
<|endoftext|>


<commit_before>extern crate iron;
extern crate unicase;

use unicase::UniCase;
use iron::prelude::*;
use iron::method::Method;
use iron::status;
use iron::headers::{Origin,
					ContentType,
                    AccessControlRequestMethod,
                    AccessControlAllowOrigin,
                    AccessControlAllowHeaders,
                    AccessControlMaxAge,
                    AccessControlAllowMethods};
use iron::middleware::{AroundMiddleware, Handler};

struct CorsMiddleware;
impl AroundMiddleware for CorsMiddleware {

    fn around(self, handler: Box<Handler>) -> Box<Handler> {
        Box::new(move | req: &mut Request | {
            let allowed_headers: Vec<unicase::UniCase<String>> = vec![
                    // To allow application/json
                    UniCase("Content-Type".to_owned()),
                    // Set by some js libs
                    UniCase("X-Requested-With".to_owned()),
                    ];
            let allowed_methods: Vec<Method> = vec![ Method::Get, Method::Put, Method::Post ];
            if req.method == Method::Options &&
                req.headers.get::<AccessControlRequestMethod>().is_some() {
                // Preflight request
                // 1.If the Origin header is not present terminate this set of steps. The request is
                // outside the scope of this specification.
                let maybe_origin = req.headers.get::<Origin>();
                if maybe_origin.is_none() {
                    let resp = Response::with((status::BadRequest,
                                               "Preflight request without Origin header"));
                    return Ok(resp);
                }
                let origin = maybe_origin.unwrap();
                let mut res = Response::with((status::NoContent));
                //
                // 2.If the value of the Origin header is not a case-sensitive match for any of the
                // values in list of origins do not set any additional headers and terminate this
                // set of steps.
                //
                // Note: Always matching is acceptable since the list of origins can be unbounded.
                //
                // Note: The Origin header can only contain a single origin as the user agent
                //       will not follow redirects.
                //
                // 3. Let method be the value as result of parsing the Access-Control-Request-Method
                // header.
                //
                // If there is no Access-Control-Request-Method header or if parsing failed, do not
                // set any additional headers and terminate this set of steps. The request is
                // outside the scope of this specification.
                //
                // 4. Let header field-names be the values as result of parsing the
                // Access-Control-Request-Headers headers.
                //
                // If there are no Access-Control-Request-Headers headers let header field-names be
                // the empty list.
                //
                // If parsing failed do not set any additional headers and terminate this set of
                // steps. The request is outside the scope of this specification.
                //
                // 5.If method is not a case-sensitive match for any of the values in list of
                //   methods do not set any additional headers and terminate this set of steps.
                //
                // Always matching is acceptable since the list of methods can be unbounded.
                //
                // 6. If any of the header field-names is not a ASCII case-insensitive match for any
                // of the values in list of headers do not set any additional headers and terminate
                // this set of steps.
                //
                // Always matching is acceptable since the list of headers can be unbounded.
                //
                // 7. If the resource supports credentials add a single Access-Control-Allow-Origin
                // header, with the value of the Origin header as value, and add a single
                // Access-Control-Allow-Credentials header with the case-sensitive string "true" as
                // value.
                //
                // Otherwise, add a single Access-Control-Allow-Origin header, with either the
                // value of the Origin header or the string "*" as value.
                res.headers.set(AccessControlAllowOrigin::Value(format!("{}", origin)));
                //
                // The string "*" cannot be used for a resource that supports credentials.
                //
                // 8. Optionally add a single Access-Control-Max-Age header with as value the amount
                // of seconds the user agent is allowed to cache the result of the request.
                res.headers.set(AccessControlMaxAge(60*60));
                //
                // 9. If method is a simple method this step may be skipped.
                //
                // Add one or more Access-Control-Allow-Methods headers consisting of (a subset of)
                // the list of methods.
                res.headers.set(AccessControlAllowMethods(allowed_methods));
                //
                // If a method is a simple method it does not need to be listed, but this is not
                // prohibited.
                //
                // Since the list of methods can be unbounded, simply returning the method
                // indicated by Access-Control-Request-Method (if supported) can be enough.
                //
                // 10.If each of the header field-names is a simple header and none is Content-Type,
                // this step may be skipped.
                //
                // Add one or more Access-Control-Allow-Headers headers consisting of (a subset of)
                // the list of headers.
                res.headers.set(AccessControlAllowHeaders(allowed_headers));
                //
                // If a header field name is a simple header and is not Content-Type, it is not
                // required to be listed. Content-Type is to be listed as only a subset of its
                // values makes it qualify as simple header.
                //
                // Since the list of headers can be unbounded, simply returning supported headers
                // from Access-Control-Allow-Headers can be enough.
                Ok(res)
            } else {
                // Normal request
                // 1.If the Origin header is not present terminate this set of steps. The request is
                // outside the scope of this specification.
                let maybe_origin = req.headers.get::<Origin>();
                if maybe_origin.is_none() {
                    return handler.handle(req);
                }
                let origin = maybe_origin.unwrap();
                //
                // 2.If the value of the Origin header is not a case-sensitive match for any of the
                // values in list of origins, do not set any additional headers and terminate this
                // set of steps.
                //
                // Note: Always matching is acceptable since the list of origins can be unbounded.
                //
                // 3. If the resource supports credentials add a single Access-Control-Allow-Origin
                // header, with the value of the Origin header as value, and add a single
                // Access-Control-Allow-Credentials header with the case-sensitive string "true" as
                // value.
                //
                // Otherwise, add a single Access-Control-Allow-Origin header, with either the
                // value of the Origin header or the string "*" as value.
                //
                // Note: The string "*" cannot be used for a resource that supports credentials.
                //
                // 4. If the list of exposed headers is not empty add one or more
                // Access-Control-Expose-Headers headers, with as values the header field names
                // given in the list of exposed headers.
                let result = handler.handle(req);
                match result {
                    Ok(mut res) => {
                        // And set CORS headers
                        res.headers.set(AccessControlAllowOrigin::Value(format!("{}", origin)));
                        Ok(res)
                    }
                    _ => result
                }
            }
        })
    }
}

#[cfg(test)]
mod tests {
	extern crate router;
	extern crate hyper;
	use iron::Listening;
	use self::router::Router;
	use iron::prelude::*;
	use iron::status;
	use self::hyper::Client;
	use self::hyper::header::Headers;
	use std::io::Read;
    use iron::headers::{Origin,
					ContentType,
                    AccessControlRequestMethod,
                    AccessControlAllowOrigin,
                    AccessControlAllowHeaders,
                    AccessControlMaxAge,
                    AccessControlAllowMethods};
    use iron::middleware::{AroundMiddleware, Handler};
    use iron::method::Method;
    use super::CorsMiddleware;
    use std::str::FromStr;

	struct AutoServer {
		listening: Listening,
		port: u16
	}

	impl AutoServer {
		pub fn new() -> AutoServer {
			let mut router = Router::new();
			router.get("/a",
               |_: &mut Request| Ok(Response::with((status::ImATeapot, ""))),
               "get_a");
            let mut chain = Chain::new(router);
            chain.link_around(CorsMiddleware);
    		let l = Iron::new(chain).http(format!("localhost:0")).unwrap();
			let p = l.socket.port();
			AutoServer { listening: l, port: p }
		}
	}

	impl Drop for AutoServer {
		fn drop(&mut self) {
			// Workaround for https://github.com/hyperium/hyper/issues/338
			self.listening.close().unwrap();
		}
	}

    #[test]
    fn normal_request_possible() {
		let server = AutoServer::new();
		let client = Client::new();
		let res = client.get(&format!("http://127.0.0.1:{}/a", server.port)).send().unwrap();
		assert_eq!(res.status, status::ImATeapot);
	}

    #[test]
    fn preflight_without_origin_is_bad_request() {
		let server = AutoServer::new();
		let client = Client::new();
		let mut headers = Headers::new();
		headers.set(AccessControlRequestMethod(Method::Get));
		let mut res = client
				.request(Method::Options, &format!("http://127.0.0.1:{}/a", server.port))
				.headers(headers)
				.send().unwrap();
		assert_eq!(res.status, status::BadRequest);
        let mut payload = String::new();
        res.read_to_string(&mut payload).unwrap();
        assert_eq!(payload, "Preflight request without Origin header");
	}

    #[test]
    fn preflight_with_origin_accepts_same_origin() {
		let server = AutoServer::new();
		let client = Client::new();
		let mut headers = Headers::new();
		headers.set(AccessControlRequestMethod(Method::Get));
		headers.set(Origin::from_str("http://www.a.com:8080").unwrap());
		let mut res = client
				.request(Method::Options, &format!("http://127.0.0.1:{}/a", server.port))
				.headers(headers)
				.send().unwrap();
		assert_eq!(res.status, status::NoContent);
        let allow_origin = res.headers.get::<AccessControlAllowOrigin>().unwrap();
        assert_eq!(format!("{}", allow_origin) , "http://www.a.com:8080");
        let allow_headers = res.headers.get::<AccessControlAllowHeaders>().unwrap();
        assert_eq!(format!("{}", allow_headers) , "Content-Type, X-Requested-With");
        let allow_methods = res.headers.get::<AccessControlAllowMethods>().unwrap();
        assert_eq!(format!("{}", allow_methods) , "GET, PUT, POST");
        let max_age = res.headers.get::<AccessControlMaxAge>().unwrap();
        assert_eq!(max_age.0, 60*60u32);
	}
}
<commit_msg>working but ugly<commit_after>extern crate iron;
extern crate unicase;

use unicase::UniCase;
use iron::prelude::*;
use iron::method::Method;
use iron::status;
use iron::headers::{Origin,
					ContentType,
                    AccessControlRequestMethod,
                    AccessControlAllowOrigin,
                    AccessControlAllowHeaders,
                    AccessControlMaxAge,
                    AccessControlAllowMethods};
use iron::middleware::{AroundMiddleware, Handler};

struct CorsMiddleware;
impl AroundMiddleware for CorsMiddleware {

    fn around(self, handler: Box<Handler>) -> Box<Handler> {
        Box::new(move | req: &mut Request | {
            let allowed_headers: Vec<unicase::UniCase<String>> = vec![
                    // To allow application/json
                    UniCase("Content-Type".to_owned()),
                    // Set by some js libs
                    UniCase("X-Requested-With".to_owned()),
                    ];
            let allowed_methods: Vec<Method> = vec![ Method::Get, Method::Put, Method::Post ];
            if req.method == Method::Options &&
                req.headers.get::<AccessControlRequestMethod>().is_some() {
                // Preflight request
                // 1.If the Origin header is not present terminate this set of steps. The request is
                // outside the scope of this specification.
                let maybe_origin = req.headers.get::<Origin>();
                if maybe_origin.is_none() {
                    let resp = Response::with((status::BadRequest,
                                               "Preflight request without Origin header"));
                    return Ok(resp);
                }
                let origin = maybe_origin.unwrap();
                let mut res = Response::with((status::NoContent));
                //
                // 2.If the value of the Origin header is not a case-sensitive match for any of the
                // values in list of origins do not set any additional headers and terminate this
                // set of steps.
                //
                // Note: Always matching is acceptable since the list of origins can be unbounded.
                //
                // Note: The Origin header can only contain a single origin as the user agent
                //       will not follow redirects.
                //
                // 3. Let method be the value as result of parsing the Access-Control-Request-Method
                // header.
                //
                // If there is no Access-Control-Request-Method header or if parsing failed, do not
                // set any additional headers and terminate this set of steps. The request is
                // outside the scope of this specification.
                //
                // 4. Let header field-names be the values as result of parsing the
                // Access-Control-Request-Headers headers.
                //
                // If there are no Access-Control-Request-Headers headers let header field-names be
                // the empty list.
                //
                // If parsing failed do not set any additional headers and terminate this set of
                // steps. The request is outside the scope of this specification.
                //
                // 5.If method is not a case-sensitive match for any of the values in list of
                //   methods do not set any additional headers and terminate this set of steps.
                //
                // Always matching is acceptable since the list of methods can be unbounded.
                //
                // 6. If any of the header field-names is not a ASCII case-insensitive match for any
                // of the values in list of headers do not set any additional headers and terminate
                // this set of steps.
                //
                // Always matching is acceptable since the list of headers can be unbounded.
                //
                // 7. If the resource supports credentials add a single Access-Control-Allow-Origin
                // header, with the value of the Origin header as value, and add a single
                // Access-Control-Allow-Credentials header with the case-sensitive string "true" as
                // value.
                //
                // Otherwise, add a single Access-Control-Allow-Origin header, with either the
                // value of the Origin header or the string "*" as value.
                res.headers.set(AccessControlAllowOrigin::Value(format!("{}", origin)));
                //
                // The string "*" cannot be used for a resource that supports credentials.
                //
                // 8. Optionally add a single Access-Control-Max-Age header with as value the amount
                // of seconds the user agent is allowed to cache the result of the request.
                res.headers.set(AccessControlMaxAge(60*60));
                //
                // 9. If method is a simple method this step may be skipped.
                //
                // Add one or more Access-Control-Allow-Methods headers consisting of (a subset of)
                // the list of methods.
                res.headers.set(AccessControlAllowMethods(allowed_methods));
                //
                // If a method is a simple method it does not need to be listed, but this is not
                // prohibited.
                //
                // Since the list of methods can be unbounded, simply returning the method
                // indicated by Access-Control-Request-Method (if supported) can be enough.
                //
                // 10.If each of the header field-names is a simple header and none is Content-Type,
                // this step may be skipped.
                //
                // Add one or more Access-Control-Allow-Headers headers consisting of (a subset of)
                // the list of headers.
                res.headers.set(AccessControlAllowHeaders(allowed_headers));
                //
                // If a header field name is a simple header and is not Content-Type, it is not
                // required to be listed. Content-Type is to be listed as only a subset of its
                // values makes it qualify as simple header.
                //
                // Since the list of headers can be unbounded, simply returning supported headers
                // from Access-Control-Allow-Headers can be enough.
                Ok(res)
            } else {
                // Normal request
                // 1.If the Origin header is not present terminate this set of steps. The request is
                // outside the scope of this specification.
                let has_origin: bool;
                {
                let maybe_origin = req.headers.get::<Origin>();
                has_origin = maybe_origin.is_some();
                }
                if !has_origin {
                    return handler.handle(req);
                }
                //
                // 2.If the value of the Origin header is not a case-sensitive match for any of the
                // values in list of origins, do not set any additional headers and terminate this
                // set of steps.
                //
                // Note: Always matching is acceptable since the list of origins can be unbounded.
                //
                // 3. If the resource supports credentials add a single Access-Control-Allow-Origin
                // header, with the value of the Origin header as value, and add a single
                // Access-Control-Allow-Credentials header with the case-sensitive string "true" as
                // value.
                //
                // Otherwise, add a single Access-Control-Allow-Origin header, with either the
                // value of the Origin header or the string "*" as value.
                //
                // Note: The string "*" cannot be used for a resource that supports credentials.
                //
                // 4. If the list of exposed headers is not empty add one or more
                // Access-Control-Expose-Headers headers, with as values the header field names
                // given in the list of exposed headers.
                let result = handler.handle(req);
                match result {
                    Ok(mut res) => {
                        // And set CORS headers
                        let origin = req.headers.get::<Origin>().unwrap();
                        res.headers.set(AccessControlAllowOrigin::Value(format!("{}", origin)));
                        Ok(res)
                    }
                    _ => result
                }
            }
        })
    }
}

#[cfg(test)]
mod tests {
	extern crate router;
	extern crate hyper;
	use iron::Listening;
	use self::router::Router;
	use iron::prelude::*;
	use iron::status;
	use self::hyper::Client;
	use self::hyper::header::Headers;
	use std::io::Read;
    use iron::headers::{Origin,
					ContentType,
                    AccessControlRequestMethod,
                    AccessControlAllowOrigin,
                    AccessControlAllowHeaders,
                    AccessControlMaxAge,
                    AccessControlAllowMethods};
    use iron::middleware::{AroundMiddleware, Handler};
    use iron::method::Method;
    use super::CorsMiddleware;
    use std::str::FromStr;

	struct AutoServer {
		listening: Listening,
		port: u16
	}

	impl AutoServer {
		pub fn new() -> AutoServer {
			let mut router = Router::new();
			router.get("/a",
               |_: &mut Request| Ok(Response::with((status::ImATeapot, ""))),
               "get_a");
            let mut chain = Chain::new(router);
            chain.link_around(CorsMiddleware);
    		let l = Iron::new(chain).http(format!("localhost:0")).unwrap();
			let p = l.socket.port();
			AutoServer { listening: l, port: p }
		}
	}

	impl Drop for AutoServer {
		fn drop(&mut self) {
			// Workaround for https://github.com/hyperium/hyper/issues/338
			self.listening.close().unwrap();
		}
	}

    #[test]
    fn normal_request_possible() {
		let server = AutoServer::new();
		let client = Client::new();
		let res = client.get(&format!("http://127.0.0.1:{}/a", server.port)).send().unwrap();
		assert_eq!(res.status, status::ImATeapot);
	}

    #[test]
    fn preflight_without_origin_is_bad_request() {
		let server = AutoServer::new();
		let client = Client::new();
		let mut headers = Headers::new();
		headers.set(AccessControlRequestMethod(Method::Get));
		let mut res = client
				.request(Method::Options, &format!("http://127.0.0.1:{}/a", server.port))
				.headers(headers)
				.send().unwrap();
		assert_eq!(res.status, status::BadRequest);
        let mut payload = String::new();
        res.read_to_string(&mut payload).unwrap();
        assert_eq!(payload, "Preflight request without Origin header");
	}

    #[test]
    fn preflight_with_origin_accepts_same_origin() {
		let server = AutoServer::new();
		let client = Client::new();
		let mut headers = Headers::new();
		headers.set(AccessControlRequestMethod(Method::Get));
		headers.set(Origin::from_str("http://www.a.com:8080").unwrap());
		let mut res = client
				.request(Method::Options, &format!("http://127.0.0.1:{}/a", server.port))
				.headers(headers)
				.send().unwrap();
		assert_eq!(res.status, status::NoContent);
        let allow_origin = res.headers.get::<AccessControlAllowOrigin>().unwrap();
        assert_eq!(format!("{}", allow_origin) , "http://www.a.com:8080");
        let allow_headers = res.headers.get::<AccessControlAllowHeaders>().unwrap();
        assert_eq!(format!("{}", allow_headers) , "Content-Type, X-Requested-With");
        let allow_methods = res.headers.get::<AccessControlAllowMethods>().unwrap();
        assert_eq!(format!("{}", allow_methods) , "GET, PUT, POST");
        let max_age = res.headers.get::<AccessControlMaxAge>().unwrap();
        assert_eq!(max_age.0, 60*60u32);
	}
}
<|endoftext|>


<commit_before>//! A minimal implementation of SHA1 for rust.
//!
//! Example:
//!
//! ```rust
//! extern crate sha1;
//! # fn main() {
//!
//! let mut m = sha1::Sha1::new();
//! m.update("Hello World!".as_bytes());
//! assert_eq!(m.hexdigest().as_slice(),
//!            "2ef7bde608ce5404e97d5f042f95f89f1c232871");
//! # }
//! ```

#![feature(slicing_syntax)]

#![experimental]

use std::io::{MemWriter, BufWriter};

/// Represents a Sha1 hash object in memory.
#[derive(Clone)]
pub struct Sha1 {
    state: [u32; 5],
    data: Vec<u8>,
    len: u64,
}

const DEFAULT_STATE : [u32; 5] =
    [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];

fn to_hex(input: &[u8]) -> String {
    let mut s = String::new();
    for b in input.iter() {
        s.push_str(format!("{:02x}", *b)[]);
    }
    return s;
}

impl Sha1 {

    /// Creates an fresh sha1 hash object.
    pub fn new() -> Sha1 {
        Sha1 {
            state: DEFAULT_STATE,
            data: Vec::new(),
            len: 0,
        }
    }

    fn process_block(&mut self, block: &[u8]) {
        assert_eq!(block.len(), 64);

        let mut words = [0u32; 80];
        for (i, chunk) in block.chunks(4).enumerate() {
            words[i] = (chunk[3] as u32) |
                       ((chunk[2] as u32) << 8) |
                       ((chunk[1] as u32) << 16) |
                       ((chunk[0] as u32) << 24);
        }

        fn ff(b: u32, c: u32, d: u32) -> u32 { d ^ (b & (c ^ d)) }
        fn gg(b: u32, c: u32, d: u32) -> u32 { b ^ c ^ d }
        fn hh(b: u32, c: u32, d: u32) -> u32 { (b & c) | (d & (b | c)) }
        fn ii(b: u32, c: u32, d: u32) -> u32 { b ^ c ^ d }

        fn left_rotate(x: u32, n: u32) -> u32 { (x << n as uint) | (x >> (32 - n) as uint) }

        for i in range(16u, 80u) {
            let n = words[i - 3] ^ words[i - 8] ^ words[i - 14] ^ words[i - 16];
            words[i] = left_rotate(n, 1);
        }

        let mut a = self.state[0];
        let mut b = self.state[1];
        let mut c = self.state[2];
        let mut d = self.state[3];
        let mut e = self.state[4];

        for i in range(0u, 80u) {
            let (f, k) = match i {
                0 ... 19 => (ff(b, c, d), 0x5a827999),
                20 ... 39 => (gg(b, c, d), 0x6ed9eba1),
                40 ... 59 => (hh(b, c, d), 0x8f1bbcdc),
                60 ... 79 => (ii(b, c, d), 0xca62c1d6),
                _ => (0, 0),
            };

            let tmp = left_rotate(a, 5) + f + e + k + words[i];
            e = d;
            d = c;
            c = left_rotate(b, 30);
            b = a;
            a = tmp;
        }

        self.state[0] += a;
        self.state[1] += b;
        self.state[2] += c;
        self.state[3] += d;
        self.state[4] += e;
    }

    /// Resets the hash object to it's initial state.
    pub fn reset(&mut self) {
        self.state = DEFAULT_STATE;
        self.data.clear();
        self.len = 0;
    }

    /// Update hash with input data.
    pub fn update(&mut self, data: &[u8]) {
        let mut d = self.data.clone();
        self.data.clear();

        d.push_all(data);

        for chunk in d[].chunks(64) {
            self.len += chunk.len() as u64;

            if chunk.len() == 64 {
                self.process_block(chunk);
            } else {
                self.data.push_all(chunk);
            }
        }
    }

    /// Retrieve digest result.  The output must be large enough to
    /// contain result (20 bytes).
    pub fn output(&self, out: &mut [u8]) {
        // these are unlikely to fail, since we're writing to memory
        #![allow(unused_must_use)]

        let mut m = Sha1 {
            state: self.state,
            data: Vec::new(),
            len: 0,
        };

        let mut w = MemWriter::new();
        w.write(self.data[]);
        w.write_u8(0x80 as u8);
        let padding = (((56 - self.len as int - 1) % 64) + 64) % 64;
        for _ in range(0, padding) {
            w.write_u8(0u8);
        }
        w.write_be_u64(self.len * 8);
        for chunk in w.get_ref()[].chunks(64) {
            m.process_block(chunk);
        }

        let mut w = BufWriter::new(out);
        for &n in m.state.iter() {
            w.write_be_u32(n);
        }
    }

    /// Shortcut for getting `output` into a new vector.
    pub fn digest(&self) -> Vec<u8> {
        let mut buf = [0u8; 20].to_vec();
        self.output(&mut *buf[]);
        buf
    }

    /// Shortcut for getting a hex output of the vector.
    pub fn hexdigest(&self) -> String {
        to_hex(self.digest()[])
    }
}

#[test]
fn test_simple() {
    let mut m = Sha1::new();

    let tests = [
        ("The quick brown fox jumps over the lazy dog",
         "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"),
        ("The quick brown fox jumps over the lazy cog",
         "de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3"),
        ("", "da39a3ee5e6b4b0d3255bfef95601890afd80709"),
        ("testing\n", "9801739daae44ec5293d4e1f53d3f4d2d426d91c"),
        ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
         "025ecbd5d70f8fb3c5457cd96bab13fda305dc59"),
    ];

    for &(s, ref h) in tests.iter() {
        let data = s.as_bytes();

        m.reset();
        m.update(data);
        let hh = m.hexdigest();

        assert_eq!(hh.len(), h.len());
        assert_eq!(hh[], *h);
    }
}
<commit_msg>update to master rust<commit_after>//! A minimal implementation of SHA1 for rust.
//!
//! Example:
//!
//! ```rust
//! extern crate sha1;
//! # fn main() {
//!
//! let mut m = sha1::Sha1::new();
//! m.update("Hello World!".as_bytes());
//! assert_eq!(m.hexdigest().as_slice(),
//!            "2ef7bde608ce5404e97d5f042f95f89f1c232871");
//! # }
//! ```

#![feature(slicing_syntax)]

#![experimental]

use std::io::BufWriter;

/// Represents a Sha1 hash object in memory.
#[derive(Clone)]
pub struct Sha1 {
    state: [u32; 5],
    data: Vec<u8>,
    len: u64,
}

const DEFAULT_STATE : [u32; 5] =
    [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];

fn to_hex(input: &[u8]) -> String {
    let mut s = String::new();
    for b in input.iter() {
        s.push_str(format!("{:02x}", *b)[]);
    }
    return s;
}

impl Sha1 {

    /// Creates an fresh sha1 hash object.
    pub fn new() -> Sha1 {
        Sha1 {
            state: DEFAULT_STATE,
            data: Vec::new(),
            len: 0,
        }
    }

    fn process_block(&mut self, block: &[u8]) {
        assert_eq!(block.len(), 64);

        let mut words = [0u32; 80];
        for (i, chunk) in block.chunks(4).enumerate() {
            words[i] = (chunk[3] as u32) |
                       ((chunk[2] as u32) << 8) |
                       ((chunk[1] as u32) << 16) |
                       ((chunk[0] as u32) << 24);
        }

        fn ff(b: u32, c: u32, d: u32) -> u32 { d ^ (b & (c ^ d)) }
        fn gg(b: u32, c: u32, d: u32) -> u32 { b ^ c ^ d }
        fn hh(b: u32, c: u32, d: u32) -> u32 { (b & c) | (d & (b | c)) }
        fn ii(b: u32, c: u32, d: u32) -> u32 { b ^ c ^ d }

        fn left_rotate(x: u32, n: u32) -> u32 { (x << n as uint) | (x >> (32 - n) as uint) }

        for i in range(16u, 80u) {
            let n = words[i - 3] ^ words[i - 8] ^ words[i - 14] ^ words[i - 16];
            words[i] = left_rotate(n, 1);
        }

        let mut a = self.state[0];
        let mut b = self.state[1];
        let mut c = self.state[2];
        let mut d = self.state[3];
        let mut e = self.state[4];

        for i in range(0u, 80u) {
            let (f, k) = match i {
                0 ... 19 => (ff(b, c, d), 0x5a827999),
                20 ... 39 => (gg(b, c, d), 0x6ed9eba1),
                40 ... 59 => (hh(b, c, d), 0x8f1bbcdc),
                60 ... 79 => (ii(b, c, d), 0xca62c1d6),
                _ => (0, 0),
            };

            let tmp = left_rotate(a, 5) + f + e + k + words[i];
            e = d;
            d = c;
            c = left_rotate(b, 30);
            b = a;
            a = tmp;
        }

        self.state[0] += a;
        self.state[1] += b;
        self.state[2] += c;
        self.state[3] += d;
        self.state[4] += e;
    }

    /// Resets the hash object to it's initial state.
    pub fn reset(&mut self) {
        self.state = DEFAULT_STATE;
        self.data.clear();
        self.len = 0;
    }

    /// Update hash with input data.
    pub fn update(&mut self, data: &[u8]) {
        let mut d = self.data.clone();
        self.data.clear();

        d.push_all(data);

        for chunk in d[].chunks(64) {
            self.len += chunk.len() as u64;

            if chunk.len() == 64 {
                self.process_block(chunk);
            } else {
                self.data.push_all(chunk);
            }
        }
    }

    /// Retrieve digest result.  The output must be large enough to
    /// contain result (20 bytes).
    pub fn output(&self, out: &mut [u8]) {
        // these are unlikely to fail, since we're writing to memory
        #![allow(unused_must_use)]

        let mut m = Sha1 {
            state: self.state,
            data: Vec::new(),
            len: 0,
        };

        let mut w = Vec::<u8>::new();
        w.write(self.data[]);
        w.write_u8(0x80 as u8);
        let padding = (((56 - self.len as int - 1) % 64) + 64) % 64;
        for _ in range(0, padding) {
            w.write_u8(0u8);
        }
        w.write_be_u64(self.len * 8);
        for chunk in w[].chunks(64) {
            m.process_block(chunk);
        }

        let mut w = BufWriter::new(out);
        for &n in m.state.iter() {
            w.write_be_u32(n);
        }
    }

    /// Shortcut for getting `output` into a new vector.
    pub fn digest(&self) -> Vec<u8> {
        let mut buf = [0u8; 20].to_vec();
        self.output(&mut *buf[]);
        buf
    }

    /// Shortcut for getting a hex output of the vector.
    pub fn hexdigest(&self) -> String {
        to_hex(self.digest()[])
    }
}

#[test]
fn test_simple() {
    let mut m = Sha1::new();

    let tests = [
        ("The quick brown fox jumps over the lazy dog",
         "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"),
        ("The quick brown fox jumps over the lazy cog",
         "de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3"),
        ("", "da39a3ee5e6b4b0d3255bfef95601890afd80709"),
        ("testing\n", "9801739daae44ec5293d4e1f53d3f4d2d426d91c"),
        ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
         "025ecbd5d70f8fb3c5457cd96bab13fda305dc59"),
    ];

    for &(s, ref h) in tests.iter() {
        let data = s.as_bytes();

        m.reset();
        m.update(data);
        let hh = m.hexdigest();

        assert_eq!(hh.len(), h.len());
        assert_eq!(hh[], *h);
    }
}
<|endoftext|>


<commit_before>//! Url Encoded middleware for Iron
#![crate_id = "urlencoded"]
#![license = "MIT"]

extern crate url;
extern crate iron;
extern crate serialize;

use iron::{Request, Response, Middleware, Alloy};
use iron::mixin::GetUrl;
use iron::middleware::{Status, Continue, Unwind};

use url::from_str;
use std::collections::HashMap;

/// `urlencoded` returns a hashmap that maps a string to a Vec
/// of strings. If there are two values assigned to the same key
/// the user can ieterate through the Vec to access all data.
#[deriving(Clone)]
pub struct Encoded(pub HashMap<String, Vec<String>>);

#[deriving(Clone)]
pub struct UrlEncoded;

impl UrlEncoded {
    pub fn new() -> UrlEncoded {
        UrlEncoded
    }
}

impl Middleware for UrlEncoded {
    fn enter(&mut self, req: &mut Request, res: &mut Response, alloy: &mut Alloy) -> Status {

        let query = match url::path_from_str(req.url().unwrap().as_slice()) {
            Ok(e) => {
                e.query
            },
            Err(e) => {
                return Continue;
            }
        };
        alloy.insert::<Encoded>(Encoded(createHash(query)));
        Continue
    }
}

fn createHash(q: Vec<(String, String)>) -> HashMap<String, Vec<String>> {
    let mut hashStrVec: HashMap<String, Vec<String>> = HashMap::new();

    for (k, v) in q.move_iter() {
        hashStrVec.find_with_or_insert_with(
            k, v,
            |_, already, new| {
                already.push(new);
            },
            |_, v| vec![v]
        );
    }
    hashStrVec
}
<commit_msg>(refactor) Clarified comments<commit_after>//! Url Encoded middleware for Iron
#![crate_id = "urlencoded"]
#![license = "MIT"]

extern crate url;
extern crate iron;
extern crate serialize;

use iron::{Request, Response, Middleware, Alloy};
use iron::mixin::GetUrl;
use iron::middleware::{Status, Continue, Unwind};

use url::from_str;
use std::collections::HashMap;

/// `urlencoded` inserts a hashmap to an alloy for future access
/// The hashmap maps a string to a Vec of strings which stores
/// values from the url parameters including multiple entries for keys
#[deriving(Clone)]
pub struct Encoded(pub HashMap<String, Vec<String>>);

#[deriving(Clone)]
pub struct UrlEncoded;

impl UrlEncoded {
    pub fn new() -> UrlEncoded {
        UrlEncoded
    }
}

impl Middleware for UrlEncoded {
    fn enter(&mut self, req: &mut Request, res: &mut Response, alloy: &mut Alloy) -> Status {

        let query = match url::path_from_str(req.url().unwrap().as_slice()) {
            Ok(e) => {
                e.query
            },
            Err(e) => {
                return Continue;
            }
        };
        alloy.insert::<Encoded>(Encoded(createHash(query)));
        Continue
    }
}

fn createHash(q: Vec<(String, String)>) -> HashMap<String, Vec<String>> {
    let mut hashStrVec: HashMap<String, Vec<String>> = HashMap::new();

    for (k, v) in q.move_iter() {
        hashStrVec.find_with_or_insert_with(
            k, v,
            |_, already, new| {
                already.push(new);
            },
            |_, v| vec![v]
        );
    }
    hashStrVec
}
<|endoftext|>