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;
}
Related
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;
}
This question already has answers here:
Save cURL content result into a string in C++
(7 answers)
Closed 1 year ago.
Here is my code:
CURL *curl;
CURLcode res;
curl = curl_easy_init();
std::string json_message = "{\r\n \"email\":\"test#abv.bg\",\r\n \"password\":\"asdasdasd\"\r\n}";
if(curl) {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_URL, "https://www.examle.com/myUrl");
curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append(headers, "Authorization: Bearer secretkeyHere");
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
const char *data = json_message.c_str();
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
curl_easy_perform(curl);
}
curl_easy_cleanup(curl);
The problem is that when I execute that code the response of the http request is printed directly into my console application. I would like to store the response in a string without printing it into the console without intention.
Do you see why it is printed unintentionally and how can I store the response in a string?
By default, curl writes the received data to stdout. You can change that by using curl_easy_setopt() to specify a custom CURLOPT_WRITEFUNCTION callback, giving it a string* pointer via CURLOPT_WRITEDATA. For example:
static size_t writeToString(void *data, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
std::string *str = static_cast<std::string*>(userp);
str->append(static_cast<char*>(data), realsize);
return realsize;
}
...
CURL *curl = curl_easy_init();
if (curl) {
...
std::string respStr;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeToString);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &respStr);
CURLcode res = curl_easy_perform(curl);
// use respStr as needed...
curl_easy_cleanup(curl);
}
Scenario:
Im writing a c++ program which should retrieve files and strings from an expressJS API..
Downloading txt.files with curlRequests works perfectly but as soon as i try to retrieve plain strings, they can only be used for printing..
Problem: When trying to do anything with the response from my GET request (from the expressjs api), my response doesnt get treated as a string.
string myText = curlRequest.GetText("/templates/names");
string myTextB = "react.txt, scss.txt"
cout << myText << endl; // prints"react.txt, scss.txt"
cout << myTextB << endl; // prints "react.txt, scss.txt"
cout << stringHelper.GetSubstringPos(myText, "scss") << endl; // printsstring::npos
cout << stringHelper.GetSubstringPos(myTextB, "scss") << endl; // printsposition of 's' as expected
Here is my GetText function of the curlrequest.h in c++
string GetText(string ACTIONURL) {
CURL* curl;
CURLcode curlRes;
string res;
string url = templateCreator.APIURL + ACTIONURL;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
curlRes = curl_easy_perform(curl);
res = curlRes;
if (curlRes == CURLE_HTTP_RETURNED_ERROR) {
res = "FAILED";
}
else if (curlRes != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(curlRes));
res = "FAILED";
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return res;
}
Here is the route in expressjs (receiving the request and responding with a string)
router.get('/names', function(req, res, next) {
var str = "react.txt, scss.txt, example.txt";
res.send(str);
});
// I also tried sending a buffer but as its sended as string i face the same problem..
// C++ could decode the buffer ..
router.get('/buf', function(req, res, next) {
let data = 'hello world: (1 + 2 !== 4)';
let buff = new Buffer.from(data);
let base64data = buff.toString('base64');
console.log(base64data);
res.send(base64data);
});
Retrieving textfiles from my expressjs API is not a problem..
void GetFile(string ACTIONURL, string OUTDIR) {
CURL* curl;
FILE* fp;
CURLcode res;
string url = templateCreator.APIURL + ACTIONURL;
curl = curl_easy_init();
if (curl)
{
fopen_s(&fp, OUTDIR.c_str(), "wb");
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
}
return;
}
(After download I can read those line by line and store in a vector).
Still im hoping to get sending actual strings working..
Does anyone have a clue why im facing problems here?
I literally spent days on this unexpected 'small' issue already..
Thank you #n.'pronouns'm.
I Updated my function and realised that res = curlRes was a nobrainer..
Also the checks for valid response is gone for now.
//those 2 lines and a write_to_string function were missing and `res = curlRes` should do their job
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_string);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
The following 2 functions replace my GetText function now, and everything works as expected.
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;
}
string GetText(string ACTIONURL) {
CURL* curl;
CURLcode res;
string response;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, templateCreator.APIURL + ACTIONURL.c_str());
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_string);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
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);
}
return response;
}
Thank you! I found the fix on 1 or 2 questions earlier too but was not aware that this was the actual problem. Working with strings is possible now!
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!
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);
}