Files
AnyPS5/core/libs/tests/GuestHttp2.cpp
T
Obi Newman 881fb01069 feat(libs): report Http2, Ssl and Net memory pool statistics (#1119)
sceHttp2GetMemoryPoolStats, sceSslGetMemoryPoolStats and
sceNetGetMemoryPoolStats threw. PPSA16064 (Minecraft Dungeons II,
Unreal Engine) queries all three while its HTTP module starts, and
aborted there.

Each library now records the pool size given at sceHttp2Init,
sceSslInit or sceNetPoolCreate and reports it with a peak and current
use of 0: the offline Http2 and Ssl models and the host-socket Net
library allocate nothing from these pools. The layouts follow the PS4
SDK's *MemoryPoolStats structures. Net returns EBADF for an unknown
pool and EINVAL for a null output, like its other pool calls; Http2 and
Ssl throw for both, as their error codes are unknown. Terminating a
context or destroying a pool forgets its size.

sceHttp2SetResolveRetry, which the title calls next, accepts and
ignores its value like the other Http2 settings. The four Http2
WebSocket exports the title imports are added as unimplemented.

guest_http2, guest_ssl and guest_sce_net check the reported sizes;
guest_sce_net also checks the null-output and unknown-pool errors.
2026-10-09 21:03:06 +13:00

100 lines
4.3 KiB
C++

#include "SceTypes.hpp"
#include <cstdint>
#include <cstdlib>
#include <stdexcept>
extern "C" {
int APS5_VABI sceHttp2Init(int, int, std::size_t, int);
int APS5_VABI sceHttp2CreateTemplate(int, const char*, int, int);
int APS5_VABI sceHttp2CreateRequestWithURL(int, const char*, const char*, std::uint64_t);
int APS5_VABI sceHttp2CreateCookieBox(int);
int APS5_VABI sceHttp2SetCookieBox(int, int);
int APS5_VABI sceHttp2CookieFlush(int);
int APS5_VABI sceHttp2SetRequestNoContentLength(int);
int APS5_VABI sceHttp2SendRequest(int, const void*, std::size_t);
int APS5_VABI sceHttp2SendRequestAsync(int, const void*, std::size_t, Http2AsyncOption*, void*);
int APS5_VABI sceHttp2WaitAsync(int, Http2AsyncResult*, std::uint32_t*, void*);
int APS5_VABI sceHttp2DeleteRequest(int);
int APS5_VABI sceHttp2Term(int);
int APS5_VABI sceKernelCreateEqueue(KernelEqueue* eq, const char* name);
int APS5_VABI sceKernelDeleteEqueue(KernelEqueue eq);
int APS5_VABI sceKernelWaitEqueue(KernelEqueue eq, KernelEvent* ev, int num, int* out, const KernelUseconds* timo);
int APS5_VABI sceKernelAddUserEventEdge(KernelEqueue eq, int id);
int APS5_VABI sceKernelDeleteUserEvent(KernelEqueue eq, int id);
uintptr_t APS5_VABI sceKernelGetEventId(const KernelEvent* ev);
void* APS5_VABI sceKernelGetEventUserData(const KernelEvent* ev);
}
struct Http2MemoryPoolStats {
std::size_t pool_size;
std::size_t max_inuse_size;
std::size_t current_inuse_size;
std::int32_t reserved;
};
extern "C" {
int APS5_VABI sceHttp2GetMemoryPoolStats(int, Http2MemoryPoolStats*);
int APS5_VABI sceHttp2SetResolveRetry(int, std::int32_t);
}
static void Require(bool value) { if (!value) std::abort(); }
template <typename TCall>
static bool Throws(TCall call) {
try {
call();
} catch (const std::exception&) {
return true;
}
return false;
}
int main() {
const int context = sceHttp2Init(1, 1, 0x10000, 4);
Require(context > 0);
Http2MemoryPoolStats stats{1, 1, 1, 1};
Require(sceHttp2GetMemoryPoolStats(context, &stats) == 0);
Require(stats.pool_size == 0x10000 && stats.max_inuse_size == 0 && stats.current_inuse_size == 0 && stats.reserved == 0);
const int box = sceHttp2CreateCookieBox(context);
const int other = sceHttp2CreateCookieBox(context);
Require(box > 0 && other > 0 && box != other && box != context);
const int tmpl = sceHttp2CreateTemplate(context, "agent", 2, 0);
Require(tmpl > 0 && tmpl != box && tmpl != other);
Require(sceHttp2SetResolveRetry(tmpl, 3) == 0);
Require(sceHttp2SetCookieBox(tmpl, box) == 0);
Require(sceHttp2SetCookieBox(tmpl, 0) == 0);
const int request = sceHttp2CreateRequestWithURL(tmpl, "POST", "https://example.com/", 16);
Require(request > 0);
Require(sceHttp2SetCookieBox(request, other) == 0);
Require(sceHttp2SetRequestNoContentLength(request) == 0);
Require(sceHttp2CookieFlush(context) == 0);
Require(sceHttp2SendRequest(request, nullptr, 0) == static_cast<int>(0x80436063));
KernelEqueue eq = 0;
Require(sceKernelCreateEqueue(&eq, "http2") == 0);
Require(sceKernelAddUserEventEdge(eq, request) == 0);
int tag = 0;
Http2AsyncOption option{};
option.equeue = eq;
option.user_event_id = request;
option.user_data = &tag;
Require(sceHttp2SendRequestAsync(request, nullptr, 0, &option, nullptr) == 0);
KernelEvent event{};
int count = 0;
const KernelUseconds timeout = 1000000;
Require(sceKernelWaitEqueue(eq, &event, 1, &count, &timeout) == 0 && count == 1);
Require(sceKernelGetEventId(&event) == static_cast<uintptr_t>(request));
Require(sceKernelGetEventUserData(&event) == &tag);
Http2AsyncResult result{};
Require(sceHttp2WaitAsync(request, &result, nullptr, nullptr) == 0);
Require(result.req_id == request && result.result == static_cast<int>(0x80436063));
Require(Throws([&] { sceHttp2WaitAsync(request, &result, nullptr, nullptr); }));
Require(Throws([&] { sceHttp2SendRequestAsync(request, nullptr, 0, nullptr, nullptr); }));
Require(sceKernelDeleteUserEvent(eq, request) == 0);
Require(Throws([&] { sceHttp2SendRequestAsync(request, nullptr, 0, &option, nullptr); }));
Require(Throws([&] { sceHttp2WaitAsync(request, &result, nullptr, nullptr); }));
Require(sceHttp2DeleteRequest(request) == 0);
Require(sceKernelDeleteEqueue(eq) == 0);
Require(sceHttp2Term(context) == 0);
}