feat(http): implement URI percent decoding
This commit is contained in:
parent
1438d99560
commit
cda1d4b211
|
|
@ -1,4 +1,5 @@
|
|||
#include <cctype>
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
|
@ -197,6 +198,26 @@ int APS5_VABI sceHttpUriEscape(char* out, size_t* require, size_t prepare, const
|
|||
return copyOut(escaped, out, require, prepare);
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttpUriUnescape(char* out, size_t* require, size_t prepare, const char* in) {
|
||||
if (!in) return ERROR_INVALID_VALUE;
|
||||
|
||||
const std::string_view input(in);
|
||||
std::string decoded;
|
||||
for (size_t i = 0; i < input.size(); ++i) {
|
||||
if (input[i] == '%' && input.size() - i >= 3) {
|
||||
unsigned int value = 0;
|
||||
const auto result = std::from_chars(input.data() + i + 1, input.data() + i + 3, value, 16);
|
||||
if (result.ec == std::errc{} && result.ptr == input.data() + i + 3) {
|
||||
decoded.push_back(static_cast<char>(value));
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
decoded.push_back(input[i]);
|
||||
}
|
||||
return copyOut(decoded, out, require, prepare);
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttpUriParse(SceHttpUriElement* out, const char* src_url, void* pool, size_t* require, size_t prepare) {
|
||||
if (!src_url) return ERROR_INVALID_URL;
|
||||
const bool write = out && pool;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ int APS5_VABI sceHttpUriParse(SceHttpUriElement*, const char*, void*, std::size_
|
|||
int APS5_VABI sceHttpSetInflateGZIPEnabled(int, int);
|
||||
int APS5_VABI sceHttpUriBuild(char*, std::size_t*, std::size_t, const SceHttpUriElement*, std::uint32_t);
|
||||
int APS5_VABI sceHttpUriEscape(char*, std::size_t*, std::size_t, const char*);
|
||||
int APS5_VABI sceHttpUriUnescape(char*, std::size_t*, std::size_t, const char*);
|
||||
int APS5_VABI sceHttpCreateEpoll(int, HttpEpollHandle*);
|
||||
int APS5_VABI sceHttpDestroyEpoll(int, HttpEpollHandle);
|
||||
int APS5_VABI sceHttpReadData(int, void*, std::size_t);
|
||||
|
|
@ -90,6 +91,37 @@ int main() {
|
|||
Require(Equal(escaped, "a%20b%2F~%C3%A9"));
|
||||
Require(sceHttpUriEscape(escaped, nullptr, sizeof(escaped), nullptr) == invalidValue);
|
||||
|
||||
constexpr const char* unescapeCases[][2] = {
|
||||
{"", ""}, {"plain+text", "plain+text"}, {"a%20b%2F~%c3%a9", "a b/~\xC3\xA9"},
|
||||
{"%41%4a%4F%ff", "AJO\xFF"}, {"%2520", "%20"},
|
||||
{"%", "%"}, {"%1", "%1"}, {"%1g%gg%+1%-1", "%1g%gg%+1%-1"}
|
||||
};
|
||||
for (const auto& row : unescapeCases) {
|
||||
const std::size_t size = std::strlen(row[1]) + 1;
|
||||
Require(sceHttpUriUnescape(nullptr, &required, 0, row[0]) == 0 && required == size);
|
||||
std::memset(escaped, 'Z', sizeof(escaped));
|
||||
Require(sceHttpUriUnescape(escaped, &required, size - 1, row[0]) == outOfMemory && required == size);
|
||||
for (char c : escaped) Require(c == 'Z');
|
||||
Require(sceHttpUriUnescape(escaped, nullptr, size, row[0]) == 0);
|
||||
Require(Equal(escaped, row[1]) && escaped[size] == 'Z');
|
||||
}
|
||||
Require(sceHttpUriUnescape(escaped, &required, sizeof(escaped), "a%00b") == 0);
|
||||
Require(required == 4 && std::memcmp(escaped, "a\0b", 4) == 0);
|
||||
std::strcpy(escaped, "%41%2f%2520");
|
||||
Require(sceHttpUriUnescape(escaped, &required, sizeof(escaped), escaped) == 0);
|
||||
Require(Equal(escaped, "A/%20") && required == 6);
|
||||
Require(sceHttpUriUnescape(nullptr, nullptr, 0, "valid") == 0);
|
||||
required = 123;
|
||||
Require(sceHttpUriUnescape(escaped, &required, sizeof(escaped), nullptr) == invalidValue);
|
||||
Require(required == 123 && Equal(escaped, "A/%20"));
|
||||
|
||||
char bytes[256], encodedBytes[766], decodedBytes[256];
|
||||
for (std::size_t i = 1; i < 256; ++i) bytes[i - 1] = static_cast<char>(i);
|
||||
bytes[255] = '\0';
|
||||
Require(sceHttpUriEscape(encodedBytes, nullptr, sizeof(encodedBytes), bytes) == 0);
|
||||
Require(sceHttpUriUnescape(decodedBytes, &required, sizeof(decodedBytes), encodedBytes) == 0);
|
||||
Require(required == sizeof(bytes) && std::memcmp(bytes, decodedBytes, sizeof(bytes)) == 0);
|
||||
|
||||
HttpEpollHandle epoll = nullptr;
|
||||
Require(sceHttpCreateEpoll(1, nullptr) == invalidValue);
|
||||
Require(sceHttpCreateEpoll(1, &epoll) == 0 && epoll != nullptr);
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [AJM Opus decoder](../../core/libs/prx/libSceAjm.native/src/Ajm.cpp) (libSceAjm.native, codec 24) - the initialize parameter layout (`u32 channels, u32 sample rate, u32`, seen as `2, 48000, 0`) and the little-endian `u16` byte count before each packet come from one title (The Smurfs Dreams); only 48000 Hz and a zero third word are accepted
|
||||
- [sceHttpParseResponseHeader](../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Net/Http.cs); parsing and error codes follow [shadPS4's reverse-engineered PS4 library](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp), not confirmed on a PS5 title or hardware. Folded values retain their line breaks and whitespace; an empty first line starts the value scan after its line feed. A consumed byte count above `INT_MAX` throws instead of wrapping.
|
||||
- [sceHttpParseStatusLine](../../core/libs/prx/libSceHttp/src/Parse.cpp) (libSceHttp) - behaviour and error codes follow the [shadPS4](https://github.com/shadps4-emu/shadPS4) reimplementation of the PS4 library, not confirmed on a PS5 title; unlike it, no byte past `lineLen` is read
|
||||
- [sceHttpUriUnescape](../../core/libs/prx/libSceHttp/src/Uri.cpp) (libSceHttp) - signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Net/Http.cs); percent decoding, invalid sequences and error codes follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp), not confirmed on a PS5 title or hardware. Size includes the final NUL; malformed escapes and `+` remain unchanged, and decoding runs once.
|
||||
- [sceHttp2GetMemoryPoolStats](../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - unknown signature
|
||||
- [sceNetGetMemoryPoolStats](../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature
|
||||
- [sceHttpSetRequestStatusCallback](../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - no reference implements it (shadPS4 only logs and returns 0); when the callback runs and what status it gets is unknown
|
||||
|
|
|
|||
Loading…
Reference in New Issue