Send to server float array using libcurl c++ - c++

I am using a portaudio callback where I receive periodically a buffer of audio and store it on main_buffer. I only set the settings bellow for curl and only use the CURLOPT_POSTFIELDS to set the float buffer before sending it with curl_easy_perform. On the server side I am using python django:
float main_buffer[256];
url_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, URL);
//portaudio periodic callback
callback(){
//...
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, main_buffer);
curl_easy_perform(curl);
}
The request received in server has no content inside body. How to send main_buffer the right way ?
Thank you !

What you need to do is to serialise the data that you send, and then deserialise what you receive. There is no "the right way". There are several ways with different advantages.
In your case, I recommend starting with a simple way. Simplest portable way to serialise floating point is to use textual encoding. A commonly used textual format that can represent arrays is JSON. For example, the array float[] main_buffer{1.0f, 1.5f, 2.0f} could be encoded as [1.0,1.5,2.0].
The reading part in Python is quite simple:
json.loads(request.body.decode('utf-8'));
The writing part in C++ is a bit more complex. There is no standard way to encode JSON in C++. You can use a full encoder based on the specification, or you could write an ad-hoc loop for this specific case:
std::stringstream ss;
ss << '[';
const char* separator = "";
for (float f : main_buffer) {
ss << separator << f;
separator = ",";
}
ss << ']';
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ss.str().c_str());
curl_slist* headers = nullptr;
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

Related

How to perform GET encoded JSON?

What I want to do is to perform CURL request with parameters and values by using GET method but using JSON.
I'm trying to perform the following:
curl -X GET \
-H "X-Parse-Application-Id: 12345_Example" \
-H "X-Parse-REST-API-Key: abcde_Example" \
-G \
--data-urlencode "where={ \"pin\":\"A string\" }" \
https://urlExample/classes/Pins
as you can see the where URL parameter constraining the value for keys should be encoded JSON.
This is my code:
std::size_t callback(
const char* in,
std::size_t size,
std::size_t num,
char* out)
{
std::string data(in, (std::size_t) size * num);
*((std::stringstream*) out) << data;
return size * num;
}
public: Json::Value query(const char* serverAddress, const char* applicationId, const char* restAPIKey) {
CURL* curl = curl_easy_init();
curl_slist* headerlist = NULL;
headerlist = curl_slist_append(headerlist, applicationId);
headerlist = curl_slist_append(headerlist, restAPIKey);
// Set HEADER.
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
// Set remote URL.
curl_easy_setopt(curl, CURLOPT_URL, serverAddress);
// Don't bother trying IPv6, which would increase DNS resolution time.
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
// Don't wait forever, time out after 10 seconds.
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);
// Follow HTTP redirects if necessary.
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
// Response information.
int httpCode(0);
std::stringstream httpData;
// Hook up data handling function.
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback);
// Hook up data container (will be passed as the last parameter to the
// callback handling function). Can be any pointer type, since it will
// internally be passed as a void pointer.
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &httpData);
// Run our HTTP GET command, capture the HTTP response code, and clean up.
curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
curl_easy_cleanup(curl);
if (httpCode == 200) {
// Response looks good - done using Curl now. Try to parse the results.
Json::Value jsonData;
Json::CharReaderBuilder jsonReader;
std::string errs;
if (Json::parseFromStream(jsonReader, httpData, &jsonData, &errs)) {
return jsonData["results"];
}
else {
std::cout << "Could not parse HTTP data as JSON" << std::endl;
std::cout << "HTTP data was:\n" << httpData.str() << std::endl;
return NULL;
}
}
else {
std::cout << "Couldn't GET from " << serverAddress << " - exiting" << std::endl;
return NULL;
}
}
What should I include in my code in order to perform the GET method with encoded JSON?
According to the documentation of the Server API I'm using, when reading objects, this is what it says for curl:
back4app API Reference
READING OBJECTS:
To retrieve an object, you'll need to send a GET request to its class
endpoint with your app's credentials in the headers and the query
parameters in the URL parameters. This task can be easily accomplished
just by calling the appropriated method of your preferred Parse SDK.
Please check how to do it in the right panel of this documentation.
Request URL https://parseapi.back4app.com/classes/Pins
Method GET
Headers X-Parse-Application-Id:
BCrUQVkk80pCdeImSXoKXL5ZCtyyEZwbN7mAb11f
X-Parse-REST-API-Key: swrFFIXJlFudtF3HkZPtfybDFRTmS7sPwvGUzQ9w
Parameters A where URL parameter constraining the value for keys. It
should be encoded JSON.
Success Response Status 200 OK
Headers content-type: application/json;
Body a JSON object that contains a results field with a JSON array
that lists the objects.
EDIT:
Based on: Daniel Stenberg's answer I tried the following:
std::string temp = "where={ \"pin\":\"A string\" }";
char* encoded = curl_easy_escape(curl, temp.c_str(), temp.length());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, std::strlen(encoded));
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, encoded);
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
But no success. Should libcurl update their API and include such a feature for this case?
OK -- I am going to answer this one more time. This time correctly. I glossed over the fact that you posted the documentation in your question. Totally skipped it. No idea why my brain does that. Maybe it hates documentation and instinctively skips it.
So, the answer to your question is quite simple.
Keep your original code that's in your question (totally ignore the code that you posted in your Edit, it's totally wrong), but instead of doing this:
curl_easy_setopt(curl, CURLOPT_URL, serverAddress);
Do this:
const std::string whereQuery(curl_easy_escape(curl, "{ \"pin\":\"A string\" }", 0));
const std::string url("https://parseapi.back4app.com/classes/Pins?where=" + whereQuery);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
Sorry for dragging that out. I need to read questions better.

libcurl won't load contents of URL

I'm trying to load the contents of this URL in order to send an SMS;
https://app2.simpletexting.com/v1/send?token=[api key]&phone=[phone number]&message=Weather%20Alert!
Using this bit of code implementing libcurl:
std::string sendSMS(std::string smsMessage, std::string usrID) {
std::string simplePath = "debugOld/libDoc.txt";
std::string preSmsURL = "https://app2.simpletexting.com/v1/send?token=";
std::cout << "\n" << getFile(simplePath) << "\n";
std::string fullSmsURL = preSmsURL + getFile(simplePath) + "&phone=" + usrID + "&message=" + smsMessage;
std::cout << fullSmsURL;
//Outputs URL contents into a file
CURL *curl;
FILE *fd;
CURLcode res;
char newFile[FILENAME_MAX] = "debugOld/noSuccess.md";
curl = curl_easy_init();
if (curl) {
fd = fopen(newFile, "wb");
curl_easy_setopt(curl, CURLOPT_URL, fullSmsURL);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fd);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fd);
}
}
I've used pretty much this exact code before to save the JSON contents of a URL to a file, although I'm trying something a bit different here.
This URL will actually send an SMS when visited. When using curl in a cli, I have no problem doing this. Although from C++, it doesn't treat anything as a error, just perhaps the actual function to send the sms isn't being actuated in the same way it would had I visited the URL physically.
I've scoured google for some kind of a solution to no avail. Perhaps I'm too novice to curl to know exactly what to search for.
Edit #1: getFile function
//Read given file
std::string getFile(std::string path) {
std::string nLine;
std::ifstream file_(path);
if (file_.is_open()) {
while (getline(file_, nLine)) {
return nLine;
}
file_.close();
}
else {
std::cout << "file is not open" << "\n";
return "Error 0x000001: inaccesable file location";
}
return "Unknown error in function 'getFile()'"; //This should never happen
}
This line is wrong:
curl_easy_setopt(curl, CURLOPT_URL, fullSmsURL);
CURLOPT_URL expects a char* pointer to null-terminated C string, not a std::string object. You need to use this instead:
curl_easy_setopt(curl, CURLOPT_URL, fullSmsURL.c_str());
Also, you are not performing any error checking on the return values of getFile(), fopen(), or curl_easy_perform() at all. So, your code could be failing in any one of those places and you would never know it.

Posting with libcurl in C++, write callback params are garbage

Developing on Win64 with Visual Studio 2013 Community, deploying to both Win64 and Linux with cross platform wxWidgets. I am trying to emulate the following curl.exe command line with C++ using libcurl:
curl.exe -X POST -g "single-url-string"
This is for an IoT feature of an app, where an end-user supplies the single-url-string to control their device. The reason this logic is not just executing curl.exe as an external process is because this logic runs in its own thread, and wxWidgets does not support launching external executables when outside the main thread.
Normally when performing a POST with curl.exe, the post data is supplied as an option. This tells curl.exe the operation is a POST to the supplied url, and here is the data for that POST. As you can see, what I'm trying to do is a GET style url (with the parameters embedded in the url) but then changing the operation to a POST. It's done this way because research shows asking end-users to supply two separate url and data strings is simply too complex for them. So we came up with this easier single string end-users must supply, which is usually just copying a string from their device manual without having to interpret the string, much less break it into separate meaningful strings.
So, the issue at hand is: I have my simple C++ libcurl POST routine in two versions, but in both versions the parameters received by the write callback are bad. The two versions are a POST with a single url string, and a POST with the post data provided as a separate option to the url string.
The problems are 1) using the single string version does not execute a POST, and it's write callback params are bad; and 2) using the two string version does execute a POST, but the write callback params are bad, in a different way.
The data pointer parameter in the write callback points to memory address 1 in both versions, the size parameter appears good in both versions, but the nmemb parameter is either a huge random value (single string version) or zero (two string POST version).
Here's my code, and yes I call curl_global_init() at app start.
size_t CX_IOT_THREAD::curl_write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
{
// storage for transferred data:
const int dataStoreSize = CURL_MAX_WRITE_SIZE + 1;
char dataStore[dataStoreSize];
memset(dataStore, 0, dataStoreSize); // zeroed out
size_t dataSize = size * nmemb; // bytes sent
if (dataSize)
{
memcpy(dataStore, ptr, dataSize); // copy into buffer sized so we'll have a terminating NULL char
wxString msg = wxString::Format(wxT("%s"), dataStore); // send as event, eventually to the log
mp_queue->Report(CX_IOTTHR_CMD_ACCESS_JOB, msg);
// must return byte count processed for libcurl to be happy:
return dataSize; /**/
}
return size; // should be dataSize, but because nmemb is bad, I’m using size; it works.
}
cx_int CX_IOT_THREAD::Post(std::string& url)
{
if (url.length() == 0)
return -1;
char errBuf[CURL_ERROR_SIZE];
errBuf[0] = '\0';
static const char *postthis = "name=Bloke&age=67";
CURLcode ret;
CURL *hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_URL, url.c_str());
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, postthis);
curl_easy_setopt(hnd, CURLOPT_POSTFIELDSIZE, (long)strlen(postthis));
curl_easy_setopt(hnd, CURLOPT_ERRORBUFFER, errBuf);
curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, &CX_IOT_THREAD::curl_write_callback);
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, NULL);
curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(hnd, CURLOPT_USERAGENT, "curl/7.49.1");
curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
// curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
ret = curl_easy_perform(hnd);
curl_easy_cleanup(hnd);
if (ret != CURLE_OK)
{
wxString msg = wxString::Format(wxT("Attempted POST failed, libcurl return code '%d'."), (cx_int)ret);
mp_queue->Report(CX_IOTTHR_CMD_ACCESS_JOB, msg, (cx_int)ret);
cx_int len = strlen(errBuf);
if (len > 0)
msg = wxString::Format("%s%s", errBuf, ((errBuf[len - 1] != '\n') ? "\n" : ""));
else msg = wxString::Format("%s\n", curl_easy_strerror(ret));
mp_queue->Report(CX_IOTTHR_CMD_ACCESS_JOB, msg, (cx_int)ret);
}
return (cx_int)ret;
}
Any ideas why the write callback parameters are bad? Any idea why the single string version does not even do a post? (The single string version is the above with the 2 POSTFIELDS options commented out and the CUSTOMREQUEST one enabled.)
As Igor Tandetnik points out, the callback must be static.

libcurl http post send data to server

curl_easy_setopt(curl, CURLOPT_URL, "127.0.0.1:8081/get.php");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS,"pulse=70 & temp=35" );
this above code run successfully but when I pass this
int pulsedata = 70;
int tempdata = 35;
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "pulse=pulsedata & temp = tempdata");
when I run this above line it give me error
how can I pass this pulsedata and tempdata ??
A possible C solution:
char sendbuffer[100];
snprintf(sendbuffer, sizeof(sendbuffer), "pulse=%d&temp=%d", pulsedate, tempdata);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, sendbuffer);
You can't use variable in strings like that, you have to format the string.
A possible C++ solution might be to use std::ostringstream like this:
std::ostringstream os;
os << "pulse=" << pulsedata << "&temp=" << tempdata;
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, os.str().c_sr());
With this solution, the std::ostringstream object (os in my example) needs to be alive until the CURL calls are all done.
Also note that the query-string I construct does not contain any spaces.

Reading the content of a PHP from C++

I am trying to read the content of a PHP / HTML file on a remote web server using C++, but haven't found a way to do it. I want to pass GET statements to it, so http://example.com/login.php?user=abc&password=def.
How would I do it?
Your best bet is to use an external library. libcurl is popular and fairly easy to use.
Here's a simple example, you need to add error checking though:
string data;
CURL *curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, url_.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWrite);
curl_easy_perform(curl);
Your callback would look something like this:
size_t curlWrite(void *ptr, size_t size, size_t nmemb, void *usrPtr)
{
size_t bytes = size * nmemb;
string *data = static_cast<string *>(usrPtr);
data->append(static_cast<const char *>(ptr), bytes);
return bytes;
}
You can add your GET parameters on the end of the URL.