use std::marker;
use std::time::Duration;
use libc::{c_int, c_char, c_void, c_long};
use curl_sys;
#[cfg(windows)]
use winapi::fd_set;
#[cfg(unix)]
use libc::fd_set;
use {MultiError, Error};
use easy::Easy;
use panic;
pub struct Multi {
raw: *mut curl_sys::CURLM,
data: Box<MultiData>,
}
struct MultiData {
socket: Box<FnMut(Socket, SocketEvents, usize) + Send>,
timer: Box<FnMut(Option<Duration>) -> bool + Send>,
}
pub struct Message<'multi> {
ptr: *mut curl_sys::CURLMsg,
_multi: &'multi Multi,
}
pub struct EasyHandle {
easy: Easy,
_marker: marker::PhantomData<&'static Multi>,
}
pub struct Events {
bits: c_int,
}
#[derive(Debug)]
pub struct SocketEvents {
bits: c_int,
}
pub type Socket = curl_sys::curl_socket_t;
impl Multi {
pub fn new() -> Multi {
unsafe {
::init();
let ptr = curl_sys::curl_multi_init();
assert!(!ptr.is_null());
Multi {
raw: ptr,
data: Box::new(MultiData {
socket: Box::new(|_, _, _| ()),
timer: Box::new(|_| true),
}),
}
}
}
pub fn socket_function<F>(&mut self, f: F) -> Result<(), MultiError>
where F: FnMut(Socket, SocketEvents, usize) + Send + 'static,
{
self._socket_function(Box::new(f))
}
fn _socket_function(&mut self,
f: Box<FnMut(Socket, SocketEvents, usize) + Send>)
-> Result<(), MultiError>
{
self.data.socket = f;
let cb: curl_sys::curl_socket_callback = cb;
try!(self.setopt_ptr(curl_sys::CURLMOPT_SOCKETFUNCTION,
cb as usize as *const c_char));
let ptr = &*self.data as *const _;
try!(self.setopt_ptr(curl_sys::CURLMOPT_SOCKETDATA,
ptr as *const c_char));
return Ok(());
extern fn cb(_easy: *mut curl_sys::CURL,
socket: curl_sys::curl_socket_t,
what: c_int,
userptr: *mut c_void,
socketp: *mut c_void) -> c_int {
panic::catch(|| unsafe {
let f = &mut (*(userptr as *mut MultiData)).socket;
f(socket, SocketEvents { bits: what }, socketp as usize)
});
0
}
}
pub fn assign(&self,
socket: Socket,
token: usize) -> Result<(), MultiError> {
unsafe {
try!(cvt(curl_sys::curl_multi_assign(self.raw, socket,
token as *mut _)));
Ok(())
}
}
pub fn timer_function<F>(&mut self, f: F) -> Result<(), MultiError>
where F: FnMut(Option<Duration>) -> bool + Send + 'static,
{
self._timer_function(Box::new(f))
}
fn _timer_function(&mut self,
f: Box<FnMut(Option<Duration>) -> bool + Send>)
-> Result<(), MultiError>
{
self.data.timer = f;
let cb: curl_sys::curl_multi_timer_callback = cb;
try!(self.setopt_ptr(curl_sys::CURLMOPT_TIMERFUNCTION,
cb as usize as *const c_char));
let ptr = &*self.data as *const _;
try!(self.setopt_ptr(curl_sys::CURLMOPT_TIMERDATA,
ptr as *const c_char));
return Ok(());
extern fn cb(_multi: *mut curl_sys::CURLM,
timeout_ms: c_long,
user: *mut c_void) -> c_int {
let keep_going = panic::catch(|| unsafe {
let f = &mut (*(user as *mut MultiData)).timer;
if timeout_ms == -1 {
f(None)
} else {
f(Some(Duration::from_millis(timeout_ms as u64)))
}
}).unwrap_or(false);
if keep_going {0} else {-1}
}
}
fn setopt_ptr(&mut self,
opt: curl_sys::CURLMoption,
val: *const c_char) -> Result<(), MultiError> {
unsafe {
cvt(curl_sys::curl_multi_setopt(self.raw, opt, val))
}
}
pub fn add(&self, mut easy: Easy) -> Result<EasyHandle, MultiError> {
easy.transfer();
unsafe {
try!(cvt(curl_sys::curl_multi_add_handle(self.raw, easy.raw())));
}
Ok(EasyHandle {
easy: easy,
_marker: marker::PhantomData,
})
}
pub fn remove(&self, easy: EasyHandle) -> Result<Easy, MultiError> {
unsafe {
try!(cvt(curl_sys::curl_multi_remove_handle(self.raw,
easy.easy.raw())));
}
Ok(easy.easy)
}
pub fn messages<F>(&self, mut f: F) where F: FnMut(Message) {
self._messages(&mut f)
}
fn _messages(&self, mut f: &mut FnMut(Message)) {
let mut queue = 0;
unsafe {
loop {
let ptr = curl_sys::curl_multi_info_read(self.raw, &mut queue);
if ptr.is_null() {
break
}
f(Message { ptr: ptr, _multi: self })
}
}
}
pub fn action(&self, socket: Socket, events: &Events)
-> Result<u32, MultiError> {
let mut remaining = 0;
unsafe {
try!(cvt(curl_sys::curl_multi_socket_action(self.raw,
socket,
events.bits,
&mut remaining)));
Ok(remaining as u32)
}
}
pub fn timeout(&self) -> Result<u32, MultiError> {
let mut remaining = 0;
unsafe {
try!(cvt(curl_sys::curl_multi_socket_action(self.raw,
curl_sys::CURL_SOCKET_BAD,
0,
&mut remaining)));
Ok(remaining as u32)
}
}
pub fn get_timeout(&self) -> Result<Option<Duration>, MultiError> {
let mut ms = 0;
unsafe {
try!(cvt(curl_sys::curl_multi_timeout(self.raw, &mut ms)));
if ms == -1 {
Ok(None)
} else {
Ok(Some(Duration::from_millis(ms as u64)))
}
}
}
pub fn perform(&self) -> Result<u32, MultiError> {
unsafe {
let mut ret = 0;
try!(cvt(curl_sys::curl_multi_perform(self.raw, &mut ret)));
Ok(ret as u32)
}
}
pub fn fdset(&self,
read: Option<&mut fd_set>,
write: Option<&mut fd_set>,
except: Option<&mut fd_set>) -> Result<Option<i32>, MultiError> {
unsafe {
let mut ret = 0;
let read = read.map(|r| r as *mut _).unwrap_or(0 as *mut _);
let write = write.map(|r| r as *mut _).unwrap_or(0 as *mut _);
let except = except.map(|r| r as *mut _).unwrap_or(0 as *mut _);
try!(cvt(curl_sys::curl_multi_fdset(self.raw, read, write, except,
&mut ret)));
if ret == -1 {
Ok(None)
} else {
Ok(Some(ret))
}
}
}
pub fn close(&self) -> Result<(), MultiError> {
unsafe {
cvt(curl_sys::curl_multi_cleanup(self.raw))
}
}
}
fn cvt(code: curl_sys::CURLMcode) -> Result<(), MultiError> {
if code == curl_sys::CURLM_OK {
Ok(())
} else {
Err(MultiError::new(code))
}
}
impl Drop for Multi {
fn drop(&mut self) {
let _ = self.close();
}
}
impl EasyHandle {
pub fn set_token(&mut self, token: usize) -> Result<(), Error> {
unsafe {
::cvt(curl_sys::curl_easy_setopt(self.easy.raw(),
curl_sys::CURLOPT_PRIVATE,
token))
}
}
}
impl<'multi> Message<'multi> {
pub fn result(&self) -> Option<Result<(), Error>> {
unsafe {
if (*self.ptr).msg == curl_sys::CURLMSG_DONE {
Some(::cvt((*self.ptr).data as curl_sys::CURLcode))
} else {
None
}
}
}
pub fn is_for(&self, handle: &EasyHandle) -> bool {
unsafe { (*self.ptr).easy_handle == handle.easy.raw() }
}
pub fn token(&self) -> Result<usize, Error> {
unsafe {
let mut p = 0usize;
try!(::cvt(curl_sys::curl_easy_getinfo((*self.ptr).easy_handle,
curl_sys::CURLINFO_PRIVATE,
&mut p)));
Ok(p)
}
}
}
impl Events {
pub fn new() -> Events {
Events { bits: 0 }
}
pub fn input(&mut self, val: bool) -> &mut Events {
self.flag(curl_sys::CURL_CSELECT_IN, val)
}
pub fn output(&mut self, val: bool) -> &mut Events {
self.flag(curl_sys::CURL_CSELECT_OUT, val)
}
pub fn error(&mut self, val: bool) -> &mut Events {
self.flag(curl_sys::CURL_CSELECT_ERR, val)
}
fn flag(&mut self, flag: c_int, val: bool) -> &mut Events {
if val {
self.bits |= flag;
} else {
self.bits &= !flag;
}
self
}
}
impl SocketEvents {
pub fn input(&self) -> bool {
self.bits & curl_sys::CURL_POLL_IN == curl_sys::CURL_POLL_IN
}
pub fn output(&self) -> bool {
self.bits & curl_sys::CURL_POLL_OUT == curl_sys::CURL_POLL_OUT
}
pub fn input_and_output(&self) -> bool {
self.bits & curl_sys::CURL_POLL_INOUT == curl_sys::CURL_POLL_INOUT
}
pub fn remove(&self) -> bool {
self.bits & curl_sys::CURL_POLL_REMOVE == curl_sys::CURL_POLL_REMOVE
}
}