Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ cUrl Send mulipart/form-data file to webserver

Tags:

c++

curl

like the title says I want to send a file with the cUrl lib in a C++ program to a webserver.

I got the url from the server and information that it wants the HTTP POST methode and in mutipart/ form-data. Also it wants a parameter file with the data.

I also got a cUrl-call which works with the curl console:

curl -X POST -H "Cache-Control: no-cache" -H "Content-Type: multipart/form-data " -F "[email protected]" "url"

If I run the code below, the server bring the answer that there is no upload or no multipart form.

And now my code:

//## File stuff
struct stat file_info;
FILE *fd;

fopen_s(&fd,"file.txt", "rb"); /* open file to upload */
if (!fd) {

    return 1; /* can't continue */
}

/* to get the file size */
if (fstat(_fileno(fd), &file_info) != 0) {

    return 1; /* can't continue */
}
//##

CURL *hnd = curl_easy_init();

curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(hnd, CURLOPT_URL, "url");

struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "cache-control: no-cache");
headers = curl_slist_append(headers, "content-type: multipart / form-data");
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);

curl_easy_setopt(hnd, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(hnd, CURLOPT_READDATA, fd);

curl_easy_setopt(hnd, CURLOPT_INFILESIZE_LARGE,(curl_off_t)file_info.st_size);

CURLcode ret = curl_easy_perform(hnd);

Edit

New code with file from buffer:

std::string contents;
std::ifstream in("empty.txt", std::ios::in | std::ios::binary);

if (in)
{
    in.seekg(0, std::ios::end);
    contents.resize(in.tellg());
    in.seekg(0, std::ios::beg);
    in.read(&contents[0], contents.size());
    in.close();
}

CURL *hnd = curl_easy_init();

uint32_t size = contents.size();

//struct curl_slist *headers = NULL;
//headers = curl_slist_append(headers, "cache-control: no-cache");
//headers = curl_slist_append(headers, "content-type: multipart/form-data");
//curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);

curl_easy_setopt(hnd, CURLOPT_URL, "url");
curl_easy_setopt(hnd, CURLOPT_POST, 1);
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, contents.data());
curl_easy_setopt(hnd, CURLOPT_POSTFIELDSIZE, size);

CURLcode ret = curl_easy_perform(hnd);

Now the server response that there is no file data. Have I also to say that it is multipart/form-data? But if i uncomment the header setting the response is an Error about the multiform boundary. I´m also confused about the "file" parameter which is wanted. Do i have to add it to the CURLOPT_POSTFIELDS?

Hope one of you can help me thanks!

Edit Final

I got it. The following code works for me with the give server settings.

std::string contents;
std::ifstream in("file.txt", std::ios::in | std::ios::binary);

if (in)
{
    in.seekg(0, std::ios::end);
    contents.resize(in.tellg());
    in.seekg(0, std::ios::beg);
    in.read(&contents[0], contents.size());
    in.close();
}
//##


CURL *curl;
CURLcode res;

struct curl_httppost *formpost = NULL;
struct curl_httppost *lastptr = NULL;
struct curl_slist *headerlist = NULL;
static const char buf[] =  "Expect:";

curl_global_init(CURL_GLOBAL_ALL);

// set up the header
curl_formadd(&formpost,
    &lastptr,
    CURLFORM_COPYNAME, "cache-control:",
    CURLFORM_COPYCONTENTS, "no-cache",
    CURLFORM_END);

curl_formadd(&formpost,
    &lastptr,
    CURLFORM_COPYNAME, "content-type:",
    CURLFORM_COPYCONTENTS, "multipart/form-data",
    CURLFORM_END);

curl_formadd(&formpost, &lastptr,
    CURLFORM_COPYNAME, "file",  // <--- the (in this case) wanted file-Tag!
    CURLFORM_BUFFER, "data",
    CURLFORM_BUFFERPTR, contents.data(),
    CURLFORM_BUFFERLENGTH, contents.size(),
    CURLFORM_END);

curl = curl_easy_init();

headerlist = curl_slist_append(headerlist, buf);
if (curl) {

    curl_easy_setopt(curl, CURLOPT_URL, "url");

    curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
    //curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
    //curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
    //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

    res = curl_easy_perform(curl);
    /* Check for errors */
    if (res != CURLE_OK)
        fprintf(stderr, "curl_easy_perform() failed: %s\n",
            curl_easy_strerror(res));


    curl_easy_cleanup(curl);


    curl_formfree(formpost);

    curl_slist_free_all(headerlist);
}
like image 937
HEvTH Avatar asked Jul 12 '16 05:07

HEvTH


1 Answers

You don't format your multipart data correctly. You can see what it should look like with the help of the netcat utility:

In terminal 1:

$ nc -lk 8080 # listen for TCP connection on port 8080

In terminal 2:

$ curl -X POST -F "[email protected]" localhost:8080

By checking terminal 1 you will see that curl sent a request similar to the following:

POST / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.43.0
Accept: */*
Content-Length: 193
Expect: 100-continue
Content-Type: multipart/form-data; boundary=------------------------a975b09b8e15800c

--------------------------a975b09b8e15800c
Content-Disposition: form-data; name="file"; filename="test.txt"
Content-Type: text/plain

qwerty

--------------------------a975b09b8e15800c--

Now, debug your C++ code by making it send the data to netcat (localhost:8080) and fix the code until its request resembles the one sent by curl.

A general advice - don't debug blindly. The response of a server that you don't control doesn't provide enough information about the problem. If possible, do the debugging in an environment that allows you to see every bit of your data and verify that the data complies with the specification and/or your expectations.

like image 197
Leon Avatar answered Oct 31 '22 04:10

Leon