1 | //! Multi - initiating multiple requests simultaneously |
---|---|
2 | |
3 | use std::fmt; |
4 | use std::marker; |
5 | use std::ptr; |
6 | use std::sync::Arc; |
7 | use std::time::Duration; |
8 | |
9 | use libc::{c_char, c_int, c_long, c_short, c_void}; |
10 | |
11 | #[cfg(unix)] |
12 | use libc::{pollfd, POLLIN, POLLOUT, POLLPRI}; |
13 | |
14 | use crate::easy::{Easy, Easy2, List}; |
15 | use crate::panic; |
16 | use 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 |
31 | pub struct Multi { |
32 | raw: Arc<RawMulti>, |
33 | data: Box<MultiData>, |
34 | } |
35 | |
36 | #[derive(Debug)] |
37 | struct RawMulti { |
38 | handle: *mut curl_sys::CURLM, |
39 | } |
40 | |
41 | struct 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. |
49 | pub 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. |
59 | pub 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. |
72 | pub 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. |
82 | struct 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. |
91 | pub 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. |
99 | pub struct SocketEvents { |
100 | bits: c_int, |
101 | } |
102 | |
103 | /// Raw underlying socket type that the multi handles use |
104 | pub type Socket = curl_sys::curl_socket_t; |
105 | |
106 | /// File descriptor to wait on for use with the `wait` method on a multi handle. |
107 | pub 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)] |
115 | pub struct MultiWaker { |
116 | raw: std::sync::Weak<RawMulti>, |
117 | } |
118 | |
119 | #[cfg(feature = "poll_7_68_0")] |
120 | unsafe impl Send for MultiWaker {} |
121 | |
122 | #[cfg(feature = "poll_7_68_0")] |
123 | unsafe impl Sync for MultiWaker {} |
124 | |
125 | impl 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 | |
818 | impl Drop for RawMulti { |
819 | fn drop(&mut self) { |
820 | unsafe { |
821 | let _ = cvt(code:curl_sys::curl_multi_cleanup(self.handle)); |
822 | } |
823 | } |
824 | } |
825 | |
826 | #[cfg(feature = "poll_7_68_0")] |
827 | impl 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 | |
849 | fn 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 | |
857 | impl fmt::Debug for Multi { |
858 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
859 | f.debug_struct("Multi").field(name: "raw", &self.raw).finish() |
860 | } |
861 | } |
862 | |
863 | macro_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 | |
917 | impl 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 | |
968 | impl fmt::Debug for EasyHandle { |
969 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
970 | self.easy.fmt(f) |
971 | } |
972 | } |
973 | |
974 | impl<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 | |
1032 | impl<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 | |
1038 | impl 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(code: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 | |
1058 | impl Drop for DetachGuard { |
1059 | fn drop(&mut self) { |
1060 | let _ = self.detach(); |
1061 | } |
1062 | } |
1063 | |
1064 | impl<'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 | |
1150 | impl<'a> fmt::Debug for Message<'a> { |
1151 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1152 | f.debug_struct("Message").field(name: "ptr", &self.ptr).finish() |
1153 | } |
1154 | } |
1155 | |
1156 | impl 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 | |
1188 | impl fmt::Debug for Events { |
1189 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1190 | f&mut DebugStruct<'_, '_>.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(name:"error", &(self.bits & curl_sys::CURL_CSELECT_ERR != 0)) |
1194 | .finish() |
1195 | } |
1196 | } |
1197 | |
1198 | impl 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 | |
1221 | impl fmt::Debug for SocketEvents { |
1222 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1223 | f&mut DebugStruct<'_, '_>.debug_struct("Events") |
1224 | .field("input", &self.input()) |
1225 | .field("output", &self.output()) |
1226 | .field(name:"remove", &self.remove()) |
1227 | .finish() |
1228 | } |
1229 | } |
1230 | |
1231 | impl 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)] |
1301 | impl From<pollfd> for WaitFd { |
1302 | fn from(pfd: pollfd) -> WaitFd { |
1303 | let mut events: i16 = 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 | |
1323 | impl fmt::Debug for WaitFd { |
1324 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1325 | f&mut DebugStruct<'_, '_>.debug_struct("WaitFd") |
1326 | .field("fd", &self.inner.fd) |
1327 | .field("events", &self.inner.fd) |
1328 | .field(name:"revents", &self.inner.fd) |
1329 | .finish() |
1330 | } |
1331 | } |
1332 |
Definitions
- Multi
- raw
- data
- RawMulti
- handle
- MultiData
- socket
- timer
- Message
- ptr
- _multi
- EasyHandle
- guard
- easy
- _marker
- Easy2Handle
- guard
- easy
- _marker
- DetachGuard
- multi
- easy
- Events
- bits
- SocketEvents
- bits
- Socket
- WaitFd
- inner
- new
- socket_function
- _socket_function
- cb
- assign
- timer_function
- _timer_function
- cb
- pipelining
- set_max_host_connections
- set_max_total_connections
- set_max_connects
- set_pipeline_length
- set_max_concurrent_streams
- setopt_long
- setopt_ptr
- add
- add2
- remove
- remove2
- messages
- _messages
- action
- timeout
- get_timeout
- wait
- timeout_i32
- perform
- fdset2
- close
- raw
- drop
- cvt
- fmt
- impl_easy_getters
- set_token
- unpause_read
- unpause_write
- raw
- fmt
- get_ref
- get_mut
- set_token
- unpause_read
- unpause_write
- raw
- fmt
- detach
- drop
- result
- result_for
- result_for2
- is_for
- is_for2
- token
- fmt
- new
- input
- output
- error
- flag
- fmt
- input
- output
- input_and_output
- remove
- fmt
- new
- set_fd
- poll_on_read
- poll_on_priority_read
- poll_on_write
- flag
- received_read
- received_priority_read
- received_write
- from
Learn Rust with the experts
Find out more