Skip to main content

curl/
multi.rs

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