-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: headers WPT conformance #108
Conversation
host_api::HttpHeadersGuard guard = Request::is_instance(obj) | ||
? host_api::HttpHeadersGuard::Request | ||
: host_api::HttpHeadersGuard::Response; | ||
// Incoming request and incoming response headers are immutable per service worker |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand this is making the implementation spec compliant which is good, but this is also a breaking change as some applications will now throw an error because of this change. With that in my, I think this should be released as a new major version (if not in StarlingMonkey then in downstream projects)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is implemented in the request-response
usage of Headers, which Fastly maintains its own version of, we can choose to not implement this yet in js-compute-runtime to maintain this as a non-breaking change.
I'll make sure to include this in the Fastly PR at fastly/js-compute-runtime#844.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree in principle, yes. In practice I'm not too concerned about this actually being hit, so I predict that we at Fermyon will end up not treating this as a breaking change so much as a bug fix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is absolutely great, thank you for all the effort you put into it! <3
I left a whole bunch of comments, but I don't think addressing them should be too hard, and none of them point out major shortcomings of any kind.
I'd still like to take one more look, so please don't force-push changes so I can do an incremental diff.
host_api::HttpHeadersGuard guard = Request::is_instance(obj) | ||
? host_api::HttpHeadersGuard::Request | ||
: host_api::HttpHeadersGuard::Response; | ||
// Incoming request and incoming response headers are immutable per service worker |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree in principle, yes. In practice I'm not too concerned about this actually being hit, so I predict that we at Fermyon will end up not treating this as a breaking change so much as a bug fix.
host-apis/wasi-0.2.0/host_api.cpp
Outdated
if (header_name.compare(header) == 0) { | ||
return false; | ||
} | ||
std::vector<string_view> HttpHeaders::get_forbidden_request_headers() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this be better as a const&
to ensure the vec itself isn't copied, while it's also not mutated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is mean't to be mutated so we can take ownership of this list and then ensure it is properly sorted.
So the only change would be just be moving the clone into the caller, which I'm not sure is necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see. I think I'd somewhat prefer asserting that it is already properly sorted and otherwise using the original allocation. Which I guess could be done by indeed having it return a const&
and then copying it in an #ifdef DEBUG
block, sorting that, and comparing it with the original. But I agree that that is a bunch of work, so feel free to land things as-is: it's certainly not the end of the world in terms of overhead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added something along these lines.
Thanks for the positive reception and detailed feedback here, I've resolved everything that's been addressed, leaving the outstanding comments open. The |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great, thank you! A few more comments, but nothing that holds up landing
@@ -187,8 +186,7 @@ jsurl::SpecSlice URLSearchParams::serialize(JSContext *cx, JS::HandleObject self | |||
|
|||
bool URLSearchParams::append(JSContext *cx, unsigned argc, JS::Value *vp) { | |||
METHOD_HEADER(2) | |||
bool err = false; | |||
auto value = append_impl_validate(cx, args[0], &err, "append"); | |||
auto value = append_impl_validate(cx, args[0], "append"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this also do a result check for value
? If there's a reason it doesn't, mind adding a comment explaining it? (Apologies for not catching this in the last review.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The URL iteration doesn't do key validation before value validation like the headers one does currently, although further investigation into the failing URL tests might indicate implementing this can resolve some remaining WPT there. As a headers PR I didn't want to get into that here though.
host-apis/wasi-0.2.0/host_api.cpp
Outdated
if (header_name.compare(header) == 0) { | ||
return false; | ||
} | ||
std::vector<string_view> HttpHeaders::get_forbidden_request_headers() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see. I think I'd somewhat prefer asserting that it is already properly sorted and otherwise using the original allocation. Which I guess could be done by indeed having it return a const&
and then copying it in an #ifdef DEBUG
block, sorting that, and comparing it with the original. But I agree that that is a bunch of work, so feel free to land things as-is: it's certainly not the end of the world in terms of overhead.
This fixes 40 web platform tests, without any WPT regressions, related to the headers subsystem including header encoding, validation, immutable guards, header ordering, multi-value sorting, iteration.
In addition it implements the
Headers.prototype.getSetCookie
method, resolving all tests for it.Resolves:
getSetCookie
: ImplementHeaders.prototypeg.getSetCookie
#109Headers in the
CachedInContent
andContentOnly
state are stored as an ordered vector ofhost_api::HostString
to match the representation on the wire, without the need for reencoding across the host API calls.A separate
sort_list
is maintained separately representing the ordered iteration through the headers, and iterator and getter functions are updated to use this list when appropriate.In addition header keys are stored with their original casing to match WhatWG, with equality and ordering defined using case-invariant comparison operations.
Header casing tests on the wire are not passing since Wasmtime will lowercase headers, but if the WASI implementation were to support header casing then this would work (eg js-compute-runtime and Jco may choose to support header casing under this implementation).
Guard validations need to happen on every append so the guard logic is moved back into the main headers implementation, with new host calls
get_forbidden_request_headers()
andget_forbidden_response_headers()
used to obtain the information for the header guards.