diff --git a/include/util/request.hpp b/include/util/request.hpp new file mode 100644 index 00000000..cd593bbb --- /dev/null +++ b/include/util/request.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + + +enum RequestMethod { + GET_METHOD, + POST_METHOD, + PUT_METHOD, + DELETE_METHOD, +}; + +/** Must be called before using this API */ +void requestInit(); +void requestDestroy(); + +/** Requests a JSON API URL over HTTP(S), using the data as the query (GET) or the body (POST, etc) */ +json_t *requestJson(RequestMethod method, std::string url, json_t *dataJ); +/** Returns the filename, blank if unsuccessful */ +bool requestDownload(std::string url, std::string filename, float *progress); + diff --git a/src/util/request.cpp b/src/util/request.cpp new file mode 100644 index 00000000..400c3274 --- /dev/null +++ b/src/util/request.cpp @@ -0,0 +1,153 @@ +#include "util/request.hpp" +#include +#include +#include +#include + + +static size_t writeStringCallback(char *ptr, size_t size, size_t nmemb, void *userdata) { + std::string *str = (std::string*) userdata; + size_t len = size * nmemb; + str->append(ptr, len); + return len; +} + + +void requestInit() { + curl_global_init(CURL_GLOBAL_NOTHING); +} + +void requestDestroy() { + curl_global_cleanup(); +} + + +json_t *requestJson(RequestMethod method, std::string url, json_t *dataJ) { + CURL *curl = curl_easy_init(); + if (!curl) + return NULL; + + assert(dataJ); + char *reqStr; + if (method != GET_METHOD) { + reqStr = json_dumps(dataJ, 0); + } + + // Set URL + if (method == GET_METHOD) { + if (dataJ) { + // Append ?key=value&... to url + url += "?"; + bool isFirst = true; + const char *key; + json_t *value; + json_object_foreach(dataJ, key, value) { + if (json_is_string(value)) { + if (!isFirst) + url += "&"; + url += key; + url += "="; + const char *str = json_string_value(value); + size_t len = json_string_length(value); + char *escapedStr = curl_easy_escape(curl, str, len); + url += escapedStr; + curl_free(escapedStr); + isFirst = false; + } + } + } + } + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + + // Set HTTP method + switch (method) { + case GET_METHOD: + // This is default + break; + case POST_METHOD: + curl_easy_setopt(curl, CURLOPT_POST, true); + break; + case PUT_METHOD: + curl_easy_setopt(curl, CURLOPT_PUT, true); + break; + case DELETE_METHOD: + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + break; + } + + // Set headers + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Accept: application/json"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + + // Body callbacks + if (method != GET_METHOD) + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, reqStr); + + std::string resText; + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeStringCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resText); + + // Perform request + printf("Requesting %s\n", url.c_str()); + CURLcode res = curl_easy_perform(curl); + + // Cleanup + if (method != GET_METHOD) + free(reqStr); + curl_easy_cleanup(curl); + curl_slist_free_all(headers); + + if (res == CURLE_OK) { + // Parse JSON response + json_error_t error; + json_t *rootJ = json_loads(resText.c_str(), 0, &error); + return rootJ; + } + return NULL; +} + + +static size_t writeFileCallback(char *ptr, size_t size, size_t nmemb, void *userdata) { + FILE *file = (FILE*) userdata; + return fwrite(ptr, size, nmemb, file); +} + +static int xferInfoCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { + float *progress = (float*) clientp; + if (progress) { + if (dltotal <= 0) + *progress = 1.0; + else + *progress = (float)dlnow / dltotal; + } + return 0; +} + +bool requestDownload(std::string url, std::string filename, float *progress) { + CURL *curl = curl_easy_init(); + if (!curl) + return false; + + FILE *file = fopen(filename.c_str(), "w"); + + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_VERBOSE, false); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFileCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, file); + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferInfoCallback); + curl_easy_setopt(curl, CURLOPT_XFERINFODATA, progress); + + printf("Downloading %s\n", url.c_str()); + CURLcode res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + + fclose(file); + + if (res != CURLE_OK) + remove(filename.c_str()); + + return res == CURLE_OK; +}