C++ CURL post file and then get response error

1

I'm trying to POST a file to an API and then check the response for various things. But whenever I try to POST a file that is larger than 0 bytes i get an error:

First-chance exception at 0x77AADF63 (ntdll.dll) in Test.exe: 
0xC0000005: Access violation writing location 0x00000014.

The relevant code is:

std::string Network::Post(std::string url, std::map<std::string, std::string> requestBody, std::string file, int port) {
    std::ostringstream response;

    std::stringstream bodyStream;
    struct stat file_info;


    for (std::map<std::string, std::string>::iterator iterator = requestBody.begin(); iterator != requestBody.end(); iterator++) {
        bodyStream << iterator->first << "=" << iterator->second << "&";
    }

    //Get file
    FILE* file_handle = fopen(file.c_str(), "rb");
    int t = fstat(_fileno(file_handle), &file_info);

    CURL *ch;
    curl_global_init(CURL_GLOBAL_ALL);
    ch = curl_easy_init();

    struct curl_slist *headers = NULL;

    curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");

    std::string bodyStreamString = bodyStream.str();
    const char* bodyStreamCharPtr = bodyStreamString.c_str();

    curl_easy_setopt(ch, CURLOPT_URL, url.c_str());
    curl_easy_setopt(ch, CURLOPT_PORT, port);
    curl_easy_setopt(ch, CURLOPT_POSTFIELDS, bodyStreamCharPtr);
    curl_easy_setopt(ch, CURLOPT_POSTFIELDSIZE, bodyStream.str().length());
    curl_easy_setopt(ch, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(ch, CURLOPT_AUTOREFERER, 1);
    curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(ch, CURLOPT_COOKIEFILE, "cookies.dat");
    curl_easy_setopt(ch, CURLOPT_COOKIEJAR, "cookies.dat");
    curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, &ResponseToString);
    curl_easy_setopt(ch, CURLOPT_VERBOSE, 1L);
    curl_easy_setopt(ch, CURLOPT_UPLOAD, 1L);
    curl_easy_setopt(ch, CURLOPT_READDATA, file_handle);
    curl_easy_setopt(ch, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size);
    curl_easy_setopt(ch, CURLOPT_WRITEDATA, &response);

    int res = curl_easy_perform(ch);
    curl_easy_cleanup(ch);

    return response.str();
}


size_t Network::ResponseToString(char *ptr, size_t size, size_t nmemb, void *stream) {
    std::ostringstream *s = (std::ostringstream*)stream;
    size_t count = size * nmemb;
    s->write(ptr, count);
    return count;
}

Any idea what's happenning? Been stuck on it for a day or so, sigh :(

More details:

It just breaks at " int res = curl_easy_perform(ch);" , can't step into it.

If I remove the WRITEFUNCTION and WRITEDATA options , it works, but then I can't get the response.

The problem doesn't seem to be in the ResponseToString method either.

c++
curl
asked on Stack Overflow Aug 27, 2015 by overburn • edited Aug 27, 2015 by overburn

2 Answers

1

The problem is your WRITEFUNC.

I'm quite sure Network::ResponseToString is not static, and this cause the problem, since a non static function will pass "this" as first argument and then screw your function parameters, I suggest to avoid using a class member at all and use this (to be placed in the same file as Network::Post and before Post method definition):

static size_t ResponseToString(char *ptr, size_t size, size_t nmemb, std::ostringstream *stream) {
    size_t count = size * nmemb;
    stream->write(ptr, count);
    return count;
}

Remember also to change:

curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, &ResponseToString);

in:

curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, ResponseToString);

Also, a lot of the headers you specify in curl opt declaration are not needed if your scope is a simple "post":

curl_easy_setopt(ch, CURLOPT_POSTFIELDSIZE, bodyStream.str().length());
curl_easy_setopt(ch, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(ch, CURLOPT_READDATA, file_handle);
curl_easy_setopt(ch, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size);
answered on Stack Overflow Aug 27, 2015 by gabry
0

Decided to pipe all the requests from my app through Fiddler and I discovered that nothing was being sent and back came an error (sent 0 bytes out of n). So i poked around a bit and discovered the issue.

Aaand, the problem was my lack of a READFUNCTION. Solved it like this:

curl_easy_setopt(ch, CURLOPT_READDATA, file_handle);
curl_easy_setopt(ch, CURLOPT_READFUNCTION, &RequestToFile);

And this is the read function. Apparently it's required on windows.

size_t EasyCloud::Network::RequestToFile(void *ptr, size_t size, size_t nmemb, FILE *stream) {

    curl_off_t nread;

    size_t retcode = fread(ptr, size, nmemb, stream);

    nread = (curl_off_t)retcode;

    return retcode;
}
answered on Stack Overflow Aug 27, 2015 by overburn

User contributions licensed under CC BY-SA 3.0