alexcrichton / curl-rust

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

Print output from curl #462

Closed bensig closed 1 year ago

bensig commented 1 year ago

How does one print the output from curl-rust?

As you can see below I am able to get this working without using curl-rust, but I want to learn how to use this for future reference. Looked all over for examples...

use curl::easy::Easy;

fn main() {
    let output = String::new();
    let data =
        format!(r#"{{"code": "swap.libre","table": "stat","scope": "BTCUSD","json": true}}"#);

    let mut easy = Easy::new();
    easy.url("https://hyperion.libre.quantumblok.com/v1/chain/get_table_rows")
        .unwrap();
    easy.post(true).unwrap();
    easy.post_field_size(data.len() as u64).unwrap();

    println!("{:?}", output);
}

The following works to print the output by using command::new("curl"):

use std::process::Command;

fn main() {
    let data = ::serde_json::json!({
            "code": "swap.libre",
            "table": "stat",
            "scope": "BTCUSD",
            "json": true
    })
    .to_string();

    let output = Command::new("curl")
        .args(&[
            "-X",
            "POST",
            "--data",
            data.as_str(),
            "-H",
            "Content-Type: application/json",
            "https://hyperion.libre.quantumblok.com/v1/chain/get_table_rows",
            "-H",
            "accept: application/json",
        ])
        .output()
        .unwrap_or_else(|e| panic!("failed to execute process: {}", e));

    if output.status.success() {
        let output = String::from_utf8_lossy(&output.stdout);

        print!("curl succeeded and stdout was:\n{}", output);
    } else {
        let output = String::from_utf8_lossy(&output.stderr);

        print!("curl failed and stderr was:\n{}", output);
    }
}
sagebind commented 1 year ago

Note that these are very different, as curl-rust uses the libcurl native API, which is different than the curl command line tool. As such, libcurl doesn't have just one "output" like a command line tool. If you want to collect the HTTP response body then you should use write_function which corresponds to CURLOPT_WRITEFUNCTION in the libcurl API. Similarly, to collect response headers you should use header_function.

That might look like this:

use curl::easy::Easy;

fn main() {
    let mut output = String::new();
    let data =
        format!(r#"{{"code": "swap.libre","table": "stat","scope": "BTCUSD","json": true}}"#);

    let mut easy = Easy::new();
    easy.url("https://hyperion.libre.quantumblok.com/v1/chain/get_table_rows")
        .unwrap();
    easy.post(true).unwrap();
    easy.post_fields_copy(data.as_bytes()).unwrap();

    { // Use the Rust-specific `transfer` method to allow the write function to borrow `output` temporarily
        let mut transfer = easy.transfer();

        transfer.write_function(|data| {
            output.push_str(&String::from_utf8_lossy(data));
            Ok(data.len())
        }).unwrap();

        // Actually execute the request
        transfer.perform().unwrap();
    }

    println!("{:?}", output);
}

Generally speaking, many of the official C examples in the libcurl project will translate to Rust since curl-rust maintains a pretty close 1-to-1 API with libcurl in most places where it is safe to do so.

bensig commented 1 year ago

Thanks. Ok... this is helpful to see. I ran the code you sent above, but it seems that the POST data is not being sent correctly.

"{\"statusCode\":400,\"error\":\"Bad Request\",\"message\":\"body should have required property 'code'\"}"

sagebind commented 1 year ago

Well if I had to guess you probably need to set the Content-Type header (and maybe the Accept header too) to application/json as that is one obvious difference between your examples. But that will be up to you to determine, as curl is working correctly at this point and I do not know how to use the particular API you are trying to use.

bensig commented 1 year ago

Ok, thanks. It's a pretty simple POST with data in it, but yes - you're probably right about the content type header. I'll give it a try and report back here in case it helps someone else.