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            time_condition_unmet -> bool,
867            effective_url -> Option<&str>,
868            effective_url_bytes -> Option<&[u8]>,
869            response_code -> u32,
870            http_connectcode -> u32,
871            filetime -> Option<i64>,
872            download_size -> f64,
873            content_length_download -> f64,
874            total_time -> Duration,
875            namelookup_time -> Duration,
876            connect_time -> Duration,
877            appconnect_time -> Duration,
878            pretransfer_time -> Duration,
879            starttransfer_time -> Duration,
880            redirect_time -> Duration,
881            redirect_count -> u32,
882            redirect_url -> Option<&str>,
883            redirect_url_bytes -> Option<&[u8]>,
884            header_size -> u64,
885            request_size -> u64,
886            content_type -> Option<&str>,
887            content_type_bytes -> Option<&[u8]>,
888            os_errno -> i32,
889            primary_ip -> Option<&str>,
890            primary_port -> u16,
891            local_ip -> Option<&str>,
892            local_port -> u16,
893            cookies -> List,
894        }
895    };
896
897    ($($name:ident -> $ret:ty,)*) => {
898        $(
899            impl_easy_getters!($name, $ret, concat!(
900                "Same as [`Easy2::",
901                stringify!($name),
902                "`](../easy/struct.Easy2.html#method.",
903                stringify!($name),
904                ")."
905            ));
906        )*
907    };
908
909    ($name:ident, $ret:ty, $doc:expr) => {
910        #[doc = $doc]
911        pub fn $name(&mut self) -> Result<$ret, Error> {
912            self.easy.$name()
913        }
914    };
915}
916
917impl EasyHandle {
918    /// Sets an internal private token for this `EasyHandle`.
919    ///
920    /// This function will set the `CURLOPT_PRIVATE` field on the underlying
921    /// easy handle.
922    pub fn set_token(&mut self, token: usize) -> Result<(), Error> {
923        unsafe {
924            crate::cvt(curl_sys::curl_easy_setopt(
925                self.easy.raw(),
926                curl_sys::CURLOPT_PRIVATE,
927                token,
928            ))
929        }
930    }
931
932    impl_easy_getters!();
933
934    /// Unpause reading on a connection.
935    ///
936    /// Using this function, you can explicitly unpause a connection that was
937    /// previously paused.
938    ///
939    /// A connection can be paused by letting the read or the write callbacks
940    /// return `ReadError::Pause` or `WriteError::Pause`.
941    ///
942    /// The chance is high that you will get your write callback called before
943    /// this function returns.
944    pub fn unpause_read(&self) -> Result<(), Error> {
945        self.easy.unpause_read()
946    }
947
948    /// Unpause writing on a connection.
949    ///
950    /// Using this function, you can explicitly unpause a connection that was
951    /// previously paused.
952    ///
953    /// A connection can be paused by letting the read or the write callbacks
954    /// return `ReadError::Pause` or `WriteError::Pause`. A write callback that
955    /// returns pause signals to the library that it couldn't take care of any
956    /// data at all, and that data will then be delivered again to the callback
957    /// when the writing is later unpaused.
958    pub fn unpause_write(&self) -> Result<(), Error> {
959        self.easy.unpause_write()
960    }
961
962    /// Get a pointer to the raw underlying CURL handle.
963    pub fn raw(&self) -> *mut curl_sys::CURL {
964        self.easy.raw()
965    }
966}
967
968impl fmt::Debug for EasyHandle {
969    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
970        self.easy.fmt(f)
971    }
972}
973
974impl<H> Easy2Handle<H> {
975    /// Acquires a reference to the underlying handler for events.
976    pub fn get_ref(&self) -> &H {
977        self.easy.get_ref()
978    }
979
980    /// Acquires a reference to the underlying handler for events.
981    pub fn get_mut(&mut self) -> &mut H {
982        self.easy.get_mut()
983    }
984
985    /// Same as `EasyHandle::set_token`
986    pub fn set_token(&mut self, token: usize) -> Result<(), Error> {
987        unsafe {
988            crate::cvt(curl_sys::curl_easy_setopt(
989                self.easy.raw(),
990                curl_sys::CURLOPT_PRIVATE,
991                token,
992            ))
993        }
994    }
995
996    impl_easy_getters!();
997
998    /// Unpause reading on a connection.
999    ///
1000    /// Using this function, you can explicitly unpause a connection that was
1001    /// previously paused.
1002    ///
1003    /// A connection can be paused by letting the read or the write callbacks
1004    /// return `ReadError::Pause` or `WriteError::Pause`.
1005    ///
1006    /// The chance is high that you will get your write callback called before
1007    /// this function returns.
1008    pub fn unpause_read(&self) -> Result<(), Error> {
1009        self.easy.unpause_read()
1010    }
1011
1012    /// Unpause writing 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`. A write callback that
1019    /// returns pause signals to the library that it couldn't take care of any
1020    /// data at all, and that data will then be delivered again to the callback
1021    /// when the writing is later unpaused.
1022    pub fn unpause_write(&self) -> Result<(), Error> {
1023        self.easy.unpause_write()
1024    }
1025
1026    /// Get a pointer to the raw underlying CURL handle.
1027    pub fn raw(&self) -> *mut curl_sys::CURL {
1028        self.easy.raw()
1029    }
1030}
1031
1032impl<H: fmt::Debug> fmt::Debug for Easy2Handle<H> {
1033    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1034        self.easy.fmt(f)
1035    }
1036}
1037
1038impl DetachGuard {
1039    /// Detach the referenced easy handle from its multi handle manually.
1040    /// Subsequent calls to this method will have no effect.
1041    fn detach(&mut self) -> Result<(), MultiError> {
1042        if !self.easy.is_null() {
1043            unsafe {
1044                cvt(curl_sys::curl_multi_remove_handle(
1045                    self.multi.handle,
1046                    self.easy,
1047                ))?
1048            }
1049
1050            // Set easy to null to signify that the handle was removed.
1051            self.easy = ptr::null_mut();
1052        }
1053
1054        Ok(())
1055    }
1056}
1057
1058impl Drop for DetachGuard {
1059    fn drop(&mut self) {
1060        let _ = self.detach();
1061    }
1062}
1063
1064impl<'multi> Message<'multi> {
1065    /// If this message indicates that a transfer has finished, returns the
1066    /// result of the transfer in `Some`.
1067    ///
1068    /// If the message doesn't indicate that a transfer has finished, then
1069    /// `None` is returned.
1070    ///
1071    /// Note that the `result*_for` methods below should be preferred as they
1072    /// provide better error messages as the associated error data on the
1073    /// handle can be associated with the error type.
1074    pub fn result(&self) -> Option<Result<(), Error>> {
1075        unsafe {
1076            if (*self.ptr).msg == curl_sys::CURLMSG_DONE {
1077                Some(crate::cvt((*self.ptr).data as curl_sys::CURLcode))
1078            } else {
1079                None
1080            }
1081        }
1082    }
1083
1084    /// Same as `result`, except only returns `Some` for the specified handle.
1085    ///
1086    /// Note that this function produces better error messages than `result` as
1087    /// it uses `take_error_buf` to associate error information with the
1088    /// returned error.
1089    pub fn result_for(&self, handle: &EasyHandle) -> Option<Result<(), Error>> {
1090        if !self.is_for(handle) {
1091            return None;
1092        }
1093        let mut err = self.result();
1094        if let Some(Err(e)) = &mut err {
1095            if let Some(s) = handle.easy.take_error_buf() {
1096                e.set_extra(s);
1097            }
1098        }
1099        err
1100    }
1101
1102    /// Same as `result`, except only returns `Some` for the specified handle.
1103    ///
1104    /// Note that this function produces better error messages than `result` as
1105    /// it uses `take_error_buf` to associate error information with the
1106    /// returned error.
1107    pub fn result_for2<H>(&self, handle: &Easy2Handle<H>) -> Option<Result<(), Error>> {
1108        if !self.is_for2(handle) {
1109            return None;
1110        }
1111        let mut err = self.result();
1112        if let Some(Err(e)) = &mut err {
1113            if let Some(s) = handle.easy.take_error_buf() {
1114                e.set_extra(s);
1115            }
1116        }
1117        err
1118    }
1119
1120    /// Returns whether this easy message was for the specified easy handle or
1121    /// not.
1122    pub fn is_for(&self, handle: &EasyHandle) -> bool {
1123        unsafe { (*self.ptr).easy_handle == handle.easy.raw() }
1124    }
1125
1126    /// Same as `is_for`, but for `Easy2Handle`.
1127    pub fn is_for2<H>(&self, handle: &Easy2Handle<H>) -> bool {
1128        unsafe { (*self.ptr).easy_handle == handle.easy.raw() }
1129    }
1130
1131    /// Returns the token associated with the easy handle that this message
1132    /// represents a completion for.
1133    ///
1134    /// This function will return the token assigned with
1135    /// `EasyHandle::set_token`. This reads the `CURLINFO_PRIVATE` field of the
1136    /// underlying `*mut CURL`.
1137    pub fn token(&self) -> Result<usize, Error> {
1138        unsafe {
1139            let mut p = 0usize;
1140            crate::cvt(curl_sys::curl_easy_getinfo(
1141                (*self.ptr).easy_handle,
1142                curl_sys::CURLINFO_PRIVATE,
1143                &mut p,
1144            ))?;
1145            Ok(p)
1146        }
1147    }
1148}
1149
1150impl<'a> fmt::Debug for Message<'a> {
1151    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1152        f.debug_struct("Message").field("ptr", &self.ptr).finish()
1153    }
1154}
1155
1156impl Events {
1157    /// Creates a new blank event bit mask.
1158    pub fn new() -> Events {
1159        Events { bits: 0 }
1160    }
1161
1162    /// Set or unset the whether these events indicate that input is ready.
1163    pub fn input(&mut self, val: bool) -> &mut Events {
1164        self.flag(curl_sys::CURL_CSELECT_IN, val)
1165    }
1166
1167    /// Set or unset the whether these events indicate that output is ready.
1168    pub fn output(&mut self, val: bool) -> &mut Events {
1169        self.flag(curl_sys::CURL_CSELECT_OUT, val)
1170    }
1171
1172    /// Set or unset the whether these events indicate that an error has
1173    /// happened.
1174    pub fn error(&mut self, val: bool) -> &mut Events {
1175        self.flag(curl_sys::CURL_CSELECT_ERR, val)
1176    }
1177
1178    fn flag(&mut self, flag: c_int, val: bool) -> &mut Events {
1179        if val {
1180            self.bits |= flag;
1181        } else {
1182            self.bits &= !flag;
1183        }
1184        self
1185    }
1186}
1187
1188impl fmt::Debug for Events {
1189    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1190        f.debug_struct("Events")
1191            .field("input", &(self.bits & curl_sys::CURL_CSELECT_IN != 0))
1192            .field("output", &(self.bits & curl_sys::CURL_CSELECT_OUT != 0))
1193            .field("error", &(self.bits & curl_sys::CURL_CSELECT_ERR != 0))
1194            .finish()
1195    }
1196}
1197
1198impl SocketEvents {
1199    /// Wait for incoming data. For the socket to become readable.
1200    pub fn input(&self) -> bool {
1201        self.bits & curl_sys::CURL_POLL_IN == curl_sys::CURL_POLL_IN
1202    }
1203
1204    /// Wait for outgoing data. For the socket to become writable.
1205    pub fn output(&self) -> bool {
1206        self.bits & curl_sys::CURL_POLL_OUT == curl_sys::CURL_POLL_OUT
1207    }
1208
1209    /// Wait for incoming and outgoing data. For the socket to become readable
1210    /// or writable.
1211    pub fn input_and_output(&self) -> bool {
1212        self.bits & curl_sys::CURL_POLL_INOUT == curl_sys::CURL_POLL_INOUT
1213    }
1214
1215    /// The specified socket/file descriptor is no longer used by libcurl.
1216    pub fn remove(&self) -> bool {
1217        self.bits & curl_sys::CURL_POLL_REMOVE == curl_sys::CURL_POLL_REMOVE
1218    }
1219}
1220
1221impl fmt::Debug for SocketEvents {
1222    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1223        f.debug_struct("Events")
1224            .field("input", &self.input())
1225            .field("output", &self.output())
1226            .field("remove", &self.remove())
1227            .finish()
1228    }
1229}
1230
1231impl WaitFd {
1232    /// Constructs an empty (invalid) WaitFd.
1233    pub fn new() -> WaitFd {
1234        WaitFd {
1235            inner: curl_sys::curl_waitfd {
1236                fd: 0,
1237                events: 0,
1238                revents: 0,
1239            },
1240        }
1241    }
1242
1243    /// Set the file descriptor to wait for.
1244    pub fn set_fd(&mut self, fd: Socket) {
1245        self.inner.fd = fd;
1246    }
1247
1248    /// Indicate that the socket should poll on read events such as new data
1249    /// received.
1250    ///
1251    /// Corresponds to `CURL_WAIT_POLLIN`.
1252    pub fn poll_on_read(&mut self, val: bool) -> &mut WaitFd {
1253        self.flag(curl_sys::CURL_WAIT_POLLIN, val)
1254    }
1255
1256    /// Indicate that the socket should poll on high priority read events such
1257    /// as out of band data.
1258    ///
1259    /// Corresponds to `CURL_WAIT_POLLPRI`.
1260    pub fn poll_on_priority_read(&mut self, val: bool) -> &mut WaitFd {
1261        self.flag(curl_sys::CURL_WAIT_POLLPRI, val)
1262    }
1263
1264    /// Indicate that the socket should poll on write events such as the socket
1265    /// being clear to write without blocking.
1266    ///
1267    /// Corresponds to `CURL_WAIT_POLLOUT`.
1268    pub fn poll_on_write(&mut self, val: bool) -> &mut WaitFd {
1269        self.flag(curl_sys::CURL_WAIT_POLLOUT, val)
1270    }
1271
1272    fn flag(&mut self, flag: c_short, val: bool) -> &mut WaitFd {
1273        if val {
1274            self.inner.events |= flag;
1275        } else {
1276            self.inner.events &= !flag;
1277        }
1278        self
1279    }
1280
1281    /// After a call to `wait`, returns `true` if `poll_on_read` was set and a
1282    /// read event occured.
1283    pub fn received_read(&self) -> bool {
1284        self.inner.revents & curl_sys::CURL_WAIT_POLLIN == curl_sys::CURL_WAIT_POLLIN
1285    }
1286
1287    /// After a call to `wait`, returns `true` if `poll_on_priority_read` was set and a
1288    /// priority read event occured.
1289    pub fn received_priority_read(&self) -> bool {
1290        self.inner.revents & curl_sys::CURL_WAIT_POLLPRI == curl_sys::CURL_WAIT_POLLPRI
1291    }
1292
1293    /// After a call to `wait`, returns `true` if `poll_on_write` was set and a
1294    /// write event occured.
1295    pub fn received_write(&self) -> bool {
1296        self.inner.revents & curl_sys::CURL_WAIT_POLLOUT == curl_sys::CURL_WAIT_POLLOUT
1297    }
1298}
1299
1300#[cfg(unix)]
1301impl From<pollfd> for WaitFd {
1302    fn from(pfd: pollfd) -> WaitFd {
1303        let mut events = 0;
1304        if pfd.events & POLLIN == POLLIN {
1305            events |= curl_sys::CURL_WAIT_POLLIN;
1306        }
1307        if pfd.events & POLLPRI == POLLPRI {
1308            events |= curl_sys::CURL_WAIT_POLLPRI;
1309        }
1310        if pfd.events & POLLOUT == POLLOUT {
1311            events |= curl_sys::CURL_WAIT_POLLOUT;
1312        }
1313        WaitFd {
1314            inner: curl_sys::curl_waitfd {
1315                fd: pfd.fd,
1316                events,
1317                revents: 0,
1318            },
1319        }
1320    }
1321}
1322
1323impl fmt::Debug for WaitFd {
1324    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1325        f.debug_struct("WaitFd")
1326            .field("fd", &self.inner.fd)
1327            .field("events", &self.inner.fd)
1328            .field("revents", &self.inner.fd)
1329            .finish()
1330    }
1331}