#include <iostream>
#include <curl/curl.h>
#include <fstream>
#include <json/json.h>
using namespace std;
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;
}
std::string ReadWebsite(const char* URL)
{
CURL* curl;
CURLcode res;
std::string readBuffer;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, URL);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
printf("Error");
}
curl_easy_strerror(res);
curl_easy_cleanup(curl);
curl_global_cleanup();
// printf(readBuffer.c_str());
return readBuffer;
}
}
int main()
{
Json::Reader reader;
Json::Value root;
std::string text = ReadWebsite("https://pastebin.com/raw/PW13MZjp");
if (reader.parse(text.c_str(), root)) {
cout << root["addresses"];
}
}
C4996: 'Json::Reader' Use CharReader and CharReaderBuilder instead.
C4996: 'Json::Reader::__autoclassinit2' Use CharReader and CharReaderBuilder instead.
C4996: 'Json::Reader::Reader' Use CharReader and CharReaderBuilder instead.
C4996: 'Json::Reader::parse' Use CharReader and CharReaderBuilder instead.
if i use CharReader and CharReadBuilder i get a Exception thrown at 0x79781F4C (ucrtbased.dll) in ConsoleApplication2.exe: 0xC0000005: Access violation reading location 0xCCCCCCCC.
Code with CharReader and CharReadBuilder
int main()
{
std::string Reply = ReadWebsite("https://pastebin.com/raw/PW13MZjp");
const auto rawJsonLength = static_cast<int>((Reply).length());
JSONCPP_STRING err;
Json::Value root;
Json::CharReaderBuilder builder;
const std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
if (!reader->parse((Reply).c_str(), Reply.c_str() + rawJsonLength, &root,&err)) {
MessageBoxA(NULL,"error","error",MB_OK);
return EXIT_FAILURE;
}
printf("%s\n\n",root["offsets"]["ls_offset"]);
system("pause");
}
Any help ill accept. Thanks!
Related
I have a program that returns a long string from a curl request, the code is as follows:
#include <iostream>
#include <string>
#include <curl/curl.h>
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 main(void)
{
std::string s;
CURL* curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_URL, "www.ecobliss.co.za/run_student_query.php?query=Select%20*%20FROM%20data%20WHERE%20ID%20%3C%2030");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
std::cout << s << std::endl;
struct curl_slist* headers = NULL;
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
res = curl_easy_perform(curl);
}
curl_easy_cleanup(curl);
//std::cout << s;
std::string arr[10000];
int i = s.find_first_of("ID");
//std::cout << i<<std::endl;
int ic = 0;
while (i>0) {
std::cout << i;
if (i > 0) {
arr[ic] = s.substr(i, i + 6);
s.erase(i,i+1);
int i = s.find_first_of("ID");
std::cout << i;
}
if (i < 0) {
break;
}
ic++;
}
for (ic = 0; ic < std::size(arr); ic++) {
std::cout << arr[ic];
}
return 0;
}
the output of the request in postman is:
<p>Results from your query:</p>
<br>{"ID":"26","datetime":"2022-03-13 03:21:07","temperature":"25.3","humidity":"80.9","pressure":"1020.2"}<br>{"ID":"27","datetime":"2022-03-13 05:12:47","temperature":"24.8","humidity":"82.1","pressure":"1020.5"}<br>{"ID":"28","datetime":"2022-03-13 05:29:05","temperature":"24.9","humidity":"83.6","pressure":"1020.5"}<br>{"ID":"29","datetime":"2022-03-13 05:29:07","temperature":"24.9","humidity":"83.8","pressure":"1020.5"}
I have tried to use string handling and extract the value the ID part holds, but it does not work at all, can someone please help.
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.
I am writing a program that pulls an image from a restful server that is delivered in JSON, parsed, processed and sent back to the server. I am having issues on sending the image back. Currently I have it stored in a string and am trying to use CURLFORM_BUFFER to send it back. I have confirmed that the image is in the string by writing it to file. No problems there. My current code is below. I am currently experiencing a seg fault 11 on the post. My code is below.
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "dist/jsoncpp.cpp"
#include "dist/json/json.h"
using namespace std;
static size_t write_data(void *contents, size_t size, size_t nmemb,
void *userp)
{
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
void uploadImage(std::string readBuffer){
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);
printf("Image length: %d\n\n", readBuffer.length());
CURLFORMcode code = curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "processedImage",
CURLFORM_BUFFER, "image.jpg",
CURLFORM_BUFFERPTR, readBuffer,
//CURLFORM_BUFFERLENGTH, readBuffer.length(),
CURLFORM_END);
if(code != 0){
printf("Something went wrong in formadd.\n");
}
curl = curl_easy_init();
/* initialize custom header list (stating that Expect: 100-continue is not
wanted */
headerlist = curl_slist_append(headerlist, buf);
if(curl) {
/* what URL that receives this POST */
curl_easy_setopt(curl, CURLOPT_URL, "uploadProcessedImageURL");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
//curl_easy_setopt(curl, CURLOPT_POSTFIELDS, formpost);
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* 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);
/* then cleanup the formpost chain */
curl_formfree(formpost);
/* free slist */
curl_slist_free_all (headerlist);
}
}
int main(void)
{
CURL *curl;
CURLcode res;
const cv::_InputArray data;
std::string readBuffer;
char *url = "requestImageFileURL";
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
readBuffer.clear();
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
//printf("%s\n\n", readBuffer.c_str());
printf("Image retrieved.\n");
Json::Value values;
Json::Reader reader;
reader.parse(readBuffer, values);
Json::Value imageArray = values.get("userUploadedImage","default
value");
Json::Value idNumber = values.get("id","default value");
Json::FastWriter fastWriter;
std::string output = fastWriter.write(imageArray);
//cout << output << endl;
std::vector<char> vectordata(output.begin(), output.end());
//for (auto i = vectordata.begin(); i != vectordata.end(); ++i)
//std::cout << *i;
cv::Mat data_mat(vectordata,true);
cv::Mat image(cv::imdecode(data_mat, 1));
std::cout<<"Height: " << image.rows <<" Width: "<<image.cols<<endl;
//cv::namedWindow( "Display Image", CV_WINDOW_AUTOSIZE );
//cv::imshow( "Display Image",image);
//cv::waitKey(0);
uploadImage(readBuffer);
return 0;
}
I really can not understand what my problem
Here is my code:
#include <iostream>
#include <curl/curl.h>
#include <boost/regex.hpp>
using namespace std;
using namespace boost;
int Write_callback(char *data, size_t size, size_t nmemb, string *buffer)
{
int result = 0;
if (buffer != NULL)
{
buffer->append(data, size * nmemb);
result = size * nmemb;
}
return result;
}
char *Get(string url)
{
CURL *curl;
CURLcode res;
char *readBuffer;
int retries = 2;
curl = curl_easy_init();
if (curl) {
do {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10);
curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "cookies.txt");
curl_easy_setopt(curl, CURLOPT_COOKIEJAR, "cookies.txt");
curl_easy_setopt(curl, CURLOPT_USERAGENT,
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.5");
res = curl_easy_perform(curl);
} while (CURLE_OK != res && retries--);
curl_easy_cleanup(curl);
return readBuffer;
}
}
int main(int argc, char** argv)
{
char *www = Get("google.com");
string Str = "dsfgdasgadgasdg";
boost::regex RegEx("(.+)");
boost::smatch Results;
boost::regex_match(Str, Results, RegEx);
cout << "Print entire match:\n " << Results[1] << endl;
return 0;
}
This compiles, but when you run an error:
terminate called after throwing an instance of 'std::length_error'
what(): basic_string::_S_create
If I comment out this:
char *www = Get("google.com");
That works
If I comment out this:
string Str = "dsfgdasgadgasdg";
boost::regex RegEx("(.+)");
boost::smatch Results;
boost::regex_match(Str, Results, RegEx);
cout << "Print entire match:\n " << Results[1] << endl;
That works too..
Why it does not work together ??
In your Get function you declare the local variable readBuffer character pointer:
char *readBuffer;
You use the address of that as the userdata for your Write_callback function. So the userdata is a char**. But in your Write_Callback you have the userdata field a a std::string:
int Write_callback(char *data, size_t size, size_t nmemb, string *buffer)
With LibCurl the callback has the userdata parameter as a void* so it isn't a std::string:
size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata);
See http://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
Pass a std::string pointer as the userdata instead of a char**. And I would keep the Write_callback parameter a void* and cast that void* to a std::string* in the callback.
Hope that helps!
int main(void)
{
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://www.google.com");
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
_getch();
return 0;
}
string contents = "";
I would like to save the result of the curl html content in a string, how do I do this?
It's a silly question but unfortunately, I couldn't find anywhere in the cURL examples for C++
thanks!
You will have to use CURLOPT_WRITEFUNCTION to set a callback for writing. I can't test to compile this right now, but the function should look something close to;
static std::string readBuffer;
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
readBuffer.append(contents, realsize);
return realsize;
}
Then call it by doing;
readBuffer.clear();
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
// ...other curl options
res = curl_easy_perform(curl);
After the call, readBuffershould have your contents.
Edit: You can use CURLOPT_WRITEDATA to pass the buffer string instead of making it static. In this case I just made it static for simplicity. A good page to look (besides the linked example above) is here for an explanation of the options.
Edit2: As requested, here's a complete working example without the static string buffer;
#include <iostream>
#include <string>
#include <curl/curl.h>
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 main(void)
{
CURL *curl;
CURLcode res;
std::string readBuffer;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://www.google.com");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
std::cout << readBuffer << std::endl;
}
return 0;
}
On my blog I have published a simple wrapper class to perform this task.
Usage example:
#include "HTTPDownloader.hpp"
int main(int argc, char** argv) {
HTTPDownloader downloader;
std::string content = downloader.download("https://stackoverflow.com");
std::cout << content << std::endl;
}
Here's the header file:
/**
* HTTPDownloader.hpp
*
* A simple C++ wrapper for the libcurl easy API.
*
* Written by Uli Köhler (techoverflow.net)
* Published under CC0 1.0 Universal (public domain)
*/
#ifndef HTTPDOWNLOADER_HPP
#define HTTPDOWNLOADER_HPP
#include <string>
/**
* A non-threadsafe simple libcURL-easy based HTTP downloader
*/
class HTTPDownloader {
public:
HTTPDownloader();
~HTTPDownloader();
/**
* Download a file using HTTP GET and store in in a std::string
* #param url The URL to download
* #return The download result
*/
std::string download(const std::string& url);
private:
void* curl;
};
#endif /* HTTPDOWNLOADER_HPP */
Here's the source code:
/**
* HTTPDownloader.cpp
*
* A simple C++ wrapper for the libcurl easy API.
*
* Written by Uli Köhler (techoverflow.net)
* Published under CC0 1.0 Universal (public domain)
*/
#include "HTTPDownloader.hpp"
#include <curl/curl.h>
#include <curl/easy.h>
#include <curl/curlbuild.h>
#include <sstream>
#include <iostream>
using namespace std;
size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) {
string data((const char*) ptr, (size_t) size * nmemb);
*((stringstream*) stream) << data;
return size * nmemb;
}
HTTPDownloader::HTTPDownloader() {
curl = curl_easy_init();
}
HTTPDownloader::~HTTPDownloader() {
curl_easy_cleanup(curl);
}
string HTTPDownloader::download(const std::string& url) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
/* example.com is redirected, so we tell libcurl to follow redirection */
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); //Prevent "longjmp causes uninitialized stack frame" bug
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "deflate");
std::stringstream out;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &out);
/* Perform the request, res will get the return code */
CURLcode res = curl_easy_perform(curl);
/* Check for errors */
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
return out.str();
}
Using the 'new' C++11 lambda functionality, this can be done in a few lines of code.
#ifndef WIN32 #define __stdcall "" #endif //For compatibility with both Linux and Windows
std::string resultBody { };
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resultBody);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, static_cast<size_t (__stdcall *)(char*, size_t, size_t, void*)>(
[](char* ptr, size_t size, size_t nmemb, void* resultBody){
*(static_cast<std::string*>(resultBody)) += std::string {ptr, size * nmemb};
return size * nmemb;
}
));
CURLcode curlResult = curl_easy_perform(curl);
std::cout << "RESULT BODY:\n" << resultBody << std::endl;
// Cleanup etc
Note the __stdcall cast is needed to comply to the C calling convention (cURL is a C library)
This might not work right away but should give you an idea:
#include <string>
#include <curl.h>
#include <stdio.h>
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t written;
written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main() {
std::string tempname = "temp";
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
FILE *fp = fopen(tempname.c_str(),"wb");
curl_easy_setopt(curl, CURLOPT_URL, "http://www.google.com");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
fp = fopen(tempname.c_str(),"rb");
fseek (fp , 0 , SEEK_END);
long lSize = ftell (fp);
rewind(fp);
char *buffer = new char[lSize+1];
fread (buffer, 1, lSize, fp);
buffer[lSize] = 0;
fclose(fp);
std::string content(buffer);
delete [] buffer;
}
}
Came out with useful, yet simple solution, which overloads std::ostream::operator<<
#include <ostream>
#include <curl/curl.h>
size_t curlCbToStream (
char * buffer,
size_t nitems,
size_t size,
std::ostream * sout
)
{
*sout << buffer;
return nitems * size;
}
std::ostream & operator<< (
std::ostream & sout,
CURL * request
)
{
::curl_easy_setopt(request, CURLOPT_WRITEDATA, & sout);
::curl_easy_setopt(request, CURLOPT_WRITEFUNCTION, curlCbToStream);
::curl_easy_perform(request);
return sout;
}
Possible drawback of taken approach could be:
typedef void CURL;
That means it covers all known pointer types.
Based on #JoachimIsaksson answer, here is a more verbose output that handles out-of-memory and has a limit for the maximum output from curl (as CURLOPT_MAXFILESIZE limits only based on header information and not on the actual size transferred ).
#DEFINE MAX_FILE_SIZE = 10485760 //10 MiB
size_t curl_to_string(void *ptr, size_t size, size_t count, void *stream)
{
if(((string*)stream)->size() + (size * count) > MAX_FILE_SIZE)
{
cerr<<endl<<"Could not allocate curl to string, output size (current_size:"<<((string*)stream)->size()<<"bytes + buffer:"<<(size * count) << "bytes) would exceed the MAX_FILE_SIZE ("<<MAX_FILE_SIZE<<"bytes)";
return 0;
}
int retry=0;
while(true)
{
try{
((string*)stream)->append((char*)ptr, 0, size*count);
break;// successful
}catch (const std::bad_alloc&) {
retry++;
if(retry>100)
{
cerr<<endl<<"Could not allocate curl to string, probably not enough memory, aborting after : "<<retry<<" tries at 10s apart";
return 0;
}
cerr<<endl<<"Could not allocate curl to string, probably not enough memory, sleeping 10s, try:"<<retry;
sleep(10);
}
}
return size*count;
}
I use Joachim Isaksson's answer with a modern C++ adaptation of CURLOPT_WRITEFUNCTION.
No nagging by the compiler for C-style casts.
static auto WriteCallback(char* ptr, size_t size, size_t nmemb, void* userdata) -> size_t {
static_cast<string*>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}