How to get the HTTP response string using Curl in C++ - c++

I'm very new to HTTP commands and the libcurl library. I know how to get the HTTP response code but not the HTTP response string. Following is the code snippet that I wrote to get the response code. Any help on how to get the response string will be highly appreciated!!!
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
CURLcode ret = curl_easy_perform(curl);
if (ret != CURLE_OK) {
LOG(INFO) << "Failed to perform the request. "
<< "Return code: " << ret;
return false;
}
std::unique_ptr<int64_t> httpCode(new int64_t);
// Get the last response code.
ret = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, httpCode.get());
if (ret != CURLE_OK) {
LOG(INFO) << "curl_easy_getinfo failed to retrieve http code. "
<< "Return code: " << ret;
return false;
}
I tried doing this as well to get the HTTP response string in readBuffer.
static size_t WriteCallback(char *contents, size_t size, size_t nmemb, void *userp)
{
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
std::string readBuffer;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
CURLcode ret = curl_easy_perform(curl);
cout << readBuffer << "\n";
But the readBuffer is empty. I don't understand where I am going wrong. Any pointers on how to solve this will be really nice!

There doesn't look to be much wrong with your code. I ran the following code, based on yours, to read the front page of the BBC news website:
#include <iostream>
#include <string>
#include <curl/curl.h>
size_t WriteCallback(char *contents, size_t size, size_t nmemb, void *userp)
{
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
int main(int argc, char * argv[])
{
curl_global_init(CURL_GLOBAL_ALL);
CURL* easyhandle = curl_easy_init();
std::string readBuffer;
curl_easy_setopt(easyhandle, CURLOPT_URL, "http://www.bbc.co.uk/news");
curl_easy_setopt(easyhandle, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(easyhandle, CURLOPT_PROXY, "http://my.proxy.net"); // replace with your actual proxy
curl_easy_setopt(easyhandle, CURLOPT_PROXYPORT, 8080L);
curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, &readBuffer);
curl_easy_perform(easyhandle);
std::cout << readBuffer << std::endl;
return 0;
}
... and I got the full HTML response. NB I had to use a proxy, and I enabled verbose mode to see what was happening. Also NB; the HTTP server might be fussy about the URL you give it: if I replace http://www.bbc.co.uk/news with http://www.bbc.co.uk/news/ (i.e. with a trailing slash) then I get no data back (a response length of zero), as noted by the verbose curl output:
Host: www.bbc.co.uk
Accept: */*
Proxy-Connection: Keep-Alive
< HTTP/1.1 301 Moved Permanently
< X-Cache-Action: PASS (no-cache-control)
< Vary: Accept-Encoding
< X-Cache-Age: 0
< Content-Type: text/html;charset=utf-8
< Date: Mon, 10 Jul 2017 16:42:20 GMT
< Location: http://www.bbc.co.uk/news
< X-XSS-Protection: 1; mode=block
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< Content-Length: 0
< X-Cache: MISS from barracuda1.[my proxy].net
< Connection: keep-alive
<
* Connection #0 to host [my proxy] left intact
Here, Content-Length is 0 and the WriteCallback() function is never called. Hope this helps.

For the numerical response code, getinfo with CURLINFO_RESPONSE_CODE is the way to go:
long response_code;
curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE,&response_code);
However there is no equivalent getinfo capture for the server's response text. If you need the server's text, inspect the raw HTTP headers. There are two ways to do this:
Enable writing headers to the payload with CURLOPT_HEADER, then extract the headers from the combined payload, splitting the body on \n\n
Set a header callback function with CURLOPT_HEADERFUNCTION and parse directly from that

Related

How to read the JSON Error response returned from Server using Libcurl

I have a requirement where I have to read the error response from backend server which returns 500 Internal Server error. The error response is in JSON Format.
Below is the code snippet used in our application
INT CCurlHTTP::HTTPSPost(const CString& endPointUrl, const CString& urlparam,const CString& cookie){
CURL *curl;
CURLcode res;
struct curl_slist *headers=NULL;
char errbuf[CURL_ERROR_SIZE];
curl = curl_easy_init();
get_request req;
req.buffer =0;
req.len =0;
req.buflen =0;
if(curl)
{
//add url, headers, and parameters to the request
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_URL, endPointUrl);
curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
headers = curl_slist_append(headers, m_httpHeadAccept);
headers = curl_slist_append(headers, m_httpContentType);
//callback function used to save response
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_String);
req.buffer = (unsigned char*) malloc(CHUNK_SIZE);
req.buflen = CHUNK_SIZE;
req.len = 0;
curl_easy_setopt(curl,CURLOPT_WRITEDATA, (void *)&req);
if (!cookie.IsEmpty())
{
headers = curl_slist_append(headers, m_DBAuthCertficate); //What is difference between this and line no 118?
CString pCookie = "DBAuthTicket=" + cookie;
curl_easy_setopt(curl,CURLOPT_COOKIE, pCookie);
}
else
{
headers = curl_slist_append(headers, m_OAuthToken);
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, urlparam);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
errbuf[0] = 0;
curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 512000);
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
{
/* if errors have occured, tell us wath's wrong with 'result'*/
m_response.Format("%s", curl_easy_strerror(res));
return res;
}
m_response = (char*)req.buffer;
m_errDescription = errbuf;
len = req.len;
buflen = req.buflen;
curl_easy_cleanup(curl);
free(req.buffer);
}
return res;
}
/****************************************************************************
Function: CurlWrite_CallbackFunc_String
Description: Read data from the connected URL
Return: String of data and size
****************************************************************************/
size_t CCurlHTTP::CurlWrite_CallbackFunc_String(void *contents, size_t size, size_t nmemb, void *userdata)
{
size_t rLen = size*nmemb;
get_request* req = (get_request*)userdata;
while(req->buflen < req->len + rLen + 1)
{
req->buffer = (unsigned char*)realloc(req->buffer,req->buflen + CHUNK_SIZE);
req->buflen += CHUNK_SIZE;
}
memcpy(&req->buffer[req->len], contents, rLen);
req->len += rLen;
req->buffer[req->len] = 0;
return rLen;
}
The above code works fine for the Success 200 OK Requests. It reads the JSON Response just fine. However, when I get a 500 Internal Server error, it does not read the JSON Error response that comes along with it. How do I read the JSON response in this scenario?
By setting the CURLOPT_FAILONERROR option to TRUE, you are telling curl_easy_perform() to fail immediately with CURLE_HTTP_RETURNED_ERROR on any HTTP response >= 400. It will not call the CURLOPT_WRITEFUNCTION callback, as it will simply close the connection and not even attempt to read the rest of the response.
To get the response data you want, simply remove the CURLOPT_FAILONERROR option. Curl's default behavior is to deliver the response data to you regardless of the HTTP response code. In which case, curl_easy_perform() will return CURLE_OK, and you can then retrieve the response code using curl_easy_getinfo(CURLINFO_RESPONSE_CODE) to check if the HTTP request was successful or not.
On a side note, since the code shown is written in C++, I would strongly advise you NOT to use a dynamic char[] buffer for get_request::buffer. Not only because you are not handling malloc()/realloc() failures at all, but also because manual memory management should be avoided in C++ in general. Use std::string or std::vector<char> instead, in which case you can eliminate get_request in this code altogether, eg:
INT CCurlHTTP::HTTPSPost(const CString& endPointUrl, const CString& urlparam,const CString& cookie){
...
std::string resp;
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
...
//curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
/* if errors have occured, tell us what's wrong with 'result'*/
m_response.Format("%s", curl_easy_strerror(res));
curl_easy_cleanup(curl);
return res;
}
m_response = resp.c_str();
m_errDescription = errbuf;
len = resp.size();
buflen = resp.capacity();
curl_easy_cleanup(curl);
return res;
}
size_t CCurlHTTP::CurlWrite_CallbackFunc_String(void *contents, size_t size, size_t nmemb, void *userdata)
{
size_t rLen = size * nmemb;
static_cast<std::string*>(userdata)->append(static_cast<char*>(contents), rLen);
return rLen;
}

Rate Beer API : Receiving 500 Code giving "POST body missing" when the data has actually been sent off

I am using libcurl with C++ in order send a POST request off to query some information off of RateBeer on a particular beer. It says the data has been sent off but I am getting a "POST fields empty" error (which is from the graphQL side I assume).
The following is a dump of the current result (I have a print of the raw post data as well)
* Trying 13.249.142.63...
* TCP_NODELAY set
* Connected to api.r8.beer (13.249.142.63) port 443 (#0)
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: none
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: CN=*.r8.beer
* start date: Aug 14 00:00:00 2018 GMT
* expire date: Sep 14 12:00:00 2019 GMT
* subjectAltName: host "api.r8.beer" matched cert's "*.r8.beer"
* issuer: C=US; O=Amazon; OU=Server CA 1B; CN=Amazon
* SSL certificate verify ok.
> POST /v1/api/graphql/ HTTP/1.1
Host: api.r8.beer
User-Agent: libcurl-agent/1.0
Content-Type:application/json
Accept:application/json
x-api-[key:Removed for privacy]
Transfer-Encoding: chunked
cb
* upload completely sent off: 210 out of 203 bytes
< HTTP/1.1 500 Internal Server Error
< Content-Type: application/json
< Content-Length: 61
< Connection: keep-alive
< Date: Thu, 31 Jan 2019 16:07:38 GMT
< x-amzn-RequestId: 547d7bc7-2572-11e9-b411-af48978c268e
< CF-RAY: 4a1d6eb288bda66b-DUB
< Access-Control-Allow-Origin: *
< Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, x-api-key, x-api-auth
< x-amzn-Remapped-Connection: keep-alive
< X-Request-Id: 54b37fa0-2572-11e9-ac7f-9d4fc71779d1
< Set-Cookie: __cfduid=d84de3db6937ad5bef73abca5ae68e43a1548950858; expires=Fri, 31-Jan-20 16:07:38 GMT; path=/; domain=.r8.beer; HttpOnly; Secure
< x-amz-apigw-id: UYGDpFkeDoEFR2w=
< x-amzn-Remapped-Server: cloudflare
< X-Powered-By: Express
< x-amzn-Remapped-Date: Thu, 31 Jan 2019 16:07:38 GMT
< X-Cache: Error from cloudfront
< Via: 1.1 feeead777aa6b11f4775062f1953fdc4.cloudfront.net (CloudFront)
< X-Amz-Cf-Id: kg0xraCi9C9zdmNff3iEZXOBpTMKPwNSQa6nzV-tyXqCdVCFFOP2Jg==
<
* Connection #0 to host api.r8.beer left intact
{"query":"query{\nTruth:beerSearch(query: \"Truth\", first : 5) {\n...beerfields\n}\n}\nfragment beerfields on BeerList{\nitems{\nname\nbrewer{\nname\n}\ndescription\nratingCount\n}\n}","variables":"{}"}
Response: POST body missing. Did you forget use body-parser middleware Response End
Some debugging I have tried in this conquest:
Changing the code to use a readback callback instead of using the POST_FIELDS default, same result
Messing with the capitalization of headers
Changed the transfer method to using chunked
Trying to POST to the sandbox API and the production API
Here is the query function:
NOTE: m_api is just a member with the api key and the api base URL
void cURLWrapperRateBeer::QueryBeerList(const CBeerList& inBeerList)
{
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl)
{
// Construct the header struct
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, "Content-Type:application/json");
headers = curl_slist_append(headers, "Accept:application/json");
std::string keyPhrase = std::string("x-api-key:").append(m_api.second);
headers = curl_slist_append(headers, keyPhrase.c_str());
headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
// Set the URL and the data we want
curl_easy_setopt(curl, CURLOPT_URL, m_api.first.c_str());
// Signify our intention to post data
curl_easy_setopt(curl, CURLOPT_POST, 1L);
// Verbose Output
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
// Give it a user agent
curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
std::string data = CreateRequestURL(inBeerList);
data.append("\0");
// Set up the data container for our push request
const char* c_data = data.c_str();
std::cout << c_data;
struct WriteStatus data_container;
data_container.p = c_data;
data_container.remaining = (long)strlen(c_data);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data_container.p);
/*
Define the buffer for writing memory and what the callback function will
use for its buffer
*/
//curl_easy_setopt(curl, CURLOPT_READFUNCTION, &cURLWrapperRateBeer::ReadData);
//curl_easy_setopt(curl, CURLOPT_READDATA, &data_container);
// Size of the data
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)data_container.remaining);
// Handler for reading response
std::string response;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &cURLWrapperRateBeer::WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
// Set our custom set of headers
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
if(res != CURLE_OK)
{
fprintf(stderr, "curl_easy_perform() failed: %s\n",curl_easy_strerror(res));
//std::cout << "DUMP : " << data_container.p;
}
#ifdef SKIP_PEER_VERIFICATION
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
#endif
#ifdef SKIP_HOSTNAME_VERIFICATION
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
#endif
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
std::cout << "Response: " << response;
std:: cout << "Response End";
/* Check for errors */
if(res != CURLE_OK)
{
fprintf(stderr, "curl_easy_perform() failed: %s\n",curl_easy_strerror(res));
}
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_global_cleanup();
}

C++ libcurl not posting data

I am working on a C++ library which is making use of the curl library to perform an HTTP POST request to a webservice.
I have a function which is responsible for performing curl and processing the response, and this function calls another function to set curl up as it can be used from multiple places with the same setup.
Its successfully connects to the web service and I can see the response returned however, no post data is sent to the web service.
Below the code that I have to perform the HTTP request.
The first function is the function that does the curl perform, it calls a function to init the curl library and then return the pointer to it for use.
string response;
struct curl_slist *list = NULL;
const char * jsonString = ddEvent.getDDEventAsJSONString().c_str();
CURL *curl = this->initCurl(ddEvent, &response, list, &jsonString);
if (curl == NULL)
{
return false;
}
list = curl_slist_append(list, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
CURLcode res = curl_easy_perform(curl);
The function that initialises curl is as follows:
CURL * MyClass::initCurl(DDEvent ddEvent, string *response, struct curl_slist *list, const char **jsonString)
{
CURL *curl = NULL;
curl = curl_easy_init();
if (curl == NULL)
{
cout << "Failed to initialise curl" << endl;
return NULL;
}
stringstream url_stream;
url_stream << "http://192.168.1.123";
string url= dd_url_stream.str();
cout << "Using URL: " << url<< endl;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, response);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &MyClass::curlResponseWriteCallback);
curl_easy_setopt(curl, CURLOPT_HEADER, 1);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, *jsonString);
return curl;
}
Also, the post data is a JSON string, its not a key/value posted form data.
Below is the output from the the verbose mode of curl
Using JSON string {"title":"HTTP Event Teesg","text":"Test warning event","alert_type":"success","tags":["simple_string_tag"]}
Using URL: http://192.168.1.123
* About to connect() to 192.168.1.123 port 80 (#0)
* Trying 192.168.1.123...
* Connected to 192.168.1.123 (192.168.1.123) port 80 (#0)
> POST / HTTP/1.1
Host: 192.168.1.123
Accept: */*
Content-Type: application/json
Content-Length: 0
< HTTP/1.1 200 OK
< Date: Sun, 04 Feb 2018 20:11:12 GMT
< Server: Apache/2.4.6 (CentOS) PHP/5.6.32
< X-Powered-By: PHP/5.6.32
< X-XSS-Protection: 1; mode=block
< Content-Length: 52
< Content-Type: text/html; charset=UTF-8
<
The Content-Length: 0 in the outgoing request suggests that the data you pass to CURLOPT_POSTFIELDS has zero length.
Note that CURLOPT_POSTFIELDS doesn't copy the data, it just points to your memory so you might want to make sure the data is still around for the entire time libcurl needs it. Since you haven't shown us the entire program we can't really tell you exactly how this happens for you.
Finally: why not just pass a 'char *' to initCurl instead of 'char **' that you need to dereference within the method?. It looks odd to me.
It seems that ddEvent.getDDEventAsJSONString() returns a temporary std::string object.
Save it in a local variable and then pass it as reference.
std::string jsonString = ddEvent.getDDEventAsJSONString();
this->initCurl(ddEvent, &response, list, jsonString);
CURL * MyClass::initCurl(DDEvent ddEvent, string *response, struct curl_slist *list, const std::string& jsonString)
You have to keep the string alive until you call curl_easy_perform() since CURLOPT_POSTFIELDS doesn't copy the string into libcurl.
Strings passed to libcurl as 'char *' arguments, are copied by the library; thus the string storage associated to the pointer argument may be overwritten after curl_easy_setopt returns. The only exception to this rule is really CURLOPT_POSTFIELDS, but the alternative that copies the string CURLOPT_COPYPOSTFIELDS has some usage characteristics you need to read up on.
There is no problem in your code where you call curl_easy_perform in the life scope of jsonString. However, if you intend to call the function afterward, instead of CURLOPT_POSTFIELDS use CURLOPT_COPYPOSTFIELDS which copy and memorize the data to post.

C++ CURL not returning data on specific URL

I want to send CGI command to IP camera.
I send e.a. CGI url to receive camera name and the function returns the right name, so I think my code is fine.
When I send another CGI url from my browser, it is working (camera returns "ok") but when I try to send it with my program, my camera is not returning anything and parameters are not set in camera.
here is my code:
string data; //will hold the url's contents
size_t writeCallback(char* buf, size_t size, size_t nmemb, void* up)
{ for (int c = 0; c<size*nmemb; c++)
{
data.push_back(buf[c]);
}
return size*nmemb; //tell curl how many bytes we handled
}
string send_CGI_command(string username, string password, string port, string URL)
{
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
curl_global_init(CURL_GLOBAL_ALL); //pretty obvious
curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_PORT , stoll (port,0,0) );
curl_easy_setopt(curl, CURLOPT_USERNAME , username.c_str());
curl_easy_setopt(curl, CURLOPT_USERPWD , (username + ":" + password).c_str());
curl_easy_setopt(curl, CURLOPT_HTTPAUTH , CURLAUTH_BASIC);
curl_easy_setopt(curl, CURLOPT_URL, (URL).c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCallback);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl_global_cleanup();
return data;
}
}
I use this command to request the CGI:
string result = send_CGI_command(get_video_login_cam1(),get_video_password_cam1(),get_video_port_cam1(),"http://" + get_video_ip_cam1() + "/cgi-bin/configManager.cgi?action=setConfig&Locales.DSTEnable=true&Locales.DSTEnd.Day=0&Locales.DSTEnd.Hour=3&Locales.DSTEnd.Minute=0&Locales.DSTEnd.Month=10&Locales.DSTEnd.Week=-1&Locales.DSTEnd.Year=" + std::to_string(1900 + ltm->tm_year) + "&Locales.DSTStart.Day=0&Locales.DSTStart.Hour=2&Locales.DSTStart.Minute=0&Locales.DSTStart.Month=3&Locales.DSTStart.Week=-1&Locales.DSTStart.Year=" + std::to_string(1900 + ltm->tm_year) + "&Locales.TimeFormat=dd-MM-YYYY HH:mm:ss");
cout << endl << result << endl;
This is the debugger info:
* Trying 192.168.1.14...
* Connected to 192.168.1.14 (192.168.1.14) port 80 (#0)
* Server auth using Basic with user 'admin'
> GET /cgi-bin/configManager.cgi?action=setConfig&Locales.DSTEnable=true&Locales.DSTEnd.Day=0&Locales.DSTEnd.Hour=3&Locales.DSTEnd.Minute=0&Locales.DSTEnd.Month=10&Locales.DSTEnd.Week=-1&Locales.DSTEnd.Year=2016&Locales.DSTStart.Day=0&Locales.DSTStart.Hour=2&Locales.DSTStart.Minute=0&Locales.DSTStart.Month=3&Locales.DSTStart.Week=-1&Locales.DSTStart.Year=2016&Locales.TimeFormat=dd-MM-YYYY HH:mm:ss HTTP/1.1
Host: 192.168.1.14
Authorization: Basic YWRtaW46YWRtaW4=
Accept: */*
* Empty reply from server
* Connection #0 to host 192.168.1.14 left intact
I have no idea why my exact same CGI is working in browser and the exact same cgi does not returing anything with CURL. Like I said in beginning, another CGI command to get device name is working in browser and with same CURL code.
Thank you in advance for your suggestions.

How can I CURL POST a file using file pointer in C++

I have a file pointer, such as the following:
FILE* f = tmpfile()
How do I use libcurl to do a HTTP POST to a URL as a field named F1?
I tried reading the file contents into a char* array but and used the following to upload:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>
char* dump_buffer(void *buffer, int buffer_size){
int i;
char *ch = malloc(buffer_size);
for(i = 0;i < buffer_size;++i){
ch[i] = ((char *)buffer)[i];
//printf("%c",((char *)buffer)[i]);
}
return ch;
}
char* readFileBytes(const char *name){
FILE *file;
char *buffer;
unsigned long fileLen;
int i;
file = fopen("index.tar", "rb");
if (!file)
{
fprintf(stderr, "can't open file %s", "1.m4v");
exit(1);
}
fseek(file, 0, SEEK_END);
fileLen=ftell(file);
fseek(file, 0, SEEK_SET);
buffer=(char *)malloc(fileLen+1);
if (!buffer)
{
fprintf(stderr, "Memory error!");
fclose(file);
exit(1);
}
fread(buffer, fileLen, 1, file);
fclose(file);
char* ret = dump_buffer(&buffer, fileLen);
for(i = 0;i < fileLen;++i){
//printf("%c",ret[i]);
}
return ret;
}
int main(int argc, char *argv[])
{
CURL *curl;
CURLcode res;
struct curl_httppost *formpost=NULL;
struct curl_httppost *lastptr=NULL;
struct curl_slist *headers=NULL;
headers = curl_slist_append(headers, "Content-Type: multipart/form-data");
curl_global_init(CURL_GLOBAL_ALL);
/* Fill in the filename field */
char* p = readFileBytes("index.tar");
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "F2",
CURLFORM_FILE, "index.tar",
CURLFORM_END);
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "F1",
CURLFORM_COPYCONTENTS, (char*)p,
CURLFORM_END);
curl = curl_easy_init();
/* initalize custom header list (stating that Expect: 100-continue is not
wanted */
if(curl) {
/* what URL that receives this POST */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_URL, "http://oceanfizz.usc.edu/upload.php");
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
/* then cleanup the formpost chain */
curl_formfree(formpost);
/* free slist */
}
return 0;
}
The output that I get is
guest-wireless-207-151-246-070:Desktop ankurcha$ ./postit2
* About to connect() to oceanfizz.usc.edu port 80 (#0)
* Trying 128.125.49.29... * connected
* Connected to oceanfizz.usc.edu (128.125.49.29) port 80 (#0)
> POST /upload.php HTTP/1.1
Host: oceanfizz.usc.edu
Accept: */*
Content-Length: 20770
Expect: 100-continue
Content-Type: multipart/form-data; boundary=----------------------------e04b6194f620
< HTTP/1.1 100 Continue
< HTTP/1.1 200 OK
< Date: Mon, 19 Apr 2010 03:51:04 GMT
< Server: Apache/2.2.12 (Ubuntu)
< X-Powered-By: PHP/5.2.10-2ubuntu6.4
< Vary: Accept-Encoding
< Content-Length: 335
< Content-Type: text/html
<
array(1) {
["F2"]=>
array(5) {
["name"]=>
string(9) "index.tar"
["type"]=>
string(24) "application/octet-stream"
["tmp_name"]=>
string(14) "/tmp/phpyOiqXh"
["error"]=>
int(0)
["size"]=>
int(20480)
}
}
array(1) {
["F1"]=>
string(0) ""
}
Sorry, there was a problem uploading your file.
* Connection #0 to host oceanfizz.usc.edu left intact
* Closing connection #0
I was expecting F1 to have binary content.
A HTTP POST can be done in many ways so there's not a single answer unless you specify more details in the question. One way to do POST programmatically with libcurl is as shown in this example:
http://curl.haxx.se/libcurl/c/post-callback.html
If you rather want to do a multipart formpost upload, possibly a better example is this:
http://curl.haxx.se/libcurl/c/postit2.html
You upload code works fine. The file uploaded is stored in a temporary file /tmp/phpyOiqXh. PHP won't store the content of uploaded file to a variable, just store the temp file's path.