1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! Downloader module


extern crate hyper;
extern crate url;
use self::hyper::Client;
use self::hyper::client::RequestBuilder;
use self::hyper::header::Headers;
use self::hyper::status::StatusCode;
use self::hyper::client::response::Response as HpResp;
use self::hyper::error::Error as HpErr;
use self::url::Url;
use std::io::{Read, Error as ReadErr};
use std::io::ErrorKind;

/// Download error occured when issueing a `Request`.
pub enum DownloadError{
    /// The status code is not Ok.
    BadStatus(HpResp),
    /// Special read error timeout.
    TimedOut(HpResp),
    /// Other read error except for timeout.
    ReadError(HpResp, ReadErr),
    /// Errors that can occur in parsing HTTP streams.
    BadRequest(HpErr),
}

/// Http methods
#[derive(Clone)]
pub enum Method {
    /// Http get
    Get,
    /// Http post
    Post,
}

/// A simple ok response including url, headers and body.
pub struct Response {
    /// The target url from the original `Request`
    pub url: Url,
    /// The reponse header
    pub headers: Headers,
    /// The response body. Can be either text or binary.
    pub body: Vec<u8>,//TODO: use a better linear container.
}

/// The content of a `Request` including url, headers and body.
#[derive(Clone)]
pub struct RequestContent{
    /// The target url
    pub url: Url,
    /// The method used to issue the `Request`
    pub method: Method,
    /// The body, text only for now
    pub body: Option<String>,
}

/// A simple request
pub struct Request
{
    /// The request content
    pub content: RequestContent,
    /// The hyper client used for issuing the request
    pub client: Client,
}

impl Request {
    /// Issuse the `Request` to the server
    pub fn download(self)-> Result<Response, DownloadError> {
        let url = self.content.url.clone();
        let mut client: RequestBuilder;
        match self.content.method {
            Method::Get => {
                client = self.client.get(url);
            },
            Method::Post => {
                client = self.client.post(url);
            },
        }
        if let Some(ref body) = self.content.body{
            client = client.body(body);
        }
        let response = client.send();
        match response{
            Ok(mut response) => {
                if let StatusCode::Ok = response.status{
                    let mut buffer = vec![];
                    match response.read_to_end(&mut buffer){
                        Ok(_) => {
                            Ok(Response{
                                url: response.url.clone(),
                                headers: response.headers.clone(),
                                body: buffer,
                            })
                        }
                        Err(e) => {
                            match e.kind(){
                                ErrorKind::TimedOut => {
                                    Err(DownloadError::TimedOut(response))
                                }
                                _ => {
                                    Err(DownloadError::ReadError(response, e))
                                }
                            }
                        }
                    }
                }
                else{
                    Err(DownloadError::BadStatus(response))
                }
            },
            Err(e) => Err(DownloadError::BadRequest(e))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn request_download() {
        let url = Url::parse("http://www.baidu.com").unwrap();
        let client = Client::new();
        let request:Request = Request{
            url: url,
            method: Method::Get,
            body: None,
            client: client,
        };
        request.download().unwrap();
    }
}