410 lines
12 KiB
C++
410 lines
12 KiB
C++
#include <cstdint>
|
|
#include <cstddef>
|
|
#include <cstring>
|
|
#include "SceTypes.hpp"
|
|
#include "prx/libc/include/General.hpp"
|
|
#include "prx/libkernel/Equeue/Equeue.hpp"
|
|
#include <atomic>
|
|
#include <deque>
|
|
#include <mutex>
|
|
#include <stdexcept>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
// No network is emulated: contexts, templates and requests can be created, but any request
|
|
// that would touch the network fails with the library's network error.
|
|
static constexpr int ERROR_NETWORK = static_cast<int>(0x80436063);
|
|
static constexpr int ERROR_INVALID_ID = static_cast<int>(0x817B1100);
|
|
static constexpr int ERROR_NULL_POINTER = static_cast<int>(0x817B1225);
|
|
|
|
enum class Http2Kind : std::uint8_t { Context, Template, Request, CookieBox };
|
|
|
|
struct Http2Handle {
|
|
Http2Kind kind;
|
|
int owner;
|
|
std::size_t poolSize;
|
|
};
|
|
|
|
static std::atomic<int> g_nextHandle{1};
|
|
static std::mutex g_handlesMutex;
|
|
static std::unordered_map<int, Http2Handle> g_handles;
|
|
|
|
static Http2Handle* FindHandle(int id, Http2Kind kind) {
|
|
const auto entry = g_handles.find(id);
|
|
return entry != g_handles.end() && entry->second.kind == kind ? &entry->second : nullptr;
|
|
}
|
|
|
|
static Http2Handle* FindAnyHandle(int id) {
|
|
const auto entry = g_handles.find(id);
|
|
return entry != g_handles.end() ? &entry->second : nullptr;
|
|
}
|
|
|
|
static std::vector<int> ReleaseOwnedHandles(int owner) {
|
|
std::vector<int> owned;
|
|
for (const auto& entry : g_handles) {
|
|
if (entry.second.owner == owner) owned.push_back(entry.first);
|
|
}
|
|
std::vector<int> removed;
|
|
for (const int id : owned) {
|
|
const std::vector<int> descendants = ReleaseOwnedHandles(id);
|
|
removed.insert(removed.end(), descendants.begin(), descendants.end());
|
|
g_handles.erase(id);
|
|
removed.push_back(id);
|
|
}
|
|
return removed;
|
|
}
|
|
|
|
static bool HandleIsLive(int id) {
|
|
std::lock_guard lock(g_handlesMutex);
|
|
return FindAnyHandle(id) != nullptr;
|
|
}
|
|
|
|
struct Http2MemoryPoolStats {
|
|
size_t pool_size;
|
|
size_t max_inuse_size;
|
|
size_t current_inuse_size;
|
|
int32_t reserved;
|
|
};
|
|
|
|
static std::mutex g_completionsMutex;
|
|
static std::unordered_map<int, std::deque<Http2AsyncResult>> g_completions;
|
|
|
|
static void EraseCompletions(const std::vector<int>& ids) {
|
|
std::lock_guard lock(g_completionsMutex);
|
|
for (const int id : ids) g_completions.erase(id);
|
|
}
|
|
|
|
static int CompleteAsync(const char* function, int req_id, const Http2AsyncOption* kqueue_option, const void* option) {
|
|
if (kqueue_option == nullptr) NotImplemented_nid_no_patch(function);
|
|
if (option != nullptr) NotImplemented_nid_no_patch(function);
|
|
bool queued = false;
|
|
{
|
|
std::lock_guard handlesLock(g_handlesMutex);
|
|
if (FindHandle(req_id, Http2Kind::Request) != nullptr) {
|
|
std::lock_guard completionsLock(g_completionsMutex);
|
|
Http2AsyncResult completion{};
|
|
completion.req_id = req_id;
|
|
completion.result = ERROR_NETWORK;
|
|
g_completions[req_id].push_back(completion);
|
|
queued = true;
|
|
}
|
|
}
|
|
if (!queued) return ERROR_INVALID_ID;
|
|
if (EqueueTriggerEvent_nid_postfix(kqueue_option->equeue, static_cast<uintptr_t>(kqueue_option->user_event_id), EVFILT_USER, kqueue_option->user_data) != 0) {
|
|
{
|
|
std::lock_guard lock(g_completionsMutex);
|
|
const auto pending = g_completions.find(req_id);
|
|
if (pending != g_completions.end() && !pending->second.empty()) pending->second.pop_back();
|
|
}
|
|
NotImplemented_nid_no_patch(function);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
int APS5_VABI sceHttp2AddRequestHeader(int id, const char* name, const char* value, uint32_t mode) {
|
|
(void)id;
|
|
(void)name;
|
|
(void)value;
|
|
(void)mode;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2CreateRequestWithURL(int tmpl_id, const char* method, const char* url, uint64_t content_length) {
|
|
(void)method;
|
|
(void)url;
|
|
(void)content_length;
|
|
std::lock_guard lock(g_handlesMutex);
|
|
if (FindHandle(tmpl_id, Http2Kind::Template) == nullptr) return ERROR_INVALID_ID;
|
|
const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed);
|
|
g_handles[id] = {Http2Kind::Request, tmpl_id, 0};
|
|
return id;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2CreateTemplate(int lib_http2_ctx_id, const char* user_agent, int http_ver, int is_auto_proxy_conf) {
|
|
(void)user_agent;
|
|
(void)http_ver;
|
|
(void)is_auto_proxy_conf;
|
|
std::lock_guard lock(g_handlesMutex);
|
|
if (FindHandle(lib_http2_ctx_id, Http2Kind::Context) == nullptr) return ERROR_INVALID_ID;
|
|
const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed);
|
|
g_handles[id] = {Http2Kind::Template, lib_http2_ctx_id, 0};
|
|
return id;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2DeleteRequest(int req_id) {
|
|
{
|
|
std::lock_guard lock(g_handlesMutex);
|
|
if (FindHandle(req_id, Http2Kind::Request) == nullptr) return ERROR_INVALID_ID;
|
|
g_handles.erase(req_id);
|
|
}
|
|
EraseCompletions({req_id});
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2DeleteTemplate(int tmpl_id) {
|
|
std::vector<int> removed;
|
|
{
|
|
std::lock_guard lock(g_handlesMutex);
|
|
if (FindHandle(tmpl_id, Http2Kind::Template) == nullptr) return ERROR_INVALID_ID;
|
|
removed = ReleaseOwnedHandles(tmpl_id);
|
|
g_handles.erase(tmpl_id);
|
|
}
|
|
EraseCompletions(removed);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2GetAllResponseHeaders(int req_id, char** header, size_t* header_size) {
|
|
(void)req_id;
|
|
(void)header;
|
|
(void)header_size;
|
|
return ERROR_NETWORK;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2GetResponseContentLength(int req_id, int* result, uint64_t* content_length) {
|
|
(void)req_id;
|
|
(void)result;
|
|
(void)content_length;
|
|
return ERROR_NETWORK;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2GetStatusCode(int req_id, int* status_code) {
|
|
(void)req_id;
|
|
(void)status_code;
|
|
return ERROR_NETWORK;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2Init(int libnet_mem_id, int libssl_ctx_id, size_t pool_size, int max_concurrent_request) {
|
|
(void)libnet_mem_id;
|
|
(void)libssl_ctx_id;
|
|
(void)max_concurrent_request;
|
|
const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed);
|
|
std::lock_guard lock(g_handlesMutex);
|
|
g_handles[id] = {Http2Kind::Context, 0, pool_size};
|
|
return id;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2ReadData(int req_id, void* data, size_t size) {
|
|
(void)req_id;
|
|
(void)data;
|
|
(void)size;
|
|
return ERROR_NETWORK;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2ReadDataAsync(int req_id, void* data, size_t size, void* kqueue_option, void* option) {
|
|
(void)req_id;
|
|
(void)data;
|
|
(void)size;
|
|
(void)kqueue_option;
|
|
(void)option;
|
|
return ERROR_NETWORK;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SendRequest(int req_id, const void* post_data, size_t size) {
|
|
(void)req_id;
|
|
(void)post_data;
|
|
(void)size;
|
|
return ERROR_NETWORK;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SendRequestAsync(int req_id, const void* post_data, size_t size, Http2AsyncOption* kqueue_option, void* option) {
|
|
(void)post_data;
|
|
(void)size;
|
|
return CompleteAsync(__func__, req_id, kqueue_option, option);
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SetAuthEnabled(int id, int is_enable) {
|
|
(void)id;
|
|
(void)is_enable;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SetAutoRedirect(int id, int enable) {
|
|
(void)id;
|
|
(void)enable;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SetConnectionWaitTimeOut(int id, uint32_t usec) {
|
|
(void)id;
|
|
(void)usec;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SetConnectTimeOut(int id, uint32_t usec) {
|
|
(void)id;
|
|
(void)usec;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SetInflateGZIPEnabled(int id, int enable) {
|
|
(void)id;
|
|
(void)enable;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SetRecvTimeOut(int id, uint32_t usec) {
|
|
(void)id;
|
|
(void)usec;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SetRedirectCallback(int id, void* cb_func, void* user_arg) {
|
|
(void)id;
|
|
(void)cb_func;
|
|
(void)user_arg;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SetRequestContentLength(int id, uint64_t content_length) {
|
|
(void)id;
|
|
(void)content_length;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SetResolveTimeOut(int id, uint32_t usec) {
|
|
(void)id;
|
|
(void)usec;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SetSendTimeOut(int id, uint32_t usec) {
|
|
(void)id;
|
|
(void)usec;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SetSslCallback(int id, void* cb_func, void* user_arg) {
|
|
(void)id;
|
|
(void)cb_func;
|
|
(void)user_arg;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SetTimeOut(int id, uint32_t usec) {
|
|
(void)id;
|
|
(void)usec;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SslDisableOption(int id, uint32_t ssl_flags) {
|
|
(void)ssl_flags;
|
|
return HandleIsLive(id) ? 0 : ERROR_INVALID_ID;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SslEnableOption(int id, uint32_t ssl_flags) {
|
|
(void)ssl_flags;
|
|
return HandleIsLive(id) ? 0 : ERROR_INVALID_ID;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SetMinSslVersion(int id, uint32_t ssl_version) {
|
|
(void)ssl_version;
|
|
return HandleIsLive(id) ? 0 : ERROR_INVALID_ID;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2Term(int lib_http2_ctx_id) {
|
|
std::vector<int> removed;
|
|
{
|
|
std::lock_guard lock(g_handlesMutex);
|
|
if (FindHandle(lib_http2_ctx_id, Http2Kind::Context) == nullptr) return ERROR_INVALID_ID;
|
|
removed = ReleaseOwnedHandles(lib_http2_ctx_id);
|
|
g_handles.erase(lib_http2_ctx_id);
|
|
}
|
|
EraseCompletions(removed);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2WaitAsync(int req_id, Http2AsyncResult* result, uint32_t* timeout, void* option) {
|
|
(void)timeout;
|
|
if (result == nullptr || option != nullptr) NotImplemented_nid_no_patch(__func__);
|
|
std::lock_guard lock(g_completionsMutex);
|
|
const auto pending = g_completions.find(req_id);
|
|
if (pending == g_completions.end() || pending->second.empty()) NotImplemented_nid_no_patch("sceHttp2WaitAsync: waiting for an operation that has not completed");
|
|
*result = pending->second.front();
|
|
pending->second.pop_front();
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2AbortRequest(int req_id) {
|
|
(void)req_id;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2GetMemoryPoolStats(int lib_http2_ctx_id, Http2MemoryPoolStats* stats) {
|
|
if (stats == nullptr) return ERROR_NULL_POINTER;
|
|
std::lock_guard lock(g_handlesMutex);
|
|
const Http2Handle* handle = FindHandle(lib_http2_ctx_id, Http2Kind::Context);
|
|
if (handle == nullptr) {
|
|
std::memset(stats, 0, sizeof(*stats));
|
|
return ERROR_INVALID_ID;
|
|
}
|
|
*stats = {handle->poolSize, 0, 0, 0};
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2CookieFlush(int id) {
|
|
(void)id;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2CreateCookieBox(int lib_http2_ctx_id) {
|
|
std::lock_guard lock(g_handlesMutex);
|
|
if (FindHandle(lib_http2_ctx_id, Http2Kind::Context) == nullptr) return ERROR_INVALID_ID;
|
|
const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed);
|
|
g_handles[id] = {Http2Kind::CookieBox, lib_http2_ctx_id, 0};
|
|
return id;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2DeleteCookieBox(int cookie_box_id) {
|
|
std::lock_guard lock(g_handlesMutex);
|
|
if (FindHandle(cookie_box_id, Http2Kind::CookieBox) == nullptr) return ERROR_INVALID_ID;
|
|
g_handles.erase(cookie_box_id);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SetCookieBox(int id, int cookie_box_id) {
|
|
(void)id;
|
|
(void)cookie_box_id;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2SetRequestNoContentLength(int id) {
|
|
(void)id;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int APS5_VABI sceHttp2SetResolveRetry(int id, int32_t retry) {
|
|
(void)id;
|
|
(void)retry;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2WebSocketCreateRequest() {
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2WebSocketCloseAsync() {
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2WebSocketSendTextMessageAsync() {
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2WebSocketSendDataMessageAsync() {
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceHttp2WebSocketSetPingInterval() {
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
}
|