[Lib] libsceHttp (#2576)

* implemented sceHttpUriParse

* argg clang

* improved in case of file:////  (probably)

* rewrote httpuriparse to support file:////

* fixed uriparse
This commit is contained in:
georgemoralis 2025-03-02 23:09:38 +02:00 committed by GitHub
parent a583a9abe0
commit 951128389d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 352 additions and 10 deletions

View file

@ -298,6 +298,7 @@ set(KERNEL_LIB src/core/libraries/kernel/sync/mutex.cpp
set(NETWORK_LIBS src/core/libraries/network/http.cpp
src/core/libraries/network/http.h
src/core/libraries/network/http_error.h
src/core/libraries/network/http2.cpp
src/core/libraries/network/http2.h
src/core/libraries/network/net.cpp

View file

@ -5,6 +5,7 @@
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "core/libraries/network/http.h"
#include "http_error.h"
namespace Libraries::Http {
@ -566,17 +567,277 @@ int PS4_SYSV_ABI sceHttpUriMerge() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpUriParse() {
int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool,
size_t* require, size_t prepare) {
LOG_INFO(Lib_Http, "srcUri = {}", std::string(srcUri));
if (!srcUri) {
LOG_ERROR(Lib_Http, "invalid url");
return ORBIS_HTTP_ERROR_INVALID_URL;
}
if (!out && !pool && !require) {
LOG_ERROR(Lib_Http, "invalid values");
return ORBIS_HTTP_ERROR_INVALID_VALUE;
}
if (out && pool) {
memset(out, 0, sizeof(OrbisHttpUriElement));
out->scheme = (char*)pool;
}
// Track the total required buffer size
size_t requiredSize = 0;
// Parse the scheme (e.g., "http:", "https:", "file:")
size_t schemeLength = 0;
while (srcUri[schemeLength] && srcUri[schemeLength] != ':') {
if (!isalnum(srcUri[schemeLength])) {
LOG_ERROR(Lib_Http, "invalid url");
return ORBIS_HTTP_ERROR_INVALID_URL;
}
schemeLength++;
}
if (pool && prepare < schemeLength + 1) {
LOG_ERROR(Lib_Http, "out of memory");
return ORBIS_HTTP_ERROR_OUT_OF_MEMORY;
}
if (out && pool) {
memcpy(out->scheme, srcUri, schemeLength);
out->scheme[schemeLength] = '\0';
}
requiredSize += schemeLength + 1;
// Move past the scheme and ':' character
size_t offset = schemeLength + 1;
// Check if "//" appears after the scheme
if (strncmp(srcUri + offset, "//", 2) == 0) {
// "//" is present
if (out) {
out->opaque = false;
}
offset += 2; // Move past "//"
} else {
// "//" is not present
if (out) {
out->opaque = true;
}
}
// Handle "file" scheme
if (strncmp(srcUri, "file", 4) == 0) {
// File URIs typically start with "file://"
if (out && !out->opaque) {
// Skip additional slashes (e.g., "////")
while (srcUri[offset] == '/') {
offset++;
}
// Parse the path (everything after the slashes)
char* pathStart = (char*)srcUri + offset;
size_t pathLength = 0;
while (pathStart[pathLength] && pathStart[pathLength] != '?' &&
pathStart[pathLength] != '#') {
pathLength++;
}
// Ensure the path starts with '/'
if (pathLength > 0 && pathStart[0] != '/') {
// Prepend '/' to the path
requiredSize += pathLength + 2; // Include '/' and null terminator
if (pool && prepare < requiredSize) {
LOG_ERROR(Lib_Http, "out of memory");
return ORBIS_HTTP_ERROR_OUT_OF_MEMORY;
}
if (out && pool) {
out->path = (char*)pool + (requiredSize - pathLength - 2);
out->path[0] = '/'; // Add leading '/'
memcpy(out->path + 1, pathStart, pathLength);
out->path[pathLength + 1] = '\0';
}
} else {
// Path already starts with '/'
requiredSize += pathLength + 1;
if (pool && prepare < requiredSize) {
LOG_ERROR(Lib_Http, "out of memory");
return ORBIS_HTTP_ERROR_OUT_OF_MEMORY;
}
if (out && pool) {
memcpy((char*)pool + (requiredSize - pathLength - 1), pathStart, pathLength);
out->path = (char*)pool + (requiredSize - pathLength - 1);
out->path[pathLength] = '\0';
}
}
// Move past the path
offset += pathLength;
}
}
// Handle non-file schemes (e.g., "http", "https")
else {
// Parse the host and port
char* hostStart = (char*)srcUri + offset;
while (*hostStart == '/') {
hostStart++;
}
size_t hostLength = 0;
while (hostStart[hostLength] && hostStart[hostLength] != '/' &&
hostStart[hostLength] != '?' && hostStart[hostLength] != ':') {
hostLength++;
}
requiredSize += hostLength + 1;
if (pool && prepare < requiredSize) {
LOG_ERROR(Lib_Http, "out of memory");
return ORBIS_HTTP_ERROR_OUT_OF_MEMORY;
}
if (out && pool) {
memcpy((char*)pool + (requiredSize - hostLength - 1), hostStart, hostLength);
out->hostname = (char*)pool + (requiredSize - hostLength - 1);
out->hostname[hostLength] = '\0';
}
// Move past the host
offset += hostLength;
// Parse the port (if present)
if (hostStart[hostLength] == ':') {
char* portStart = hostStart + hostLength + 1;
size_t portLength = 0;
while (portStart[portLength] && isdigit(portStart[portLength])) {
portLength++;
}
requiredSize += portLength + 1;
if (pool && prepare < requiredSize) {
LOG_ERROR(Lib_Http, "out of memory");
return ORBIS_HTTP_ERROR_OUT_OF_MEMORY;
}
// Convert the port string to a uint16_t
char portStr[6]; // Max length for a port number (65535)
if (portLength > 5) {
LOG_ERROR(Lib_Http, "invalid url");
return ORBIS_HTTP_ERROR_INVALID_URL;
}
memcpy(portStr, portStart, portLength);
portStr[portLength] = '\0';
uint16_t port = (uint16_t)atoi(portStr);
if (port == 0 && portStr[0] != '0') {
LOG_ERROR(Lib_Http, "invalid url");
return ORBIS_HTTP_ERROR_INVALID_URL;
}
// Set the port in the output structure
if (out) {
out->port = port;
}
// Move past the port
offset += portLength + 1;
}
}
// Parse the path (if present)
if (srcUri[offset] == '/') {
char* pathStart = (char*)srcUri + offset;
size_t pathLength = 0;
while (pathStart[pathLength] && pathStart[pathLength] != '?' &&
pathStart[pathLength] != '#') {
pathLength++;
}
requiredSize += pathLength + 1;
if (pool && prepare < requiredSize) {
LOG_ERROR(Lib_Http, "out of memory");
return ORBIS_HTTP_ERROR_OUT_OF_MEMORY;
}
if (out && pool) {
memcpy((char*)pool + (requiredSize - pathLength - 1), pathStart, pathLength);
out->path = (char*)pool + (requiredSize - pathLength - 1);
out->path[pathLength] = '\0';
}
// Move past the path
offset += pathLength;
}
// Parse the query (if present)
if (srcUri[offset] == '?') {
char* queryStart = (char*)srcUri + offset + 1;
size_t queryLength = 0;
while (queryStart[queryLength] && queryStart[queryLength] != '#') {
queryLength++;
}
requiredSize += queryLength + 1;
if (pool && prepare < requiredSize) {
LOG_ERROR(Lib_Http, "out of memory");
return ORBIS_HTTP_ERROR_OUT_OF_MEMORY;
}
if (out && pool) {
memcpy((char*)pool + (requiredSize - queryLength - 1), queryStart, queryLength);
out->query = (char*)pool + (requiredSize - queryLength - 1);
out->query[queryLength] = '\0';
}
// Move past the query
offset += queryLength + 1;
}
// Parse the fragment (if present)
if (srcUri[offset] == '#') {
char* fragmentStart = (char*)srcUri + offset + 1;
size_t fragmentLength = 0;
while (fragmentStart[fragmentLength]) {
fragmentLength++;
}
requiredSize += fragmentLength + 1;
if (pool && prepare < requiredSize) {
LOG_ERROR(Lib_Http, "out of memory");
return ORBIS_HTTP_ERROR_OUT_OF_MEMORY;
}
if (out && pool) {
memcpy((char*)pool + (requiredSize - fragmentLength - 1), fragmentStart,
fragmentLength);
out->fragment = (char*)pool + (requiredSize - fragmentLength - 1);
out->fragment[fragmentLength] = '\0';
}
}
// Calculate the total required buffer size
if (require) {
*require = requiredSize; // Update with actual required size
}
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, size_t srcSize) {
LOG_ERROR(Lib_Http, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpUriSweepPath() {
LOG_ERROR(Lib_Http, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceHttpUriUnescape() {
int PS4_SYSV_ABI sceHttpUriUnescape(char* out, size_t* require, size_t prepare, const char* in) {
LOG_ERROR(Lib_Http, "(STUBBED) called");
return ORBIS_OK;
}

View file

@ -11,6 +11,19 @@ class SymbolsResolver;
namespace Libraries::Http {
struct OrbisHttpUriElement {
bool opaque;
char* scheme;
char* username;
char* password;
char* hostname;
char* path;
char* query;
char* fragment;
u16 port;
u8 reserved[10];
};
int PS4_SYSV_ABI sceHttpAbortRequest();
int PS4_SYSV_ABI sceHttpAbortRequestForce();
int PS4_SYSV_ABI sceHttpAbortWaitRequest();
@ -122,9 +135,10 @@ int PS4_SYSV_ABI sceHttpUriBuild();
int PS4_SYSV_ABI sceHttpUriCopy();
int PS4_SYSV_ABI sceHttpUriEscape();
int PS4_SYSV_ABI sceHttpUriMerge();
int PS4_SYSV_ABI sceHttpUriParse();
int PS4_SYSV_ABI sceHttpUriSweepPath();
int PS4_SYSV_ABI sceHttpUriUnescape();
int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool,
size_t* require, size_t prepare);
int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, size_t srcSize);
int PS4_SYSV_ABI sceHttpUriUnescape(char* out, size_t* require, size_t prepare, const char* in);
int PS4_SYSV_ABI sceHttpWaitRequest();
void RegisterlibSceHttp(Core::Loader::SymbolsResolver* sym);

View file

@ -0,0 +1,66 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/libraries/error_codes.h"
constexpr int ORBIS_HTTP_ERROR_BEFORE_INIT = 0x80431001;
constexpr int ORBIS_HTTP_ERROR_ALREADY_INITED = 0x80431020;
constexpr int ORBIS_HTTP_ERROR_BUSY = 0x80431021;
constexpr int ORBIS_HTTP_ERROR_OUT_OF_MEMORY = 0x80431022;
constexpr int ORBIS_HTTP_ERROR_NOT_FOUND = 0x80431025;
constexpr int ORBIS_HTTP_ERROR_INVALID_VERSION = 0x8043106a;
constexpr int ORBIS_HTTP_ERROR_INVALID_ID = 0x80431100;
constexpr int ORBIS_HTTP_ERROR_OUT_OF_SIZE = 0x80431104;
constexpr int ORBIS_HTTP_ERROR_INVALID_VALUE = 0x804311fe;
constexpr int ORBIS_HTTP_ERROR_INVALID_URL = 0x80433060;
constexpr int ORBIS_HTTP_ERROR_UNKNOWN_SCHEME = 0x80431061;
constexpr int ORBIS_HTTP_ERROR_NETWORK = 0x80431063;
constexpr int ORBIS_HTTP_ERROR_BAD_RESPONSE = 0x80431064;
constexpr int ORBIS_HTTP_ERROR_BEFORE_SEND = 0x80431065;
constexpr int ORBIS_HTTP_ERROR_AFTER_SEND = 0x80431066;
constexpr int ORBIS_HTTP_ERROR_TIMEOUT = 0x80431068;
constexpr int ORBIS_HTTP_ERROR_UNKNOWN_AUTH_TYPE = 0x80431069;
constexpr int ORBIS_HTTP_ERROR_UNKNOWN_METHOD = 0x8043106b;
constexpr int ORBIS_HTTP_ERROR_READ_BY_HEAD_METHOD = 0x8043106f;
constexpr int ORBIS_HTTP_ERROR_NOT_IN_COM = 0x80431070;
constexpr int ORBIS_HTTP_ERROR_NO_CONTENT_LENGTH = 0x80431071;
constexpr int ORBIS_HTTP_ERROR_CHUNK_ENC = 0x80431072;
constexpr int ORBIS_HTTP_ERROR_TOO_LARGE_RESPONSE_HEADER = 0x80431073;
constexpr int ORBIS_HTTP_ERROR_SSL = 0x80431075;
constexpr int ORBIS_HTTP_ERROR_INSUFFICIENT_STACKSIZE = 0x80431076;
constexpr int ORBIS_HTTP_ERROR_ABORTED = 0x80431080;
constexpr int ORBIS_HTTP_ERROR_UNKNOWN = 0x80431081;
constexpr int ORBIS_HTTP_ERROR_EAGAIN = 0x80431082;
constexpr int ORBIS_HTTP_ERROR_PROXY = 0x80431084;
constexpr int ORBIS_HTTP_ERROR_BROKEN = 0x80431085;
constexpr int ORBIS_HTTP_ERROR_PARSE_HTTP_NOT_FOUND = 0x80432025;
constexpr int ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE = 0x80432060;
constexpr int ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_VALUE = 0x804321fe;
constexpr int ORBIS_HTTP_ERROR_RESOLVER_EPACKET = 0x80436001;
constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENODNS = 0x80436002;
constexpr int ORBIS_HTTP_ERROR_RESOLVER_ETIMEDOUT = 0x80436003;
constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENOSUPPORT = 0x80436004;
constexpr int ORBIS_HTTP_ERROR_RESOLVER_EFORMAT = 0x80436005;
constexpr int ORBIS_HTTP_ERROR_RESOLVER_ESERVERFAILURE = 0x80436006;
constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENOHOST = 0x80436007;
constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENOTIMPLEMENTED = 0x80436008;
constexpr int ORBIS_HTTP_ERROR_RESOLVER_ESERVERREFUSED = 0x80436009;
constexpr int ORBIS_HTTP_ERROR_RESOLVER_ENORECORD = 0x8043600a;
constexpr int ORBIS_HTTPS_ERROR_CERT = 0x80435060;
constexpr int ORBIS_HTTPS_ERROR_HANDSHAKE = 0x80435061;
constexpr int ORBIS_HTTPS_ERROR_IO = 0x80435062;
constexpr int ORBIS_HTTPS_ERROR_INTERNAL = 0x80435063;
constexpr int ORBIS_HTTPS_ERROR_PROXY = 0x80435064;
constexpr int ORBIS_HTTPS_ERROR_SSL_INTERNAL = 0x01;
constexpr int ORBIS_HTTPS_ERROR_SSL_INVALID_CERT = 0x02;
constexpr int ORBIS_HTTPS_ERROR_SSL_CN_CHECK = 0x04;
constexpr int ORBIS_HTTPS_ERROR_SSL_NOT_AFTER_CHECK = 0x08;
constexpr int ORBIS_HTTPS_ERROR_SSL_NOT_BEFORE_CHECK = 0x10;
constexpr int ORBIS_HTTPS_ERROR_SSL_UNKNOWN_CA = 0x20;