Getting cURL JSON response C++ - c++

I would like to know how to get a JSON response with cURL. My current code doesn't work, it just returns an empty string. Any help would be greatly appreciated! I've included my current code below.
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp)
{
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
int ipCheck(std::string ip)
{
nlohmann::json j;
CURL* curl;
CURLcode res;
std::string response;
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append(headers, "Content-Type: application/json");
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://api.ipstack.com/" + ip + "?access_key=SECRET_KEY");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, response);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
if (response.empty()) {
std::cout << "\n\nInvalid request!";
_getch();
}
std::cout << response;
}

you have an error here :
curl_easy_setopt(curl, CURLOPT_WRITEDATA, response);
you are passing a temporary copy of std::string here instead of a pointer to the variable you expect to be passed to the callback so the original variable isn't touched ! worse : the callback is passed a dangling pointer which is the casted to std::string and operated on (undefined behavior) so expect to see strange things (usually crash)

Related

cURL Release Mode Invalid Parameter

I am working on a C++ program that uses cURL to "sign" into my Valorant account. The code works just fine in debug mode (Visual Studio 2019) but has an Invalid Parameter error when I switch to release mode. After doing some research I found that it was because of libcurl, but there were no fixes online. I am also using json and a small code snippet for base64 decoding, but I believe those are irrelevant. My project properties are whatever the default properties are, I even tried making a new project and copying my code over but got the same result.
Code:
#include <iostream>
#include "json.hpp"
#include <curl/curl.h>
#include <string>
#include <sstream>
#include "base64.hpp"
#include <stdio.h>
using json = nlohmann::json;
CURL* hnd = curl_easy_init();
size_t WriteCallback(char* contents, size_t size, size_t nmemb, void* userp)
{
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
void authCookies()
{
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(hnd, CURLOPT_URL, "https://auth.riotgames.com/api/v1/authorization");
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, "{\"client_id\":\"play-valorant-web-prod\",\"nonce\":\"1\",\"redirect_uri\":\"https://playvalorant.com/opt_in\",\"response_type\":\"token id_token\"}");
CURLcode ret = curl_easy_perform(hnd);
}
std::string getAuthToken(std::string username, std::string password)
{
std::string token = "";
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "PUT");
curl_easy_setopt(hnd, CURLOPT_URL, "https://auth.riotgames.com/api/v1/authorization");
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);
std::string response;
json toSend = {
{"type", "auth"},
{"username", username},
{"password", password},
{"remember", false},
{"language", "en_US"}
};
std::string sendText = toSend.dump(4);
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, sendText.c_str());
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &response);
CURLcode ret = curl_easy_perform(hnd);
json resp = json::parse(response);
//parse the uri and look for a token
std::string uri = std::string(resp["response"]["parameters"]["uri"]);
size_t loc = uri.find("#access_token=");
for (int i = loc + 14; i < uri.length(); i++)
{
if (uri[i] != '&') { token += uri[i]; }
else { break; }
}
return token;
}
void cookieReauth()
{
std::string response;
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "GET");
curl_easy_setopt(hnd, CURLOPT_URL, "https://auth.riotgames.com/authorize?redirect_uri=https%3A%2F%2Fplayvalorant.com%2Fopt_in&client_id=play-valorant-web-prod&response_type=token%20id_token");
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, "");
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &response);
CURLcode ret = curl_easy_perform(hnd);
}
std::string getEntitlement(std::string token)
{
std::string out = "";
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(hnd, CURLOPT_URL, "https://entitlements.auth.riotgames.com/api/token/v1");
struct curl_slist* headers = NULL;
std::string auth = "Authorization: Bearer " + token;
headers = curl_slist_append(headers, auth.c_str());
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, "");
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &out);
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);
CURLcode ret = curl_easy_perform(hnd);
json resp = json::parse(out);
return std::string(resp["entitlements_token"]);
}
struct valorantUser
{
std::string displayName;
std::string puuid;
std::string name;
std::string tag;
};
std::vector<valorantUser> getUsersByPuuid(std::string token, std::string entitlement, json puuidList, std::string region)
{
std::vector<valorantUser> userList;
std::string response;
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "PUT");
curl_easy_setopt(hnd, CURLOPT_URL, std::string("https://pd." + region + ".a.pvp.net/name-service/v2/players").c_str());
struct curl_slist* headers = NULL;
std::string auth = "Authorization: Bearer " + token;
std::string ent = "Entitlements: " + entitlement;
headers = curl_slist_append(headers, auth.c_str());
headers = curl_slist_append(headers, ent.c_str());
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);
std::string toSend = puuidList.dump();
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, toSend.c_str());
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &response);
CURLcode ret = curl_easy_perform(hnd);
json resData = json::parse(response);
for (int i = 0; i < resData.size(); i++)
{
valorantUser dat;
dat.displayName = std::string(resData[i]["DisplayName"]);
dat.puuid = std::string(resData[i]["Subject"]);
dat.name = std::string(resData[i]["GameName"]);
dat.tag = std::string(resData[i]["TagLine"]);
userList.push_back(dat);
}
return userList;
}
std::string getTokenPuuid(std::string token)
{
std::vector<std::string> toks;
std::stringstream test(token);
std::string cur;
while (std::getline(test, cur, '.'))
{
toks.push_back(cur);
}
json decoded = json::parse(base64_decode(toks[1]));
return decoded["sub"];
}
int main()
{
curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(hnd, CURLOPT_COOKIEJAR, "");
authCookies(); //get auth cookies
std::string token = getAuthToken("username", "password"); //get auth token
cookieReauth(); //get more auth cookies
std::string entitlement = getEntitlement(token); //get entitlement token
std::string puuid = getTokenPuuid(token); //get user puuid from token
json puuidList = json::array({puuid});
valorantUser botData = getUsersByPuuid(token, entitlement, puuidList, "na")[0]; //get user name from puuid
std::cout << "Signed in as " << botData.name << "#" << botData.tag;
}
Thanks
Edit:
By commenting out all of my functions, the error stopped. If I remove the comment from even the authCookies() function, the error comes back. It is also very strange that the error does not appear until the end of the code.
int main()
{
curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(hnd, CURLOPT_COOKIEJAR, "");
/*
authCookies(); //get auth cookies
std::string token = getAuthToken("username", "password"); //get auth token
cookieReauth(); //get more auth cookies
std::string entitlement = getEntitlement(token); //get entitlement token
std::string puuid = getTokenPuuid(token); //get user puuid from token
json puuidList = json::array({ puuid });
valorantUser botData = getUsersByPuuid(token, entitlement, puuidList, "na")[0]; //get user name from puuid
std::cout << "Signed in as " << botData.name << "#" << botData.tag;*/
curl_easy_cleanup(hnd);
return 0;
}
When authCookies() is called, WriteCallback gets userp set to something that is (void*)stdout (default value if CURLOPT_WRITEDATA is not assigned). ((std::string*)userp)->append((char*)contents, size * nmemb); dereferences FILE* as std::string*.
Why do you cast char* contents to char*?

How to rewrite POST request from python to C++ with curl

I have POST request on python with a lot of settings, and I don't uderstand how their look like in curl.
data_str = '{' + '"username": "{}", "domain_id": {}, "password": {}'.format(login, domain_id, password) + '}'
try:
data = requests.post("https://example.com/v/session",
proxies=proxy,
verify=False,
data=data_str,
headers={"Content-Type": "application/json;charset=UTF-8",
"Accept": "application/json"})
if is_json(data.text):
print(data)
I find that url set parament CURLOPT_URL, headers - CURLOPT_HTTPHEADER. But how set proxy, verify, data ? How get json as in python ?
how to complete the code that it have the same result as in python:
CURL *curl = curl_easy_init();
struct curl_slist *list = NULL;
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
list = curl_slist_append(list, "Shoesize: 10");
list = curl_slist_append(list, "Accept:");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
curl_easy_perform(curl);
curl_slist_free_all(list); /* free the list again */
}
In order to get the return data from the curl request, we need a callback function for the CURLOPT_WRITEFUNCTION option.
The proxy, data, verify parameters should be set as following :
#include <iostream>
#include <string>
#include <curl/curl.h>
size_t curlWriter(void *contents, size_t size, size_t nmemb, std::string *s)
{
size_t newLength = size*nmemb;
try
{
s->append((char*)contents, newLength);
}
catch(std::bad_alloc &e)
{
//memory problem
return 0;
}
return newLength;
}
int main()
{
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl)
{
std::string strResponse;
std::string strPostData = "my data post";
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/v/session");
curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L);
//set the proxy
curl_easy_setopt(curl, CURLOPT_PROXY, "http://proxy.net");
curl_easy_setopt(curl, CURLOPT_PROXYPORT, 8080L);
//verify=False. SSL checking disabled
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
//set the callback function
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriter);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &strResponse);
/* size of the POST data */
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strPostData.length() );
/* pass in a pointer to the data - libcurl will not copy */
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, strPostData.c_str() );
/* Execute the request */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
{
std::cerr << "CURL error : " << curl_easy_strerror(res) << std::endl;
}else {
std::cout << "CURL result : " << strResponse << std::endl;
}
curl_easy_cleanup(curl);
}
}

Heap increase - Curl HTTP request

I am currently using CURL library, I tried a simple example and I noticed that the heap memory increases every time I make a request. This is a very important problem, especially when you are trying to use multithread.
Does anyone know the problem?
static int Swriter(char *data, size_t size, size_t nmemb, std::string *writerData)
{
if(writerData == NULL)
return 0;
writerData->append(data, size*nmemb);
return size * nmemb;
}
static void RequestReadJson(std::string url, std::string &content)
{
CURL *curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Swriter);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &content);
curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
}
int main(int argc, wchar_t* argv[]) {
curl_global_init(CURL_GLOBAL_DEFAULT);
std::string content;
std::string url("www.google.com");
for(int i=0;i<300;i++)
RequestReadJson(url, content); //Heap increase
curl_global_cleanup();
}
Heap increase
You append the new downloaded content to the old, hence the heap increase:
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &content);
which leads to:
writerData->append(data, size*nmemb);
You'd better return a fresh string:
static std::string RequestReadJson(std::string url)
{
std::string content;
CURL *curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Swriter);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &content);
curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
return content;
}

libcurl uninitialized variable curl error

I am trying to make a HTTP Request to retrieve some JSON data; I get the error that curl variable is not initialized though I easy_init() it. Any help on how to go around this error would be very kind!!
Below is my code:
#pragma once
#include "stdafx.h"
#include "RequestJson.h"
#include <string.h>
#include <include/curl/curl.h>
#include <fstream>
#include <iostream>
#include <sstream>
using namespace std;
class RequestJson
{
public:
static std::string RequestJsonString(std::string URL)
{
//set to get the JSON Response on listed loans; open a CSV file and read unemployment and other indices.
::CURL *curl;
CURLcode res;
struct curl_slist *headers = NULL;
std::ostringstream oss;
//curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
curl_slist_append(headers, "Accept: application/json");
curl_slist_append(headers, "Content-Type: application/json");
curl_easy_cleanup(curl);
if (curl)
{
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writer); //define a write-function below.
res = curl_easy_perform(curl);
if (CURLE_OK == res)
{
char *ct;
res = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct);
if ((CURLE_OK == res) && ct)
{
return *DownloadedResponse;
}
}
}
}
//parse the JSON String and return the downloaded string.
static std::string *DownloadedResponse;
static int writer(char *data, size_t size, size_t nmemb, std::string *buffer_in)
{
if (buffer_in != NULL)
{
buffer_in->append(data, size * nmemb);
DownloadedResponse = buffer_in;
return size * nmemb;
}
return 0;
}
};
From the curl_easy_cleanup reference:
This function must be the last function to call for an easy session. It is the opposite of the curl_easy_init function and must be called with the same handle as input that a curl_easy_init call returned.
[Emphasis mine]
When you call curl_easy_cleanup it cleans up all resources allocated by curl_easy_init. After that you can't use the CURL pointer any more.
As the reference says: Put it last, when you're done.

libcurl curl_easy_setopt "Unknown error"

I'm trying to use C++ cURL library for sending Json data via PUT method and my code looks something like this
CURL* m_curlHandle;
CURLcode m_returnValue;
//function1 start
curl_global_init(CURL_GLOBAL_ALL);
struct curl_slist* headers = NULL;
std::ostringstream oss;
struct curl_slist* slist = NULL;
slist = curl_slist_append(headers, "Accept: application/json");
slist = curl_slist_append(headers, "Content-Type: application/json");
slist = curl_slist_append(headers, "charsets: utf-8");
m_curlHandle = curl_easy_init();
if (!m_curlHandle)
// throw exception
curl_easy_setopt(m_curlHandle, CURLOPT_HTTPHEADER, headers);
//function1 end
//function2 start
std::string url = "some URL"; // my url
curl_easy_setopt(m_curlHandle, CURLOPT_URL, url.c_str());
unsigned int timeout = 5;
curl_easy_setopt(m_curlHandle, CURLOPT_TIMEOUT, timeout);
std::string localIp = "some IP"; // my IP address
curl_easy_setopt(m_curlHandle, CURLOPT_INTERFACE, localIp.c_str());
curl_easy_setopt(m_curlHandle, CURLOPT_CUSTOMREQUEST, "PUT");
std::string json = "some json struct"; //my json struct
curl_easy_setopt(m_curlHandle, CURLOPT_POSTFIELDS, json.c_str());
curl_easy_setopt(m_curlHandle, CURLOPT_WRITEFUNCTION, callbackWriter); //static size_t callbackWriter(char* buffer, size_t size, size_t nmemb, void* userp);
m_returnValue = curl_easy_perform(m_curlHandle);
//function2 end
I call function1 then function2 and the problem is that for all curl_easy_setopt calls I get error code 1685083487 and error description "Unknown error". So what may cause to a such result and how to fix this?
Thank you in advance!
I would use CURLOPT_POSTFIELDS instead, my func was something like this
void poolStr(const std::vector<unsigned char> &data, const std::string &url)
{
curl_global_init(CURL_GLOBAL_DEFAULT);
mCurl = curl_easy_init();
if(mCurl)
{
curl_easy_setopt(mCurl, CURLOPT_URL, url.c_str());
curl_easy_setopt(mCurl, CURLOPT_POST, 1L);
curl_easy_setopt(mCurl, CURLOPT_POSTFIELDSIZE, data.size());
curl_easy_setopt(mCurl, CURLOPT_POSTFIELDS, &data[0]);
mChunk = curl_slist_append(mChunk, "Content-Type: application/binary");
mChunk = curl_slist_append(mChunk, "Expect:");
curl_easy_setopt(mCurl, CURLOPT_HTTPHEADER, mChunk);
CURLcode res = curl_easy_perform(mCurl);
if(res != CURLE_OK)
{
LOGERROR << "curl_easy_perform() failed: " << curl_easy_strerror(res) << "\n Attepmt number: " << attempt;
}
else
{
LOGINFO << "Data being sent.";
}
}
}
}
and calling like poolStr(data, mHost);