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
use std::fmt;
use std::string::FromUtf8Error;
use std::io;

use futures::{Future, Stream};
use futures::future;
use tokio_core::reactor::Handle;
use hyper;
use hyper::{Client, Request, Post};
use hyper::error::Error as HyperError;
use hyper::header::{Header, ContentType, ContentLength, Raw, Formatter};

#[derive(Clone, Debug)]
pub struct Action(String);

impl Action {
    pub fn new(action: &str) -> Action {
        Action(action.into())
    }
}

impl Header for Action {
    fn header_name() -> &'static str {
        "SOAPAction"
    }

    #[allow(unused_variables)]
    fn parse_header(raw: &Raw) -> hyper::Result<Action> {
        // Leave unimplemented as we shouldn't need it.
        unimplemented!();
    }

    fn fmt_header(&self, f: &mut Formatter) -> fmt::Result {
        f.fmt_line(&self.0)
    }
}

pub enum Error {
    HttpError(HyperError),
    IoError(io::Error),
}

impl From<HyperError> for Error {
    fn from(err: HyperError) -> Error {
        Error::HttpError(err)
    }
}

impl From<hyper::error::UriError> for Error {
    fn from(err: hyper::error::UriError) -> Error {
        Error::HttpError(HyperError::from(err))
    }
}

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

impl From<FromUtf8Error> for Error {
    fn from(err: FromUtf8Error) -> Error {
        Error::HttpError(HyperError::from(err))
    }
}

pub fn send_async(
    url: &str,
    action: Action,
    body: &str,
    handle: &Handle,
) -> Box<Future<Item = String, Error = Error>> {
    let client = Client::new(&handle);
    let uri = match url.parse() {
        Ok(uri) => uri,
        Err(err) => return Box::new(future::err(Error::from(err))),
    };
    let mut req = Request::new(Post, uri);
    req.headers_mut().set(action);
    req.headers_mut().set(ContentType::xml());
    req.headers_mut().set(ContentLength(body.len() as u64));
    req.set_body(body.to_owned());
    let future = client
        .request(req)
        .and_then(|resp| resp.body().concat2())
        .map_err(|err| Error::from(err))
        .and_then(|bytes| {
            String::from_utf8(bytes.to_vec()).map_err(|err| Error::from(err))
        });
    Box::new(future)
}