From 3a1f510af09148cbf58c49a727631b28d9ce3fe0 Mon Sep 17 00:00:00 2001 From: Simon McMahon Date: Tue, 1 Aug 2023 21:40:30 +1200 Subject: [PATCH] LibWeb: Support for Access-Control-Expose-Headers in Fetch This adds the headers named in Access-Control-Expose-Headers to the response's CORS-exposed header-name list which allows those headers to be accessed from JS. --- .../LibWeb/Fetch/Fetching/Fetching.cpp | 24 +++++++++++++------ .../Fetch/Infrastructure/HTTP/Headers.cpp | 17 +++++++++++++ .../Fetch/Infrastructure/HTTP/Headers.h | 2 ++ 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/Userland/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp b/Userland/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp index 700e123f20..a046a5822d 100644 --- a/Userland/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp +++ b/Userland/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp @@ -393,13 +393,23 @@ WebIDL::ExceptionOr>> main_fetch(JS:: if (!response->is_network_error() && !is(*response)) { // 1. If request’s response tainting is "cors", then: if (request->response_tainting() == Infrastructure::Request::ResponseTainting::CORS) { - // FIXME: 1. Let headerNames be the result of extracting header list values given - // `Access-Control-Expose-Headers` and response’s header list. - // FIXME: 2. If request’s credentials mode is not "include" and headerNames contains `*`, then set - // response’s CORS-exposed header-name list to all unique header names in response’s header - // list. - // FIXME: 3. Otherwise, if headerNames is not null or failure, then set response’s CORS-exposed - // header-name list to headerNames. + // 1. Let headerNames be the result of extracting header list values given + // `Access-Control-Expose-Headers` and response’s header list. + auto header_names_or_failure = TRY_OR_IGNORE(Infrastructure::extract_header_list_values("Access-Control-Expose-Headers"sv.bytes(), response->header_list())); + auto header_names = header_names_or_failure.has>() ? header_names_or_failure.get>() : Vector {}; + + // 2. If request’s credentials mode is not "include" and headerNames contains `*`, then set + // response’s CORS-exposed header-name list to all unique header names in response’s header + // list. + if (request->credentials_mode() != Infrastructure::Request::CredentialsMode::Include && header_names.contains_slow("*"sv.bytes())) { + auto unique_header_names = TRY_OR_IGNORE(response->header_list()->unique_names()); + response->set_cors_exposed_header_name_list(move(unique_header_names)); + } + // 3. Otherwise, if headerNames is not null or failure, then set response’s CORS-exposed + // header-name list to headerNames. + else if (!header_names.is_empty()) { + response->set_cors_exposed_header_name_list(move(header_names)); + } } // 2. Set response to the following filtered response with response as its internal response, depending diff --git a/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.cpp b/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.cpp index 628fad1f74..5d8d2869a1 100644 --- a/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.cpp +++ b/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.cpp @@ -53,6 +53,23 @@ JS::NonnullGCPtr HeaderList::create(JS::VM& vm) return vm.heap().allocate_without_realm(); } +// Non-standard +ErrorOr> HeaderList::unique_names() const +{ + Vector header_names_set; + HashTable> header_names_seen; + + for (auto const& header : *this) { + if (header_names_seen.contains(header.name)) + continue; + auto bytes = TRY(ByteBuffer::copy(header.name)); + TRY(header_names_seen.try_set(header.name)); + TRY(header_names_set.try_append(move(bytes))); + } + + return header_names_set; +} + // https://fetch.spec.whatwg.org/#header-list-contains bool HeaderList::contains(ReadonlyBytes name) const { diff --git a/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.h b/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.h index 04694036a7..23f31652ed 100644 --- a/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.h +++ b/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.h @@ -61,6 +61,8 @@ public: [[nodiscard]] ErrorOr extract_length() const; [[nodiscard]] ErrorOr> extract_mime_type() const; + + ErrorOr> unique_names() const; }; struct RangeHeaderValue {