feat(libSceHttp): implement URI parse, build and escape
sceHttpUriParse, sceHttpUriBuild and sceHttpUriEscape work on strings only and are implemented. sceHttpCreateRequestWithURL, sceHttpReadData, sceHttpSetChunkedTransferEnabled and sceHttpCreateEpoll follow the existing no-network behaviour. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
bc51b337ad
commit
0a8cc7f543
|
|
@ -0,0 +1,20 @@
|
|||
add_library(libSceHttp SHARED EXCLUDE_FROM_ALL Export.cpp src/Uri.cpp)
|
||||
target_include_directories(libSceHttp PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
configure_windows_unwind(libSceHttp)
|
||||
|
||||
if(MINGW)
|
||||
foreach(runtimeLinkFlag IN ITEMS -static-libgcc -static-libstdc++ -static)
|
||||
string(REPLACE "${runtimeLinkFlag}" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_test_executable(guest_http_tests ${LIBS_INCLUDE_DIR}/tests/GuestHttp.cpp)
|
||||
target_include_directories(guest_http_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_http_tests PRIVATE libSceHttp)
|
||||
add_test(NAME guest_http COMMAND guest_http_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_http PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceHttp>")
|
||||
endif()
|
||||
endif()
|
||||
|
|
@ -2,11 +2,11 @@
|
|||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libSceHttp/src/HttpErrors.hpp"
|
||||
#include <atomic>
|
||||
|
||||
// No network is emulated: contexts, templates and requests can be created, but any request
|
||||
// that would touch the network fails with the library's network error.
|
||||
static constexpr int ERROR_NETWORK = static_cast<int>(0x80431063);
|
||||
static std::atomic<int> g_nextHandle{1};
|
||||
|
||||
extern "C" {
|
||||
|
|
@ -41,10 +41,10 @@ int APS5_VABI sceHttpCreateConnectionWithURL(int tmpl_id, const char* url, int e
|
|||
}
|
||||
|
||||
int APS5_VABI sceHttpCreateEpoll(int http_ctx_id, HttpEpollHandle* eh) {
|
||||
(void)http_ctx_id;
|
||||
(void)eh;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
(void)http_ctx_id;
|
||||
if (!eh) return ERROR_INVALID_VALUE;
|
||||
*eh = new HttpEpoll{};
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttpCreateRequest(int conn_id, int method, const char* path, uint64_t content_length) {
|
||||
|
|
@ -88,7 +88,7 @@ int APS5_VABI sceHttpDeleteTemplate(int tmpl_id) {
|
|||
|
||||
int APS5_VABI sceHttpDestroyEpoll(int http_ctx_id, HttpEpollHandle eh) {
|
||||
(void)http_ctx_id;
|
||||
(void)eh;
|
||||
delete eh;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -216,35 +216,6 @@ int APS5_VABI sceHttpUnsetEpoll(int id) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttpUriBuild(char* out, size_t* require, size_t prepare, const SceHttpUriElement* src_element, uint32_t option) {
|
||||
(void)out;
|
||||
(void)require;
|
||||
(void)prepare;
|
||||
(void)src_element;
|
||||
(void)option;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttpUriEscape(char* out, size_t* require, size_t prepare, const char* in) {
|
||||
(void)out;
|
||||
(void)require;
|
||||
(void)prepare;
|
||||
(void)in;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttpUriParse(SceHttpUriElement* out, const char* src_url, void* pool, size_t* require, size_t prepare) {
|
||||
(void)out;
|
||||
(void)src_url;
|
||||
(void)pool;
|
||||
(void)require;
|
||||
(void)prepare;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttpWaitRequest(HttpEpollHandle eh, HttpNBEvent* nbev, int maxevents, int timeout) {
|
||||
(void)eh;
|
||||
(void)nbev;
|
||||
|
|
@ -253,19 +224,25 @@ int APS5_VABI sceHttpWaitRequest(HttpEpollHandle eh, HttpNBEvent* nbev, int maxe
|
|||
return ERROR_NETWORK;
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttpCreateRequestWithURL() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceHttpCreateRequestWithURL(int conn_id, int method, const char* url, uint64_t content_length) {
|
||||
(void)conn_id;
|
||||
(void)method;
|
||||
(void)url;
|
||||
(void)content_length;
|
||||
return g_nextHandle.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttpReadData() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceHttpReadData(int request_id, void* data, size_t size) {
|
||||
(void)request_id;
|
||||
(void)data;
|
||||
(void)size;
|
||||
return ERROR_NETWORK;
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttpSetChunkedTransferEnabled() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceHttpSetChunkedTransferEnabled(int id, int enable) {
|
||||
(void)id;
|
||||
(void)enable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBSCEHTTP_SRC_HTTPERRORS_HPP
|
||||
#define CORE_LIBS_PRX_LIBSCEHTTP_SRC_HTTPERRORS_HPP
|
||||
|
||||
inline constexpr int ERROR_OUT_OF_MEMORY = static_cast<int>(0x80431022);
|
||||
inline constexpr int ERROR_NETWORK = static_cast<int>(0x80431063);
|
||||
inline constexpr int ERROR_INVALID_VALUE = static_cast<int>(0x804311FE);
|
||||
inline constexpr int ERROR_INVALID_URL = static_cast<int>(0x80433060);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,236 @@
|
|||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libSceHttp/src/HttpErrors.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t URI_BUILD_WITH_SCHEME = 0x01;
|
||||
constexpr uint32_t URI_BUILD_WITH_HOSTNAME = 0x02;
|
||||
constexpr uint32_t URI_BUILD_WITH_PORT = 0x04;
|
||||
constexpr uint32_t URI_BUILD_WITH_PATH = 0x08;
|
||||
constexpr uint32_t URI_BUILD_WITH_USERNAME = 0x10;
|
||||
constexpr uint32_t URI_BUILD_WITH_PASSWORD = 0x20;
|
||||
constexpr uint32_t URI_BUILD_WITH_QUERY = 0x40;
|
||||
constexpr uint32_t URI_BUILD_WITH_FRAGMENT = 0x80;
|
||||
|
||||
struct UriParts {
|
||||
bool opaque = false;
|
||||
std::string_view scheme;
|
||||
std::string_view username;
|
||||
std::string_view password;
|
||||
std::string_view hostname;
|
||||
std::string path;
|
||||
std::string_view query;
|
||||
std::string_view fragment;
|
||||
uint16_t port = 0;
|
||||
};
|
||||
|
||||
bool equalsIgnoreCase(std::string_view left, std::string_view right) {
|
||||
if (left.size() != right.size()) return false;
|
||||
for (size_t i = 0; i < left.size(); ++i) {
|
||||
if (std::tolower(static_cast<unsigned char>(left[i])) != std::tolower(static_cast<unsigned char>(right[i]))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t defaultPort(std::string_view scheme) {
|
||||
if (equalsIgnoreCase(scheme, "http")) return 80;
|
||||
if (equalsIgnoreCase(scheme, "https")) return 443;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isHostCharacter(char c) {
|
||||
return std::isalnum(static_cast<unsigned char>(c)) || c == '-' || c == '.' || c == '_';
|
||||
}
|
||||
|
||||
std::string removeDotSegments(std::string_view input) {
|
||||
std::string output;
|
||||
while (!input.empty()) {
|
||||
if (input.starts_with("../")) input.remove_prefix(3);
|
||||
else if (input.starts_with("./")) input.remove_prefix(2);
|
||||
else if (input.starts_with("/./")) input.remove_prefix(2);
|
||||
else if (input == "/.") input = "/";
|
||||
else if (input.starts_with("/../") || input == "/..") {
|
||||
input = input.size() == 3 ? std::string_view("/") : input.substr(3);
|
||||
const size_t slash = output.rfind('/');
|
||||
output.erase(slash == std::string::npos ? 0 : slash);
|
||||
} else if (input == "." || input == "..") input = {};
|
||||
else {
|
||||
const size_t end = input.find('/', 1);
|
||||
const size_t length = end == std::string_view::npos ? input.size() : end;
|
||||
output.append(input.substr(0, length));
|
||||
input.remove_prefix(length);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
int parseUri(std::string_view uri, UriParts& parts) {
|
||||
size_t schemeEnd = 0;
|
||||
if (!uri.empty() && std::isalpha(static_cast<unsigned char>(uri[0]))) {
|
||||
schemeEnd = 1;
|
||||
while (schemeEnd < uri.size() && (std::isalnum(static_cast<unsigned char>(uri[schemeEnd])) || uri[schemeEnd] == '+'
|
||||
|| uri[schemeEnd] == '-' || uri[schemeEnd] == '.')) ++schemeEnd;
|
||||
}
|
||||
if (schemeEnd > 0 && schemeEnd < uri.size() && uri[schemeEnd] == ':') {
|
||||
parts.scheme = uri.substr(0, schemeEnd);
|
||||
uri.remove_prefix(schemeEnd + 1);
|
||||
}
|
||||
|
||||
parts.opaque = !uri.starts_with("//");
|
||||
if (!parts.opaque) uri.remove_prefix(2);
|
||||
|
||||
std::string_view authority = uri.substr(0, uri.find_first_of("/?#"));
|
||||
uri.remove_prefix(authority.size());
|
||||
|
||||
const size_t at = authority.rfind('@');
|
||||
if (at != std::string_view::npos) {
|
||||
const std::string_view userinfo = authority.substr(0, at);
|
||||
const size_t colon = userinfo.find(':');
|
||||
parts.username = userinfo.substr(0, colon);
|
||||
if (colon != std::string_view::npos) parts.password = userinfo.substr(colon + 1);
|
||||
authority.remove_prefix(at + 1);
|
||||
}
|
||||
|
||||
std::string_view port;
|
||||
if (authority.starts_with('[')) {
|
||||
const size_t close = authority.find(']');
|
||||
if (close == std::string_view::npos) return ERROR_INVALID_URL;
|
||||
parts.hostname = authority.substr(1, close - 1);
|
||||
authority.remove_prefix(close + 1);
|
||||
if (!authority.empty() && authority[0] != ':') return ERROR_INVALID_URL;
|
||||
if (!authority.empty()) port = authority.substr(1);
|
||||
for (const char c : parts.hostname) {
|
||||
if (!isHostCharacter(c) && c != ':') return ERROR_INVALID_URL;
|
||||
}
|
||||
} else {
|
||||
const size_t colon = authority.find(':');
|
||||
parts.hostname = authority.substr(0, colon);
|
||||
if (colon != std::string_view::npos) port = authority.substr(colon + 1);
|
||||
for (const char c : parts.hostname) {
|
||||
if (!isHostCharacter(c)) return ERROR_INVALID_URL;
|
||||
}
|
||||
}
|
||||
|
||||
if (port.size() > 5) return ERROR_INVALID_URL;
|
||||
uint32_t portValue = 0;
|
||||
for (const char c : port) {
|
||||
if (!std::isdigit(static_cast<unsigned char>(c))) return ERROR_INVALID_URL;
|
||||
portValue = portValue * 10 + static_cast<uint32_t>(c - '0');
|
||||
}
|
||||
if (portValue > 0xFFFF) return ERROR_INVALID_URL;
|
||||
parts.port = port.empty() ? defaultPort(parts.scheme) : static_cast<uint16_t>(portValue);
|
||||
|
||||
const std::string_view path = uri.substr(0, uri.find_first_of("?#"));
|
||||
parts.path = removeDotSegments(path);
|
||||
uri.remove_prefix(path.size());
|
||||
|
||||
if (uri.starts_with('?')) {
|
||||
parts.query = uri.substr(0, uri.find('#'));
|
||||
uri.remove_prefix(parts.query.size());
|
||||
}
|
||||
parts.fragment = uri;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int copyOut(const std::string& value, char* out, size_t* require, size_t prepare) {
|
||||
if (require) *require = value.size() + 1;
|
||||
if (!out) return 0;
|
||||
if (prepare < value.size() + 1) return ERROR_OUT_OF_MEMORY;
|
||||
std::memcpy(out, value.c_str(), value.size() + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceHttpUriBuild(char* out, size_t* require, size_t prepare, const SceHttpUriElement* src_element, uint32_t option) {
|
||||
if (!src_element) return ERROR_INVALID_URL;
|
||||
if (!out && !require) return ERROR_INVALID_VALUE;
|
||||
|
||||
auto field = [](const char* value) { return value ? std::string_view(value) : std::string_view(); };
|
||||
const std::string_view scheme = field(src_element->scheme);
|
||||
const std::string_view username = option & URI_BUILD_WITH_USERNAME ? field(src_element->username) : std::string_view();
|
||||
const std::string_view password = option & URI_BUILD_WITH_PASSWORD ? field(src_element->password) : std::string_view();
|
||||
const std::string_view hostname = option & URI_BUILD_WITH_HOSTNAME ? field(src_element->hostname) : std::string_view();
|
||||
|
||||
std::string uri;
|
||||
if (option & URI_BUILD_WITH_SCHEME && !scheme.empty()) uri.append(scheme).push_back(':');
|
||||
if (!src_element->opaque && option & URI_BUILD_WITH_HOSTNAME) uri.append("//");
|
||||
uri.append(username);
|
||||
if (!password.empty()) uri.append(":").append(password);
|
||||
if (!username.empty() || !password.empty()) uri.push_back('@');
|
||||
if (hostname.find(':') != std::string_view::npos) uri.append("[").append(hostname).append("]");
|
||||
else uri.append(hostname);
|
||||
if (option & URI_BUILD_WITH_PORT && src_element->port != 0 && src_element->port != defaultPort(scheme)) {
|
||||
uri.append(":").append(std::to_string(src_element->port));
|
||||
}
|
||||
if (option & URI_BUILD_WITH_PATH) uri.append(field(src_element->path));
|
||||
if (option & URI_BUILD_WITH_QUERY) uri.append(field(src_element->query));
|
||||
if (option & URI_BUILD_WITH_FRAGMENT) uri.append(field(src_element->fragment));
|
||||
return copyOut(uri, out, require, prepare);
|
||||
}
|
||||
|
||||
int APS5_VABI sceHttpUriEscape(char* out, size_t* require, size_t prepare, const char* in) {
|
||||
if (!in) return ERROR_INVALID_VALUE;
|
||||
if (!out && !require) return ERROR_INVALID_VALUE;
|
||||
|
||||
static constexpr char hex[] = "0123456789ABCDEF";
|
||||
std::string escaped;
|
||||
for (const char* c = in; *c; ++c) {
|
||||
const auto value = static_cast<unsigned char>(*c);
|
||||
if (std::isalnum(value) || value == '-' || value == '_' || value == '.' || value == '~') {
|
||||
escaped.push_back(*c);
|
||||
} else {
|
||||
escaped.push_back('%');
|
||||
escaped.push_back(hex[value >> 4]);
|
||||
escaped.push_back(hex[value & 0xF]);
|
||||
}
|
||||
}
|
||||
return copyOut(escaped, 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;
|
||||
if (!write && !require) return ERROR_INVALID_VALUE;
|
||||
|
||||
UriParts parts;
|
||||
if (const int result = parseUri(src_url, parts); result != 0) return result;
|
||||
|
||||
const std::string_view fields[] = {parts.scheme, parts.username, parts.password, parts.hostname, parts.path, parts.query, parts.fragment};
|
||||
size_t size = 0;
|
||||
for (const std::string_view field : fields) size += field.size() + 1;
|
||||
if (require) *require = size;
|
||||
if (!write) return 0;
|
||||
if (prepare < size) return ERROR_OUT_OF_MEMORY;
|
||||
|
||||
char* next = static_cast<char*>(pool);
|
||||
char* strings[7];
|
||||
for (size_t i = 0; i < 7; ++i) {
|
||||
strings[i] = next;
|
||||
std::memcpy(next, fields[i].data(), fields[i].size());
|
||||
next[fields[i].size()] = '\0';
|
||||
next += fields[i].size() + 1;
|
||||
}
|
||||
*out = {};
|
||||
out->opaque = parts.opaque;
|
||||
out->scheme = strings[0];
|
||||
out->username = strings[1];
|
||||
out->password = strings[2];
|
||||
out->hostname = strings[3];
|
||||
out->path = strings[4];
|
||||
out->query = strings[5];
|
||||
out->fragment = strings[6];
|
||||
out->port = parts.port;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI sceHttpUriParse(SceHttpUriElement*, const char*, void*, std::size_t*, std::size_t);
|
||||
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 sceHttpCreateEpoll(int, HttpEpollHandle*);
|
||||
int APS5_VABI sceHttpDestroyEpoll(int, HttpEpollHandle);
|
||||
int APS5_VABI sceHttpReadData(int, void*, std::size_t);
|
||||
}
|
||||
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
|
||||
static bool Equal(const char* left, const char* right) { return std::strcmp(left, right) == 0; }
|
||||
|
||||
int main() {
|
||||
constexpr int outOfMemory = static_cast<int>(0x80431022);
|
||||
constexpr int invalidValue = static_cast<int>(0x804311FE);
|
||||
constexpr int invalidUrl = static_cast<int>(0x80433060);
|
||||
constexpr int network = static_cast<int>(0x80431063);
|
||||
constexpr std::uint32_t buildAll = 0xFF;
|
||||
|
||||
const char* url = "https://user:secret@example.com:8443/a/./b/../c?x=1&y=2#top";
|
||||
std::size_t required = 0;
|
||||
Require(sceHttpUriParse(nullptr, url, nullptr, &required, 0) == 0);
|
||||
Require(required == 6 + 5 + 7 + 12 + 5 + 9 + 5);
|
||||
|
||||
char pool[256];
|
||||
SceHttpUriElement element;
|
||||
Require(sceHttpUriParse(&element, url, pool, nullptr, required - 1) == outOfMemory);
|
||||
Require(sceHttpUriParse(&element, url, pool, nullptr, required) == 0);
|
||||
Require(element.opaque == 0);
|
||||
Require(Equal(element.scheme, "https") && Equal(element.username, "user") && Equal(element.password, "secret"));
|
||||
Require(Equal(element.hostname, "example.com") && element.port == 8443);
|
||||
Require(Equal(element.path, "/a/c") && Equal(element.query, "?x=1&y=2") && Equal(element.fragment, "#top"));
|
||||
|
||||
char built[256];
|
||||
Require(sceHttpUriBuild(nullptr, &required, 0, &element, buildAll) == 0);
|
||||
Require(required == std::strlen("https://user:secret@example.com:8443/a/c?x=1&y=2#top") + 1);
|
||||
Require(sceHttpUriBuild(built, nullptr, required - 1, &element, buildAll) == outOfMemory);
|
||||
Require(sceHttpUriBuild(built, nullptr, sizeof(built), &element, buildAll) == 0);
|
||||
Require(Equal(built, "https://user:secret@example.com:8443/a/c?x=1&y=2#top"));
|
||||
Require(sceHttpUriBuild(built, nullptr, sizeof(built), &element, 0x08 | 0x40) == 0);
|
||||
Require(Equal(built, "/a/c?x=1&y=2"));
|
||||
|
||||
Require(sceHttpUriParse(&element, "HTTP://Example.com/", pool, nullptr, sizeof(pool)) == 0);
|
||||
Require(element.port == 80 && Equal(element.username, "") && Equal(element.query, ""));
|
||||
Require(sceHttpUriBuild(built, nullptr, sizeof(built), &element, buildAll) == 0);
|
||||
Require(Equal(built, "HTTP://Example.com/"));
|
||||
|
||||
Require(sceHttpUriParse(&element, "http://[::1]:8080/index.html", pool, nullptr, sizeof(pool)) == 0);
|
||||
Require(Equal(element.hostname, "::1") && element.port == 8080 && Equal(element.path, "/index.html"));
|
||||
Require(sceHttpUriBuild(built, nullptr, sizeof(built), &element, buildAll) == 0);
|
||||
Require(Equal(built, "http://[::1]:8080/index.html"));
|
||||
|
||||
Require(sceHttpUriParse(&element, "mailto:someone@example.com", pool, nullptr, sizeof(pool)) == 0);
|
||||
Require(element.opaque != 0 && element.port == 0);
|
||||
Require(Equal(element.username, "someone") && Equal(element.hostname, "example.com"));
|
||||
Require(sceHttpUriBuild(built, nullptr, sizeof(built), &element, buildAll) == 0);
|
||||
Require(Equal(built, "mailto:someone@example.com"));
|
||||
|
||||
Require(sceHttpUriParse(&element, "/path/only?q", pool, nullptr, sizeof(pool)) == 0);
|
||||
Require(Equal(element.scheme, "") && Equal(element.hostname, "") && Equal(element.path, "/path/only"));
|
||||
|
||||
Require(sceHttpUriParse(&element, nullptr, pool, nullptr, sizeof(pool)) == invalidUrl);
|
||||
Require(sceHttpUriParse(nullptr, url, nullptr, nullptr, 0) == invalidValue);
|
||||
Require(sceHttpUriParse(nullptr, "http://host:65536/", nullptr, &required, 0) == invalidUrl);
|
||||
Require(sceHttpUriParse(nullptr, "http://host:80a/", nullptr, &required, 0) == invalidUrl);
|
||||
Require(sceHttpUriParse(nullptr, "http://[::1/", nullptr, &required, 0) == invalidUrl);
|
||||
Require(sceHttpUriParse(nullptr, "http://bad host/", nullptr, &required, 0) == invalidUrl);
|
||||
Require(sceHttpUriBuild(built, nullptr, sizeof(built), nullptr, buildAll) == invalidUrl);
|
||||
Require(sceHttpUriBuild(nullptr, nullptr, 0, &element, buildAll) == invalidValue);
|
||||
|
||||
char escaped[64];
|
||||
Require(sceHttpUriEscape(nullptr, &required, 0, "a b/~\xC3\xA9") == 0);
|
||||
Require(required == std::strlen("a%20b%2F~%C3%A9") + 1);
|
||||
Require(sceHttpUriEscape(escaped, nullptr, required - 1, "a b/~\xC3\xA9") == outOfMemory);
|
||||
Require(sceHttpUriEscape(escaped, nullptr, sizeof(escaped), "a b/~\xC3\xA9") == 0);
|
||||
Require(Equal(escaped, "a%20b%2F~%C3%A9"));
|
||||
Require(sceHttpUriEscape(escaped, nullptr, sizeof(escaped), nullptr) == invalidValue);
|
||||
|
||||
HttpEpollHandle epoll = nullptr;
|
||||
Require(sceHttpCreateEpoll(1, nullptr) == invalidValue);
|
||||
Require(sceHttpCreateEpoll(1, &epoll) == 0 && epoll != nullptr);
|
||||
Require(sceHttpDestroyEpoll(1, epoll) == 0);
|
||||
|
||||
char data[16];
|
||||
Require(sceHttpReadData(1, data, sizeof(data)) == network);
|
||||
}
|
||||
Loading…
Reference in New Issue