Skip to main content

curl/
multi.rs

1//! Multi - initiating multiple requests simultaneously
2
3use std::cell::RefCell;
4use std::fmt;
5use std::marker;
6use std::ptr;
7use std::rc::Rc;
8use std::sync::Arc;
9use std::time::Duration;
10
11use libc::{c_char, c_int, c_long, c_short, c_void};
12
13#[cfg(unix)]
14use libc::{pollfd, POLLIN, POLLOUT, POLLPRI};
15
16use crate::easy::{Easy, Easy2, List};
17use crate::panic;
18use crate::{Error, MultiError};
19
20/// A multi handle for initiating multiple connections simultaneously.
21///
22/// This structure corresponds to `CURLM` in libcurl and provides the ability to
23/// have multiple transfers in flight simultaneously. This handle is then used
24/// to manage each transfer. The main purpose of a `CURLM` is for the
25/// *application* to drive the I/O rather than libcurl itself doing all the
26/// blocking. Methods like `action` allow the application to inform libcurl of
27/// when events have happened.
28///
29/// Lots more documentation can be found on the libcurl [multi tutorial] where
30/// the APIs correspond pretty closely with this crate.
31///
32/// [multi tutorial]: https://curl.haxx.se/libcurl/c/libcurl-multi.html
33pub struct Multi {
34    inner: Rc<MultiInner>,
35}
36
37#[derive(Debug)]
38struct RawMulti {
39    handle: *mut curl_sys::CURLM,
40}
41
42/// Inner data of a safe multi handle. Holds a reference to the raw multi
43/// handle, as well as callback functions set by the user. This struct is
44/// `!Send` and is locked to whatever thread the `Multi` is created on. However,
45/// the inner `RawMulti` can be shared with another thread, but only to be used
46/// for calling thread-safe functions.
47struct MultiInner {
48    raw: Arc<RawMulti>,
49    socket_function: RefCell<Box<dyn FnMut(Socket, SocketEvents, usize) + Send>>,
50    timer_function: RefCell<Box<dyn FnMut(Option<Duration>) -> bool + Send>>,
51}
52
53/// Message from the `messages` function of a multi handle.
54///
55/// Currently only indicates whether a transfer is done.
56pub struct Message<'multi> {
57    ptr: *mut curl_sys::CURLMsg,
58    _multi: &'multi Multi,
59}
60
61/// Wrapper around an easy handle while it's owned by a multi handle.
62///
63/// Once an easy handle has been added to a multi handle then it can no longer
64/// be used via `perform`. This handle is also used to remove the easy handle
65/// from the multi handle when desired.
66pub struct EasyHandle {
67    // Safety: This *must* be before `easy` as it must be dropped first.
68    guard: DetachGuard,
69    easy: Easy,
70    // This is now effectively bound to a `Multi`, so it is no longer sendable.
71    _marker: marker::PhantomData<&'static Multi>,
72}
73
74/// Wrapper around an easy handle while it's owned by a multi handle.
75///
76/// Once an easy handle has been added to a multi handle then it can no longer
77/// be used via `perform`. This handle is also used to remove the easy handle
78/// from the multi handle when desired.
79pub struct Easy2Handle<H> {
80    // Safety: This *must* be before `easy` as it must be dropped first.
81    guard: DetachGuard,
82    easy: Easy2<H>,
83    // This is now effectively bound to a `Multi`, so it is no longer sendable.
84    _marker: marker::PhantomData<&'static Multi>,
85}
86
87/// A guard struct which guarantees that `curl_multi_remove_handle` will be
88/// called on an easy handle, either manually or on drop.
89struct DetachGuard {
90    multi: Rc<MultiInner>,
91    easy: *mut curl_sys::CURL,
92}
93
94/// Notification of the events that have happened on a socket.
95///
96/// This type is passed as an argument to the `action` method on a multi handle
97/// to indicate what events have occurred on a socket.
98pub struct Events {
99    bits: c_int,
100}
101
102/// Notification of events that are requested on a socket.
103///
104/// This type is yielded to the `socket_function` callback to indicate what
105/// events are requested on a socket.
106pub struct SocketEvents {
107    bits: c_int,
108}
109
110/// Raw underlying socket type that the multi handles use
111pub type Socket = curl_sys::curl_socket_t;
112
113/// File descriptor to wait on for use with the `wait` method on a multi handle.
114pub struct WaitFd {
115    inner: curl_sys::curl_waitfd,
116}
117
118/// A handle that can be used to wake up a thread that's blocked in [Multi::poll].
119/// The handle can be passed to and used from any thread.
120#[cfg(feature = "poll_7_68_0")]
121#[derive(Debug, Clone)]
122pub struct MultiWaker {
123    raw: std::sync::Weak<RawMulti>,
124}
125
126#[cfg(feature = "poll_7_68_0")]
127unsafe impl Send for MultiWaker {}
128
129#[cfg(feature = "poll_7_68_0")]
130unsafe impl Sync for MultiWaker {}
131
132impl Multi {
133    /// Creates a new multi session through which multiple HTTP transfers can be
134    /// initiated.
135    pub fn new() -> Multi {
136        unsafe {
137            crate::init();
138            let ptr = curl_sys::curl_multi_init();
139            assert!(!ptr.is_null());
140            Multi {
141                inner: Rc::new(MultiInner {
142                    raw: Arc::new(RawMulti { handle: ptr }),
143                    socket_function: RefCell::new(Box::new(|_, _, _| ())),
144                    timer_function: RefCell::new(Box::new(|_| true)),
145                }),
146            }
147        }
148    }
149
150    /// Set the callback informed about what to wait for
151    ///
152    /// When the `action` function runs, it informs the application about
153    /// updates in the socket (file descriptor) status by doing none, one, or
154    /// multiple calls to the socket callback. The callback gets status updates
155    /// with changes since the previous time the callback was called. See
156    /// `action` for more details on how the callback is used and should work.
157    ///
158    /// The `SocketEvents` parameter informs the callback on the status of the
159    /// given socket, and the methods on that type can be used to learn about
160    /// what's going on with the socket.
161    ///
162    /// The third `usize` parameter is a custom value set by the `assign` method
163    /// below.
164    pub fn socket_function<F>(&mut self, f: F) -> Result<(), MultiError>
165    where
166        F: FnMut(Socket, SocketEvents, usize) + Send + 'static,
167    {
168        self._socket_function(Box::new(f))
169    }
170
171    fn _socket_function(
172        &mut self,
173        f: Box<dyn FnMut(Socket, SocketEvents, usize) + Send>,
174    ) -> Result<(), MultiError> {
175        let _ = self.inner.socket_function.replace(f);
176        let cb: curl_sys::curl_socket_callback = cb;
177        self.setopt_ptr(
178            curl_sys::CURLMOPT_SOCKETFUNCTION,
179            cb as usize as *const c_char,
180        )?;
181        let ptr = Rc::as_ptr(&self.inner).cast();
182        self.setopt_ptr(curl_sys::CURLMOPT_SOCKETDATA, ptr)?;
183        return Ok(());
184
185        // TODO: figure out how to expose `_easy`
186        extern "C" fn cb(
187            _easy: *mut curl_sys::CURL,
188            socket: curl_sys::curl_socket_t,
189            what: c_int,
190            userptr: *mut c_void,
191            socketp: *mut c_void,
192        ) -> c_int {
193            panic::catch(|| unsafe {
194                let inner = &*userptr.cast_const().cast::<MultiInner>();
195                if let Ok(mut f) = inner.socket_function.try_borrow_mut() {
196                    f(socket, SocketEvents { bits: what }, socketp as usize);
197                    0
198                } else {
199                    -1
200                }
201            })
202            .unwrap_or(-1)
203        }
204    }
205
206    /// Set data to associate with an internal socket
207    ///
208    /// This function creates an association in the multi handle between the
209    /// given socket and a private token of the application. This is designed
210    /// for `action` uses.
211    ///
212    /// When set, the token will be passed to all future socket callbacks for
213    /// the specified socket.
214    ///
215    /// If the given socket isn't already in use by libcurl, this function will
216    /// return an error.
217    ///
218    /// libcurl only keeps one single token associated with a socket, so
219    /// calling this function several times for the same socket will make the
220    /// last set token get used.
221    ///
222    /// The idea here being that this association (socket to token) is something
223    /// that just about every application that uses this API will need and then
224    /// libcurl can just as well do it since it already has an internal hash
225    /// table lookup for this.
226    ///
227    /// # Typical Usage
228    ///
229    /// In a typical application you allocate a struct or at least use some kind
230    /// of semi-dynamic data for each socket that we must wait for action on
231    /// when using the `action` approach.
232    ///
233    /// When our socket-callback gets called by libcurl and we get to know about
234    /// yet another socket to wait for, we can use `assign` to point out the
235    /// particular data so that when we get updates about this same socket
236    /// again, we don't have to find the struct associated with this socket by
237    /// ourselves.
238    pub fn assign(&self, socket: Socket, token: usize) -> Result<(), MultiError> {
239        unsafe {
240            cvt(curl_sys::curl_multi_assign(
241                self.inner.raw.handle,
242                socket,
243                token as *mut _,
244            ))?;
245            Ok(())
246        }
247    }
248
249    /// Set callback to receive timeout values
250    ///
251    /// Certain features, such as timeouts and retries, require you to call
252    /// libcurl even when there is no activity on the file descriptors.
253    ///
254    /// Your callback function should install a non-repeating timer with the
255    /// interval specified. Each time that timer fires, call either `action` or
256    /// `perform`, depending on which interface you use.
257    ///
258    /// A timeout value of `None` means you should delete your timer.
259    ///
260    /// A timeout value of 0 means you should call `action` or `perform` (once)
261    /// as soon as possible.
262    ///
263    /// This callback will only be called when the timeout changes.
264    ///
265    /// The timer callback should return `true` on success, and `false` on
266    /// error. This callback can be used instead of, or in addition to,
267    /// `get_timeout`.
268    pub fn timer_function<F>(&mut self, f: F) -> Result<(), MultiError>
269    where
270        F: FnMut(Option<Duration>) -> bool + Send + 'static,
271    {
272        self._timer_function(Box::new(f))
273    }
274
275    fn _timer_function(
276        &mut self,
277        f: Box<dyn FnMut(Option<Duration>) -> bool + Send>,
278    ) -> Result<(), MultiError> {
279        let _ = self.inner.timer_function.replace(f);
280        let cb: curl_sys::curl_multi_timer_callback = cb;
281        self.setopt_ptr(
282            curl_sys::CURLMOPT_TIMERFUNCTION,
283            cb as usize as *const c_char,
284        )?;
285        let ptr = Rc::as_ptr(&self.inner).cast();
286        self.setopt_ptr(curl_sys::CURLMOPT_TIMERDATA, ptr)?;
287        return Ok(());
288
289        // TODO: figure out how to expose `_multi`
290        extern "C" fn cb(
291            _multi: *mut curl_sys::CURLM,
292            timeout_ms: c_long,
293            user: *mut c_void,
294        ) -> c_int {
295            let keep_going = panic::catch(|| unsafe {
296                let inner = &*user.cast_const().cast::<MultiInner>();
297                if let Ok(mut f) = inner.timer_function.try_borrow_mut() {
298                    if timeout_ms == -1 {
299                        f(None)
300                    } else {
301                        f(Some(Duration::from_millis(timeout_ms as u64)))
302                    }
303                } else {
304                    false
305                }
306            })
307            .unwrap_or(false);
308            if keep_going {
309                0
310            } else {
311                -1
312            }
313        }
314    }
315
316    /// Enable or disable HTTP pipelining and multiplexing.
317    ///
318    /// When http_1 is true, enable HTTP/1.1 pipelining, which means that if
319    /// you add a second request that can use an already existing connection,
320    /// the second request will be "piped" on the same connection rather than
321    /// being executed in parallel.
322    ///
323    /// When multiplex is true, enable HTTP/2 multiplexing, which means that
324    /// follow-up requests can re-use an existing connection and send the new
325    /// request multiplexed over that at the same time as other transfers are
326    /// already using that single connection.
327    pub fn pipelining(&mut self, http_1: bool, multiplex: bool) -> Result<(), MultiError> {
328        let bitmask = if http_1 { curl_sys::CURLPIPE_HTTP1 } else { 0 }
329            | if multiplex {
330                curl_sys::CURLPIPE_MULTIPLEX
331            } else {
332                0
333            };
334        self.setopt_long(curl_sys::CURLMOPT_PIPELINING, bitmask)
335    }
336
337    /// Sets the max number of connections to a single host.
338    ///
339    /// Pass a long to indicate the max number of simultaneously open connections
340    /// to a single host (a host being the same as a host name + port number pair).
341    /// For each new session to a host, libcurl will open up a new connection up to the
342    /// limit set by the provided value. When the limit is reached, the sessions will
343    /// be pending until a connection becomes available. If pipelining is enabled,
344    /// libcurl will try to pipeline if the host is capable of it.
345    pub fn set_max_host_connections(&mut self, val: usize) -> Result<(), MultiError> {
346        self.setopt_long(curl_sys::CURLMOPT_MAX_HOST_CONNECTIONS, val as c_long)
347    }
348
349    /// Sets the max simultaneously open connections.
350    ///
351    /// The set number will be used as the maximum number of simultaneously open
352    /// connections in total using this multi handle. For each new session,
353    /// libcurl will open a new connection up to the limit set by the provided
354    /// value. When the limit is reached, the sessions will be pending until
355    /// there are available connections. If pipelining is enabled, libcurl will
356    /// try to pipeline or use multiplexing if the host is capable of it.
357    pub fn set_max_total_connections(&mut self, val: usize) -> Result<(), MultiError> {
358        self.setopt_long(curl_sys::CURLMOPT_MAX_TOTAL_CONNECTIONS, val as c_long)
359    }
360
361    /// Set size of connection cache.
362    ///
363    /// The set number will be used as the maximum amount of simultaneously open
364    /// connections that libcurl may keep in its connection cache after
365    /// completed use. By default libcurl will enlarge the size for each added
366    /// easy handle to make it fit 4 times the number of added easy handles.
367    ///
368    /// By setting this option, you can prevent the cache size from growing
369    /// beyond the limit set by you.
370    ///
371    /// When the cache is full, curl closes the oldest one in the cache to
372    /// prevent the number of open connections from increasing.
373    ///
374    /// See [`set_max_total_connections`](#method.set_max_total_connections) for
375    /// limiting the number of active connections.
376    pub fn set_max_connects(&mut self, val: usize) -> Result<(), MultiError> {
377        self.setopt_long(curl_sys::CURLMOPT_MAXCONNECTS, val as c_long)
378    }
379
380    /// Sets the pipeline length.
381    ///
382    /// This sets the max number that will be used as the maximum amount of
383    /// outstanding requests in an HTTP/1.1 pipelined connection. This option
384    /// is only used for HTTP/1.1 pipelining, and not HTTP/2 multiplexing.
385    pub fn set_pipeline_length(&mut self, val: usize) -> Result<(), MultiError> {
386        self.setopt_long(curl_sys::CURLMOPT_MAX_PIPELINE_LENGTH, val as c_long)
387    }
388
389    /// Sets the number of max concurrent streams for http2.
390    ///
391    /// This sets the max number will be used as the maximum number of
392    /// concurrent streams for a connections that libcurl should support on
393    /// connections done using HTTP/2. Defaults to 100.
394    pub fn set_max_concurrent_streams(&mut self, val: usize) -> Result<(), MultiError> {
395        self.setopt_long(curl_sys::CURLMOPT_MAX_CONCURRENT_STREAMS, val as c_long)
396    }
397
398    fn setopt_long(&mut self, opt: curl_sys::CURLMoption, val: c_long) -> Result<(), MultiError> {
399        unsafe { cvt(curl_sys::curl_multi_setopt(self.inner.raw.handle, opt, val)) }
400    }
401
402    fn setopt_ptr(
403        &mut self,
404        opt: curl_sys::CURLMoption,
405        val: *const c_char,
406    ) -> Result<(), MultiError> {
407        unsafe { cvt(curl_sys::curl_multi_setopt(self.inner.raw.handle, opt, val)) }
408    }
409
410    /// Add an easy handle to a multi session
411    ///
412    /// Adds a standard easy handle to the multi stack. This function call will
413    /// make this multi handle control the specified easy handle.
414    ///
415    /// When an easy interface is added to a multi handle, it will use a shared
416    /// connection cache owned by the multi handle. Removing and adding new easy
417    /// handles will not affect the pool of connections or the ability to do
418    /// connection re-use.
419    ///
420    /// If you have `timer_function` set in the multi handle (and you really
421    /// should if you're working event-based with `action` and friends), that
422    /// callback will be called from within this function to ask for an updated
423    /// timer so that your main event loop will get the activity on this handle
424    /// to get started.
425    ///
426    /// The easy handle will remain added to the multi handle until you remove
427    /// it again with `remove` on the returned handle - even when a transfer
428    /// with that specific easy handle is completed.
429    pub fn add(&self, mut easy: Easy) -> Result<EasyHandle, MultiError> {
430        // Clear any configuration set by previous transfers because we're
431        // moving this into a `Send+'static` situation now basically.
432        easy.transfer();
433
434        unsafe {
435            cvt(curl_sys::curl_multi_add_handle(
436                self.inner.raw.handle,
437                easy.raw(),
438            ))?;
439        }
440        Ok(EasyHandle {
441            guard: DetachGuard {
442                multi: self.inner.clone(),
443                easy: easy.raw(),
444            },
445            easy,
446            _marker: marker::PhantomData,
447        })
448    }
449
450    /// Same as `add`, but works with the `Easy2` type.
451    pub fn add2<H>(&self, easy: Easy2<H>) -> Result<Easy2Handle<H>, MultiError> {
452        unsafe {
453            cvt(curl_sys::curl_multi_add_handle(
454                self.inner.raw.handle,
455                easy.raw(),
456            ))?;
457        }
458        Ok(Easy2Handle {
459            guard: DetachGuard {
460                multi: self.inner.clone(),
461                easy: easy.raw(),
462            },
463            easy,
464            _marker: marker::PhantomData,
465        })
466    }
467
468    /// Remove an easy handle from this multi session
469    ///
470    /// Removes the easy handle from this multi handle. This will make the
471    /// returned easy handle be removed from this multi handle's control.
472    ///
473    /// When the easy handle has been removed from a multi stack, it is again
474    /// perfectly legal to invoke `perform` on it.
475    ///
476    /// Removing an easy handle while being used is perfectly legal and will
477    /// effectively halt the transfer in progress involving that easy handle.
478    /// All other easy handles and transfers will remain unaffected.
479    pub fn remove(&self, mut easy: EasyHandle) -> Result<Easy, MultiError> {
480        easy.guard.detach()?;
481        Ok(easy.easy)
482    }
483
484    /// Same as `remove`, but for `Easy2Handle`.
485    pub fn remove2<H>(&self, mut easy: Easy2Handle<H>) -> Result<Easy2<H>, MultiError> {
486        easy.guard.detach()?;
487        Ok(easy.easy)
488    }
489
490    /// Read multi stack informationals
491    ///
492    /// Ask the multi handle if there are any messages/informationals from the
493    /// individual transfers. Messages may include informationals such as an
494    /// error code from the transfer or just the fact that a transfer is
495    /// completed. More details on these should be written down as well.
496    pub fn messages<F>(&self, mut f: F)
497    where
498        F: FnMut(Message),
499    {
500        self._messages(&mut f)
501    }
502
503    fn _messages(&self, f: &mut dyn FnMut(Message)) {
504        let mut queue = 0;
505        unsafe {
506            loop {
507                let ptr = curl_sys::curl_multi_info_read(self.inner.raw.handle, &mut queue);
508                if ptr.is_null() {
509                    break;
510                }
511                f(Message { ptr, _multi: self })
512            }
513        }
514    }
515
516    /// Inform of reads/writes available data given an action
517    ///
518    /// When the application has detected action on a socket handled by libcurl,
519    /// it should call this function with the sockfd argument set to
520    /// the socket with the action. When the events on a socket are known, they
521    /// can be passed `events`. When the events on a socket are unknown, pass
522    /// `Events::new()` instead, and libcurl will test the descriptor
523    /// internally.
524    ///
525    /// The returned integer will contain the number of running easy handles
526    /// within the multi handle. When this number reaches zero, all transfers
527    /// are complete/done. When you call `action` on a specific socket and the
528    /// counter decreases by one, it DOES NOT necessarily mean that this exact
529    /// socket/transfer is the one that completed. Use `messages` to figure out
530    /// which easy handle that completed.
531    ///
532    /// The `action` function informs the application about updates in the
533    /// socket (file descriptor) status by doing none, one, or multiple calls to
534    /// the socket callback function set with the `socket_function` method. They
535    /// update the status with changes since the previous time the callback was
536    /// called.
537    pub fn action(&self, socket: Socket, events: &Events) -> Result<u32, MultiError> {
538        let mut remaining = 0;
539        unsafe {
540            cvt(curl_sys::curl_multi_socket_action(
541                self.inner.raw.handle,
542                socket,
543                events.bits,
544                &mut remaining,
545            ))?;
546            Ok(remaining as u32)
547        }
548    }
549
550    /// Inform libcurl that a timeout has expired and sockets should be tested.
551    ///
552    /// The returned integer will contain the number of running easy handles
553    /// within the multi handle. When this number reaches zero, all transfers
554    /// are complete/done. When you call `action` on a specific socket and the
555    /// counter decreases by one, it DOES NOT necessarily mean that this exact
556    /// socket/transfer is the one that completed. Use `messages` to figure out
557    /// which easy handle that completed.
558    ///
559    /// Get the timeout time by calling the `timer_function` method. Your
560    /// application will then get called with information on how long to wait
561    /// for socket actions at most before doing the timeout action: call the
562    /// `timeout` method. You can also use the `get_timeout` function to
563    /// poll the value at any given time, but for an event-based system using
564    /// the callback is far better than relying on polling the timeout value.
565    pub fn timeout(&self) -> Result<u32, MultiError> {
566        let mut remaining = 0;
567        unsafe {
568            cvt(curl_sys::curl_multi_socket_action(
569                self.inner.raw.handle,
570                curl_sys::CURL_SOCKET_BAD,
571                0,
572                &mut remaining,
573            ))?;
574            Ok(remaining as u32)
575        }
576    }
577
578    /// Get how long to wait for action before proceeding
579    ///
580    /// An application using the libcurl multi interface should call
581    /// `get_timeout` to figure out how long it should wait for socket actions -
582    /// at most - before proceeding.
583    ///
584    /// Proceeding means either doing the socket-style timeout action: call the
585    /// `timeout` function, or call `perform` if you're using the simpler and
586    /// older multi interface approach.
587    ///
588    /// The timeout value returned is the duration at this very moment. If 0, it
589    /// means you should proceed immediately without waiting for anything. If it
590    /// returns `None`, there's no timeout at all set.
591    ///
592    /// Note: if libcurl returns a `None` timeout here, it just means that
593    /// libcurl currently has no stored timeout value. You must not wait too
594    /// long (more than a few seconds perhaps) before you call `perform` again.
595    pub fn get_timeout(&self) -> Result<Option<Duration>, MultiError> {
596        let mut ms = 0;
597        unsafe {
598            cvt(curl_sys::curl_multi_timeout(self.inner.raw.handle, &mut ms))?;
599            if ms == -1 {
600                Ok(None)
601            } else {
602                Ok(Some(Duration::from_millis(ms as u64)))
603            }
604        }
605    }
606
607    /// Block until activity is detected or a timeout passes.
608    ///
609    /// The timeout is used in millisecond-precision. Large durations are
610    /// clamped at the maximum value curl accepts.
611    ///
612    /// The returned integer will contain the number of internal file
613    /// descriptors on which interesting events occured.
614    ///
615    /// This function is a simpler alternative to using `fdset()` and `select()`
616    /// and does not suffer from file descriptor limits.
617    ///
618    /// # Example
619    ///
620    /// ```
621    /// use curl::multi::Multi;
622    /// use std::time::Duration;
623    ///
624    /// let m = Multi::new();
625    ///
626    /// // Add some Easy handles...
627    ///
628    /// while m.perform().unwrap() > 0 {
629    ///     m.wait(&mut [], Duration::from_secs(1)).unwrap();
630    /// }
631    /// ```
632    pub fn wait(&self, waitfds: &mut [WaitFd], timeout: Duration) -> Result<u32, MultiError> {
633        let timeout_ms = Multi::timeout_i32(timeout);
634        unsafe {
635            let mut ret = 0;
636            cvt(curl_sys::curl_multi_wait(
637                self.inner.raw.handle,
638                waitfds.as_mut_ptr() as *mut _,
639                waitfds.len() as u32,
640                timeout_ms,
641                &mut ret,
642            ))?;
643            Ok(ret as u32)
644        }
645    }
646
647    fn timeout_i32(timeout: Duration) -> i32 {
648        // Duration too large, clamp at maximum value.
649        timeout.as_millis().min(i32::MAX as u128) as i32
650    }
651
652    /// Block until activity is detected or a timeout passes.
653    ///
654    /// The timeout is used in millisecond-precision. Large durations are
655    /// clamped at the maximum value curl accepts.
656    ///
657    /// The returned integer will contain the number of internal file
658    /// descriptors on which interesting events occurred.
659    ///
660    /// This function is a simpler alternative to using `fdset()` and `select()`
661    /// and does not suffer from file descriptor limits.
662    ///
663    /// While this method is similar to [Multi::wait], with the following
664    /// distinctions:
665    /// * If there are no handles added to the multi, poll will honor the
666    /// provided timeout, while [Multi::wait] returns immediately.
667    /// * If poll has blocked due to there being no activity on the handles in
668    /// the Multi, it can be woken up from any thread and at any time before
669    /// the timeout expires.
670    ///
671    /// Requires libcurl 7.66.0 or later.
672    ///
673    /// # Example
674    ///
675    /// ```
676    /// use curl::multi::Multi;
677    /// use std::time::Duration;
678    ///
679    /// let m = Multi::new();
680    ///
681    /// // Add some Easy handles...
682    ///
683    /// while m.perform().unwrap() > 0 {
684    ///     m.poll(&mut [], Duration::from_secs(1)).unwrap();
685    /// }
686    /// ```
687    #[cfg(feature = "poll_7_68_0")]
688    pub fn poll(&self, waitfds: &mut [WaitFd], timeout: Duration) -> Result<u32, MultiError> {
689        let timeout_ms = Multi::timeout_i32(timeout);
690        unsafe {
691            let mut ret = 0;
692            cvt(curl_sys::curl_multi_poll(
693                self.inner.raw.handle,
694                waitfds.as_mut_ptr() as *mut _,
695                waitfds.len() as u32,
696                timeout_ms,
697                &mut ret,
698            ))?;
699            Ok(ret as u32)
700        }
701    }
702
703    /// Returns a new [MultiWaker] that can be used to wake up a thread that's
704    /// currently blocked in [Multi::poll].
705    #[cfg(feature = "poll_7_68_0")]
706    pub fn waker(&self) -> MultiWaker {
707        MultiWaker::new(Arc::downgrade(&self.inner.raw))
708    }
709
710    /// Reads/writes available data from each easy handle.
711    ///
712    /// This function handles transfers on all the added handles that need
713    /// attention in an non-blocking fashion.
714    ///
715    /// When an application has found out there's data available for this handle
716    /// or a timeout has elapsed, the application should call this function to
717    /// read/write whatever there is to read or write right now etc.  This
718    /// method returns as soon as the reads/writes are done. This function does
719    /// not require that there actually is any data available for reading or
720    /// that data can be written, it can be called just in case. It will return
721    /// the number of handles that still transfer data.
722    ///
723    /// If the amount of running handles is changed from the previous call (or
724    /// is less than the amount of easy handles you've added to the multi
725    /// handle), you know that there is one or more transfers less "running".
726    /// You can then call `info` to get information about each individual
727    /// completed transfer, and that returned info includes `Error` and more.
728    /// If an added handle fails very quickly, it may never be counted as a
729    /// running handle.
730    ///
731    /// When running_handles is set to zero (0) on the return of this function,
732    /// there is no longer any transfers in progress.
733    ///
734    /// # Return
735    ///
736    /// Before libcurl version 7.20.0: If you receive `is_call_perform`, this
737    /// basically means that you should call `perform` again, before you select
738    /// on more actions. You don't have to do it immediately, but the return
739    /// code means that libcurl may have more data available to return or that
740    /// there may be more data to send off before it is "satisfied". Do note
741    /// that `perform` will return `is_call_perform` only when it wants to be
742    /// called again immediately. When things are fine and there is nothing
743    /// immediate it wants done, it'll return `Ok` and you need to wait for
744    /// "action" and then call this function again.
745    ///
746    /// This function only returns errors etc regarding the whole multi stack.
747    /// Problems still might have occurred on individual transfers even when
748    /// this function returns `Ok`. Use `info` to figure out how individual
749    /// transfers did.
750    pub fn perform(&self) -> Result<u32, MultiError> {
751        unsafe {
752            let mut ret = 0;
753            cvt(curl_sys::curl_multi_perform(
754                self.inner.raw.handle,
755                &mut ret,
756            ))?;
757            Ok(ret as u32)
758        }
759    }
760
761    /// Extracts file descriptor information from a multi handle
762    ///
763    /// This function extracts file descriptor information from a given
764    /// handle, and libcurl returns its `fd_set` sets. The application can use
765    /// these to `select()` on, but be sure to `FD_ZERO` them before calling
766    /// this function as curl_multi_fdset only adds its own descriptors, it
767    /// doesn't zero or otherwise remove any others. The curl_multi_perform
768    /// function should be called as soon as one of them is ready to be read
769    /// from or written to.
770    ///
771    /// If no file descriptors are set by libcurl, this function will return
772    /// `Ok(None)`. Otherwise `Ok(Some(n))` will be returned where `n` the
773    /// highest descriptor number libcurl set. When `Ok(None)` is returned it
774    /// is because libcurl currently does something that isn't possible for
775    /// your application to monitor with a socket and unfortunately you can
776    /// then not know exactly when the current action is completed using
777    /// `select()`. You then need to wait a while before you proceed and call
778    /// `perform` anyway.
779    ///
780    /// When doing `select()`, you should use `get_timeout` to figure out
781    /// how long to wait for action. Call `perform` even if no activity has
782    /// been seen on the `fd_set`s after the timeout expires as otherwise
783    /// internal retries and timeouts may not work as you'd think and want.
784    ///
785    /// If one of the sockets used by libcurl happens to be larger than what
786    /// can be set in an `fd_set`, which on POSIX systems means that the file
787    /// descriptor is larger than `FD_SETSIZE`, then libcurl will try to not
788    /// set it. Setting a too large file descriptor in an `fd_set` implies an out
789    /// of bounds write which can cause crashes, or worse. The effect of NOT
790    /// storing it will possibly save you from the crash, but will make your
791    /// program NOT wait for sockets it should wait for...
792    pub fn fdset2(
793        &self,
794        read: Option<&mut curl_sys::fd_set>,
795        write: Option<&mut curl_sys::fd_set>,
796        except: Option<&mut curl_sys::fd_set>,
797    ) -> Result<Option<i32>, MultiError> {
798        unsafe {
799            let mut ret = 0;
800            let read = read.map(|r| r as *mut _).unwrap_or(ptr::null_mut());
801            let write = write.map(|r| r as *mut _).unwrap_or(ptr::null_mut());
802            let except = except.map(|r| r as *mut _).unwrap_or(ptr::null_mut());
803            cvt(curl_sys::curl_multi_fdset(
804                self.inner.raw.handle,
805                read,
806                write,
807                except,
808                &mut ret,
809            ))?;
810            if ret == -1 {
811                Ok(None)
812            } else {
813                Ok(Some(ret))
814            }
815        }
816    }
817
818    /// Does nothing and returns `Ok(())`. This method remains for backwards
819    /// compatibility.
820    ///
821    /// This method will be changed to take `self` in a future release.
822    #[doc(hidden)]
823    #[deprecated(
824        since = "0.4.30",
825        note = "cannot close safely without consuming self; \
826                will be changed or removed in a future release"
827    )]
828    pub fn close(&self) -> Result<(), MultiError> {
829        Ok(())
830    }
831
832    /// Get a pointer to the raw underlying CURLM handle.
833    pub fn raw(&self) -> *mut curl_sys::CURLM {
834        self.inner.raw.handle
835    }
836}
837
838impl Drop for MultiInner {
839    fn drop(&mut self) {
840        // The actual raw multi handle can outlive our wrapper (any attached
841        // easy handles will keep it alive until they are also dropped), so we
842        // need to make sure to remove any references to local data that we may
843        // have set on the multi handle before we are dropped.
844
845        // Unset callback functions that are stored in our own struct.
846        unsafe {
847            curl_sys::curl_multi_setopt(
848                self.raw.handle,
849                curl_sys::CURLMOPT_SOCKETFUNCTION,
850                ptr::null::<c_void>(),
851            );
852            curl_sys::curl_multi_setopt(
853                self.raw.handle,
854                curl_sys::CURLMOPT_SOCKETDATA,
855                ptr::null::<c_void>(),
856            );
857            curl_sys::curl_multi_setopt(
858                self.raw.handle,
859                curl_sys::CURLMOPT_TIMERFUNCTION,
860                ptr::null::<c_void>(),
861            );
862            curl_sys::curl_multi_setopt(
863                self.raw.handle,
864                curl_sys::CURLMOPT_TIMERDATA,
865                ptr::null::<c_void>(),
866            );
867        }
868    }
869}
870
871impl Drop for RawMulti {
872    fn drop(&mut self) {
873        unsafe {
874            let _ = cvt(curl_sys::curl_multi_cleanup(self.handle));
875        }
876    }
877}
878
879#[cfg(feature = "poll_7_68_0")]
880impl MultiWaker {
881    /// Creates a new MultiWaker handle.
882    fn new(raw: std::sync::Weak<RawMulti>) -> Self {
883        Self { raw }
884    }
885
886    /// Wakes up a thread that is blocked in [Multi::poll]. This method can be
887    /// invoked from any thread.
888    ///
889    /// Will return an error if the RawMulti has already been dropped.
890    ///
891    /// Requires libcurl 7.68.0 or later.
892    pub fn wakeup(&self) -> Result<(), MultiError> {
893        if let Some(raw) = self.raw.upgrade() {
894            unsafe { cvt(curl_sys::curl_multi_wakeup(raw.handle)) }
895        } else {
896            // This happens if the RawMulti has already been dropped:
897            Err(MultiError::new(curl_sys::CURLM_BAD_HANDLE))
898        }
899    }
900}
901
902fn cvt(code: curl_sys::CURLMcode) -> Result<(), MultiError> {
903    if code == curl_sys::CURLM_OK {
904        Ok(())
905    } else {
906        Err(MultiError::new(code))
907    }
908}
909
910impl fmt::Debug for Multi {
911    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
912        f.debug_struct("Multi")
913            .field("raw", &self.inner.raw)
914            .finish()
915    }
916}
917
918macro_rules! impl_easy_getters {
919    () => {
920        impl_easy_getters! {
921            get {
922                time_condition_unmet -> bool,
923                effective_url -> Option<&str>,
924                effective_url_bytes -> Option<&[u8]>,
925                response_code -> u32,
926                http_connectcode -> u32,
927                filetime -> Option<i64>,
928                download_size -> f64,
929                upload_size -> f64,
930                content_length_download -> f64,
931                total_time -> Duration,
932                namelookup_time -> Duration,
933                connect_time -> Duration,
934                appconnect_time -> Duration,
935                pretransfer_time -> Duration,
936                starttransfer_time -> Duration,
937                redirect_time -> Duration,
938                redirect_count -> u32,
939                redirect_url -> Option<&str>,
940                redirect_url_bytes -> Option<&[u8]>,
941                header_size -> u64,
942                request_size -> u64,
943                content_type -> Option<&str>,
944                content_type_bytes -> Option<&[u8]>,
945                os_errno -> i32,
946                primary_ip -> Option<&str>,
947                primary_port -> u16,
948                local_ip -> Option<&str>,
949                local_port -> u16,
950                num_connects -> u64,
951            }
952
953            get_mut {
954                cookies -> List,
955            }
956        }
957    };
958
959    (get { $($name:ident -> $ret:ty,)* } get_mut { $($mname:ident -> $mret:ty,)* }) => {
960        $(
961            impl_easy_getters!(@ro $name, $ret, concat!(
962                "Same as [`Easy2::",
963                stringify!($name),
964                "`](../easy/struct.Easy2.html#method.",
965                stringify!($name),
966                ")."
967            ));
968        )*
969        $(
970            impl_easy_getters!(@mut $mname, $mret, concat!(
971                "Same as [`Easy2::",
972                stringify!($mname),
973                "`](../easy/struct.Easy2.html#method.",
974                stringify!($mname),
975                ")."
976            ));
977        )*
978    };
979
980    (@ro $name:ident, $ret:ty, $doc:expr) => {
981        #[doc = $doc]
982        pub fn $name(&self) -> Result<$ret, Error> {
983            self.easy.$name()
984        }
985    };
986
987    (@mut $name:ident, $ret:ty, $doc:expr) => {
988        #[doc = $doc]
989        pub fn $name(&mut self) -> Result<$ret, Error> {
990            self.easy.$name()
991        }
992    };
993}
994
995impl EasyHandle {
996    /// Sets an internal private token for this `EasyHandle`.
997    ///
998    /// This function will set the `CURLOPT_PRIVATE` field on the underlying
999    /// easy handle.
1000    pub fn set_token(&mut self, token: usize) -> Result<(), Error> {
1001        unsafe {
1002            crate::cvt(curl_sys::curl_easy_setopt(
1003                self.easy.raw(),
1004                curl_sys::CURLOPT_PRIVATE,
1005                token,
1006            ))
1007        }
1008    }
1009
1010    impl_easy_getters!();
1011
1012    /// Unpause reading on a connection.
1013    ///
1014    /// Using this function, you can explicitly unpause a connection that was
1015    /// previously paused.
1016    ///
1017    /// A connection can be paused by letting the read or the write callbacks
1018    /// return `ReadError::Pause` or `WriteError::Pause`.
1019    ///
1020    /// The chance is high that you will get your write callback called before
1021    /// this function returns.
1022    pub fn unpause_read(&self) -> Result<(), Error> {
1023        self.easy.unpause_read()
1024    }
1025
1026    /// Unpause writing on a connection.
1027    ///
1028    /// Using this function, you can explicitly unpause a connection that was
1029    /// previously paused.
1030    ///
1031    /// A connection can be paused by letting the read or the write callbacks
1032    /// return `ReadError::Pause` or `WriteError::Pause`. A write callback that
1033    /// returns pause signals to the library that it couldn't take care of any
1034    /// data at all, and that data will then be delivered again to the callback
1035    /// when the writing is later unpaused.
1036    pub fn unpause_write(&self) -> Result<(), Error> {
1037        self.easy.unpause_write()
1038    }
1039
1040    /// Get a pointer to the raw underlying CURL handle.
1041    pub fn raw(&self) -> *mut curl_sys::CURL {
1042        self.easy.raw()
1043    }
1044}
1045
1046impl fmt::Debug for EasyHandle {
1047    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1048        self.easy.fmt(f)
1049    }
1050}
1051
1052impl<H> Easy2Handle<H> {
1053    /// Acquires a reference to the underlying handler for events.
1054    pub fn get_ref(&self) -> &H {
1055        self.easy.get_ref()
1056    }
1057
1058    /// Acquires a reference to the underlying handler for events.
1059    pub fn get_mut(&mut self) -> &mut H {
1060        self.easy.get_mut()
1061    }
1062
1063    /// Same as `EasyHandle::set_token`
1064    pub fn set_token(&mut self, token: usize) -> Result<(), Error> {
1065        unsafe {
1066            crate::cvt(curl_sys::curl_easy_setopt(
1067                self.easy.raw(),
1068                curl_sys::CURLOPT_PRIVATE,
1069                token,
1070            ))
1071        }
1072    }
1073
1074    impl_easy_getters!();
1075
1076    /// Unpause reading on a connection.
1077    ///
1078    /// Using this function, you can explicitly unpause a connection that was
1079    /// previously paused.
1080    ///
1081    /// A connection can be paused by letting the read or the write callbacks
1082    /// return `ReadError::Pause` or `WriteError::Pause`.
1083    ///
1084    /// The chance is high that you will get your write callback called before
1085    /// this function returns.
1086    pub fn unpause_read(&self) -> Result<(), Error> {
1087        self.easy.unpause_read()
1088    }
1089
1090    /// Unpause writing on a connection.
1091    ///
1092    /// Using this function, you can explicitly unpause a connection that was
1093    /// previously paused.
1094    ///
1095    /// A connection can be paused by letting the read or the write callbacks
1096    /// return `ReadError::Pause` or `WriteError::Pause`. A write callback that
1097    /// returns pause signals to the library that it couldn't take care of any
1098    /// data at all, and that data will then be delivered again to the callback
1099    /// when the writing is later unpaused.
1100    pub fn unpause_write(&self) -> Result<(), Error> {
1101        self.easy.unpause_write()
1102    }
1103
1104    /// Get a pointer to the raw underlying CURL handle.
1105    pub fn raw(&self) -> *mut curl_sys::CURL {
1106        self.easy.raw()
1107    }
1108}
1109
1110impl<H: fmt::Debug> fmt::Debug for Easy2Handle<H> {
1111    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1112        self.easy.fmt(f)
1113    }
1114}
1115
1116impl DetachGuard {
1117    /// Detach the referenced easy handle from its multi handle manually.
1118    /// Subsequent calls to this method will have no effect.
1119    fn detach(&mut self) -> Result<(), MultiError> {
1120        if !self.easy.is_null() {
1121            unsafe {
1122                cvt(curl_sys::curl_multi_remove_handle(
1123                    self.multi.raw.handle,
1124                    self.easy,
1125                ))?
1126            }
1127
1128            // Set easy to null to signify that the handle was removed.
1129            self.easy = ptr::null_mut();
1130        }
1131
1132        Ok(())
1133    }
1134}
1135
1136impl Drop for DetachGuard {
1137    fn drop(&mut self) {
1138        let _ = self.detach();
1139    }
1140}
1141
1142impl<'multi> Message<'multi> {
1143    /// If this message indicates that a transfer has finished, returns the
1144    /// result of the transfer in `Some`.
1145    ///
1146    /// If the message doesn't indicate that a transfer has finished, then
1147    /// `None` is returned.
1148    ///
1149    /// Note that the `result*_for` methods below should be preferred as they
1150    /// provide better error messages as the associated error data on the
1151    /// handle can be associated with the error type.
1152    pub fn result(&self) -> Option<Result<(), Error>> {
1153        unsafe {
1154            if (*self.ptr).msg == curl_sys::CURLMSG_DONE {
1155                Some(crate::cvt((*self.ptr).data as curl_sys::CURLcode))
1156            } else {
1157                None
1158            }
1159        }
1160    }
1161
1162    /// Same as `result`, except only returns `Some` for the specified handle.
1163    ///
1164    /// Note that this function produces better error messages than `result` as
1165    /// it uses `take_error_buf` to associate error information with the
1166    /// returned error.
1167    pub fn result_for(&self, handle: &EasyHandle) -> Option<Result<(), Error>> {
1168        if !self.is_for(handle) {
1169            return None;
1170        }
1171        let mut err = self.result();
1172        if let Some(Err(e)) = &mut err {
1173            if let Some(s) = handle.easy.take_error_buf() {
1174                e.set_extra(s);
1175            }
1176        }
1177        err
1178    }
1179
1180    /// Same as `result`, except only returns `Some` for the specified handle.
1181    ///
1182    /// Note that this function produces better error messages than `result` as
1183    /// it uses `take_error_buf` to associate error information with the
1184    /// returned error.
1185    pub fn result_for2<H>(&self, handle: &Easy2Handle<H>) -> Option<Result<(), Error>> {
1186        if !self.is_for2(handle) {
1187            return None;
1188        }
1189        let mut err = self.result();
1190        if let Some(Err(e)) = &mut err {
1191            if let Some(s) = handle.easy.take_error_buf() {
1192                e.set_extra(s);
1193            }
1194        }
1195        err
1196    }
1197
1198    /// Returns whether this easy message was for the specified easy handle or
1199    /// not.
1200    pub fn is_for(&self, handle: &EasyHandle) -> bool {
1201        unsafe { (*self.ptr).easy_handle == handle.easy.raw() }
1202    }
1203
1204    /// Same as `is_for`, but for `Easy2Handle`.
1205    pub fn is_for2<H>(&self, handle: &Easy2Handle<H>) -> bool {
1206        unsafe { (*self.ptr).easy_handle == handle.easy.raw() }
1207    }
1208
1209    /// Returns the token associated with the easy handle that this message
1210    /// represents a completion for.
1211    ///
1212    /// This function will return the token assigned with
1213    /// `EasyHandle::set_token`. This reads the `CURLINFO_PRIVATE` field of the
1214    /// underlying `*mut CURL`.
1215    pub fn token(&self) -> Result<usize, Error> {
1216        unsafe {
1217            let mut p = 0usize;
1218            crate::cvt(curl_sys::curl_easy_getinfo(
1219                (*self.ptr).easy_handle,
1220                curl_sys::CURLINFO_PRIVATE,
1221                &mut p,
1222            ))?;
1223            Ok(p)
1224        }
1225    }
1226}
1227
1228impl<'a> fmt::Debug for Message<'a> {
1229    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1230        f.debug_struct("Message").field("ptr", &self.ptr).finish()
1231    }
1232}
1233
1234impl Events {
1235    /// Creates a new blank event bit mask.
1236    pub fn new() -> Events {
1237        Events { bits: 0 }
1238    }
1239
1240    /// Set or unset the whether these events indicate that input is ready.
1241    pub fn input(&mut self, val: bool) -> &mut Events {
1242        self.flag(curl_sys::CURL_CSELECT_IN, val)
1243    }
1244
1245    /// Set or unset the whether these events indicate that output is ready.
1246    pub fn output(&mut self, val: bool) -> &mut Events {
1247        self.flag(curl_sys::CURL_CSELECT_OUT, val)
1248    }
1249
1250    /// Set or unset the whether these events indicate that an error has
1251    /// happened.
1252    pub fn error(&mut self, val: bool) -> &mut Events {
1253        self.flag(curl_sys::CURL_CSELECT_ERR, val)
1254    }
1255
1256    fn flag(&mut self, flag: c_int, val: bool) -> &mut Events {
1257        if val {
1258            self.bits |= flag;
1259        } else {
1260            self.bits &= !flag;
1261        }
1262        self
1263    }
1264}
1265
1266impl fmt::Debug for Events {
1267    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1268        f.debug_struct("Events")
1269            .field("input", &(self.bits & curl_sys::CURL_CSELECT_IN != 0))
1270            .field("output", &(self.bits & curl_sys::CURL_CSELECT_OUT != 0))
1271            .field("error", &(self.bits & curl_sys::CURL_CSELECT_ERR != 0))
1272            .finish()
1273    }
1274}
1275
1276impl SocketEvents {
1277    /// Wait for incoming data. For the socket to become readable.
1278    pub fn input(&self) -> bool {
1279        self.bits & curl_sys::CURL_POLL_IN == curl_sys::CURL_POLL_IN
1280    }
1281
1282    /// Wait for outgoing data. For the socket to become writable.
1283    pub fn output(&self) -> bool {
1284        self.bits & curl_sys::CURL_POLL_OUT == curl_sys::CURL_POLL_OUT
1285    }
1286
1287    /// Wait for incoming and outgoing data. For the socket to become readable
1288    /// or writable.
1289    pub fn input_and_output(&self) -> bool {
1290        self.bits & curl_sys::CURL_POLL_INOUT == curl_sys::CURL_POLL_INOUT
1291    }
1292
1293    /// The specified socket/file descriptor is no longer used by libcurl.
1294    pub fn remove(&self) -> bool {
1295        self.bits & curl_sys::CURL_POLL_REMOVE == curl_sys::CURL_POLL_REMOVE
1296    }
1297}
1298
1299impl fmt::Debug for SocketEvents {
1300    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1301        f.debug_struct("Events")
1302            .field("input", &self.input())
1303            .field("output", &self.output())
1304            .field("remove", &self.remove())
1305            .finish()
1306    }
1307}
1308
1309impl WaitFd {
1310    /// Constructs an empty (invalid) WaitFd.
1311    pub fn new() -> WaitFd {
1312        WaitFd {
1313            inner: curl_sys::curl_waitfd {
1314                fd: 0,
1315                events: 0,
1316                revents: 0,
1317            },
1318        }
1319    }
1320
1321    /// Set the file descriptor to wait for.
1322    pub fn set_fd(&mut self, fd: Socket) {
1323        self.inner.fd = fd;
1324    }
1325
1326    /// Indicate that the socket should poll on read events such as new data
1327    /// received.
1328    ///
1329    /// Corresponds to `CURL_WAIT_POLLIN`.
1330    pub fn poll_on_read(&mut self, val: bool) -> &mut WaitFd {
1331        self.flag(curl_sys::CURL_WAIT_POLLIN, val)
1332    }
1333
1334    /// Indicate that the socket should poll on high priority read events such
1335    /// as out of band data.
1336    ///
1337    /// Corresponds to `CURL_WAIT_POLLPRI`.
1338    pub fn poll_on_priority_read(&mut self, val: bool) -> &mut WaitFd {
1339        self.flag(curl_sys::CURL_WAIT_POLLPRI, val)
1340    }
1341
1342    /// Indicate that the socket should poll on write events such as the socket
1343    /// being clear to write without blocking.
1344    ///
1345    /// Corresponds to `CURL_WAIT_POLLOUT`.
1346    pub fn poll_on_write(&mut self, val: bool) -> &mut WaitFd {
1347        self.flag(curl_sys::CURL_WAIT_POLLOUT, val)
1348    }
1349
1350    fn flag(&mut self, flag: c_short, val: bool) -> &mut WaitFd {
1351        if val {
1352            self.inner.events |= flag;
1353        } else {
1354            self.inner.events &= !flag;
1355        }
1356        self
1357    }
1358
1359    /// After a call to `wait`, returns `true` if `poll_on_read` was set and a
1360    /// read event occured.
1361    pub fn received_read(&self) -> bool {
1362        self.inner.revents & curl_sys::CURL_WAIT_POLLIN == curl_sys::CURL_WAIT_POLLIN
1363    }
1364
1365    /// After a call to `wait`, returns `true` if `poll_on_priority_read` was set and a
1366    /// priority read event occured.
1367    pub fn received_priority_read(&self) -> bool {
1368        self.inner.revents & curl_sys::CURL_WAIT_POLLPRI == curl_sys::CURL_WAIT_POLLPRI
1369    }
1370
1371    /// After a call to `wait`, returns `true` if `poll_on_write` was set and a
1372    /// write event occured.
1373    pub fn received_write(&self) -> bool {
1374        self.inner.revents & curl_sys::CURL_WAIT_POLLOUT == curl_sys::CURL_WAIT_POLLOUT
1375    }
1376}
1377
1378#[cfg(unix)]
1379impl From<pollfd> for WaitFd {
1380    fn from(pfd: pollfd) -> WaitFd {
1381        let mut events = 0;
1382        if pfd.events & POLLIN == POLLIN {
1383            events |= curl_sys::CURL_WAIT_POLLIN;
1384        }
1385        if pfd.events & POLLPRI == POLLPRI {
1386            events |= curl_sys::CURL_WAIT_POLLPRI;
1387        }
1388        if pfd.events & POLLOUT == POLLOUT {
1389            events |= curl_sys::CURL_WAIT_POLLOUT;
1390        }
1391        WaitFd {
1392            inner: curl_sys::curl_waitfd {
1393                fd: pfd.fd,
1394                events,
1395                revents: 0,
1396            },
1397        }
1398    }
1399}
1400
1401impl fmt::Debug for WaitFd {
1402    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1403        f.debug_struct("WaitFd")
1404            .field("fd", &self.inner.fd)
1405            .field("events", &self.inner.fd)
1406            .field("revents", &self.inner.fd)
1407            .finish()
1408    }
1409}
1410
1411#[cfg(test)]
1412mod tests {
1413    use super::*;
1414    use std::time::Duration;
1415
1416    static_assertions::assert_not_impl_any!(Multi: Send, Sync);
1417
1418    #[test]
1419    fn timeout_i32_clamps_at_max() {
1420        assert_eq!(Multi::timeout_i32(Duration::from_millis(1500)), 1500);
1421        assert_eq!(
1422            Multi::timeout_i32(Duration::new(2_147_483, 647_999_999)),
1423            i32::MAX
1424        );
1425        assert_eq!(
1426            Multi::timeout_i32(Duration::new(2_147_483, 648_000_000)),
1427            i32::MAX
1428        );
1429        assert_eq!(Multi::timeout_i32(Duration::MAX), i32::MAX);
1430    }
1431}