alexcrichton / curl-rust

Rust bindings to libcurl
MIT License
1k stars 234 forks source link

Multi::perform() references a mysterious "info", which doesn't appear to exist – how to get the result for an individual #498

Open nabijaczleweli opened 1 year ago

nabijaczleweli commented 1 year ago

Quoth https://docs.rs/curl/latest/curl/multi/struct.Multi.html#method.perform at 0.4.44:

If the amount of running handles is changed from the previous call (or is less than the amount of easy handles you’ve added to the multi handle), you know that there is one or more transfers less “running”. You can then call info to get information about each individual completed transfer, and that returned info includes Error and more. If an added handle fails very quickly, it may never be counted as a running handle.

and

This function only returns errors etc regarding the whole multi stack. Problems still might have occurred on individual transfers even when this function returns Ok. Use info to figure out how individual transfers did.

And yet!

curl-rust$ git grep '\binfo('
curl-rust$ git grep '\binfo<'
curl-rust$ git grep '\binfo:'
curl-sys/lib.rs:    pub fn curl_easy_getinfo(curl: *mut CURL, info: CURLINFO, ...) -> CURLcode;
src/easy/handler.rs:    /// More info: <https://curl.se/libcurl/c/CURLOPT_EXPECT_100_TIMEOUT_MS.html>

and rustdoc search unsurprisingly returns nothing as well.

Indeed, it appears I'm actually hitting this in https://github.com/nabijaczleweli/cargo-update/issues/234 where response_code() returns 0, i.e. "no server response code has been received". To that end – how do I get the individual results?

https://curl.se/libcurl/c/curl_multi_perform.html says

This function returns errors regarding the whole multi stack. Problems on individual transfers may have occurred even when this function returns CURLM_OK. Use curl_multi_info_read to figure out how individual transfers did.

which is obviously what the rustdoc is copied from, and indeed

$ git grep curl_multi_info_read
curl-sys/lib.rs:    pub fn curl_multi_info_read(
src/multi.rs:                let ptr = curl_sys::curl_multi_info_read(self.raw.handle, &mut queue);

which is

    /// Read multi stack informationals
    ///
    /// Ask the multi handle if there are any messages/informationals from the
    /// individual transfers. Messages may include informationals such as an
    /// error code from the transfer or just the fact that a transfer is
    /// completed. More details on these should be written down as well.
    pub fn messages<F>(&self, mut f: F)
    where
        F: FnMut(Message),
    {
        self._messages(&mut f)
    }

    fn _messages(&self, f: &mut dyn FnMut(Message)) {
        let mut queue = 0;
        unsafe {
            loop {
                let ptr = curl_sys::curl_multi_info_read(self.raw.handle, &mut queue);
                if ptr.is_null() {
                    break;
                }
                f(Message { ptr, _multi: self })
            }
        }
    }

This had also confused me originally when using the multi API. To that end: instead of referencing a mystical "info", please just point to messages() :)