http part5 (#4453)

* added async state sending support

* typo

* implemented blocking

* clang is not my friend

* fixed stephen's review

* code organize / nothing changed
This commit is contained in:
georgemoralis 2026-05-21 17:14:03 +03:00 committed by GitHub
parent 03ebac577b
commit 82beca2bbf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 452 additions and 197 deletions

View File

@ -3,16 +3,19 @@
#include <atomic> #include <atomic>
#include <cctype> #include <cctype>
#include <condition_variable>
#include <cstring> #include <cstring>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <thread>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/emulator_settings.h"
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/orbis_error.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
@ -35,6 +38,7 @@ struct HttpSettings {
bool auto_redirect = true; bool auto_redirect = true;
bool inflate_gzip = true; bool inflate_gzip = true;
u32 ssl_flags = ORBIS_HTTPS_FLAG_SDK_DEFAULT; // SSL flag mask. Bitmask of OrbisHttpsFlags. u32 ssl_flags = ORBIS_HTTPS_FLAG_SDK_DEFAULT; // SSL flag mask. Bitmask of OrbisHttpsFlags.
bool nonblock = false; // false = blocking (default), true = nonblock (EAGAIN)
}; };
struct HttpTemplate { struct HttpTemplate {
@ -56,6 +60,15 @@ struct HttpConnection {
HttpSettings settings; HttpSettings settings;
}; };
struct HttpResponse {
int status_code = 0;
u64 content_length = 0;
int content_length_result = ORBIS_HTTP_ERROR_NO_CONTENT_LENGTH;
std::vector<u8> body;
u64 read_cursor = 0;
std::string all_headers_blob; // Pre-formatted "Name: Value\r\n..." string.
};
struct HttpRequest { struct HttpRequest {
int conn_id = 0; int conn_id = 0;
int method = 0; int method = 0;
@ -66,6 +79,9 @@ struct HttpRequest {
bool deleted = false; bool deleted = false;
s32 last_errno = 0; // populated by SendRequest, read by GetLastErrno s32 last_errno = 0; // populated by SendRequest, read by GetLastErrno
HttpSettings settings; HttpSettings settings;
HttpResponse res;
std::condition_variable cv; // waiters in blocking getters block on this
// notified when state leaves Sending.
}; };
struct HttpState { struct HttpState {
@ -82,6 +98,10 @@ struct HttpState {
static HttpState g_state; static HttpState g_state;
//***********************************
// Helper functions
//***********************************
static HttpSettings* ResolveSettings(int id, const char*& level) { static HttpSettings* ResolveSettings(int id, const char*& level) {
if (auto it = g_state.templates.find(id); it != g_state.templates.end()) { if (auto it = g_state.templates.find(id); it != g_state.templates.end()) {
level = "template"; level = "template";
@ -99,6 +119,40 @@ static HttpSettings* ResolveSettings(int id, const char*& level) {
return nullptr; return nullptr;
} }
// Populate a response object with the shape a transport-level failure produces:
// no status line, no headers, no body. Used by the no-internet path.
static void SynthesizeTransportFailureResponse(HttpResponse& res) {
res.status_code = 0;
res.content_length = 0;
res.content_length_result = ORBIS_HTTP_ERROR_NO_CONTENT_LENGTH;
res.body.clear();
res.read_cursor = 0;
res.all_headers_blob.clear();
}
static int WaitForResponseReady(HttpRequest& req, std::unique_lock<std::mutex>& lock) {
if (req.state == HttpRequestState::Aborted) {
return ORBIS_HTTP_ERROR_ABORTED;
}
if (req.state == HttpRequestState::Created) {
return ORBIS_HTTP_ERROR_BEFORE_SEND;
}
if (req.state == HttpRequestState::Sent) {
return ORBIS_OK;
}
// state == Sending. Honor nonblock: return EAGAIN instead of blocking.
if (req.settings.nonblock) {
return ORBIS_HTTP_ERROR_EAGAIN;
}
req.cv.wait(lock, [&req]() {
return req.state != HttpRequestState::Sending || g_state.shutting_down.load();
});
if (g_state.shutting_down.load() || req.state == HttpRequestState::Aborted) {
return ORBIS_HTTP_ERROR_ABORTED;
}
return ORBIS_OK;
}
void NormalizeAndAppendPath(char* dest, char* src) { void NormalizeAndAppendPath(char* dest, char* src) {
char* lastSlash; char* lastSlash;
u64 length; u64 length;
@ -160,15 +214,9 @@ static bool ContainsCrLf(const char* s) {
return false; return false;
} }
int PS4_SYSV_ABI sceHttpAbortRequest(int reqId) { //***********************************
LOG_ERROR(Lib_Http, "(STUBBED) called reqId={}", reqId); // TODO/WIP/Stubbed functions
return ORBIS_OK; //***********************************
}
int PS4_SYSV_ABI sceHttpAbortRequestForce(int reqId) {
LOG_ERROR(Lib_Http, "(STUBBED) called reqId={}", reqId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpAbortWaitRequest(OrbisHttpEpollHandle eh) { int PS4_SYSV_ABI sceHttpAbortWaitRequest(OrbisHttpEpollHandle eh) {
LOG_ERROR(Lib_Http, "(STUBBED) called eh={}", fmt::ptr(eh)); LOG_ERROR(Lib_Http, "(STUBBED) called eh={}", fmt::ptr(eh));
@ -493,53 +541,6 @@ int PS4_SYSV_ABI sceHttpDbgShowStat() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttpDeleteConnection(int connId) {
LOG_INFO(Lib_Http, "called connId={}", connId);
std::lock_guard<std::mutex> lock(g_state.m_mutex);
if (!g_state.inited) {
LOG_ERROR(Lib_Http, "Not initialized");
return ORBIS_HTTP_ERROR_BEFORE_INIT;
}
if (g_state.connections.erase(connId) == 0) {
LOG_ERROR(Lib_Http, "Invalid connId={}", connId);
return ORBIS_HTTP_ERROR_INVALID_ID;
}
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpDeleteRequest(int reqId) {
LOG_INFO(Lib_Http, "called reqId={}", reqId);
std::lock_guard<std::mutex> lock(g_state.m_mutex);
if (!g_state.inited) {
LOG_ERROR(Lib_Http, "Not initialized");
return ORBIS_HTTP_ERROR_BEFORE_INIT;
}
auto it = g_state.requests.find(reqId);
if (it == g_state.requests.end()) {
LOG_ERROR(Lib_Http, "Invalid reqId={}", reqId);
return ORBIS_HTTP_ERROR_INVALID_ID;
}
auto req_ptr = it->second;
req_ptr->deleted = true;
req_ptr->state = HttpRequestState::Aborted;
g_state.requests.erase(it);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpDeleteTemplate(int tmplId) {
LOG_INFO(Lib_Http, "called tmplId={}", tmplId);
std::lock_guard<std::mutex> lock(g_state.m_mutex);
if (!g_state.inited) {
LOG_ERROR(Lib_Http, "Not initialized");
return ORBIS_HTTP_ERROR_BEFORE_INIT;
}
if (g_state.templates.erase(tmplId) == 0) {
LOG_ERROR(Lib_Http, "Invalid tmplId={}", tmplId);
return ORBIS_HTTP_ERROR_INVALID_ID;
}
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpDestroyEpoll(int libhttpCtxId, OrbisHttpEpollHandle eh) { int PS4_SYSV_ABI sceHttpDestroyEpoll(int libhttpCtxId, OrbisHttpEpollHandle eh) {
LOG_ERROR(Lib_Http, "(STUBBED) called libhttpCtxId={}, eh={}", libhttpCtxId, fmt::ptr(eh)); LOG_ERROR(Lib_Http, "(STUBBED) called libhttpCtxId={}, eh={}", libhttpCtxId, fmt::ptr(eh));
return ORBIS_OK; return ORBIS_OK;
@ -550,12 +551,6 @@ int PS4_SYSV_ABI sceHttpGetAcceptEncodingGZIPEnabled(int id, int* isEnable) {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttpGetAllResponseHeaders(int reqId, char** header, u64* headerSize) {
LOG_ERROR(Lib_Http, "(STUBBED) called reqId={}, header={}, headerSize={}", reqId,
fmt::ptr(header), fmt::ptr(headerSize));
return ORBIS_FAIL;
}
int PS4_SYSV_ABI sceHttpGetAuthEnabled(int id, int* isEnable) { int PS4_SYSV_ABI sceHttpGetAuthEnabled(int id, int* isEnable) {
LOG_ERROR(Lib_Http, "(STUBBED) called id={}, isEnable={}", id, fmt::ptr(isEnable)); LOG_ERROR(Lib_Http, "(STUBBED) called id={}, isEnable={}", id, fmt::ptr(isEnable));
return ORBIS_OK; return ORBIS_OK;
@ -605,57 +600,11 @@ int PS4_SYSV_ABI sceHttpGetMemoryPoolStats(int libhttpCtxId,
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttpGetNonblock(int id, int* isEnable) {
LOG_ERROR(Lib_Http, "(STUBBED) called id={}, isEnable={}", id, fmt::ptr(isEnable));
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpGetRegisteredCtxIds() { int PS4_SYSV_ABI sceHttpGetRegisteredCtxIds() {
LOG_ERROR(Lib_Http, "(STUBBED) called"); LOG_ERROR(Lib_Http, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttpGetResponseContentLength(int reqId, int* result, u64* contentLength) {
LOG_ERROR(Lib_Http, "(STUBBED) called reqId={}, result={}, contentLength={}", reqId,
fmt::ptr(result), fmt::ptr(contentLength));
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpGetStatusCode(int reqId, int* statusCode) {
LOG_INFO(Lib_Http, "(STUBBED) called reqId={}, statusCode={}", reqId, fmt::ptr(statusCode));
#if 0
if (!g_state.inited)
return ORBIS_HTTP_ERROR_BEFORE_INIT;
if (statusCode == nullptr)
return ORBIS_HTTP_ERROR_INVALID_VALUE;
int ret = 0;
// Lookup HttpRequestInternal by reqId
HttpRequestInternal* request = nullptr;
ret = HttpRequestInternal_Acquire(&request, reqId);
if (ret < 0)
return ret;
request->m_mutex.lock();
if (request->state > 0x11) {
if (request->state == 0x16) {
ret = request->errorCode;
} else {
*statusCode = request->httpStatusCode;
ret = 0;
}
} else {
ret = ORBIS_HTTP_ERROR_BEFORE_SEND;
}
request->m_mutex.unlock();
HttpRequestInternal_Release(request);
return ret;
#else
return ORBIS_HTTP_ERROR_BEFORE_SEND;
#endif
}
int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, u64 poolSize) { int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, u64 poolSize) {
LOG_INFO(Lib_Http, "called libnetMemId={}, libsslCtxId={}, poolSize={}", libnetMemId, LOG_INFO(Lib_Http, "called libnetMemId={}, libsslCtxId={}, poolSize={}", libnetMemId,
libsslCtxId, poolSize); libsslCtxId, poolSize);
@ -673,11 +622,6 @@ int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, u64 poolSize) {
return ctx_id; return ctx_id;
} }
int PS4_SYSV_ABI sceHttpReadData(s32 reqId, void* data, u64 size) {
LOG_ERROR(Lib_Http, "(STUBBED) called reqId={}, data={}, size={}", reqId, fmt::ptr(data), size);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpRedirectCacheFlush(int libhttpCtxId) { int PS4_SYSV_ABI sceHttpRedirectCacheFlush(int libhttpCtxId) {
LOG_ERROR(Lib_Http, "(STUBBED) called libhttpCtxId={}", libhttpCtxId); LOG_ERROR(Lib_Http, "(STUBBED) called libhttpCtxId={}", libhttpCtxId);
return ORBIS_OK; return ORBIS_OK;
@ -695,31 +639,59 @@ int PS4_SYSV_ABI sceHttpRequestGetAllHeaders() {
int PS4_SYSV_ABI sceHttpSendRequest(int reqId, const void* postData, u64 size) { int PS4_SYSV_ABI sceHttpSendRequest(int reqId, const void* postData, u64 size) {
LOG_INFO(Lib_Http, "called reqId={}, postData={}, size={}", reqId, fmt::ptr(postData), size); LOG_INFO(Lib_Http, "called reqId={}, postData={}, size={}", reqId, fmt::ptr(postData), size);
std::lock_guard<std::mutex> lock(g_state.m_mutex); std::shared_ptr<HttpRequest> req_ptr;
if (!g_state.inited) { {
LOG_ERROR(Lib_Http, "Not initialized"); std::lock_guard<std::mutex> lock(g_state.m_mutex);
return ORBIS_HTTP_ERROR_BEFORE_INIT; if (!g_state.inited) {
} LOG_ERROR(Lib_Http, "Not initialized");
auto it = g_state.requests.find(reqId); return ORBIS_HTTP_ERROR_BEFORE_INIT;
if (it == g_state.requests.end()) { }
LOG_ERROR(Lib_Http, "Invalid reqId={}", reqId); auto it = g_state.requests.find(reqId);
return ORBIS_HTTP_ERROR_INVALID_ID; if (it == g_state.requests.end()) {
} LOG_ERROR(Lib_Http, "Invalid reqId={}", reqId);
auto& req = *it->second; return ORBIS_HTTP_ERROR_INVALID_ID;
if (req.state == HttpRequestState::Sent) { }
LOG_ERROR(Lib_Http, "Request already sent (reqId={})", reqId); auto& req = *it->second;
return ORBIS_HTTP_ERROR_AFTER_SEND; if (req.state == HttpRequestState::Sending || req.state == HttpRequestState::Sent) {
} LOG_ERROR(Lib_Http, "Request already sent (reqId={})", reqId);
if (req.state == HttpRequestState::Aborted) { return ORBIS_HTTP_ERROR_AFTER_SEND;
LOG_ERROR(Lib_Http, "Request was aborted (reqId={})", reqId); }
return ORBIS_HTTP_ERROR_ABORTED; if (req.state == HttpRequestState::Aborted) {
LOG_ERROR(Lib_Http, "Request was aborted (reqId={})", reqId);
return ORBIS_HTTP_ERROR_ABORTED;
}
// Created -> Sending. Worker thread will move to Sent.
req.state = HttpRequestState::Sending;
req_ptr = it->second;
} }
req.last_errno = ORBIS_HTTP_ERROR_RESOLVER_ENODNS; LOG_INFO(Lib_Http, "reqId={} dispatched to async worker [{}]", reqId,
req.state = HttpRequestState::Sent; EmulatorSettings.IsConnectedToNetwork() ? "ONLINE (TODO real I/O)"
LOG_INFO(Lib_Http, "reqId={} send failed: last_errno={:#x} (no-internet path)", reqId, : "OFFLINE no-internet path");
static_cast<u32>(req.last_errno)); std::thread([req_ptr, reqId]() {
return ORBIS_HTTP_ERROR_RESOLVER_ENODNS; HttpResponse local_res;
if (!EmulatorSettings.IsConnectedToNetwork()) {
SynthesizeTransportFailureResponse(local_res);
} else {
// TODO: real network I/O path but for now return the same so switching doesn't affect
// something
SynthesizeTransportFailureResponse(local_res);
}
std::lock_guard<std::mutex> lock(g_state.m_mutex);
if (g_state.shutting_down.load() || req_ptr->deleted ||
req_ptr->state == HttpRequestState::Aborted) {
req_ptr->cv.notify_all();
return;
}
req_ptr->res = std::move(local_res);
req_ptr->state = HttpRequestState::Sent;
req_ptr->last_errno = ORBIS_HTTP_ERROR_RESOLVER_ENODNS;
LOG_INFO(Lib_Http, "(TRANSPORT FAIL) reqId={} -> 0 (body 0 bytes, errno={:#x})", reqId,
static_cast<u32>(req_ptr->last_errno));
req_ptr->cv.notify_all();
}).detach();
return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttpSetAcceptEncodingGZIPEnabled(int id, int isEnable) { int PS4_SYSV_ABI sceHttpSetAcceptEncodingGZIPEnabled(int id, int isEnable) {
@ -809,11 +781,6 @@ int PS4_SYSV_ABI sceHttpSetHttp09Enabled(int id, int isEnable) {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttpSetNonblock(int id, int isEnable) {
LOG_ERROR(Lib_Http, "(STUBBED) called id={}, isEnable={}", id, isEnable);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpSetPolicyOption() { int PS4_SYSV_ABI sceHttpSetPolicyOption() {
LOG_ERROR(Lib_Http, "(STUBBED) called"); LOG_ERROR(Lib_Http, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
@ -934,6 +901,7 @@ int PS4_SYSV_ABI sceHttpTerm(int libhttpCtxId) {
for (auto& [id, req_ptr] : g_state.requests) { for (auto& [id, req_ptr] : g_state.requests) {
req_ptr->deleted = true; req_ptr->deleted = true;
req_ptr->state = HttpRequestState::Aborted; req_ptr->state = HttpRequestState::Aborted;
req_ptr->cv.notify_all(); // wake blocked waiters before wiping the map
} }
g_state.requests.clear(); g_state.requests.clear();
g_state.connections.clear(); g_state.connections.clear();
@ -946,16 +914,6 @@ int PS4_SYSV_ABI sceHttpTerm(int libhttpCtxId) {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceHttpTryGetNonblock(int id, int* isEnable) {
LOG_ERROR(Lib_Http, "(STUBBED) called id={}, isEnable={}", id, fmt::ptr(isEnable));
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpTrySetNonblock(int id, int isEnable) {
LOG_ERROR(Lib_Http, "(STUBBED) called id={}, isEnable={}", id, isEnable);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpUnsetEpoll(int id) { int PS4_SYSV_ABI sceHttpUnsetEpoll(int id) {
LOG_ERROR(Lib_Http, "(STUBBED) called id={}", id); LOG_ERROR(Lib_Http, "(STUBBED) called id={}", id);
return ORBIS_OK; return ORBIS_OK;
@ -973,6 +931,127 @@ int PS4_SYSV_ABI sceHttpUriCopy() {
return ORBIS_OK; return ORBIS_OK;
} }
//***********************************
// Non-blocking processing functions
//***********************************
int PS4_SYSV_ABI sceHttpGetNonblock(int id, int* isEnable) {
LOG_INFO(Lib_Http, "called id={}, isEnable={}", id, fmt::ptr(isEnable));
std::lock_guard<std::mutex> lock(g_state.m_mutex);
if (!g_state.inited) {
LOG_ERROR(Lib_Http, "Not initialized");
return ORBIS_HTTP_ERROR_BEFORE_INIT;
}
if (!isEnable) {
LOG_ERROR(Lib_Http, "isEnable output pointer is null");
return ORBIS_HTTP_ERROR_INVALID_VALUE;
}
const char* level = "";
HttpSettings* s = ResolveSettings(id, level);
if (!s) {
LOG_ERROR(Lib_Http, "Invalid id={}", id);
return ORBIS_HTTP_ERROR_INVALID_ID;
}
*isEnable = s->nonblock ? 1 : 0;
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpSetNonblock(int id, int isEnable) {
LOG_INFO(Lib_Http, "called id={}, isEnable={}", id, isEnable);
std::lock_guard<std::mutex> lock(g_state.m_mutex);
if (!g_state.inited) {
LOG_ERROR(Lib_Http, "Not initialized");
return ORBIS_HTTP_ERROR_BEFORE_INIT;
}
const char* level = "";
HttpSettings* s = ResolveSettings(id, level);
if (!s) {
LOG_ERROR(Lib_Http, "Invalid id={}", id);
return ORBIS_HTTP_ERROR_INVALID_ID;
}
s->nonblock = (isEnable != 0);
LOG_INFO(Lib_Http, "set {} id={} nonblock={}", level, id, s->nonblock);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpTryGetNonblock(int id, int* isEnable) {
LOG_INFO(Lib_Http, "called id={}, isEnable={}", id, fmt::ptr(isEnable));
return sceHttpGetNonblock(id, isEnable);
}
int PS4_SYSV_ABI sceHttpTrySetNonblock(int id, int isEnable) {
LOG_INFO(Lib_Http, "called id={}, isEnable={}", id, isEnable);
return sceHttpSetNonblock(id, isEnable);
}
//***********************************
// Http Communication functions
//***********************************
int PS4_SYSV_ABI sceHttpReadData(s32 reqId, void* data, u64 size) {
LOG_INFO(Lib_Http, "called reqId={}, data={}, size={}", reqId, fmt::ptr(data), size);
std::unique_lock<std::mutex> lock(g_state.m_mutex);
if (!g_state.inited) {
LOG_ERROR(Lib_Http, "Not initialized");
return ORBIS_HTTP_ERROR_BEFORE_INIT;
}
if (!data) {
LOG_ERROR(Lib_Http, "data output pointer is null");
return ORBIS_HTTP_ERROR_INVALID_VALUE;
}
auto it = g_state.requests.find(reqId);
if (it == g_state.requests.end()) {
LOG_ERROR(Lib_Http, "Invalid reqId={}", reqId);
return ORBIS_HTTP_ERROR_INVALID_ID;
}
auto& req = *it->second;
int wr = WaitForResponseReady(req, lock);
if (wr != ORBIS_OK) {
if (wr == ORBIS_HTTP_ERROR_EAGAIN) {
LOG_DEBUG(Lib_Http, "reqId={}: EAGAIN (response not yet ready)", reqId);
} else {
LOG_ERROR(Lib_Http, "Wait failed for reqId={}: {:#x}", reqId, static_cast<u32>(wr));
}
return wr;
}
u64 remaining = req.res.body.size() - req.res.read_cursor;
u64 to_copy = std::min(size, remaining);
if (to_copy > 0) {
std::memcpy(data, req.res.body.data() + req.res.read_cursor, to_copy);
req.res.read_cursor += to_copy;
}
LOG_INFO(Lib_Http, "reqId={} copied {} bytes (cursor {}/{}) ", reqId, to_copy,
req.res.read_cursor, req.res.body.size());
return static_cast<int>(to_copy);
}
int PS4_SYSV_ABI sceHttpAbortRequest(int reqId) {
LOG_INFO(Lib_Http, "called reqId={}", reqId);
std::lock_guard<std::mutex> lock(g_state.m_mutex);
if (!g_state.inited) {
LOG_ERROR(Lib_Http, "Not initialized");
return ORBIS_HTTP_ERROR_BEFORE_INIT;
}
auto it = g_state.requests.find(reqId);
if (it == g_state.requests.end()) {
LOG_ERROR(Lib_Http, "Invalid reqId={}", reqId);
return ORBIS_HTTP_ERROR_INVALID_ID;
}
auto& req = *it->second;
if (req.state == HttpRequestState::Created || req.state == HttpRequestState::Sending) {
req.state = HttpRequestState::Aborted;
req.cv.notify_all();
LOG_INFO(Lib_Http, "reqId={} marked Aborted", reqId);
} else {
LOG_INFO(Lib_Http, "reqId={} already Sent/Aborted, no-op", reqId);
}
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpAbortRequestForce(int reqId) {
LOG_INFO(Lib_Http, "called reqId={}", reqId);
return sceHttpAbortRequest(reqId);
}
//*********************************** //***********************************
// Https Option setting functions // Https Option setting functions
//*********************************** //***********************************
@ -1075,6 +1154,112 @@ int PS4_SYSV_ABI sceHttpsEnableOptionPrivate(int id, u32 sslFlags) {
//*********************************** //***********************************
// Response Information functions // Response Information functions
//*********************************** //***********************************
int PS4_SYSV_ABI sceHttpGetAllResponseHeaders(int reqId, char** header, u64* headerSize) {
LOG_INFO(Lib_Http, "called reqId={}, header={}, headerSize={}", reqId, fmt::ptr(header),
fmt::ptr(headerSize));
std::unique_lock<std::mutex> lock(g_state.m_mutex);
if (!g_state.inited) {
LOG_ERROR(Lib_Http, "Not initialized");
return ORBIS_HTTP_ERROR_BEFORE_INIT;
}
if (!header || !headerSize) {
LOG_ERROR(Lib_Http, "header or headerSize output pointer is null");
return ORBIS_HTTP_ERROR_INVALID_VALUE;
}
auto it = g_state.requests.find(reqId);
if (it == g_state.requests.end()) {
LOG_ERROR(Lib_Http, "Invalid reqId={}", reqId);
return ORBIS_HTTP_ERROR_INVALID_ID;
}
auto& req = *it->second;
int wr = WaitForResponseReady(req, lock);
if (wr != ORBIS_OK) {
if (wr == ORBIS_HTTP_ERROR_EAGAIN) {
LOG_DEBUG(Lib_Http, "reqId={}: EAGAIN (response not yet ready)", reqId);
} else {
LOG_ERROR(Lib_Http, "Wait failed for reqId={}: {:#x}", reqId, static_cast<u32>(wr));
}
return wr;
}
if (req.res.all_headers_blob.empty()) {
*header = nullptr;
*headerSize = 0;
} else {
*header = const_cast<char*>(req.res.all_headers_blob.c_str());
*headerSize = req.res.all_headers_blob.size();
}
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpGetResponseContentLength(int reqId, int* result, u64* contentLength) {
LOG_INFO(Lib_Http, "called reqId={}, result={}, contentLength={}", reqId, fmt::ptr(result),
fmt::ptr(contentLength));
std::unique_lock<std::mutex> lock(g_state.m_mutex);
if (!g_state.inited) {
LOG_ERROR(Lib_Http, "Not initialized");
return ORBIS_HTTP_ERROR_BEFORE_INIT;
}
if (!result || !contentLength) {
LOG_ERROR(Lib_Http, "result or contentLength output pointer is null");
return ORBIS_HTTP_ERROR_INVALID_VALUE;
}
auto it = g_state.requests.find(reqId);
if (it == g_state.requests.end()) {
LOG_ERROR(Lib_Http, "Invalid reqId={}", reqId);
return ORBIS_HTTP_ERROR_INVALID_ID;
}
auto& req = *it->second;
int wr = WaitForResponseReady(req, lock);
if (wr == ORBIS_HTTP_ERROR_EAGAIN) {
LOG_DEBUG(Lib_Http, "reqId={}: response not yet ready, returning BEFORE_SEND", reqId);
return ORBIS_HTTP_ERROR_BEFORE_SEND;
}
if (wr != ORBIS_OK) {
LOG_ERROR(Lib_Http, "Wait failed for reqId={}: {:#x}", reqId, static_cast<u32>(wr));
return wr;
}
*result = req.res.content_length_result;
*contentLength = req.res.content_length;
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpGetStatusCode(int reqId, int* statusCode) {
LOG_INFO(Lib_Http, "called reqId={}, statusCode={}", reqId, fmt::ptr(statusCode));
std::unique_lock<std::mutex> lock(g_state.m_mutex);
if (!g_state.inited) {
LOG_ERROR(Lib_Http, "Not initialized");
return ORBIS_HTTP_ERROR_BEFORE_INIT;
}
if (!statusCode) {
LOG_ERROR(Lib_Http, "statusCode output pointer is null");
return ORBIS_HTTP_ERROR_INVALID_VALUE;
}
auto it = g_state.requests.find(reqId);
if (it == g_state.requests.end()) {
LOG_ERROR(Lib_Http, "Invalid reqId={}", reqId);
return ORBIS_HTTP_ERROR_INVALID_ID;
}
auto& req = *it->second;
int wr = WaitForResponseReady(req, lock);
if (wr == ORBIS_HTTP_ERROR_EAGAIN) {
LOG_DEBUG(Lib_Http, "reqId={}: response not yet ready, returning BEFORE_SEND", reqId);
return ORBIS_HTTP_ERROR_BEFORE_SEND;
}
if (wr != ORBIS_OK) {
LOG_ERROR(Lib_Http, "Wait failed for reqId={}: {:#x}", reqId, static_cast<u32>(wr));
return wr;
}
// Transport failure: no status line was ever received from a server.
if (req.res.status_code == 0 && req.last_errno != 0) {
LOG_INFO(Lib_Http, "reqId={} transport failure, errno={:#x}, returning BEFORE_SEND", reqId,
static_cast<u32>(req.last_errno));
return ORBIS_HTTP_ERROR_BEFORE_SEND;
}
*statusCode = req.res.status_code;
LOG_INFO(Lib_Http, "reqId={} status={}", reqId, req.res.status_code);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpSetInflateGZIPEnabled(int id, int isEnable) { int PS4_SYSV_ABI sceHttpSetInflateGZIPEnabled(int id, int isEnable) {
LOG_INFO(Lib_Http, "called id={}, isEnable={}", id, isEnable); LOG_INFO(Lib_Http, "called id={}, isEnable={}", id, isEnable);
std::lock_guard<std::mutex> lock(g_state.m_mutex); std::lock_guard<std::mutex> lock(g_state.m_mutex);
@ -1216,9 +1401,63 @@ int PS4_SYSV_ABI sceHttpSetRecvTimeOut(int id, u32 usec) {
return ORBIS_OK; return ORBIS_OK;
} }
//***********************************
// Connection functions
//***********************************
int PS4_SYSV_ABI sceHttpDeleteConnection(int connId) {
LOG_INFO(Lib_Http, "called connId={}", connId);
std::lock_guard<std::mutex> lock(g_state.m_mutex);
if (!g_state.inited) {
LOG_ERROR(Lib_Http, "Not initialized");
return ORBIS_HTTP_ERROR_BEFORE_INIT;
}
if (g_state.connections.erase(connId) == 0) {
LOG_ERROR(Lib_Http, "Invalid connId={}", connId);
return ORBIS_HTTP_ERROR_INVALID_ID;
}
return ORBIS_OK;
}
//***********************************
// Template functions
//***********************************
int PS4_SYSV_ABI sceHttpDeleteTemplate(int tmplId) {
LOG_INFO(Lib_Http, "called tmplId={}", tmplId);
std::lock_guard<std::mutex> lock(g_state.m_mutex);
if (!g_state.inited) {
LOG_ERROR(Lib_Http, "Not initialized");
return ORBIS_HTTP_ERROR_BEFORE_INIT;
}
if (g_state.templates.erase(tmplId) == 0) {
LOG_ERROR(Lib_Http, "Invalid tmplId={}", tmplId);
return ORBIS_HTTP_ERROR_INVALID_ID;
}
return ORBIS_OK;
}
//*********************************** //***********************************
// Request functions // Request functions
//*********************************** //***********************************
int PS4_SYSV_ABI sceHttpDeleteRequest(int reqId) {
LOG_INFO(Lib_Http, "called reqId={}", reqId);
std::lock_guard<std::mutex> lock(g_state.m_mutex);
if (!g_state.inited) {
LOG_ERROR(Lib_Http, "Not initialized");
return ORBIS_HTTP_ERROR_BEFORE_INIT;
}
auto it = g_state.requests.find(reqId);
if (it == g_state.requests.end()) {
LOG_ERROR(Lib_Http, "Invalid reqId={}", reqId);
return ORBIS_HTTP_ERROR_INVALID_ID;
}
auto req_ptr = it->second;
req_ptr->deleted = true;
req_ptr->state = HttpRequestState::Aborted;
req_ptr->cv.notify_all();
g_state.requests.erase(it);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpCreateRequest2(int connId, const char* method, const char* path, int PS4_SYSV_ABI sceHttpCreateRequest2(int connId, const char* method, const char* path,
u64 contentLength) { u64 contentLength) {
LOG_INFO(Lib_Http, "called connId={}, method={}, path={}, contentLength={}", connId, LOG_INFO(Lib_Http, "called connId={}, method={}, path={}, contentLength={}", connId,
@ -1241,42 +1480,47 @@ int PS4_SYSV_ABI sceHttpCreateRequest2(int connId, const char* method, const cha
return ORBIS_HTTP_METHOD_TRACE; return ORBIS_HTTP_METHOD_TRACE;
return -1; return -1;
}; };
// Resolve the connection's URL under the lock, then delegate. // Resolve the connection's URL under the lock, then drop the lock before
// delegating to sceHttpCreateRequestWithURL
std::string url; std::string url;
std::lock_guard<std::mutex> lock(g_state.m_mutex); int int_method;
if (!g_state.inited) { {
LOG_ERROR(Lib_Http, "Not initialized"); std::lock_guard<std::mutex> lock(g_state.m_mutex);
return ORBIS_HTTP_ERROR_BEFORE_INIT; if (!g_state.inited) {
} LOG_ERROR(Lib_Http, "Not initialized");
auto it = g_state.connections.find(connId); return ORBIS_HTTP_ERROR_BEFORE_INIT;
if (it == g_state.connections.end()) { }
LOG_ERROR(Lib_Http, "Invalid connId={}", connId); auto it = g_state.connections.find(connId);
return ORBIS_HTTP_ERROR_INVALID_ID; if (it == g_state.connections.end()) {
} LOG_ERROR(Lib_Http, "Invalid connId={}", connId);
if (!path) { return ORBIS_HTTP_ERROR_INVALID_ID;
LOG_ERROR(Lib_Http, "path is null"); }
return ORBIS_HTTP_ERROR_INVALID_VALUE; if (!path) {
} LOG_ERROR(Lib_Http, "path is null");
if (ContainsCrLf(path)) {
LOG_ERROR(Lib_Http, "path contains CR/LF (CRLF-injection rejected): {}", path);
return ORBIS_HTTP_ERROR_INVALID_VALUE;
}
int int_method = map_method(method);
if (int_method < 0) {
if (!method) {
LOG_ERROR(Lib_Http, "method is null");
return ORBIS_HTTP_ERROR_INVALID_VALUE; return ORBIS_HTTP_ERROR_INVALID_VALUE;
} }
LOG_INFO(Lib_Http, "method '{}' not in standard table; routing via CUSTOM slot", method); if (ContainsCrLf(path)) {
int_method = ORBIS_HTTP_METHOD_CUSTOM; LOG_ERROR(Lib_Http, "path contains CR/LF (CRLF-injection rejected): {}", path);
} return ORBIS_HTTP_ERROR_INVALID_VALUE;
const auto& conn = it->second; }
url = conn.scheme + "://" + conn.hostname + ":" + std::to_string(conn.port); int_method = map_method(method);
if (path[0] != '\0') { if (int_method < 0) {
if (path[0] != '/') { if (!method) {
url.push_back('/'); LOG_ERROR(Lib_Http, "method is null");
return ORBIS_HTTP_ERROR_INVALID_VALUE;
}
LOG_INFO(Lib_Http, "method '{}' not in standard table; routing via CUSTOM slot",
method);
int_method = ORBIS_HTTP_METHOD_CUSTOM;
}
const auto& conn = it->second;
url = conn.scheme + "://" + conn.hostname + ":" + std::to_string(conn.port);
if (path[0] != '\0') {
if (path[0] != '/') {
url.push_back('/');
}
url.append(path);
} }
url.append(path);
} }
int reqId = sceHttpCreateRequestWithURL(connId, int_method, url.c_str(), contentLength); int reqId = sceHttpCreateRequestWithURL(connId, int_method, url.c_str(), contentLength);
if (reqId > 0 && method) { if (reqId > 0 && method) {

View File

@ -129,8 +129,6 @@ using OrbisHttpsCallback = int(PS4_SYSV_ABI*)(int libsslCtxId, u32 verifyErr, vo
using OrbisHttpsCaList = Libraries::Ssl::OrbisSslCaList; using OrbisHttpsCaList = Libraries::Ssl::OrbisSslCaList;
// Functions // Functions
int PS4_SYSV_ABI sceHttpAbortRequest(int reqId);
int PS4_SYSV_ABI sceHttpAbortRequestForce(int reqId);
int PS4_SYSV_ABI sceHttpAbortWaitRequest(OrbisHttpEpollHandle eh); int PS4_SYSV_ABI sceHttpAbortWaitRequest(OrbisHttpEpollHandle eh);
int PS4_SYSV_ABI sceHttpAddCookie(int libhttpCtxId, const char* url, const char* cookie, int PS4_SYSV_ABI sceHttpAddCookie(int libhttpCtxId, const char* url, const char* cookie,
u64 cookieLength); u64 cookieLength);
@ -162,12 +160,8 @@ int PS4_SYSV_ABI sceHttpDbgShowConnectionStat();
int PS4_SYSV_ABI sceHttpDbgShowMemoryPoolStat(); int PS4_SYSV_ABI sceHttpDbgShowMemoryPoolStat();
int PS4_SYSV_ABI sceHttpDbgShowRequestStat(); int PS4_SYSV_ABI sceHttpDbgShowRequestStat();
int PS4_SYSV_ABI sceHttpDbgShowStat(); int PS4_SYSV_ABI sceHttpDbgShowStat();
int PS4_SYSV_ABI sceHttpDeleteConnection(int connId);
int PS4_SYSV_ABI sceHttpDeleteRequest(int reqId);
int PS4_SYSV_ABI sceHttpDeleteTemplate(int tmplId);
int PS4_SYSV_ABI sceHttpDestroyEpoll(int libhttpCtxId, OrbisHttpEpollHandle eh); int PS4_SYSV_ABI sceHttpDestroyEpoll(int libhttpCtxId, OrbisHttpEpollHandle eh);
int PS4_SYSV_ABI sceHttpGetAcceptEncodingGZIPEnabled(int id, int* isEnable); int PS4_SYSV_ABI sceHttpGetAcceptEncodingGZIPEnabled(int id, int* isEnable);
int PS4_SYSV_ABI sceHttpGetAllResponseHeaders(int reqId, char** header, u64* headerSize);
int PS4_SYSV_ABI sceHttpGetAuthEnabled(int id, int* isEnable); int PS4_SYSV_ABI sceHttpGetAuthEnabled(int id, int* isEnable);
int PS4_SYSV_ABI sceHttpGetConnectionStat(); int PS4_SYSV_ABI sceHttpGetConnectionStat();
int PS4_SYSV_ABI sceHttpGetCookie(int libhttpCtxId, const char* url, char* cookie, u64* required, int PS4_SYSV_ABI sceHttpGetCookie(int libhttpCtxId, const char* url, char* cookie, u64* required,
@ -177,12 +171,8 @@ int PS4_SYSV_ABI sceHttpGetCookieStats(int libhttpCtxId, OrbisHttpCookieStats* s
int PS4_SYSV_ABI sceHttpGetEpoll(int id, OrbisHttpEpollHandle* eh, void** userArg); int PS4_SYSV_ABI sceHttpGetEpoll(int id, OrbisHttpEpollHandle* eh, void** userArg);
int PS4_SYSV_ABI sceHttpGetEpollId(); int PS4_SYSV_ABI sceHttpGetEpollId();
int PS4_SYSV_ABI sceHttpGetMemoryPoolStats(int libhttpCtxId, OrbisHttpMemoryPoolStats* currentStat); int PS4_SYSV_ABI sceHttpGetMemoryPoolStats(int libhttpCtxId, OrbisHttpMemoryPoolStats* currentStat);
int PS4_SYSV_ABI sceHttpGetNonblock(int id, int* isEnable);
int PS4_SYSV_ABI sceHttpGetRegisteredCtxIds(); int PS4_SYSV_ABI sceHttpGetRegisteredCtxIds();
int PS4_SYSV_ABI sceHttpGetResponseContentLength(int reqId, int* result, u64* contentLength);
int PS4_SYSV_ABI sceHttpGetStatusCode(int reqId, int* statusCode);
int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, u64 poolSize); int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, u64 poolSize);
int PS4_SYSV_ABI sceHttpReadData(s32 reqId, void* data, u64 size);
int PS4_SYSV_ABI sceHttpRedirectCacheFlush(int libhttpCtxId); int PS4_SYSV_ABI sceHttpRedirectCacheFlush(int libhttpCtxId);
int PS4_SYSV_ABI sceHttpRemoveRequestHeader(int id, const char* name); int PS4_SYSV_ABI sceHttpRemoveRequestHeader(int id, const char* name);
int PS4_SYSV_ABI sceHttpRequestGetAllHeaders(); int PS4_SYSV_ABI sceHttpRequestGetAllHeaders();
@ -206,7 +196,6 @@ int PS4_SYSV_ABI sceHttpSetDelayBuildRequestEnabled(int id, int isEnable);
int PS4_SYSV_ABI sceHttpSetEpoll(int id, OrbisHttpEpollHandle eh, void* userArg); int PS4_SYSV_ABI sceHttpSetEpoll(int id, OrbisHttpEpollHandle eh, void* userArg);
int PS4_SYSV_ABI sceHttpSetEpollId(); int PS4_SYSV_ABI sceHttpSetEpollId();
int PS4_SYSV_ABI sceHttpSetHttp09Enabled(int id, int isEnable); int PS4_SYSV_ABI sceHttpSetHttp09Enabled(int id, int isEnable);
int PS4_SYSV_ABI sceHttpSetNonblock(int id, int isEnable);
int PS4_SYSV_ABI sceHttpSetPolicyOption(); int PS4_SYSV_ABI sceHttpSetPolicyOption();
int PS4_SYSV_ABI sceHttpSetPriorityOption(); int PS4_SYSV_ABI sceHttpSetPriorityOption();
int PS4_SYSV_ABI sceHttpSetProxy(); int PS4_SYSV_ABI sceHttpSetProxy();
@ -229,13 +218,23 @@ int PS4_SYSV_ABI sceHttpsSetSslCallback(int id, OrbisHttpsCallback cbfunc, void*
int PS4_SYSV_ABI sceHttpsSetSslVersion(int id, int version); int PS4_SYSV_ABI sceHttpsSetSslVersion(int id, int version);
int PS4_SYSV_ABI sceHttpsUnloadCert(int libhttpCtxId); int PS4_SYSV_ABI sceHttpsUnloadCert(int libhttpCtxId);
int PS4_SYSV_ABI sceHttpTerm(int libhttpCtxId); int PS4_SYSV_ABI sceHttpTerm(int libhttpCtxId);
int PS4_SYSV_ABI sceHttpTryGetNonblock(int id, int* isEnable);
int PS4_SYSV_ABI sceHttpTrySetNonblock(int id, int isEnable);
int PS4_SYSV_ABI sceHttpUnsetEpoll(int id); int PS4_SYSV_ABI sceHttpUnsetEpoll(int id);
int PS4_SYSV_ABI sceHttpWaitRequest(OrbisHttpEpollHandle eh, OrbisHttpNBEvent* nbev, int maxevents, int PS4_SYSV_ABI sceHttpWaitRequest(OrbisHttpEpollHandle eh, OrbisHttpNBEvent* nbev, int maxevents,
int timeout); int timeout);
int PS4_SYSV_ABI sceHttpUriCopy(); int PS4_SYSV_ABI sceHttpUriCopy();
//***********************************
// Non-blocking processing functions
//***********************************
int PS4_SYSV_ABI sceHttpGetNonblock(int id, int* isEnable);
int PS4_SYSV_ABI sceHttpSetNonblock(int id, int isEnable);
int PS4_SYSV_ABI sceHttpTryGetNonblock(int id, int* isEnable);
int PS4_SYSV_ABI sceHttpTrySetNonblock(int id, int isEnable);
//***********************************
// Http Communication functions
//***********************************
int PS4_SYSV_ABI sceHttpReadData(s32 reqId, void* data, u64 size);
int PS4_SYSV_ABI sceHttpAbortRequest(int reqId);
int PS4_SYSV_ABI sceHttpAbortRequestForce(int reqId);
//*********************************** //***********************************
// Https Option setting functions // Https Option setting functions
//*********************************** //***********************************
@ -246,6 +245,9 @@ int PS4_SYSV_ABI sceHttpsEnableOptionPrivate(int id, u32 sslFlags);
//*********************************** //***********************************
// Response Information functions // Response Information functions
//*********************************** //***********************************
int PS4_SYSV_ABI sceHttpGetAllResponseHeaders(int reqId, char** header, u64* headerSize);
int PS4_SYSV_ABI sceHttpGetResponseContentLength(int reqId, int* result, u64* contentLength);
int PS4_SYSV_ABI sceHttpGetStatusCode(int reqId, int* statusCode);
int PS4_SYSV_ABI sceHttpSetInflateGZIPEnabled(int id, int isEnable); int PS4_SYSV_ABI sceHttpSetInflateGZIPEnabled(int id, int isEnable);
//*********************************** //***********************************
// Http Header setting functions // Http Header setting functions
@ -263,8 +265,17 @@ int PS4_SYSV_ABI sceHttpSetConnectTimeOut(int id, u32 usec);
int PS4_SYSV_ABI sceHttpSetSendTimeOut(int id, u32 usec); int PS4_SYSV_ABI sceHttpSetSendTimeOut(int id, u32 usec);
int PS4_SYSV_ABI sceHttpSetRecvTimeOut(int id, u32 usec); int PS4_SYSV_ABI sceHttpSetRecvTimeOut(int id, u32 usec);
//*********************************** //***********************************
// Connection functions
//***********************************
int PS4_SYSV_ABI sceHttpDeleteConnection(int connId);
//***********************************
// Template functions
//***********************************
int PS4_SYSV_ABI sceHttpDeleteTemplate(int tmplId);
//***********************************
// Request functions // Request functions
//*********************************** //***********************************
int PS4_SYSV_ABI sceHttpDeleteRequest(int reqId);
int PS4_SYSV_ABI sceHttpCreateRequest2(int connId, const char* method, const char* path, int PS4_SYSV_ABI sceHttpCreateRequest2(int connId, const char* method, const char* path,
u64 contentLength); u64 contentLength);
int PS4_SYSV_ABI sceHttpCreateRequestWithURL2(int connId, const char* method, const char* url, int PS4_SYSV_ABI sceHttpCreateRequestWithURL2(int connId, const char* method, const char* url,