Quite new to c++ and all of this so sorry for any awful awful mistakes that I may make.
I have a post function that I use to send data to a website and then returns other data to me.
The problem is that it times out at curl_easy_perform
Here's my source.
It was working not too long ago but it stopped for some weird reason. I can assure you that it is not the website as I've tried with many others.
size_t WriteCallback(char *contents, size_t size, size_t nmemb, void *userp)
{
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
string post(string source) {
CURL *curl;
CURLcode res;
string readBuffer;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "mywebsite");
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, source.length());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, source.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
res = curl_easy_perform(curl);
cout << res << endl;
curl_easy_cleanup(curl);
return readBuffer;
}
curl_global_cleanup();
return 0;
}
Solved. Turns out something was wrong with how I initialized my console and main functions which I don't quite understand. Thanks anyways!
Related
I'm trying to create a basic login program that's connected to server but when I try to save the response from a post request to a variable I wont get a response at all but if I don't try to save it I do get a response, I'm not really sure what I'm doing wrong, any help would be appreciated, cheers.
int post(string str1, string str2, string str3) {
string query = "user=" + str1 + "&pass=" + str2;
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://www.vlone.cc/pwned.php");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, query.c_str());
// when i comment out these 2 lines I get a response otherwise I don't
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_string);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &str3);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
Write to string
size_t write_to_string(void *ptr, size_t size, size_t count, void *stream) {
((string*)stream)->append((char*)ptr, 0, size*count);
return size*count;
}
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.
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);
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;
}
I am trying to run a simple example using libcurl, but just running this simple example gives me CURLE_WRITE_ERROR when I execute the curl_easy_perform(...) command. Does anyone have any idea what I am doing wrong? I have also tried other sites besides example.com.
CURL *curl = curl_easy_init();
if(curl)
{
CURLcode res;
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/");
res = curl_easy_perform(curl); // returns CURLE_WRITE_ERROR always!
curl_easy_cleanup(curl);
}
OK turns out Joachim is right. I did need a write callback
size_t CurlWriteCallback(char* buf, size_t size, size_t nmemb, void* up)
{
TRACE("CURL - Response received:\n%s", buf);
TRACE("CURL - Response handled %d bytes:\n%s", size*nmemb);
// tell curl how many bytes we handled
return size*nmemb;
}
// ...
CURL *curl = curl_easy_init();
if(curl)
{
CURLcode res;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &CurlWriteCallback);
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/");
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
I'm performing a server request with curl in C++ which return responses in pieces and those pieces's size may also vary.
At the time of arrival of each piece, the callback function is being called. The problem is I can't detect when the connection finished in order to perform an another callback to my parent class.
And by the way, I want to know if we can set and detect timeout for a curl?
Here is my code in short:
CURL *curl = curl_easy_init();
curl_global_init(CURL_GLOBAL_ALL);
curl_easy_setopt(curl, CURLOPT_URL, "My URL");
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "My Postfields");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
curl_easy_perform(curl);
curl_easy_cleanup(curl);
curl_global_cleanup();
The default callback:
size_t writeCallback(char* buf, size_t size, size_t nmemb, void* up)
{
//do something
//But how can I detect the last callback when connection finished
//in order to call an another one?
return size*nmemb;
}
The data you want can be saved off during the callback, then used once curl_easy_perform returns. Example:
CURL *curl = curl_easy_init();
curl_global_init(CURL_GLOBAL_ALL);
// NOTE: added to accumulate data.
std::string result;
curl_easy_setopt(curl, CURLOPT_URL, "My URL");
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "My Postfields");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result); // NOTE: added
curl_easy_perform(curl);
// TODO: do something with your data stored in result
curl_easy_cleanup(curl);
curl_global_cleanup();
And in your write callback:
size_t writeCallback(char* buf, size_t size, size_t nmemb, void* up)
{
std::string* pstr = static_cast<std::string*>(up);
std::copy(buf, buf+size*nmemb, std::back_inserter(*pstr));
return size*nmemb;
}
or something along those lines. I leave all the error checking to you (and sorry for any typos; I don't have a compiler to validate this on immediately available to me).
Regarding timeout length, there are a multitude of timeout options available to a easy-mode curl request. Too many to mention here, in fact. See the documentation for curl_easy_setopt, in particular the connection options approximately 2/3rd of the way down the page.
Best of luck.
Take a look at the following code
static size_t reader(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t retcode = fread(ptr, size, nmemb, stream);
cout << "*** We read " << retcode << " bytes from file" << endl;
return retcode;
}
void upload() { //upload() is called from ouside
FILE *pFile;
pFile = fopen("map.txt" , "r");
struct stat file_info;
stat("map.txt", &file_info);
size_t size = (size_t)file_info.st_size;
uploadFile(pFile, size);
}
bool uploadFile(void* data, size_t datasize) {
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
char *post_params = ...;
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_params);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long) strlen(post_params));
curl_easy_setopt(curl, CURLOPT_READFUNCTION, reader);
curl_easy_setopt(curl, CURLOPT_READDATA, data);
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) datasize);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
return true;
}
When the code is executed, the following is outputed
*** We read 490 bytes from file
*** We read 0 bytes from file
after that the app does nothing (even not exiting).
Can someone point out at what's wrong here?
Will be grateful for any help!!!
There's some serious confusions shown in this code. Let me try to explain:
CURLOPT_UPLOAD - this will ask libcurl to PUT the file when the protocol of choice is HTTP
CURLOPT_POSTFIELDS - tells libcurl to POST the data that is provided in the additional argument (which has the size set with CURLOPT_POSTFIELDSIZE)
CURLOPT_READFUNCTION - provides libcurl an alternative way to get data than CURLOPT_POSTFIELDS to allow a POST that reads the data from a file. When using CURLOPT_UPLOAD this is the only way to provide data.
So in the end the questions left for you are:
Do you want PUT or POST?
Do you want to provide the data as a string or do you want it provided with a callback?