sceNpWebApi2AddHttpRequestHeader

This commit is contained in:
Stephen Miller 2026-06-28 13:36:13 -05:00
parent 8f04464ada
commit 710aa909c3
5 changed files with 190 additions and 24 deletions

View File

@ -17,9 +17,15 @@ s32 PS4_SYSV_ABI sceNpWebApi2AbortRequest(s64 request_id) {
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpWebApi2AddHttpRequestHeader() {
LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called");
return ORBIS_OK;
s32 PS4_SYSV_ABI sceNpWebApi2AddHttpRequestHeader(s64 request_id, const char* field_name,
const char* field_value) {
if (!field_name || !field_value) {
LOG_ERROR(Lib_NpWebApi2, "Invalid parameters");
return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT;
}
LOG_INFO(Lib_NpWebApi2, "called, request_id = {:#x}, field_name = {}, field_value = {}",
request_id, field_name, field_value);
return addHttpRequestHeader(request_id, field_name, field_value);
}
s32 PS4_SYSV_ABI sceNpWebApi2AddMultipartPart() {
@ -134,7 +140,8 @@ s32 PS4_SYSV_ABI sceNpWebApi2Initialize(s32 lib_http_ctx_id, u64 pool_size) {
LOG_DEBUG(Lib_NpWebApi2, "called, lib_http_ctx_id = {:#x}, pool_size = {:#x}", lib_http_ctx_id,
pool_size);
s32 ctx_id = createLibraryContext(lib_http_ctx_id, pool_size, nullptr);
// Uses a sceLncUtilGetAppStatus check to enable debug mode. For now, default to normal.
s32 ctx_id = createLibraryContext(lib_http_ctx_id, 1, pool_size, nullptr);
if (ctx_id > 0) {
LOG_INFO(Lib_NpWebApi2, "created lib_ctx_id = {:#x}", ctx_id);
}
@ -145,7 +152,7 @@ s32 PS4_SYSV_ABI sceNpWebApi2InitializeForPresence(s32 lib_http_ctx_id, u64 pool
LOG_DEBUG(Lib_NpWebApi2, "called, lib_http_ctx_id = {:#x}, pool_size = {:#x}", lib_http_ctx_id,
pool_size);
s32 ctx_id = createLibraryContext(lib_http_ctx_id, pool_size, nullptr);
s32 ctx_id = createLibraryContext(lib_http_ctx_id, 3, pool_size, nullptr);
if (ctx_id > 0) {
LOG_INFO(Lib_NpWebApi2, "created lib_ctx_id = {:#x}", ctx_id);
}
@ -165,7 +172,7 @@ s32 PS4_SYSV_ABI sceNpWebApi2IntInitialize(const OrbisNpWebApi2IntInitializeArgs
LOG_DEBUG(Lib_NpWebApi2, "called, lib_http_ctx_id = {:#x}, pool_size = {:#x}, name = {}",
args->lib_http_ctx_id, args->pool_size, args->name ? args->name : "(null)");
s32 ctx_id = createLibraryContext(args->lib_http_ctx_id, args->pool_size, args->name);
s32 ctx_id = createLibraryContext(args->lib_http_ctx_id, 2, args->pool_size, args->name);
if (ctx_id > 0) {
LOG_INFO(Lib_NpWebApi2, "created lib_ctx_id = {:#x}", ctx_id);
}
@ -184,7 +191,7 @@ s32 PS4_SYSV_ABI sceNpWebApi2IntInitialize2(const OrbisNpWebApi2IntInitialize2Ar
args->lib_http_ctx_id, args->pool_size, args->name ? args->name : "(null)",
args->push_config_group);
s32 ctx_id = createLibraryContext(args->lib_http_ctx_id, args->pool_size, args->name);
s32 ctx_id = createLibraryContext(args->lib_http_ctx_id, 2, args->pool_size, args->name);
if (ctx_id > 0) {
LOG_INFO(Lib_NpWebApi2, "created lib_ctx_id = {:#x}", ctx_id);
}

View File

@ -73,15 +73,16 @@ s32 UserContext::Initialize() {
const char* name = this->parent_ctx->GetName();
char sw_version[8];
memset(sw_version, 0, sizeof(sw_version));
std::memset(sw_version, 0, sizeof(sw_version));
Libraries::Np::NpCommon::sceNpGetSdkVersion(sw_version);
char user_agent_buf[0x40];
memset(user_agent_buf, 0, sizeof(user_agent_buf));
std::memset(user_agent_buf, 0, sizeof(user_agent_buf));
if (name) {
snprintf(user_agent_buf, sizeof(user_agent_buf), "NpWebApi2/%s (%s)", sw_version, name);
std::snprintf(user_agent_buf, sizeof(user_agent_buf), "NpWebApi2/%s (%s)", sw_version,
name);
} else {
snprintf(user_agent_buf, sizeof(user_agent_buf), "NpWebApi2/%s", sw_version);
std::snprintf(user_agent_buf, sizeof(user_agent_buf), "NpWebApi2/%s", sw_version);
}
this->user_agent = std::string{user_agent_buf};
@ -131,4 +132,64 @@ s32 UserContext::CreateRequest(const char* api_group, const char* path, const ch
return ORBIS_OK;
}
Request* UserContext::GetRequest(s64 request_id) {
this->Lock();
if (!this->requests.contains(request_id)) {
return nullptr;
}
Request* request = this->requests[request_id];
request->AddUser();
this->Unlock();
return request;
}
bool IsInternalHeader(const char* header_name) {
std::string lower_name{header_name};
std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(),
[](char c) { return std::tolower(c); });
// These headers are all skipped if the request's library context
// was not created through the IntInitialize functions.
return lower_name.compare("content-type") == 0 || lower_name.compare("user-agent") == 0 ||
lower_name.compare("authorization") == 0 ||
lower_name.compare("x-psn-np-title-id") == 0 ||
lower_name.compare("x-psn-np-title-token") == 0 ||
lower_name.compare("x-psn-request-id") == 0 ||
lower_name.compare("x-psn-sdk-ver") == 0 ||
lower_name.compare("x-psn-debug-settings") == 0 || lower_name.compare("npdebug") == 0 ||
lower_name.compare("x-psn-trc-check-notification-enabled") == 0 ||
lower_name.compare("x-psn-webtrace-enabled") == 0 ||
lower_name.compare("x-psn-npwebapi2-context") == 0 ||
lower_name.compare("x-psn-fake-rate-limit-enabled") == 0 ||
lower_name.compare("x-psn-fake-rate-limit-targets") == 0;
}
s32 Request::AddHttpRequestHeader(const char* field_name, const char* field_value) {
std::string name{field_name};
std::string value{field_value};
if (!this->parent_ctx->IsInternal() && IsInternalHeader(field_name)) {
LOG_WARNING(Lib_NpWebApi2, "This is not an internal context, skipping field name {}",
field_name);
return ORBIS_OK;
}
this->Lock();
HttpRequestHeader* new_header = new HttpRequestHeader(nullptr, nullptr, name, value);
if (!this->http_headers) {
// Creates a new blank head node
this->http_headers = new HttpRequestHeader(nullptr, nullptr, "", "");
this->http_headers->prev = this->http_headers;
this->http_headers->next = this->http_headers;
}
new_header->prev = this->http_headers->prev;
new_header->next = this->http_headers;
this->http_headers->prev->next = new_header;
this->http_headers->prev = new_header;
http_header_count++;
this->Unlock();
return ORBIS_OK;
}
}; // namespace Libraries::Np::NpWebApi2

View File

@ -18,10 +18,12 @@ class Request;
class LibraryContext {
public:
LibraryContext(s32 ctx_id, s32 http_ctx_id, u64 pool_size)
: id(ctx_id), http_ctx_id(http_ctx_id), pool_size(pool_size) {}
LibraryContext(s32 ctx_id, s32 http_ctx_id, u64 pool_size, const char* name)
: id(ctx_id), http_ctx_id(http_ctx_id), pool_size(pool_size), name(name) {}
LibraryContext(s32 ctx_id, s32 type, s32 http_ctx_id, u64 pool_size)
: id(ctx_id), type(static_cast<LibraryContextType>(type)), http_ctx_id(http_ctx_id),
pool_size(pool_size) {}
LibraryContext(s32 ctx_id, s32 type, s32 http_ctx_id, u64 pool_size, const char* name)
: id(ctx_id), type(static_cast<LibraryContextType>(type)), http_ctx_id(http_ctx_id),
pool_size(pool_size), name(name) {}
void Lock() {
lock.lock();
@ -57,6 +59,22 @@ public:
return name.empty() ? nullptr : name.data();
}
bool IsDebug() {
return type == LibraryContextType::Debug;
}
bool IsNormal() {
return type == LibraryContextType::Normal;
}
bool IsInternal() {
return type == LibraryContextType::Internal;
}
bool IsPresence() {
return type == LibraryContextType::Presence;
}
s32 CreateUserContext(Libraries::UserService::OrbisUserServiceUserId user_id);
UserContext* GetUserContext(s32 user_ctx_id);
UserContext* GetUserContextByUserId(Libraries::UserService::OrbisUserServiceUserId user_id);
@ -70,6 +88,12 @@ private:
s32 id{};
s32 http_ctx_id{};
s32 user_count{};
enum LibraryContextType : s32 {
Debug = 0,
Normal = 1,
Internal = 2,
Presence = 3,
} type;
u64 pool_size{};
std::recursive_mutex lock{};
std::string name{};
@ -93,15 +117,15 @@ public:
}
void AddUser() {
parent_ctx->Lock();
Lock();
user_count++;
parent_ctx->Unlock();
Unlock();
}
void RemoveUser() {
parent_ctx->Lock();
Lock();
user_count--;
parent_ctx->Unlock();
Unlock();
}
s32 GetId() {
@ -117,6 +141,7 @@ public:
s32 CreateRequest(const char* api_group, const char* path, const char* method,
const OrbisNpWebApi2ContentParameter* content_parameter, bool multipart,
Request** request);
Request* GetRequest(s64 request_id);
private:
s32 id{};
@ -140,11 +165,35 @@ public:
: parent_ctx(parent), id(request_id), api_group(api_group), path(path), method(method),
multipart_supported(multipart), content_type(content_parameter->content_type) {}
void Lock() {
parent_ctx->Lock();
}
void Unlock() {
parent_ctx->Unlock();
}
void AddUser() {
Lock();
user_count++;
Unlock();
}
void RemoveUser() {
Lock();
user_count--;
Unlock();
}
s64 GetId() {
return id;
}
s32 Initialize();
bool HasSent() {
return sent;
}
s32 AddHttpRequestHeader(const char* field_name, const char* field_value);
private:
s64 id{};
@ -156,6 +205,15 @@ private:
std::string method{};
std::string content_type{};
bool multipart_supported{};
bool sent{};
struct HttpRequestHeader {
HttpRequestHeader* prev;
HttpRequestHeader* next;
std::string field_name;
std::string field_value;
}* http_headers{};
s32 http_header_count{};
};
}; // namespace Libraries::Np::NpWebApi2

View File

@ -17,7 +17,7 @@ std::recursive_mutex g_mutex{};
s32 g_current_lib_context_id{};
std::map<s32, LibraryContext*> g_lib_contexts{};
s32 createLibraryContext(s32 http_ctx_id, u64 pool_size, const char* name) {
s32 createLibraryContext(s32 http_ctx_id, s32 type, u64 pool_size, const char* name) {
std::scoped_lock lk{g_mutex};
if (g_lib_contexts.size() >= 0x8000) {
@ -34,10 +34,10 @@ s32 createLibraryContext(s32 http_ctx_id, u64 pool_size, const char* name) {
if (!name) {
g_lib_contexts[g_current_lib_context_id] =
new LibraryContext(g_current_lib_context_id, http_ctx_id, pool_size);
new LibraryContext(g_current_lib_context_id, type, http_ctx_id, pool_size);
} else {
g_lib_contexts[g_current_lib_context_id] =
new LibraryContext(g_current_lib_context_id, http_ctx_id, pool_size, name);
new LibraryContext(g_current_lib_context_id, type, http_ctx_id, pool_size, name);
}
return g_current_lib_context_id;
}
@ -146,4 +146,43 @@ s32 createRequest(s32 user_ctx_id, const char* api_group, const char* path, cons
return result;
}
s32 addHttpRequestHeader(s64 request_id, const char* field_name, const char* field_value) {
s32 lib_ctx_id = static_cast<s32>(request_id >> 0x30);
s32 user_ctx_id = static_cast<s32>(request_id >> 0x20);
LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id);
if (!lib_ctx) {
LOG_ERROR(Lib_NpWebApi2, "No library context for request id {:#x}", request_id);
return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND;
}
UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id);
if (!user_ctx) {
LOG_ERROR(Lib_NpWebApi2, "No user context for request id {:#x}", request_id);
lib_ctx->RemoveUser();
return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND;
}
Request* request = user_ctx->GetRequest(request_id);
if (!request) {
LOG_ERROR(Lib_NpWebApi2, "No request with id {:#x}", request_id);
user_ctx->RemoveUser();
lib_ctx->RemoveUser();
return ORBIS_NP_WEBAPI2_ERROR_REQUEST_NOT_FOUND;
}
if (request->HasSent()) {
LOG_ERROR(Lib_NpWebApi2, "Request is already sent");
request->RemoveUser();
user_ctx->RemoveUser();
lib_ctx->RemoveUser();
return ORBIS_NP_WEBAPI2_ERROR_AFTER_SEND;
}
s32 result = request->AddHttpRequestHeader(field_name, field_value);
request->RemoveUser();
user_ctx->RemoveUser();
lib_ctx->RemoveUser();
return result;
}
}; // namespace Libraries::Np::NpWebApi2

View File

@ -5,10 +5,11 @@
namespace Libraries::Np::NpWebApi2 {
s32 createLibraryContext(s32 http_ctx_id, u64 pool_size, const char* name);
s32 createLibraryContext(s32 http_ctx_id, s32 type, u64 pool_size, const char* name);
s32 getMemoryPoolStats(s32 lib_ctx_id, OrbisNpWebApi2MemoryPoolStats* stats);
s32 createUserContext(s32 lib_ctx_id, Libraries::UserService::OrbisUserServiceUserId user_id);
s32 createRequest(s32 user_ctx_id, const char* api_group, const char* path, const char* method,
const OrbisNpWebApi2ContentParameter* content_parameter, bool multipart,
s64* request_id);
s32 addHttpRequestHeader(s64 request_id, const char* field_name, const char* field_value);
}; // namespace Libraries::Np::NpWebApi2