Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions core/libs/prx/libSceHttp2/Transport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,10 @@ void AddHeader(Settings& settings, const char* name, const char* value, std::uin
"sceHttp2AddRequestHeader: invalid header name");
Require(nghttp2_check_header_value_rfc9113(reinterpret_cast<const std::uint8_t*>(value), std::strlen(value)),
"sceHttp2AddRequestHeader: invalid header value");
if (key == "host") Require(value[0] != '\0' && std::string_view(value).find_first_of(" \t") == std::string_view::npos,
"sceHttp2AddRequestHeader: invalid host header value");
Require(key != "connection" && key != "proxy-connection" && key != "keep-alive" && key != "upgrade" &&
key != "transfer-encoding" && key != "host" && key != "content-length" &&
key != "transfer-encoding" && key != "content-length" &&
(key != "te" || std::string_view(value) == "trailers"), "sceHttp2AddRequestHeader: unsupported HTTP/2 control header");
auto updated = settings.headers;
if (mode == 0) std::erase_if(updated, [&](const auto& header) { return header.first == key; });
Expand Down Expand Up @@ -468,11 +470,13 @@ struct Request::State {
CheckNg(nghttp2_session_client_new2(&session, raw, this, optionsRaw));
const nghttp2_settings_entry settingsEntry{NGHTTP2_SETTINGS_ENABLE_PUSH, 0};
CheckNg(nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &settingsEntry, 1));
std::string authority = url.authority;
for (const auto& [name, value] : settings.headers) if (name == "host") authority = value;
std::vector<std::pair<std::string, std::string>> fields{
{":method", method}, {":scheme", "https"}, {":authority", url.authority}, {":path", url.path}};
{":method", method}, {":scheme", "https"}, {":authority", authority}, {":path", url.path}};
if (!settings.userAgent.empty() && std::none_of(settings.headers.begin(), settings.headers.end(), [](const auto& header) { return header.first == "user-agent"; }))
fields.emplace_back("user-agent", settings.userAgent);
fields.insert(fields.end(), settings.headers.begin(), settings.headers.end());
for (const auto& header : settings.headers) if (header.first != "host") fields.push_back(header);
if (!settings.noContentLength) fields.emplace_back("content-length", std::to_string(settings.contentLength));
std::vector<nghttp2_nv> headers;
for (auto& [name, value] : fields) headers.push_back({reinterpret_cast<std::uint8_t*>(name.data()),
Expand Down
17 changes: 17 additions & 0 deletions core/libs/tests/GuestHttp2Transport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,22 @@ void Deadline(int ssl, Mode mode, bool overall) {

}

void HostAuthority(int ssl) {
std::fprintf(stderr, "HTTP2 host header scenario\n");
Server server(Mode::Normal);
Client client(ssl);
Require(sceHttp2AddRequestHeader(client.tmpl, "Host", "template.example", 0) == 0, "template host");
client.Create(server.Url());
Require(sceHttp2AddRequestHeader(client.request, "host", "request.example", 0) == 0, "request host");
Require(sceHttp2SendRequest(client.request, nullptr, 0) == 0, "send host request");
std::array<char, 64> buffer;
while (sceHttp2ReadData(client.request, buffer.data(), buffer.size()) > 0) {}
server.Join();
const auto& headers = server.Headers();
Require(headers.count(":authority") == 1 && headers.find(":authority")->second == "request.example", "host header becomes the authority");
Require(headers.count("host") == 0, "host header is not sent");
}

int main(int argc, char** argv) {
#ifndef _WIN32
std::signal(SIGPIPE, SIG_IGN);
Expand Down Expand Up @@ -550,6 +566,7 @@ int main(int argc, char** argv) {
Failure(untrusted, Mode::Normal);
Success(untrusted, Mode::Normal, true);
Require(sceSslTerm_nid_postfix(untrusted) == 0, "untrusted SSL term");
HostAuthority(ssl);
}
Require(sceSslTerm_nid_postfix(ssl) == 0, "SSL term");
std::puts("HTTP2 loopback transport passed");
Expand Down
1 change: 1 addition & 0 deletions docs/dev/debt/libSceHttp2.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- [libSceHttp2 TLS settings](../../../core/libs/prx/libSceHttp2/Export.cpp) - minimum-version selection, nonnull SSL callbacks and flags outside `0x3d` throw. Supported certificate-check bits follow SharpProspero and use libSceSsl's TLS 1.2 backend and CA context; native defaults and error translation are unverified. Settings on object kinds other than templates/requests throw; native object-kind restrictions are unverified. Settings become immutable when transport is created. Zero timeout values, nonzero resolve retries, gzip inflation and connection-slot wait timeouts throw. Host HTTP allocations are not charged to the context pool; `sceHttp2GetMemoryPoolStats` throws for live contexts instead of reporting zero usage.
- [sceHttp2GetAllResponseHeaders](../../../core/libs/prx/libSceHttp2/Export.cpp) throws: the serialization, size and lifetime of a decoded HTTP/2 header block have not been established. Status and Content-Length fields are decoded; absent Content-Length reports `NotFound`, following SharpProspero.
- [libSceHttp2 asynchronous requests](../../../core/libs/prx/libSceHttp2/Export.cpp) - send/read submission and `sceHttp2WaitAsync` throw. The previous offline implementation queued a fabricated network failure and used event type zero; public sources do not establish the completion event types, wait timeout updates, cancellation results or ownership of async buffers. PPSA17221's kqueue layout and PPSA15508's 24-byte wait result establish caller ABI only. No completion or equeue event is manufactured.
- [Host request header](../../../core/libs/prx/libSceHttp2/Transport.cpp) (libSceHttp2) - a `Host` header set with `sceHttp2AddRequestHeader` becomes the request's `:authority` pseudo-header and is not sent as a `Host` field, following the HTTP/1.1-to-HTTP/2 translation in [RFC 9113 section 8.3.1](https://www.rfc-editor.org/rfc/rfc9113.html#section-8.3.1). The last value set wins. The console's handling of a `Host` that differs from the URL authority and of whitespace in the value is not measured; here whitespace throws.

- [WebSocket ping settings](../../../core/libs/prx/libSceHttp2/Export.cpp): both setters take a 32-bit request ID and a 32-bit interval/timeout in microseconds, confirmed by the timer consumer and its clock conversion. The protected implementations remain unchanged.

Expand Down
Loading