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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! A tiny asynchronous HTTP/1.1 client library.
//!
//! # Examples
//!
//! ```no_run
//! # extern crate fibers_global;
//! # extern crate fibers_http_client;
//! # extern crate url;
//! use fibers_http_client::connection::Oneshot;
//! use fibers_http_client::Client;
//! use url::Url;
//!
//! # fn main() {
//! let url = Url::parse("http://localhost/foo/bar").unwrap();
//! let mut client = Client::new(Oneshot);
//! let future = client.request(&url).get();
//!
//! let response = fibers_global::execute(future).unwrap();
//! println!("STATUS: {:?}", response.status_code());
//! println!("BODY: {:?}", response.body());
//! # }
//! ```
#![warn(missing_docs)]
extern crate bytecodec;
extern crate fibers;
#[cfg(test)]
extern crate fibers_global;
#[cfg(test)]
extern crate fibers_http_server;
extern crate futures;
extern crate httpcodec;
extern crate prometrics;
#[macro_use]
extern crate trackable;
extern crate url;

pub use client::Client;
pub use error::{Error, ErrorKind};
pub use request::RequestBuilder;

mod client;
mod connection_pool;
mod error;
mod request;

pub mod connection;
pub mod metrics;

/// This crate specific `Result` type.
pub type Result<T> = std::result::Result<T, Error>;

#[cfg(test)]
mod test {
    use bytecodec::bytes::Utf8Encoder;
    use bytecodec::null::NullDecoder;
    use fibers_http_server::{HandleRequest, Reply, Req, Res, ServerBuilder, Status};
    use futures::future::{ok, Future};
    use httpcodec::{BodyDecoder, BodyEncoder};
    use std;
    use url::Url;

    use super::*;
    use connection::ConnectionPoolBuilder;

    struct Hello;
    impl HandleRequest for Hello {
        const METHOD: &'static str = "GET";
        const PATH: &'static str = "/hello";

        type ReqBody = ();
        type ResBody = String;
        type Decoder = BodyDecoder<NullDecoder>;
        type Encoder = BodyEncoder<Utf8Encoder>;
        type Reply = Reply<Self::ResBody>;

        fn handle_request(&self, _req: Req<Self::ReqBody>) -> Self::Reply {
            Box::new(ok(Res::new(Status::Ok, "hello".to_owned())))
        }
    }

    #[test]
    fn oneshot_connection_works() {
        let addr = "127.0.0.1:14757".parse().unwrap();

        // server
        let mut builder = ServerBuilder::new(addr);
        builder.add_handler(Hello).unwrap();
        let server = builder.finish(fibers_global::handle());
        fibers_global::spawn(server.map_err(|e| panic!("{}", e)));
        std::thread::sleep(std::time::Duration::from_millis(50));

        // client: GET => 200
        let url = Url::parse(&format!("http://{}/hello", addr)).unwrap();
        let mut client = Client::new(connection::Oneshot);
        let future = client.request(&url).get();
        let response = fibers_global::execute(future).unwrap();
        assert_eq!(response.status_code().as_u16(), 200);
        assert_eq!(response.body(), b"hello");

        // client: DELETE => 405
        let url = Url::parse(&format!("http://{}/hello", addr)).unwrap();
        let mut client = Client::new(connection::Oneshot);
        let future = client.request(&url).delete();
        let response = fibers_global::execute(future).unwrap();
        assert_eq!(response.status_code().as_u16(), 405);

        // client: PUT => 404
        let url = Url::parse(&format!("http://{}/world", addr)).unwrap();
        let mut client = Client::new(connection::Oneshot);
        let future = client.request(&url).put(vec![1, 2, 3]);
        let response = fibers_global::execute(future).unwrap();
        assert_eq!(response.status_code().as_u16(), 404);
    }

    #[test]
    fn connection_pool_works() {
        let addr = "127.0.0.1:14758".parse().unwrap();

        // server
        let mut builder = ServerBuilder::new(addr);
        builder.add_handler(Hello).unwrap();
        let server = builder.finish(fibers_global::handle());
        fibers_global::spawn(server.map_err(|e| panic!("{}", e)));
        std::thread::sleep(std::time::Duration::from_millis(50));

        // connection pool
        let pool = ConnectionPoolBuilder::new()
            .max_pool_size(2)
            .finish(fibers_global::handle());
        let pool_handle = pool.handle();
        let metrics = pool.metrics().clone();
        fibers_global::spawn(pool.map_err(|e| panic!("{}", e)));

        // client: GET => 200
        let url = Url::parse(&format!("http://{}/hello", addr)).unwrap();
        let mut client = Client::new(pool_handle.clone());
        let future = client.request(&url).get();
        let response = fibers_global::execute(future).unwrap();
        assert_eq!(response.status_code().as_u16(), 200);
        assert_eq!(response.body(), b"hello");

        // client: DELETE => 405
        let url = Url::parse(&format!("http://{}/hello", addr)).unwrap();
        let mut client = Client::new(pool_handle.clone());
        let future = client.request(&url).delete();
        let response = fibers_global::execute(future).unwrap();
        assert_eq!(response.status_code().as_u16(), 405);

        // client: PUT => 404
        let url = Url::parse(&format!("http://{}/world", addr)).unwrap();
        let mut client = Client::new(pool_handle.clone());
        let future = client.request(&url).put(vec![1, 2, 3]);
        let response = fibers_global::execute(future).unwrap();
        assert_eq!(response.status_code().as_u16(), 404);

        assert_eq!(metrics.lent_connections(), 3);
        assert_eq!(metrics.returned_connections(), 3);
    }
}