mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 03:25:13 +00:00
LibWeb: Ensure Headers API can handle non-ascii characters
This patch ensure Headers object's associated header list is ISO-8859-1 encoded when set using `Infra::isomorphic_encode`, and correctly decoded using `Infra::isomorphic_decode`. Follow-up of https://github.com/LadybirdBrowser/ladybird/pull/1893
This commit is contained in:
parent
5e62f548db
commit
824e91ffdb
Notes:
github-actions[bot]
2024-12-11 23:50:13 +00:00
Author: https://github.com/F3n67u Commit: https://github.com/LadybirdBrowser/ladybird/commit/824e91ffdb5 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2814 Reviewed-by: https://github.com/ADKaster ✅ Reviewed-by: https://github.com/Gingeh ✅
7 changed files with 188 additions and 13 deletions
|
@ -9,6 +9,7 @@
|
|||
#include <LibWeb/Bindings/HeadersPrototype.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/Fetch/Headers.h>
|
||||
#include <LibWeb/Infra/Strings.h>
|
||||
|
||||
namespace Web::Fetch {
|
||||
|
||||
|
@ -56,10 +57,7 @@ void Headers::visit_edges(JS::Cell::Visitor& visitor)
|
|||
WebIDL::ExceptionOr<void> Headers::append(String const& name_string, String const& value_string)
|
||||
{
|
||||
// The append(name, value) method steps are to append (name, value) to this.
|
||||
auto header = Infrastructure::Header {
|
||||
.name = MUST(ByteBuffer::copy(name_string.bytes())),
|
||||
.value = MUST(ByteBuffer::copy(value_string.bytes())),
|
||||
};
|
||||
auto header = Infrastructure::Header::from_string_pair(name_string, value_string);
|
||||
TRY(append(move(header)));
|
||||
return {};
|
||||
}
|
||||
|
@ -106,7 +104,7 @@ WebIDL::ExceptionOr<Optional<String>> Headers::get(String const& name_string)
|
|||
|
||||
// 2. Return the result of getting name from this’s header list.
|
||||
auto byte_buffer = m_header_list->get(name);
|
||||
return byte_buffer.has_value() ? MUST(String::from_utf8(*byte_buffer)) : Optional<String> {};
|
||||
return byte_buffer.has_value() ? Infra::isomorphic_decode(*byte_buffer) : Optional<String> {};
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#dom-headers-getsetcookie
|
||||
|
@ -123,7 +121,7 @@ Vector<String> Headers::get_set_cookie()
|
|||
// `Set-Cookie`, in order.
|
||||
for (auto const& header : *m_header_list) {
|
||||
if (StringView { header.name }.equals_ignoring_ascii_case("Set-Cookie"sv))
|
||||
values.append(MUST(String::from_utf8(header.value)));
|
||||
values.append(Infra::isomorphic_decode(header.value));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
@ -152,10 +150,7 @@ WebIDL::ExceptionOr<void> Headers::set(String const& name_string, String const&
|
|||
// 1. Normalize value.
|
||||
auto normalized_value = Infrastructure::normalize_header_value(value);
|
||||
|
||||
auto header = Infrastructure::Header {
|
||||
.name = MUST(ByteBuffer::copy(name)),
|
||||
.value = move(normalized_value),
|
||||
};
|
||||
auto header = Infrastructure::Header::from_string_pair(name, normalized_value);
|
||||
|
||||
// 2. If validating (name, value) for headers returns false, then return.
|
||||
if (!TRY(validate(header)))
|
||||
|
@ -197,7 +192,7 @@ JS::ThrowCompletionOr<void> Headers::for_each(ForEachCallback callback)
|
|||
auto const& pair = pairs[i];
|
||||
|
||||
// 2. Invoke idlCallback with « pair’s value, pair’s key, idlObject » and with thisArg as the callback this value.
|
||||
TRY(callback(MUST(String::from_utf8(pair.name)), MUST(String::from_utf8(pair.value))));
|
||||
TRY(callback(Infra::isomorphic_decode(pair.name), Infra::isomorphic_decode(pair.value)));
|
||||
|
||||
// 3. Set pairs to idlObject’s current list of value pairs to iterate over. (It might have changed.)
|
||||
pairs = value_pairs_to_iterate_over();
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <LibWeb/Bindings/HeadersIteratorPrototype.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/Fetch/HeadersIterator.h>
|
||||
#include <LibWeb/Infra/Strings.h>
|
||||
|
||||
namespace Web::Bindings {
|
||||
|
||||
|
@ -65,8 +66,8 @@ GC::Ref<JS::Object> HeadersIterator::next()
|
|||
return create_iterator_result_object(vm(), JS::js_undefined(), true);
|
||||
|
||||
auto const& pair = pairs[m_index++];
|
||||
StringView pair_name { pair.name };
|
||||
StringView pair_value { pair.value };
|
||||
auto pair_name = Infra::isomorphic_decode(pair.name);
|
||||
auto pair_value = Infra::isomorphic_decode(pair.value);
|
||||
|
||||
switch (m_iteration_kind) {
|
||||
case JS::Object::PropertyKind::Key:
|
||||
|
|
34
Tests/LibWeb/Text/expected/Fetch/fetch-headers-non-ascii.txt
Normal file
34
Tests/LibWeb/Text/expected/Fetch/fetch-headers-non-ascii.txt
Normal file
|
@ -0,0 +1,34 @@
|
|||
--------------------------------
|
||||
Headers constructor
|
||||
--------------------------------
|
||||
Accept: before-æøå-after
|
||||
X-Test: before-ß-after
|
||||
|
||||
--------------------------------
|
||||
Headers.append()
|
||||
--------------------------------
|
||||
Accept: before-æøå-after
|
||||
X-Test: before-ß-after
|
||||
|
||||
--------------------------------
|
||||
Headers.set()
|
||||
--------------------------------
|
||||
Accept: before-æøå-after
|
||||
X-Test: before-ß-after
|
||||
|
||||
--------------------------------
|
||||
Headers.getSetCookie()
|
||||
--------------------------------
|
||||
Set-Cookie: before-æøå-after
|
||||
|
||||
--------------------------------
|
||||
Headers iterator
|
||||
--------------------------------
|
||||
accept: before-æøå-after
|
||||
x-test: before-ß-after
|
||||
|
||||
--------------------------------
|
||||
Headers.forEach()
|
||||
--------------------------------
|
||||
accept: before-æøå-after
|
||||
x-test: before-ß-after
|
|
@ -0,0 +1,8 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 3 tests
|
||||
|
||||
3 Pass
|
||||
Pass Create headers with not normalized values
|
||||
Pass Check append method with not normalized values
|
||||
Pass Check set method with not normalized values
|
66
Tests/LibWeb/Text/input/Fetch/fetch-headers-non-ascii.html
Normal file
66
Tests/LibWeb/Text/input/Fetch/fetch-headers-non-ascii.html
Normal file
|
@ -0,0 +1,66 @@
|
|||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
// This test ensures that the Headers get() methods can handle non-ASCII latin1 characters: code points U+0080-U+00FF.
|
||||
test(() => {
|
||||
println("--------------------------------");
|
||||
println("Headers constructor")
|
||||
println("--------------------------------");
|
||||
const headers = new Headers({
|
||||
"Accept": "before-æøå-after",
|
||||
"X-Test": "before-ß-after"
|
||||
});
|
||||
println("Accept: " + headers.get("Accept"));
|
||||
println("X-Test: " + headers.get("X-Test"));
|
||||
|
||||
println("\n--------------------------------");
|
||||
println("Headers.append()")
|
||||
println("--------------------------------");
|
||||
const headers2 = new Headers();
|
||||
headers2.append("Accept", "before-æøå-after");
|
||||
headers2.append("X-Test", "before-ß-after");
|
||||
println("Accept: " + headers2.get("Accept"));
|
||||
println("X-Test: " + headers2.get("X-Test"));
|
||||
|
||||
println("\n--------------------------------");
|
||||
println("Headers.set()")
|
||||
println("--------------------------------");
|
||||
const headers3 = new Headers({
|
||||
"X-Test": "should be overwritten"
|
||||
});
|
||||
headers3.set("Accept", "before-æøå-after");
|
||||
headers3.set("X-Test", "before-ß-after");
|
||||
println("Accept: " + headers3.get("Accept"));
|
||||
println("X-Test: " + headers3.get("X-Test"));
|
||||
|
||||
println("\n--------------------------------");
|
||||
println("Headers.getSetCookie()")
|
||||
println("--------------------------------");
|
||||
const headers4 = new Headers({
|
||||
"Set-Cookie": "before-æøå-after",
|
||||
});
|
||||
println("Set-Cookie: " + headers4.getSetCookie());
|
||||
|
||||
println("\n--------------------------------");
|
||||
println("Headers iterator")
|
||||
println("--------------------------------");
|
||||
const headers5 = new Headers({
|
||||
"Accept": "before-æøå-after",
|
||||
"X-Test": "before-ß-after"
|
||||
});
|
||||
for (const [key, value] of headers5) {
|
||||
println(`${key}: ${value}`);
|
||||
}
|
||||
|
||||
println("\n--------------------------------");
|
||||
println("Headers.forEach()")
|
||||
println("--------------------------------");
|
||||
const headers6 = new Headers({
|
||||
"Accept": "before-æøå-after",
|
||||
"X-Test": "before-ß-after"
|
||||
});
|
||||
headers6.forEach((value, key) => {
|
||||
println(`${key}: ${value}`);
|
||||
});
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,15 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>Headers normalize values</title>
|
||||
<script>
|
||||
self.GLOBAL = {
|
||||
isWindow: function() { return true; },
|
||||
isWorker: function() { return false; },
|
||||
isShadowRealm: function() { return false; },
|
||||
};
|
||||
</script>
|
||||
<script src="../../../resources/testharness.js"></script>
|
||||
<script src="../../../resources/testharnessreport.js"></script>
|
||||
|
||||
<div id=log></div>
|
||||
<script src="../../../fetch/api/headers/headers-normalize.any.js"></script>
|
|
@ -0,0 +1,56 @@
|
|||
// META: title=Headers normalize values
|
||||
// META: global=window,worker
|
||||
|
||||
"use strict";
|
||||
|
||||
const expectations = {
|
||||
"name1": [" space ", "space"],
|
||||
"name2": ["\ttab\t", "tab"],
|
||||
"name3": [" spaceAndTab\t", "spaceAndTab"],
|
||||
"name4": ["\r\n newLine", "newLine"], //obs-fold cases
|
||||
"name5": ["newLine\r\n ", "newLine"],
|
||||
"name6": ["\r\n\tnewLine", "newLine"],
|
||||
"name7": ["\t\f\tnewLine\n", "\f\tnewLine"],
|
||||
"name8": ["newLine\xa0", "newLine\xa0"], // \xa0 == non breaking space
|
||||
};
|
||||
|
||||
test(function () {
|
||||
const headerDict = Object.fromEntries(
|
||||
Object.entries(expectations).map(([name, [actual]]) => [name, actual]),
|
||||
);
|
||||
var headers = new Headers(headerDict);
|
||||
for (const name in expectations) {
|
||||
const expected = expectations[name][1];
|
||||
assert_equals(
|
||||
headers.get(name),
|
||||
expected,
|
||||
"name: " + name + " has normalized value: " + expected,
|
||||
);
|
||||
}
|
||||
}, "Create headers with not normalized values");
|
||||
|
||||
test(function () {
|
||||
var headers = new Headers();
|
||||
for (const name in expectations) {
|
||||
headers.append(name, expectations[name][0]);
|
||||
const expected = expectations[name][1];
|
||||
assert_equals(
|
||||
headers.get(name),
|
||||
expected,
|
||||
"name: " + name + " has value: " + expected,
|
||||
);
|
||||
}
|
||||
}, "Check append method with not normalized values");
|
||||
|
||||
test(function () {
|
||||
var headers = new Headers();
|
||||
for (const name in expectations) {
|
||||
headers.set(name, expectations[name][0]);
|
||||
const expected = expectations[name][1];
|
||||
assert_equals(
|
||||
headers.get(name),
|
||||
expected,
|
||||
"name: " + name + " has value: " + expected,
|
||||
);
|
||||
}
|
||||
}, "Check set method with not normalized values");
|
Loading…
Add table
Reference in a new issue