guitarflow
guitarflow

Reputation: 2970

libcurl retrieve response from HTTP request

In my OS X application, I've been using NSURLRequest for quite a while to send an HTTP request to a server and receive the response. Now I need to migrate this code to use libcurl instead to be able to run it on Windows and Mac.

For some reason, I can't get it to work...

Here's what I'm trying to do:

When the block that is sent along with the request is valid, an "application/octet-stream" data block is sent back to the requesting application right after. If the file is not valid, a return code 403 is returned.

When I try to put the post request URL and the data block directly in the browser, it works. If the data block is valid, a file will be downloaded, if not: a 403 page is shown.

My libcurl code looks like this:

curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();

if (curl)
{
    struct url_data data;
    data.size = 0;
    data.data = (char*) malloc(4096); /* reasonable size initial buffer */

    if(NULL == data.data)
    {
        fprintf(stderr, "Failed to allocate memory.\n");
        return NULL;
    }

    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
    curl_easy_setopt(curl, CURLOPT_URL, "<my-url>");
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, [postData cStringUsingEncoding:NSASCIIStringEncoding]);

    curl_slist_append(headerlist, "Content-Type: application/x-www-form-urlencoded");
    curl_slist_append(headerlist, [length cStringUsingEncoding:NSASCIIStringEncoding]);

    curl_easy_setopt(curl, CURLOPT_HEADER, headerlist);

    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);

    curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");

    res = curl_easy_perform(curl);

    if(res != CURLE_OK)
    {
        fprintf(stderr, "curl_easy_perform() failed: %s\n",
                         curl_easy_strerror(res));
    }

    curl_easy_cleanup(curl);
    }
    curl_global_cleanup();
}

And my write function looks like this:

struct url_data 
{
    size_t size;
    char* data;
};

size_t write_data(void *ptr, size_t size, size_t nmemb, struct url_data *data)
{
    size_t index = data->size;
    size_t n = (size * nmemb);
    char* tmp;

    data->size += (size * nmemb);

    fprintf(stderr, "data at %p size=%ld nmemb=%ld\n", ptr, size, nmemb);
    tmp = (char*) realloc(data->data, data->size + 1); /* +1 for '\0' */

    memcpy((data->data + index), ptr, n);
    data->data[data->size] = '\0';

    return size * nmemb;
}

I am receiving the right return codes and also the 403 page in case the block is not valid. In case it's valid, I'm not receiving the response block. The write function is never being called. The connection is always closed immediately.

Can anyone tell me what I'm doing wrong?

Here's a part of the log output of libcurl:

* Adding handle: conn: 0x2a9ee00
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x2a9ee00) send_pipe: 1, recv_pipe: 0
* About to connect() to <my-url> port 80 (#0)
*   Trying <my-url-ip>...
* Connected to <my-url> (<my-url-ip>) port 80 (#0)

Does this recv-pipe cause the problem maybe? Should it be 1?

I'm curious to hear your thoughts! Thanks in advance!

Upvotes: 0

Views: 1259

Answers (2)

jaeheung
jaeheung

Reputation: 1208

The only problem I found is that you didn't saved the new address from realloc(). It will surely crash, but the write_data() should have been called at least once.

How do you know it is never being called?

Please try this write_data() instead:

size_t write_data(void *ptr, size_t size, size_t nmemb, struct url_data *data)
{ 
    size_t index = data->size;
    size_t n = (size * nmemb);
    char* tmp;

    data->size += (size * nmemb);

    fprintf(stderr, "data at %p size=%ld nmemb=%ld\n", ptr, size, nmemb);
    tmp = (char*) realloc(data->data, data->size + 1); /* +1 for '\0' */
    if (tmp == NULL) {
        fputs("Error (re)allocating memory", stderr);
        return 0;
    }
    data->data = tmp;

    memcpy((data->data + index), ptr, n);
    data->data[data->size] = '\0';

    return size * nmemb;
}

Upvotes: 0

deltheil
deltheil

Reputation: 16121

You should pass your custom headers with curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); and not using CURLOPT_HEADER that expects a long as parameter (0 or 1 to not include/include the header in the body output).

Upvotes: 1

Related Questions