Skip to content

Commit

Permalink
Abstract https_request out of cloud.cpp
Browse files Browse the repository at this point in the history
  • Loading branch information
OFFTKP committed Dec 21, 2023
1 parent 79cd9cd commit 55ce152
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 219 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ if(ENABLE_HTTP_CONTROL_SERVER)
set(SKYEMU_SRC ${SKYEMU_SRC} src/http_control_server.cpp)
endif()

set(SKYEMU_SRC ${SKYEMU_SRC} src/cloud.cpp)
set(SKYEMU_SRC ${SKYEMU_SRC} src/cloud.cpp src/https.cpp)

if(UNICODE_GUI)
set(SKYEMU_SRC ${SKYEMU_SRC} src/utf8proc/utf8proc.c)
Expand Down
219 changes: 2 additions & 217 deletions src/cloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,11 @@ const char* se_get_pref_path();
#define XXH_IMPLEMENTATION
#define XXH_STATIC_LINKING_ONLY
#include "xxhash.h"
#include "https.hpp"

#ifndef EMSCRIPTEN
#include "httplib.h" // for server only
#include <curl/curl.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
extern "C" {
#include "res.h"
}
Expand Down Expand Up @@ -112,53 +111,7 @@ static void em_flush_fs()
#endif
}

#ifndef EMSCRIPTEN
size_t curl_write_data(void* buffer, size_t size, size_t nmemb, void* d)
{
std::vector<uint8_t>* data = (std::vector<uint8_t>*)d;
data->insert(data->end(), (uint8_t*)buffer, (uint8_t*)buffer + size * nmemb);
return size * nmemb;
}
#else
EM_JS(void, em_https_request, (const char* type, const char* url, const char* body, size_t body_size, const char* headers, void* callback), {
var xhr = new XMLHttpRequest();
var method = UTF8ToString(type);
var url_str = UTF8ToString(url);
var body_arr = new Uint8Array(Module.HEAPU8.buffer, body, body_size);
xhr.open(method, url_str);
xhr.responseType = "arraybuffer";

var headers_str = UTF8ToString(headers);
if (headers_str.length > 0) {
var headers_arr = headers_str.split('\n');
for (var i = 0; i < headers_arr.length; i++) {
var header = headers_arr[i].split(':');
if (header.length == 2) {
xhr.setRequestHeader(header[0], header[1]);
} else {
console.log('Invalid header: ' + headers_arr[i]);
}
}
}

xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
var response_size = xhr.response.byteLength;
var response_buffer = Module._malloc(response_size);
var response_view = new Uint8Array(xhr.response);
Module.HEAPU8.set(response_view, response_buffer);
Module.ccall('em_https_request_callback_wrapper', 'void', ['number', 'number', 'number'], [callback, response_buffer, response_size]);
Module._free(response_buffer);
} else {
console.log('The request failed: ' + xhr.status + ' ' + xhr.statusText);
}
};
xhr.onerror = function() {
console.log('The request failed!');
};
xhr.send(body_arr);
});

#ifdef EMSCRIPTEN
EM_JS(void, em_oath_sign_in, (void* drive, const char* client_id), {
var client_id_str = UTF8ToString(client_id);
var redirect_uri = window.location.origin + '/authorized.html';
Expand Down Expand Up @@ -219,15 +172,6 @@ EM_JS(void, em_oath_sign_in, (void* drive, const char* client_id), {
}, 500);
});

extern "C" void em_https_request_callback_wrapper(void* callback, void* data, int size)
{
std::vector<uint8_t> result((uint8_t*)data, (uint8_t*)data + (size_t)size);
std::function<void(const std::vector<uint8_t>&)>* fcallback =
(std::function<void(const std::vector<uint8_t>&)>*)callback;
(*fcallback)(result);
delete fcallback;
}

extern "C" void em_oath_sign_in_callback(cloud_drive_t* drive, const char* refresh_token,
const char* access_token)
{
Expand Down Expand Up @@ -257,165 +201,6 @@ extern "C" void em_oath_sign_in_callback(cloud_drive_t* drive, const char* refre
}
#endif

enum class http_request_e
{
GET,
POST,
PATCH,
};

#ifndef EMSCRIPTEN
// See cacertinmem.c example from libcurl
CURLcode sslctx_function(CURL *curl, void *sslctx, void *parm)
{
CURLcode rv = CURLE_ABORTED_BY_CALLBACK;

uint64_t cacert_pem_len;
const uint8_t* cacert_pem = se_get_resource(SE_CACERT_PEM, &cacert_pem_len);

BIO *cbio = BIO_new_mem_buf(cacert_pem, cacert_pem_len);
X509_STORE *cts = SSL_CTX_get_cert_store((SSL_CTX *)sslctx);
int i;
STACK_OF(X509_INFO) *inf;
(void)curl;
(void)parm;

if(!cts || !cbio) {
return rv;
}

inf = PEM_X509_INFO_read_bio(cbio, NULL, NULL, NULL);

if(!inf) {
BIO_free(cbio);
return rv;
}

for(i = 0; i < sk_X509_INFO_num(inf); i++) {
X509_INFO *itmp = sk_X509_INFO_value(inf, i);
if(itmp->x509) {
X509_STORE_add_cert(cts, itmp->x509);
}
if(itmp->crl) {
X509_STORE_add_crl(cts, itmp->crl);
}
}

sk_X509_INFO_pop_free(inf, X509_INFO_free);
BIO_free(cbio);

rv = CURLE_OK;
return rv;
}
#endif

// Abstraction layer for http requests
void https_request(http_request_e type, const std::string& url, const std::string& body,
const std::vector<std::pair<std::string, std::string>>& headers,
std::function<void(const std::vector<uint8_t>&)> callback)
{
#ifndef EMSCRIPTEN
CURL* curl = curl_easy_init();
if (!curl)
{
printf("[cloud] failed to initialize curl\n");
return;
}

std::vector<uint8_t> result;
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&result);

/* Turn off the default CA locations, otherwise libcurl will load CA
* certificates from the locations that were detected/specified at
* build-time
*/
curl_easy_setopt(curl, CURLOPT_CAINFO, NULL);
curl_easy_setopt(curl, CURLOPT_CAPATH, NULL);

curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctx_function);

switch (type)
{
case http_request_e::GET:
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
break;
case http_request_e::POST:
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.size());
break;
case http_request_e::PATCH:
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.size());
break;
default:
printf("[cloud] invalid request type\n");
return;
}

struct curl_slist* chunk = NULL;
for (auto& header : headers)
{
std::string header_string = header.first + ": " + header.second;
chunk = curl_slist_append(chunk, header_string.c_str());
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);

CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
printf("[cloud] curl failed: %s\n", curl_easy_strerror(res));
return;
}
else
{
callback(result);
}

curl_slist_free_all(chunk);
curl_easy_cleanup(curl);
#else
std::string method;
switch (type)
{
case http_request_e::GET:
method = "GET";
break;
case http_request_e::POST:
method = "POST";
break;
case http_request_e::PATCH:
method = "PATCH";
break;
default:
return;
}

std::string hstring;
for (auto& header : headers)
{
hstring += header.first + ":" + header.second;
if (header != headers.back())
{
hstring += "\n";
}
}

// Deleted when the callback is called
std::function<void(const std::vector<uint8_t>&)>* fcallback =
new std::function<void(const std::vector<uint8_t>&)>(callback);
return em_https_request(method.c_str(), url.c_str(), body.c_str(), body.size(), hstring.c_str(),
(void*)fcallback);
#endif
}

void google_use_refresh_token(cloud_drive_t* drive, std::function<void(cloud_drive_t*)> callback)
{
#ifdef EMSCRIPTEN
Expand Down
Loading

0 comments on commit 55ce152

Please sign in to comment.