Yuki
Yuki

Reputation: 4173

libcurl ignore body in case HTTP non-ok

I am downloading file quite commonly with curl. However, the server does a tricky thing: it return non-200 code and still sends some data. The problem is that I have the HTTP code after the data are written, but I do not want to write anything if it is non-200. Does anyone know a way to do that without storing data on disk or memory?

curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteHandler);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr);
res = curl_easy_perform(curl);
if (res == CURLE_OK) {
    return 0;
}

long response_code;
curl_easy_getinfo(curl_.get(), CURLINFO_RESPONSE_CODE, &response_code);
if (response_code != 200) {
    return 0;
}

size_t curlWriteHandler(char* chars, size_t size, size_t nmemb, void* userp) {
    // write to file

    return size * nmemb;
}

Upvotes: 1

Views: 301

Answers (1)

Schwern
Schwern

Reputation: 164939

Setting CURLOPT_FAILONERROR should do it for 4xx and 5xx errors.

When this option is used and an error is detected, it will cause the connection to get closed and CURLE_HTTP_RETURNED_ERROR is returned.

curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);

Closing connection is not good for me, it is important to reuse one. Can you think about anything else?

Unfortunately I can't find a way to make CURLOPT_FAILONERROR not close the connection.

The other option is to make the write function aware of the response. Unfortunately the curl handle is not passed into the callback.

We could make the curl variable global. Or we can take advantage of the void *userdata option to the write callback and pass in a struct containing both the curl handle and the buffer.

Here's a rough sketch demonstrating how the write callback can get access to the response code and also save the response body.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <curl/curl.h>

typedef struct {
    CURL *curl;
    char *buf;
} curl_write_data;

size_t curlWriteHandler(char* chars, size_t size, size_t nmemb, void* userp) {
    curl_write_data *curl_data = (curl_write_data*)userp;
    long response_code;
    curl_easy_getinfo(curl_data->curl, CURLINFO_RESPONSE_CODE, &response_code);
    printf("Response: %ld\n", response_code);

    // Now we can save if we like.
    if( response_code < 300 ) {
        curl_data->buf = malloc(size*(nmemb+1));
        strcpy(curl_data->buf, chars);
        strcat(curl_data->buf, "\0");
        return size * nmemb;
    }
    else {
        return  0;
    }
}

int main() {
    CURL *curl = curl_easy_init();
    if(!curl) {
        perror("Cant' init curl");
    }

    curl_write_data curl_data = { .curl = curl, .buf = NULL };
    curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/alsdfjalj");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteHandler);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &curl_data);

    curl_easy_perform(curl);

    if( curl_data.buf ) {
        puts(curl_data.buf);
    }
}

I'm not sure if this is the best idea, its what I came up with.

Upvotes: 2

Related Questions