Browse Source

Add cookie support to network::requestJson() and network::requestDownload().

tags/v2.0.0
Andrew Belt 4 years ago
parent
commit
825adb155d
3 changed files with 65 additions and 30 deletions
  1. +6
    -2
      include/network.hpp
  2. +51
    -21
      src/network.cpp
  3. +8
    -7
      src/plugin.cpp

+ 6
- 2
include/network.hpp View File

@@ -1,4 +1,6 @@
#pragma once
#include <map>

#include <jansson.h>

#include <common.hpp>
@@ -12,6 +14,8 @@ namespace rack {
namespace network {


typedef std::map<std::string, std::string> CookieMap;

enum Method {
METHOD_GET,
METHOD_POST,
@@ -23,9 +27,9 @@ void init();
/** Requests a JSON API URL over HTTP(S), using the data as the query (GET) or the body (POST, etc)
Caller must json_decref().
*/
json_t* requestJson(Method method, std::string url, json_t* dataJ);
json_t* requestJson(Method method, const std::string& url, json_t* dataJ, const CookieMap& cookies = {});
/** Returns true if downloaded successfully */
bool requestDownload(std::string url, const std::string& filename, float* progress);
bool requestDownload(const std::string& url, const std::string& filename, float* progress, const CookieMap& cookies = {});
/** URL-encodes `s` */
std::string encodeUrl(const std::string& s);
/** Gets the path portion of the URL. */


+ 51
- 21
src/network.cpp View File

@@ -1,3 +1,5 @@
#include <vector>

#define CURL_STATICLIB
#include <curl/curl.h>

@@ -9,6 +11,11 @@ namespace rack {
namespace network {


static const std::vector<std::string> methodNames = {
"GET", "POST", "PUT", "DELETE",
};


static CURL* createCurl() {
CURL* curl = curl_easy_init();
assert(curl);
@@ -31,6 +38,18 @@ static size_t writeStringCallback(char* ptr, size_t size, size_t nmemb, void* us
}


static std::string getCookieString(const CookieMap& cookies) {
std::string s;
for (const auto& pair : cookies) {
s += encodeUrl(pair.first);
s += "=";
s += encodeUrl(pair.second);
s += ";";
}
return s;
}


void init() {
// curl_easy_init() calls this automatically, but it's good to make sure this is done on the main thread before other threads are spawned.
// https://curl.haxx.se/libcurl/c/curl_easy_init.html
@@ -38,7 +57,8 @@ void init() {
}


json_t* requestJson(Method method, std::string url, json_t* dataJ) {
json_t* requestJson(Method method, const std::string& url, json_t* dataJ, const CookieMap& cookies) {
std::string urlS = url;
CURL* curl = createCurl();
char* reqStr = NULL;

@@ -46,20 +66,20 @@ json_t* requestJson(Method method, std::string url, json_t* dataJ) {
if (dataJ) {
if (method == METHOD_GET) {
// Append ?key=value&... to url
url += "?";
urlS += "?";
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 += "=";
urlS += "&";
urlS += key;
urlS += "=";
const char* str = json_string_value(value);
size_t len = json_string_length(value);
char* escapedStr = curl_easy_escape(curl, str, len);
url += escapedStr;
urlS += escapedStr;
curl_free(escapedStr);
isFirst = false;
}
@@ -70,22 +90,20 @@ json_t* requestJson(Method method, std::string url, json_t* dataJ) {
}
}

curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_URL, urlS.c_str());

// Set HTTP method
switch (method) {
case METHOD_GET:
// This is CURL's default
break;
case METHOD_POST:
curl_easy_setopt(curl, CURLOPT_POST, true);
break;
case METHOD_PUT:
curl_easy_setopt(curl, CURLOPT_PUT, true);
break;
case METHOD_DELETE:
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
break;
if (method == METHOD_GET) {
// This is CURL's default
}
else if (method == METHOD_POST) {
curl_easy_setopt(curl, CURLOPT_POST, true);
}
else if (method == METHOD_PUT) {
curl_easy_setopt(curl, CURLOPT_PUT, true);
}
else if (method == METHOD_DELETE) {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
}

// Set headers
@@ -94,6 +112,11 @@ json_t* requestJson(Method method, std::string url, json_t* dataJ) {
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

// Cookies
if (!cookies.empty()) {
curl_easy_setopt(curl, CURLOPT_COOKIE, getCookieString(cookies).c_str());
}

// Body callbacks
if (reqStr)
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, reqStr);
@@ -103,6 +126,7 @@ json_t* requestJson(Method method, std::string url, json_t* dataJ) {
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resText);

// Perform request
INFO("Requesting %s %s", methodNames[method].c_str(), urlS.c_str());
CURLcode res = curl_easy_perform(curl);

// Cleanup
@@ -132,7 +156,7 @@ static int xferInfoCallback(void* clientp, curl_off_t dltotal, curl_off_t dlnow,
return 0;
}

bool requestDownload(std::string url, const std::string& filename, float* progress) {
bool requestDownload(const std::string& url, const std::string& filename, float* progress, const CookieMap& cookies) {
CURL* curl = createCurl();

FILE* file = fopen(filename.c_str(), "wb");
@@ -148,6 +172,12 @@ bool requestDownload(std::string url, const std::string& filename, float* progre
// Fail on 4xx and 5xx HTTP codes
curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);

// Cookies
if (!cookies.empty()) {
curl_easy_setopt(curl, CURLOPT_COOKIE, getCookieString(cookies).c_str());
}

INFO("Downloading %s", url.c_str());
CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl);



+ 8
- 7
src/plugin.cpp View File

@@ -331,10 +331,9 @@ void queryUpdates() {

// Get user's plugins list
std::string pluginsUrl = API_URL + "/plugins";
json_t* pluginsReqJ = json_object();
json_object_set(pluginsReqJ, "token", json_string(settings::token.c_str()));
json_t* pluginsResJ = network::requestJson(network::METHOD_GET, pluginsUrl, pluginsReqJ);
json_decref(pluginsReqJ);
network::CookieMap cookies;
cookies["token"] = settings::token;
json_t* pluginsResJ = network::requestJson(network::METHOD_GET, pluginsUrl, NULL, cookies);
if (!pluginsResJ) {
WARN("Request for user's plugins failed");
updateStatus = "Could not query updates";
@@ -440,16 +439,18 @@ void syncUpdate(Update* update) {
});

std::string downloadUrl = API_URL + "/download";
downloadUrl += "?token=" + network::encodeUrl(settings::token);
downloadUrl += "&slug=" + network::encodeUrl(update->pluginSlug);
downloadUrl += "?slug=" + network::encodeUrl(update->pluginSlug);
downloadUrl += "&version=" + network::encodeUrl(update->version);
downloadUrl += "&arch=" + network::encodeUrl(APP_ARCH);

network::CookieMap cookies;
cookies["token"] = settings::token;

INFO("Downloading plugin %s %s %s", update->pluginSlug.c_str(), update->version.c_str(), APP_ARCH.c_str());

// Download zip
std::string pluginDest = asset::pluginsPath + "/" + update->pluginSlug + ".zip";
if (!network::requestDownload(downloadUrl, pluginDest, &update->progress)) {
if (!network::requestDownload(downloadUrl, pluginDest, &update->progress, cookies)) {
WARN("Plugin %s download was unsuccessful", update->pluginSlug.c_str());
return;
}


Loading…
Cancel
Save