]> git.proxmox.com Git - rustc.git/blob - src/vendor/curl/src/multi.rs
New upstream version 1.19.0+dfsg1
[rustc.git] / src / vendor / curl / src / multi.rs
1 //! Multi - initiating multiple requests simultaneously
2
3 use std::marker;
4 use std::time::Duration;
5
6 use libc::{c_int, c_char, c_void, c_long, c_short};
7 use curl_sys;
8
9 #[cfg(windows)]
10 use winapi::fd_set;
11 #[cfg(unix)]
12 use libc::{fd_set, pollfd, POLLIN, POLLPRI, POLLOUT};
13
14 use {MultiError, Error};
15 use easy::Easy;
16 use panic;
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: *mut curl_sys::CURLM,
33 data: Box<MultiData>,
34 }
35
36 struct MultiData {
37 socket: Box<FnMut(Socket, SocketEvents, usize) + Send>,
38 timer: Box<FnMut(Option<Duration>) -> bool + Send>,
39 }
40
41 /// Message from the `messages` function of a multi handle.
42 ///
43 /// Currently only indicates whether a transfer is done.
44 pub struct Message<'multi> {
45 ptr: *mut curl_sys::CURLMsg,
46 _multi: &'multi Multi,
47 }
48
49 /// Wrapper around an easy handle while it's owned by a multi handle.
50 ///
51 /// Once an easy handle has been added to a multi handle then it can no longer
52 /// be used via `perform`. This handle is also used to remove the easy handle
53 /// from the multi handle when desired.
54 pub struct EasyHandle {
55 easy: Easy,
56 // This is now effecitvely bound to a `Multi`, so it is no longer sendable.
57 _marker: marker::PhantomData<&'static Multi>,
58 }
59
60 /// Notification of the events that have happened on a socket.
61 ///
62 /// This type is passed as an argument to the `action` method on a multi handle
63 /// to indicate what events have occurred on a socket.
64 pub struct Events {
65 bits: c_int,
66 }
67
68 /// Notification of events that are requested on a socket.
69 ///
70 /// This type is yielded to the `socket_function` callback to indicate what
71 /// events are requested on a socket.
72 #[derive(Debug)]
73 pub struct SocketEvents {
74 bits: c_int,
75 }
76
77 /// Raw underlying socket type that the multi handles use
78 pub type Socket = curl_sys::curl_socket_t;
79
80 /// File descriptor to wait on for use with the `wait` method on a multi handle.
81 pub struct WaitFd {
82 inner: curl_sys::curl_waitfd,
83 }
84
85 impl Multi {
86 /// Creates a new multi session through which multiple HTTP transfers can be
87 /// initiated.
88 pub fn new() -> Multi {
89 unsafe {
90 ::init();
91 let ptr = curl_sys::curl_multi_init();
92 assert!(!ptr.is_null());
93 Multi {
94 raw: ptr,
95 data: Box::new(MultiData {
96 socket: Box::new(|_, _, _| ()),
97 timer: Box::new(|_| true),
98 }),
99 }
100 }
101 }
102
103 /// Set the callback informed about what to wait for
104 ///
105 /// When the `action` function runs, it informs the application about
106 /// updates in the socket (file descriptor) status by doing none, one, or
107 /// multiple calls to the socket callback. The callback gets status updates
108 /// with changes since the previous time the callback was called. See
109 /// `action` for more details on how the callback is used and should work.
110 ///
111 /// The `SocketEvents` parameter informs the callback on the status of the
112 /// given socket, and the methods on that type can be used to learn about
113 /// what's going on with the socket.
114 ///
115 /// The third `usize` parameter is a custom value set by the `assign` method
116 /// below.
117 pub fn socket_function<F>(&mut self, f: F) -> Result<(), MultiError>
118 where F: FnMut(Socket, SocketEvents, usize) + Send + 'static,
119 {
120 self._socket_function(Box::new(f))
121 }
122
123 fn _socket_function(&mut self,
124 f: Box<FnMut(Socket, SocketEvents, usize) + Send>)
125 -> Result<(), MultiError>
126 {
127 self.data.socket = f;
128 let cb: curl_sys::curl_socket_callback = cb;
129 try!(self.setopt_ptr(curl_sys::CURLMOPT_SOCKETFUNCTION,
130 cb as usize as *const c_char));
131 let ptr = &*self.data as *const _;
132 try!(self.setopt_ptr(curl_sys::CURLMOPT_SOCKETDATA,
133 ptr as *const c_char));
134 return Ok(());
135
136 // TODO: figure out how to expose `_easy`
137 extern fn cb(_easy: *mut curl_sys::CURL,
138 socket: curl_sys::curl_socket_t,
139 what: c_int,
140 userptr: *mut c_void,
141 socketp: *mut c_void) -> c_int {
142 panic::catch(|| unsafe {
143 let f = &mut (*(userptr as *mut MultiData)).socket;
144 f(socket, SocketEvents { bits: what }, socketp as usize)
145 });
146 0
147 }
148 }
149
150 /// Set data to associate with an internal socket
151 ///
152 /// This function creates an association in the multi handle between the
153 /// given socket and a private token of the application. This is designed
154 /// for `action` uses.
155 ///
156 /// When set, the token will be passed to all future socket callbacks for
157 /// the specified socket.
158 ///
159 /// If the given socket isn't already in use by libcurl, this function will
160 /// return an error.
161 ///
162 /// libcurl only keeps one single token associated with a socket, so
163 /// calling this function several times for the same socket will make the
164 /// last set token get used.
165 ///
166 /// The idea here being that this association (socket to token) is something
167 /// that just about every application that uses this API will need and then
168 /// libcurl can just as well do it since it already has an internal hash
169 /// table lookup for this.
170 ///
171 /// # Typical Usage
172 ///
173 /// In a typical application you allocate a struct or at least use some kind
174 /// of semi-dynamic data for each socket that we must wait for action on
175 /// when using the `action` approach.
176 ///
177 /// When our socket-callback gets called by libcurl and we get to know about
178 /// yet another socket to wait for, we can use `assign` to point out the
179 /// particular data so that when we get updates about this same socket
180 /// again, we don't have to find the struct associated with this socket by
181 /// ourselves.
182 pub fn assign(&self,
183 socket: Socket,
184 token: usize) -> Result<(), MultiError> {
185 unsafe {
186 try!(cvt(curl_sys::curl_multi_assign(self.raw, socket,
187 token as *mut _)));
188 Ok(())
189 }
190 }
191
192 /// Set callback to receive timeout values
193 ///
194 /// Certain features, such as timeouts and retries, require you to call
195 /// libcurl even when there is no activity on the file descriptors.
196 ///
197 /// Your callback function should install a non-repeating timer with the
198 /// interval specified. Each time that timer fires, call either `action` or
199 /// `perform`, depending on which interface you use.
200 ///
201 /// A timeout value of `None` means you should delete your timer.
202 ///
203 /// A timeout value of 0 means you should call `action` or `perform` (once)
204 /// as soon as possible.
205 ///
206 /// This callback will only be called when the timeout changes.
207 ///
208 /// The timer callback should return `true` on success, and `false` on
209 /// error. This callback can be used instead of, or in addition to,
210 /// `get_timeout`.
211 pub fn timer_function<F>(&mut self, f: F) -> Result<(), MultiError>
212 where F: FnMut(Option<Duration>) -> bool + Send + 'static,
213 {
214 self._timer_function(Box::new(f))
215 }
216
217 fn _timer_function(&mut self,
218 f: Box<FnMut(Option<Duration>) -> bool + Send>)
219 -> Result<(), MultiError>
220 {
221 self.data.timer = f;
222 let cb: curl_sys::curl_multi_timer_callback = cb;
223 try!(self.setopt_ptr(curl_sys::CURLMOPT_TIMERFUNCTION,
224 cb as usize as *const c_char));
225 let ptr = &*self.data as *const _;
226 try!(self.setopt_ptr(curl_sys::CURLMOPT_TIMERDATA,
227 ptr as *const c_char));
228 return Ok(());
229
230 // TODO: figure out how to expose `_multi`
231 extern fn cb(_multi: *mut curl_sys::CURLM,
232 timeout_ms: c_long,
233 user: *mut c_void) -> c_int {
234 let keep_going = panic::catch(|| unsafe {
235 let f = &mut (*(user as *mut MultiData)).timer;
236 if timeout_ms == -1 {
237 f(None)
238 } else {
239 f(Some(Duration::from_millis(timeout_ms as u64)))
240 }
241 }).unwrap_or(false);
242 if keep_going {0} else {-1}
243 }
244 }
245
246 fn setopt_ptr(&mut self,
247 opt: curl_sys::CURLMoption,
248 val: *const c_char) -> Result<(), MultiError> {
249 unsafe {
250 cvt(curl_sys::curl_multi_setopt(self.raw, opt, val))
251 }
252 }
253
254 /// Add an easy handle to a multi session
255 ///
256 /// Adds a standard easy handle to the multi stack. This function call will
257 /// make this multi handle control the specified easy handle.
258 ///
259 /// When an easy interface is added to a multi handle, it will use a shared
260 /// connection cache owned by the multi handle. Removing and adding new easy
261 /// handles will not affect the pool of connections or the ability to do
262 /// connection re-use.
263 ///
264 /// If you have `timer_function` set in the multi handle (and you really
265 /// should if you're working event-based with `action` and friends), that
266 /// callback will be called from within this function to ask for an updated
267 /// timer so that your main event loop will get the activity on this handle
268 /// to get started.
269 ///
270 /// The easy handle will remain added to the multi handle until you remove
271 /// it again with `remove` on the returned handle - even when a transfer
272 /// with that specific easy handle is completed.
273 pub fn add(&self, mut easy: Easy) -> Result<EasyHandle, MultiError> {
274 // Clear any configuration set by previous transfers because we're
275 // moving this into a `Send+'static` situation now basically.
276 easy.transfer();
277
278 unsafe {
279 try!(cvt(curl_sys::curl_multi_add_handle(self.raw, easy.raw())));
280 }
281 Ok(EasyHandle {
282 easy: easy,
283 _marker: marker::PhantomData,
284 })
285 }
286
287 /// Remove an easy handle from this multi session
288 ///
289 /// Removes the easy handle from this multi handle. This will make the
290 /// returned easy handle be removed from this multi handle's control.
291 ///
292 /// When the easy handle has been removed from a multi stack, it is again
293 /// perfectly legal to invoke `perform` on it.
294 ///
295 /// Removing an easy handle while being used is perfectly legal and will
296 /// effectively halt the transfer in progress involving that easy handle.
297 /// All other easy handles and transfers will remain unaffected.
298 pub fn remove(&self, easy: EasyHandle) -> Result<Easy, MultiError> {
299 unsafe {
300 try!(cvt(curl_sys::curl_multi_remove_handle(self.raw,
301 easy.easy.raw())));
302 }
303 Ok(easy.easy)
304 }
305
306 /// Read multi stack informationals
307 ///
308 /// Ask the multi handle if there are any messages/informationals from the
309 /// individual transfers. Messages may include informationals such as an
310 /// error code from the transfer or just the fact that a transfer is
311 /// completed. More details on these should be written down as well.
312 pub fn messages<F>(&self, mut f: F) where F: FnMut(Message) {
313 self._messages(&mut f)
314 }
315
316 fn _messages(&self, mut f: &mut FnMut(Message)) {
317 let mut queue = 0;
318 unsafe {
319 loop {
320 let ptr = curl_sys::curl_multi_info_read(self.raw, &mut queue);
321 if ptr.is_null() {
322 break
323 }
324 f(Message { ptr: ptr, _multi: self })
325 }
326 }
327 }
328
329 /// Inform of reads/writes available data given an action
330 ///
331 /// When the application has detected action on a socket handled by libcurl,
332 /// it should call this function with the sockfd argument set to
333 /// the socket with the action. When the events on a socket are known, they
334 /// can be passed `events`. When the events on a socket are unknown, pass
335 /// `Events::new()` instead, and libcurl will test the descriptor
336 /// internally.
337 ///
338 /// The returned integer will contain the number of running easy handles
339 /// within the multi handle. When this number reaches zero, all transfers
340 /// are complete/done. When you call `action` on a specific socket and the
341 /// counter decreases by one, it DOES NOT necessarily mean that this exact
342 /// socket/transfer is the one that completed. Use `messages` to figure out
343 /// which easy handle that completed.
344 ///
345 /// The `action` function informs the application about updates in the
346 /// socket (file descriptor) status by doing none, one, or multiple calls to
347 /// the socket callback function set with the `socket_function` method. They
348 /// update the status with changes since the previous time the callback was
349 /// called.
350 pub fn action(&self, socket: Socket, events: &Events)
351 -> Result<u32, MultiError> {
352 let mut remaining = 0;
353 unsafe {
354 try!(cvt(curl_sys::curl_multi_socket_action(self.raw,
355 socket,
356 events.bits,
357 &mut remaining)));
358 Ok(remaining as u32)
359 }
360 }
361
362 /// Inform libcurl that a timeout has expired and sockets should be tested.
363 ///
364 /// The returned integer will contain the number of running easy handles
365 /// within the multi handle. When this number reaches zero, all transfers
366 /// are complete/done. When you call `action` on a specific socket and the
367 /// counter decreases by one, it DOES NOT necessarily mean that this exact
368 /// socket/transfer is the one that completed. Use `messages` to figure out
369 /// which easy handle that completed.
370 ///
371 /// Get the timeout time by calling the `timer_function` method. Your
372 /// application will then get called with information on how long to wait
373 /// for socket actions at most before doing the timeout action: call the
374 /// `timeout` method. You can also use the `get_timeout` function to
375 /// poll the value at any given time, but for an event-based system using
376 /// the callback is far better than relying on polling the timeout value.
377 pub fn timeout(&self) -> Result<u32, MultiError> {
378 let mut remaining = 0;
379 unsafe {
380 try!(cvt(curl_sys::curl_multi_socket_action(self.raw,
381 curl_sys::CURL_SOCKET_BAD,
382 0,
383 &mut remaining)));
384 Ok(remaining as u32)
385 }
386 }
387
388 /// Get how long to wait for action before proceeding
389 ///
390 /// An application using the libcurl multi interface should call
391 /// `get_timeout` to figure out how long it should wait for socket actions -
392 /// at most - before proceeding.
393 ///
394 /// Proceeding means either doing the socket-style timeout action: call the
395 /// `timeout` function, or call `perform` if you're using the simpler and
396 /// older multi interface approach.
397 ///
398 /// The timeout value returned is the duration at this very moment. If 0, it
399 /// means you should proceed immediately without waiting for anything. If it
400 /// returns `None`, there's no timeout at all set.
401 ///
402 /// Note: if libcurl returns a `None` timeout here, it just means that
403 /// libcurl currently has no stored timeout value. You must not wait too
404 /// long (more than a few seconds perhaps) before you call `perform` again.
405 pub fn get_timeout(&self) -> Result<Option<Duration>, MultiError> {
406 let mut ms = 0;
407 unsafe {
408 try!(cvt(curl_sys::curl_multi_timeout(self.raw, &mut ms)));
409 if ms == -1 {
410 Ok(None)
411 } else {
412 Ok(Some(Duration::from_millis(ms as u64)))
413 }
414 }
415 }
416
417 /// Block until activity is detected or a timeout passes.
418 ///
419 /// The timeout is used in millisecond-precision. Large durations are
420 /// clamped at the maximum value curl accepts.
421 ///
422 /// The returned integer will contain the number of internal file
423 /// descriptors on which interesting events occured.
424 ///
425 /// This function is a simpler alternative to using `fdset()` and `select()`
426 /// and does not suffer from file descriptor limits.
427 ///
428 /// # Example
429 ///
430 /// ```
431 /// use curl::multi::Multi;
432 /// use std::time::Duration;
433 ///
434 /// let m = Multi::new();
435 ///
436 /// // Add some Easy handles...
437 ///
438 /// while m.perform().unwrap() > 0 {
439 /// m.wait(&mut [], Duration::from_secs(1)).unwrap();
440 /// }
441 /// ```
442 pub fn wait(&self, waitfds: &mut [WaitFd], timeout: Duration)
443 -> Result<u32, MultiError> {
444 let timeout_ms = {
445 let secs = timeout.as_secs();
446 if secs > (i32::max_value() / 1000) as u64 {
447 // Duration too large, clamp at maximum value.
448 i32::max_value()
449 } else {
450 secs as i32 * 1000 + timeout.subsec_nanos() as i32 / 1000_000
451 }
452 };
453 unsafe {
454 let mut ret = 0;
455 try!(cvt(curl_sys::curl_multi_wait(self.raw,
456 waitfds.as_mut_ptr() as *mut _,
457 waitfds.len() as u32,
458 timeout_ms,
459 &mut ret)));
460 Ok(ret as u32)
461 }
462 }
463
464 /// Reads/writes available data from each easy handle.
465 ///
466 /// This function handles transfers on all the added handles that need
467 /// attention in an non-blocking fashion.
468 ///
469 /// When an application has found out there's data available for this handle
470 /// or a timeout has elapsed, the application should call this function to
471 /// read/write whatever there is to read or write right now etc. This
472 /// method returns as soon as the reads/writes are done. This function does
473 /// not require that there actually is any data available for reading or
474 /// that data can be written, it can be called just in case. It will return
475 /// the number of handles that still transfer data.
476 ///
477 /// If the amount of running handles is changed from the previous call (or
478 /// is less than the amount of easy handles you've added to the multi
479 /// handle), you know that there is one or more transfers less "running".
480 /// You can then call `info` to get information about each individual
481 /// completed transfer, and that returned info includes `Error` and more.
482 /// If an added handle fails very quickly, it may never be counted as a
483 /// running handle.
484 ///
485 /// When running_handles is set to zero (0) on the return of this function,
486 /// there is no longer any transfers in progress.
487 ///
488 /// # Return
489 ///
490 /// Before libcurl version 7.20.0: If you receive `is_call_perform`, this
491 /// basically means that you should call `perform` again, before you select
492 /// on more actions. You don't have to do it immediately, but the return
493 /// code means that libcurl may have more data available to return or that
494 /// there may be more data to send off before it is "satisfied". Do note
495 /// that `perform` will return `is_call_perform` only when it wants to be
496 /// called again immediately. When things are fine and there is nothing
497 /// immediate it wants done, it'll return `Ok` and you need to wait for
498 /// "action" and then call this function again.
499 ///
500 /// This function only returns errors etc regarding the whole multi stack.
501 /// Problems still might have occurred on individual transfers even when
502 /// this function returns `Ok`. Use `info` to figure out how individual
503 /// transfers did.
504 pub fn perform(&self) -> Result<u32, MultiError> {
505 unsafe {
506 let mut ret = 0;
507 try!(cvt(curl_sys::curl_multi_perform(self.raw, &mut ret)));
508 Ok(ret as u32)
509 }
510 }
511
512 /// Extracts file descriptor information from a multi handle
513 ///
514 /// This function extracts file descriptor information from a given
515 /// handle, and libcurl returns its `fd_set` sets. The application can use
516 /// these to `select()` on, but be sure to `FD_ZERO` them before calling
517 /// this function as curl_multi_fdset only adds its own descriptors, it
518 /// doesn't zero or otherwise remove any others. The curl_multi_perform
519 /// function should be called as soon as one of them is ready to be read
520 /// from or written to.
521 ///
522 /// If no file descriptors are set by libcurl, this function will return
523 /// `Ok(None)`. Otherwise `Ok(Some(n))` will be returned where `n` the
524 /// highest descriptor number libcurl set. When `Ok(None)` is returned it
525 /// is because libcurl currently does something that isn't possible for
526 /// your application to monitor with a socket and unfortunately you can
527 /// then not know exactly when the current action is completed using
528 /// `select()`. You then need to wait a while before you proceed and call
529 /// `perform` anyway.
530 ///
531 /// When doing `select()`, you should use `get_timeout` to figure out
532 /// how long to wait for action. Call `perform` even if no activity has
533 /// been seen on the `fd_set`s after the timeout expires as otherwise
534 /// internal retries and timeouts may not work as you'd think and want.
535 ///
536 /// If one of the sockets used by libcurl happens to be larger than what
537 /// can be set in an `fd_set`, which on POSIX systems means that the file
538 /// descriptor is larger than `FD_SETSIZE`, then libcurl will try to not
539 /// set it. Setting a too large file descriptor in an `fd_set` implies an out
540 /// of bounds write which can cause crashes, or worse. The effect of NOT
541 /// storing it will possibly save you from the crash, but will make your
542 /// program NOT wait for sockets it should wait for...
543 pub fn fdset(&self,
544 read: Option<&mut fd_set>,
545 write: Option<&mut fd_set>,
546 except: Option<&mut fd_set>) -> Result<Option<i32>, MultiError> {
547 unsafe {
548 let mut ret = 0;
549 let read = read.map(|r| r as *mut _).unwrap_or(0 as *mut _);
550 let write = write.map(|r| r as *mut _).unwrap_or(0 as *mut _);
551 let except = except.map(|r| r as *mut _).unwrap_or(0 as *mut _);
552 try!(cvt(curl_sys::curl_multi_fdset(self.raw, read, write, except,
553 &mut ret)));
554 if ret == -1 {
555 Ok(None)
556 } else {
557 Ok(Some(ret))
558 }
559 }
560 }
561
562 /// Attempt to close the multi handle and clean up all associated resources.
563 ///
564 /// Cleans up and removes a whole multi stack. It does not free or touch any
565 /// individual easy handles in any way - they still need to be closed
566 /// individually.
567 pub fn close(&self) -> Result<(), MultiError> {
568 unsafe {
569 cvt(curl_sys::curl_multi_cleanup(self.raw))
570 }
571 }
572 }
573
574 fn cvt(code: curl_sys::CURLMcode) -> Result<(), MultiError> {
575 if code == curl_sys::CURLM_OK {
576 Ok(())
577 } else {
578 Err(MultiError::new(code))
579 }
580 }
581
582 impl Drop for Multi {
583 fn drop(&mut self) {
584 let _ = self.close();
585 }
586 }
587
588 impl EasyHandle {
589 /// Sets an internal private token for this `EasyHandle`.
590 ///
591 /// This function will set the `CURLOPT_PRIVATE` field on the underlying
592 /// easy handle.
593 pub fn set_token(&mut self, token: usize) -> Result<(), Error> {
594 unsafe {
595 ::cvt(curl_sys::curl_easy_setopt(self.easy.raw(),
596 curl_sys::CURLOPT_PRIVATE,
597 token))
598 }
599 }
600 }
601
602 impl<'multi> Message<'multi> {
603 /// If this message indicates that a transfer has finished, returns the
604 /// result of the transfer in `Some`.
605 ///
606 /// If the message doesn't indicate that a transfer has finished, then
607 /// `None` is returned.
608 pub fn result(&self) -> Option<Result<(), Error>> {
609 unsafe {
610 if (*self.ptr).msg == curl_sys::CURLMSG_DONE {
611 Some(::cvt((*self.ptr).data as curl_sys::CURLcode))
612 } else {
613 None
614 }
615 }
616 }
617
618 /// Returns whether this easy message was for the specified easy handle or
619 /// not.
620 pub fn is_for(&self, handle: &EasyHandle) -> bool {
621 unsafe { (*self.ptr).easy_handle == handle.easy.raw() }
622 }
623
624 /// Returns the token associated with the easy handle that this message
625 /// represents a completion for.
626 ///
627 /// This function will return the token assigned with
628 /// `EasyHandle::set_token`. This reads the `CURLINFO_PRIVATE` field of the
629 /// underlying `*mut CURL`.
630 pub fn token(&self) -> Result<usize, Error> {
631 unsafe {
632 let mut p = 0usize;
633 try!(::cvt(curl_sys::curl_easy_getinfo((*self.ptr).easy_handle,
634 curl_sys::CURLINFO_PRIVATE,
635 &mut p)));
636 Ok(p)
637 }
638 }
639 }
640
641 impl Events {
642 /// Creates a new blank event bit mask.
643 pub fn new() -> Events {
644 Events { bits: 0 }
645 }
646
647 /// Set or unset the whether these events indicate that input is ready.
648 pub fn input(&mut self, val: bool) -> &mut Events {
649 self.flag(curl_sys::CURL_CSELECT_IN, val)
650 }
651
652 /// Set or unset the whether these events indicate that output is ready.
653 pub fn output(&mut self, val: bool) -> &mut Events {
654 self.flag(curl_sys::CURL_CSELECT_OUT, val)
655 }
656
657 /// Set or unset the whether these events indicate that an error has
658 /// happened.
659 pub fn error(&mut self, val: bool) -> &mut Events {
660 self.flag(curl_sys::CURL_CSELECT_ERR, val)
661 }
662
663 fn flag(&mut self, flag: c_int, val: bool) -> &mut Events {
664 if val {
665 self.bits |= flag;
666 } else {
667 self.bits &= !flag;
668 }
669 self
670 }
671 }
672
673 impl SocketEvents {
674 /// Wait for incoming data. For the socket to become readable.
675 pub fn input(&self) -> bool {
676 self.bits & curl_sys::CURL_POLL_IN == curl_sys::CURL_POLL_IN
677 }
678
679 /// Wait for outgoing data. For the socket to become writable.
680 pub fn output(&self) -> bool {
681 self.bits & curl_sys::CURL_POLL_OUT == curl_sys::CURL_POLL_OUT
682 }
683
684 /// Wait for incoming and outgoing data. For the socket to become readable
685 /// or writable.
686 pub fn input_and_output(&self) -> bool {
687 self.bits & curl_sys::CURL_POLL_INOUT == curl_sys::CURL_POLL_INOUT
688 }
689
690 /// The specified socket/file descriptor is no longer used by libcurl.
691 pub fn remove(&self) -> bool {
692 self.bits & curl_sys::CURL_POLL_REMOVE == curl_sys::CURL_POLL_REMOVE
693 }
694 }
695
696 impl WaitFd {
697 /// Constructs an empty (invalid) WaitFd.
698 pub fn new() -> WaitFd {
699 WaitFd {
700 inner: curl_sys::curl_waitfd {
701 fd: 0,
702 events: 0,
703 revents: 0,
704 }
705 }
706 }
707
708 /// Set the file descriptor to wait for.
709 pub fn set_fd(&mut self, fd: Socket) {
710 self.inner.fd = fd;
711 }
712
713 /// Indicate that the socket should poll on read events such as new data
714 /// received.
715 ///
716 /// Corresponds to `CURL_WAIT_POLLIN`.
717 pub fn poll_on_read(&mut self, val: bool) -> &mut WaitFd {
718 self.flag(curl_sys::CURL_WAIT_POLLIN, val)
719 }
720
721 /// Indicate that the socket should poll on high priority read events such
722 /// as out of band data.
723 ///
724 /// Corresponds to `CURL_WAIT_POLLPRI`.
725 pub fn poll_on_priority_read(&mut self, val: bool) -> &mut WaitFd {
726 self.flag(curl_sys::CURL_WAIT_POLLPRI, val)
727 }
728
729 /// Indicate that the socket should poll on write events such as the socket
730 /// being clear to write without blocking.
731 ///
732 /// Corresponds to `CURL_WAIT_POLLOUT`.
733 pub fn poll_on_write(&mut self, val: bool) -> &mut WaitFd {
734 self.flag(curl_sys::CURL_WAIT_POLLOUT, val)
735 }
736
737 fn flag(&mut self, flag: c_short, val: bool) -> &mut WaitFd {
738 if val {
739 self.inner.events |= flag;
740 } else {
741 self.inner.events &= !flag;
742 }
743 self
744 }
745
746 /// After a call to `wait`, returns `true` if `poll_on_read` was set and a
747 /// read event occured.
748 pub fn received_read(&self) -> bool {
749 self.inner.revents & curl_sys::CURL_WAIT_POLLIN == curl_sys::CURL_WAIT_POLLIN
750 }
751
752 /// After a call to `wait`, returns `true` if `poll_on_priority_read` was set and a
753 /// priority read event occured.
754 pub fn received_priority_read(&self) -> bool {
755 self.inner.revents & curl_sys::CURL_WAIT_POLLPRI == curl_sys::CURL_WAIT_POLLPRI
756 }
757
758 /// After a call to `wait`, returns `true` if `poll_on_write` was set and a
759 /// write event occured.
760 pub fn received_write(&self) -> bool {
761 self.inner.revents & curl_sys::CURL_WAIT_POLLOUT == curl_sys::CURL_WAIT_POLLOUT
762 }
763 }
764
765 #[cfg(unix)]
766 impl From<pollfd> for WaitFd {
767 fn from(pfd: pollfd) -> WaitFd {
768 let mut events = 0;
769 if pfd.events & POLLIN == POLLIN {
770 events |= curl_sys::CURL_WAIT_POLLIN;
771 }
772 if pfd.events & POLLPRI == POLLPRI {
773 events |= curl_sys::CURL_WAIT_POLLPRI;
774 }
775 if pfd.events & POLLOUT == POLLOUT {
776 events |= curl_sys::CURL_WAIT_POLLOUT;
777 }
778 WaitFd {
779 inner: curl_sys::curl_waitfd {
780 fd: pfd.fd,
781 events: events,
782 revents: 0,
783 }
784 }
785 }
786 }