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}