[go: up one dir, main page]

curl 0.3.0

Rust bindings to libcurl for making HTTP requests
Documentation
#![cfg(unix)]

extern crate curl;
extern crate mio;

use std::collections::HashMap;
use std::io::{Read, Cursor};
use std::time::Duration;

use curl::easy::{Easy, List};
use curl::multi::Multi;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
    })
}

use server::Server;
mod server;

#[test]
fn smoke() {
    let m = Multi::new();
    let mut e = Easy::new();

    let s = Server::new();
    s.receive("\
GET / HTTP/1.1\r\n\
Host: 127.0.0.1:$PORT\r\n\
Accept: */*\r\n\
\r\n");
    s.send("HTTP/1.1 200 OK\r\n\r\n");

    t!(e.url(&s.url("/")));
    let _e = t!(m.add(e));
    while t!(m.perform()) > 0 {
        // ...
    }
}

#[test]
fn smoke2() {
    let m = Multi::new();

    let s1 = Server::new();
    s1.receive("\
GET / HTTP/1.1\r\n\
Host: 127.0.0.1:$PORT\r\n\
Accept: */*\r\n\
\r\n");
    s1.send("HTTP/1.1 200 OK\r\n\r\n");

    let s2 = Server::new();
    s2.receive("\
GET / HTTP/1.1\r\n\
Host: 127.0.0.1:$PORT\r\n\
Accept: */*\r\n\
\r\n");
    s2.send("HTTP/1.1 200 OK\r\n\r\n");

    let mut e1 = Easy::new();
    t!(e1.url(&s1.url("/")));
    let _e1 = t!(m.add(e1));
    let mut e2 = Easy::new();
    t!(e2.url(&s2.url("/")));
    let _e2 = t!(m.add(e2));

    while t!(m.perform()) > 0 {
        // ...
    }

    let mut done = 0;
    m.messages(|msg| {
        msg.result().unwrap().unwrap();
        done += 1;
    });
    assert_eq!(done, 2);
}

#[test]
fn upload_lots() {
    use curl::multi::{Socket, SocketEvents, Events};

    enum Message {
        Timeout(Option<Duration>),
        Wait(Socket, SocketEvents, usize),
    }

    let mut m = Multi::new();
    let mut l = t!(mio::EventLoop::new());
    let io = l.channel();
    t!(m.socket_function(move |socket, events, token| {
        t!(io.send(Message::Wait(socket, events, token)));
    }));
    let io = l.channel();
    t!(m.timer_function(move |dur| {
        t!(io.send(Message::Timeout(dur)));
        true
    }));

    let s = Server::new();
    s.receive(&format!("\
PUT / HTTP/1.1\r\n\
Host: 127.0.0.1:$PORT\r\n\
Accept: */*\r\n\
Content-Length: 131072\r\n\
\r\n\
{}", vec!["a"; 128 * 1024].join("")));
    s.send("\
HTTP/1.1 200 OK\r\n\
\r\n");

    let mut data = Cursor::new(vec![b'a'; 128 * 1024]);
    let mut list = List::new();
    t!(list.append("Expect:"));
    let mut h = Easy::new();
    t!(h.url(&s.url("/")));
    t!(h.put(true));
    t!(h.read_function(move |buf| {
        Ok(data.read(buf).unwrap())
    }));
    t!(h.in_filesize(128 * 1024));
    t!(h.upload(true));
    t!(h.http_headers(list));

    let e = t!(m.add(h));

    assert!(t!(m.perform()) > 0);
    t!(l.run(&mut Handler {
        multi: &m,
        cur_timeout: None,
        next_token: 1,
        token_map: HashMap::new(),
    }));

    let mut done = 0;
    m.messages(|m| {
        m.result().unwrap().unwrap();
        done += 1;
    });
    assert_eq!(done, 1);

    let mut e = t!(e.remove());
    assert_eq!(t!(e.response_code()), 200);

    struct Handler<'a> {
        multi: &'a Multi,
        cur_timeout: Option<mio::Timeout>,
        next_token: usize,
        token_map: HashMap<usize, Socket>,
    }

    impl<'a> mio::Handler for Handler<'a> {
        type Timeout = ();
        type Message = Message;

        fn ready(&mut self,
                 l: &mut mio::EventLoop<Handler<'a>>,
                 token: mio::Token,
                 events: mio::EventSet) {
            let socket = self.token_map[&token.as_usize()];
            let mut e = Events::new();
            if events.is_readable() {
                e.input(true);
            }
            if events.is_writable() {
                e.output(true);
            }
            if events.is_error() {
                e.error(true);
            }
            let remaining = t!(self.multi.action(socket, &e));
            if remaining == 0 {
                l.shutdown();
            }
        }

        fn timeout(&mut self,
                   l: &mut mio::EventLoop<Handler<'a>>,
                   _msg: ()) {
            if t!(self.multi.timeout()) == 0 {
                l.shutdown();
            }
        }

        fn notify(&mut self,
                  l: &mut mio::EventLoop<Handler<'a>>,
                  msg: Message) {
            match msg {
                Message::Timeout(dur) => {
                    if let Some(t) = self.cur_timeout.take() {
                        l.clear_timeout(t);
                    }
                    if let Some(dur) = dur {
                        let ms = dur.as_secs() * 1_000 +
                                 (dur.subsec_nanos() / 1_000_000) as u64;
                        self.cur_timeout = Some(t!(l.timeout_ms((), ms)));
                    }
                }
                Message::Wait(socket, events, token) => {
                    let evented = mio::unix::EventedFd(&socket);
                    if events.remove() {
                        t!(l.deregister(&evented));
                        self.token_map.remove(&token).unwrap();
                    } else {
                        let mut e = mio::EventSet::none();
                        if events.input() {
                            e = e | mio::EventSet::readable();
                        }
                        if events.output() {
                            e = e | mio::EventSet::writable();
                        }
                        if token == 0 {
                            let token = self.next_token;
                            self.next_token += 1;
                            t!(self.multi.assign(socket, token));
                            self.token_map.insert(token, socket);
                            t!(l.register(&evented,
                                          mio::Token(token),
                                          e,
                                          mio::PollOpt::level()));
                        } else {
                            t!(l.reregister(&evented,
                                            mio::Token(token),
                                            e,
                                            mio::PollOpt::level()));
                        }
                    }
                }
            }
        }
    }
}