mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-24 15:21:05 +00:00
Added initial http_c.cpp code using libcurl.
This commit is contained in:
parent
3fed45a76a
commit
619400fb5b
@ -2,32 +2,296 @@
|
|||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
#include "core/hle/hle.h"
|
#include "core/hle/hle.h"
|
||||||
#include "core/hle/service/http_c.h"
|
#include "core/hle/service/http_c.h"
|
||||||
|
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "common/make_unique.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Namespace HTTP_C
|
// Namespace HTTP_C
|
||||||
|
|
||||||
namespace HTTP_C {
|
namespace HTTP_C {
|
||||||
|
|
||||||
|
typedef u32 ContextHandle;
|
||||||
|
|
||||||
|
enum RequestType : u32 {
|
||||||
|
REQUEST_TYPE_NONE = 0,
|
||||||
|
REQUEST_TYPE_GET = 1,
|
||||||
|
REQUEST_TYPE_POST = 2,
|
||||||
|
REQUEST_TYPE_HEAD = 3,
|
||||||
|
REQUEST_TYPE_PUT = 4,
|
||||||
|
REQUEST_TYPE_DELETE = 5,
|
||||||
|
REQUEST_TYPE_POST_ = 6,
|
||||||
|
REQUEST_TYPE_PUT_ = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RequestState : u32 {
|
||||||
|
REQUEST_STATE_IN_PROGRESS = 5,
|
||||||
|
REQUEST_STATE_READY = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HttpContext {
|
||||||
|
std::string url;
|
||||||
|
RequestType req_type;
|
||||||
|
RequestState req_state;
|
||||||
|
curl_slist* request_hdrs;
|
||||||
|
std::vector<u8> response_hdrs;
|
||||||
|
std::vector<u8> response_data;
|
||||||
|
long response_code;
|
||||||
|
double download_size;
|
||||||
|
double downloaded_size;
|
||||||
|
|
||||||
|
~HttpContext() {
|
||||||
|
curl_slist_free_all(request_hdrs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::unordered_map<ContextHandle, std::unique_ptr<HttpContext>> handle_map;
|
||||||
|
static ContextHandle next_handle;
|
||||||
|
|
||||||
|
static int writer_callback(u8* data, size_t size, size_t nmemb, std::vector<u8>* writer_data) {
|
||||||
|
if (writer_data == nullptr)
|
||||||
|
return 0;
|
||||||
|
writer_data->reserve(writer_data->size() + size * nmemb);
|
||||||
|
writer_data->insert(writer_data->end(), data, data + size * nmemb);
|
||||||
|
return (int)(size * nmemb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Initialize(Service::Interface* self) {
|
||||||
|
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||||
|
|
||||||
|
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||||
|
|
||||||
|
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CreateContext(Service::Interface* self) {
|
||||||
|
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||||
|
|
||||||
|
u32 url_len = cmd_buff[1];
|
||||||
|
RequestType req_type = static_cast<RequestType>(cmd_buff[2]);
|
||||||
|
u32 url_ptr = cmd_buff[4];
|
||||||
|
|
||||||
|
std::string url(reinterpret_cast<const char*>(Memory::GetPointer(url_ptr)), url_len);
|
||||||
|
|
||||||
|
// This should never even happen in the first place with 64-bit handles,
|
||||||
|
while (handle_map.count(next_handle) != 0) {
|
||||||
|
++next_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG(Service, "context url=%s req_type=%u",
|
||||||
|
url.c_str(), req_type);
|
||||||
|
|
||||||
|
HttpContext* context = new HttpContext({});
|
||||||
|
context->req_type = req_type;
|
||||||
|
context->url = url;
|
||||||
|
|
||||||
|
handle_map.emplace(next_handle, std::unique_ptr<HttpContext>(context));
|
||||||
|
|
||||||
|
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||||
|
cmd_buff[2] = next_handle;
|
||||||
|
|
||||||
|
next_handle++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CloseContext(Service::Interface* self) {
|
||||||
|
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||||
|
|
||||||
|
ContextHandle handle = static_cast<ContextHandle>(cmd_buff[1]);
|
||||||
|
auto map_it = handle_map.find(handle);
|
||||||
|
if (map_it == handle_map.end()) {
|
||||||
|
cmd_buff[1] = -1; // TODO: Find proper result code for invalid handle
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handle_map.erase(handle);
|
||||||
|
|
||||||
|
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetRequestState(Service::Interface* self) {
|
||||||
|
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||||
|
|
||||||
|
ContextHandle handle = static_cast<ContextHandle>(cmd_buff[1]);
|
||||||
|
|
||||||
|
auto map_it = handle_map.find(handle);
|
||||||
|
if (map_it == handle_map.end()) {
|
||||||
|
cmd_buff[1] = -1; // TODO: Find proper result code for invalid handle
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||||
|
cmd_buff[2] = map_it->second->req_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetDownloadSizeState(Service::Interface* self) {
|
||||||
|
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||||
|
|
||||||
|
ContextHandle handle = static_cast<ContextHandle>(cmd_buff[1]);
|
||||||
|
|
||||||
|
auto map_it = handle_map.find(handle);
|
||||||
|
if (map_it == handle_map.end()) {
|
||||||
|
cmd_buff[1] = -1; // TODO: Find proper result code for invalid handle
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpContext* context = map_it->second.get();
|
||||||
|
|
||||||
|
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||||
|
cmd_buff[2] = (u32)context->downloaded_size;
|
||||||
|
// TODO: invalid content-length?
|
||||||
|
cmd_buff[3] = (u32)context->download_size;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BeginRequest(Service::Interface* self) {
|
||||||
|
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||||
|
|
||||||
|
ContextHandle handle = static_cast<ContextHandle>(cmd_buff[1]);
|
||||||
|
|
||||||
|
auto map_it = handle_map.find(handle);
|
||||||
|
if (map_it == handle_map.end()) {
|
||||||
|
cmd_buff[1] = -1; // TODO: Find proper result code for invalid handle
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpContext* context = map_it->second.get();
|
||||||
|
CURL* connection = curl_easy_init();
|
||||||
|
|
||||||
|
CURLcode res;
|
||||||
|
res = curl_easy_setopt(connection, CURLOPT_URL, context->url.c_str());
|
||||||
|
switch (context->req_type) {
|
||||||
|
case REQUEST_TYPE_GET:
|
||||||
|
res = curl_easy_setopt(connection, CURLOPT_HTTPGET, 1);
|
||||||
|
break;
|
||||||
|
case REQUEST_TYPE_POST:
|
||||||
|
case REQUEST_TYPE_POST_:
|
||||||
|
res = curl_easy_setopt(connection, CURLOPT_POST, 1);
|
||||||
|
break;
|
||||||
|
case REQUEST_TYPE_PUT:
|
||||||
|
case REQUEST_TYPE_PUT_:
|
||||||
|
res = curl_easy_setopt(connection, CURLOPT_UPLOAD, 1);
|
||||||
|
case REQUEST_TYPE_DELETE:
|
||||||
|
break;
|
||||||
|
case REQUEST_TYPE_HEAD:
|
||||||
|
res = curl_easy_setopt(connection, CURLOPT_NOBODY, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = curl_easy_setopt(connection, CURLOPT_WRITEFUNCTION, writer_callback);
|
||||||
|
res = curl_easy_setopt(connection, CURLOPT_WRITEHEADER, &context->response_hdrs);
|
||||||
|
res = curl_easy_setopt(connection, CURLOPT_WRITEDATA, &context->response_data);
|
||||||
|
|
||||||
|
curl_easy_setopt(connection, CURLOPT_HTTPHEADER, context->request_hdrs);
|
||||||
|
|
||||||
|
context->req_state = REQUEST_STATE_IN_PROGRESS;
|
||||||
|
res = curl_easy_perform(connection);
|
||||||
|
context->req_state = REQUEST_STATE_READY;
|
||||||
|
|
||||||
|
curl_easy_getinfo(connection, CURLINFO_RESPONSE_CODE, &context->response_code);
|
||||||
|
curl_easy_getinfo(connection, CURLINFO_SIZE_DOWNLOAD, &context->downloaded_size);
|
||||||
|
curl_easy_getinfo(connection, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &context->download_size);
|
||||||
|
|
||||||
|
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ReceiveData(Service::Interface* self) {
|
||||||
|
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||||
|
|
||||||
|
ContextHandle handle = static_cast<ContextHandle>(cmd_buff[1]);
|
||||||
|
u32 buf_size = cmd_buff[2];
|
||||||
|
u8* buffer = Memory::GetPointer(cmd_buff[4]);
|
||||||
|
|
||||||
|
auto map_it = handle_map.find(handle);
|
||||||
|
if (map_it == handle_map.end()) {
|
||||||
|
cmd_buff[1] = -1; // TODO: Find proper result code for invalid handle
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<u8>& data = map_it->second->response_data;
|
||||||
|
if (buf_size < data.size()) {
|
||||||
|
cmd_buff[1] = 0xd840a02b;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::copy(data.begin(), data.end(), buffer);
|
||||||
|
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AddRequestHeader(Service::Interface* self) {
|
||||||
|
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||||
|
|
||||||
|
ContextHandle handle = static_cast<ContextHandle>(cmd_buff[1]);
|
||||||
|
u32 hdr_name_len = cmd_buff[2];
|
||||||
|
u32 hdr_val_len = cmd_buff[3];
|
||||||
|
u32 hdr_name_buf = cmd_buff[5];
|
||||||
|
u32 hdr_val_buf = cmd_buff[7];
|
||||||
|
|
||||||
|
// TODO: something is NULL
|
||||||
|
// TODO: header value is empty
|
||||||
|
// TODO: header name is empty
|
||||||
|
std::string header = Common::StringFromFormat("%s: %s",
|
||||||
|
std::string(reinterpret_cast<const char*>(Memory::GetPointer(hdr_name_buf)), hdr_name_len).c_str(),
|
||||||
|
std::string(reinterpret_cast<const char*>(Memory::GetPointer(hdr_val_buf)), hdr_val_len).c_str());
|
||||||
|
|
||||||
|
auto map_it = handle_map.find(handle);
|
||||||
|
if (map_it == handle_map.end()) {
|
||||||
|
cmd_buff[1] = -1; // TODO: Find proper result code for invalid handle
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
map_it->second->request_hdrs = curl_slist_append(map_it->second->request_hdrs, header.c_str());
|
||||||
|
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetResponseStatusCode(Service::Interface* self) {
|
||||||
|
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||||
|
|
||||||
|
ContextHandle handle = static_cast<ContextHandle>(cmd_buff[1]);
|
||||||
|
|
||||||
|
auto map_it = handle_map.find(handle);
|
||||||
|
if (map_it == handle_map.end()) {
|
||||||
|
cmd_buff[1] = -1; // TODO: Find proper result code for invalid handle
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||||
|
cmd_buff[2] = (u32)map_it->second->response_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Finalize(Service::Interface* self) {
|
||||||
|
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||||
|
|
||||||
|
handle_map.clear();
|
||||||
|
next_handle = 0;
|
||||||
|
curl_global_cleanup();
|
||||||
|
|
||||||
|
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||||
|
}
|
||||||
|
|
||||||
const Interface::FunctionInfo FunctionTable[] = {
|
const Interface::FunctionInfo FunctionTable[] = {
|
||||||
{0x00010044, nullptr, "Initialize"},
|
{0x00010044, Initialize, "Initialize"},
|
||||||
{0x00020082, nullptr, "CreateContext"},
|
{0x00020082, CreateContext, "CreateContext"},
|
||||||
{0x00030040, nullptr, "CloseContext"},
|
{0x00030040, CloseContext, "CloseContext"},
|
||||||
{0x00040040, nullptr, "CancelConnection"},
|
{0x00040040, nullptr, "CancelConnection"},
|
||||||
{0x00050040, nullptr, "GetRequestState"},
|
{0x00050040, GetRequestState, "GetRequestState"},
|
||||||
{0x00060040, nullptr, "GetDownloadSizeState"},
|
{0x00060040, GetDownloadSizeState, "GetDownloadSizeState"},
|
||||||
{0x00070040, nullptr, "GetRequestError"},
|
{0x00070040, nullptr, "GetRequestError"},
|
||||||
{0x00080042, nullptr, "InitializeConnectionSession"},
|
{0x00080042, nullptr, "InitializeConnectionSession"},
|
||||||
{0x00090040, nullptr, "BeginRequest"},
|
{0x00090040, BeginRequest, "BeginRequest"},
|
||||||
{0x000A0040, nullptr, "BeginRequestAsync"},
|
{0x000A0040, nullptr, "BeginRequestAsync"},
|
||||||
{0x000B0082, nullptr, "ReceiveData"},
|
{0x000B0082, ReceiveData, "ReceiveData"},
|
||||||
{0x000C0102, nullptr, "ReceiveDataTimeout"},
|
{0x000C0102, nullptr, "ReceiveDataTimeout"},
|
||||||
{0x000D0146, nullptr, "SetProxy"},
|
{0x000D0146, nullptr, "SetProxy"},
|
||||||
{0x000E0040, nullptr, "SetProxyDefault"},
|
{0x000E0040, nullptr, "SetProxyDefault"},
|
||||||
{0x000F00C4, nullptr, "SetBasicAuthorization"},
|
{0x000F00C4, nullptr, "SetBasicAuthorization"},
|
||||||
{0x00100080, nullptr, "SetSocketBufferSize"},
|
{0x00100080, nullptr, "SetSocketBufferSize"},
|
||||||
{0x001100C4, nullptr, "AddRequestHeader"},
|
{0x001100C4, AddRequestHeader, "AddRequestHeader"},
|
||||||
{0x001200C4, nullptr, "AddPostDataAscii"},
|
{0x001200C4, nullptr, "AddPostDataAscii"},
|
||||||
{0x001300C4, nullptr, "AddPostDataBinary"},
|
{0x001300C4, nullptr, "AddPostDataBinary"},
|
||||||
{0x00140082, nullptr, "AddPostDataRaw"},
|
{0x00140082, nullptr, "AddPostDataRaw"},
|
||||||
@ -37,20 +301,20 @@ const Interface::FunctionInfo FunctionTable[] = {
|
|||||||
{0x001800C4, nullptr, "SendPostDataBinary"},
|
{0x001800C4, nullptr, "SendPostDataBinary"},
|
||||||
{0x00190144, nullptr, "SendPostDataBinaryTimeout"},
|
{0x00190144, nullptr, "SendPostDataBinaryTimeout"},
|
||||||
{0x001A0082, nullptr, "SendPostDataRaw"},
|
{0x001A0082, nullptr, "SendPostDataRaw"},
|
||||||
{0x001B0102, nullptr, "SendPOSTDataRawTimeout"},
|
{0x001B0102, nullptr, "SendPostDataRawTimeout"},
|
||||||
{0x001C0080, nullptr, "SetPostDataEncoding"},
|
{0x001C0080, nullptr, "SetPostDataEncoding"},
|
||||||
{0x001D0040, nullptr, "NotifyFinishSendPostData"},
|
{0x001D0040, nullptr, "NotifyFinishSendPostData"},
|
||||||
{0x001E00C4, nullptr, "GetResponseHeader"},
|
{0x001E00C4, nullptr, "GetResponseHeader"},
|
||||||
{0x001F0144, nullptr, "GetResponseHeaderTimeout"},
|
{0x001F0144, nullptr, "GetResponseHeaderTimeout"},
|
||||||
{0x00200082, nullptr, "GetResponseData"},
|
{0x00200082, nullptr, "GetResponseData"},
|
||||||
{0x00210102, nullptr, "GetResponseDataTimeout"},
|
{0x00210102, nullptr, "GetResponseDataTimeout"},
|
||||||
{0x00220040, nullptr, "GetResponseStatusCode"},
|
{0x00220040, GetResponseStatusCode, "GetResponseStatusCode"},
|
||||||
{0x002300C0, nullptr, "GetResponseStatusCodeTimeout"},
|
{0x002300C0, nullptr, "GetResponseStatusCodeTimeout"},
|
||||||
{0x00240082, nullptr, "AddTrustedRootCA"},
|
{0x00240082, nullptr, "AddTrustedRootCA"},
|
||||||
{0x00350186, nullptr, "SetDefaultProxy"},
|
{0x00350186, nullptr, "SetDefaultProxy"},
|
||||||
{0x00360000, nullptr, "ClearDNSCache"},
|
{0x00360000, nullptr, "ClearDNSCache"},
|
||||||
{0x00370080, nullptr, "SetKeepAlive"},
|
{0x00370080, nullptr, "SetKeepAlive"},
|
||||||
{0x003800C0, nullptr, "Finalize"},
|
{0x003800C0, Finalize, "Finalize"},
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
Loading…
Reference in New Issue
Block a user