Skip to content
This repository was archived by the owner on Nov 25, 2025. It is now read-only.

feat: allow independent retrieval of trailers#154

Merged
dicej merged 1 commit intoWebAssembly:mainfrom
vados-cosmonic:refactor=require-independent-retrieval-of-body-trailers
Mar 10, 2025
Merged

feat: allow independent retrieval of trailers#154
dicej merged 1 commit intoWebAssembly:mainfrom
vados-cosmonic:refactor=require-independent-retrieval-of-body-trailers

Conversation

@vados-cosmonic
Copy link
Contributor

@vados-cosmonic vados-cosmonic commented Feb 19, 2025

This commit changes wasi:http/types#body.finish to forward the optionally present trailers, rather than retrieve them with the possibility of generating an error along the way.

With this change, callers of body.finish will have to retreive trailers on their own, which may produce an efficiency gain in the case where trailers are present but not needed, and avoiding work of checking for trailers in the first place.

👀 Implementation preview

As this is a WASI p3 feature (for which work is ongoing in the wasip3-prototyping repository), you can "preview" the effects of this change on the implementation in the PR there.

@dicej
Copy link
Collaborator

dicej commented Feb 19, 2025

@vados-cosmonic I'm wondering if we should stick with the option<future<trailers>> param type in the body constructor. I realize it's asymmetric with respect to body.finish, but it's arguably ideal from an ergonomics standpoint given that most applications will neither send nor receive trailers. When creating a new body without trailers, the easiest thing to do is specify none rather than go through the motions of creating a new future just to send none to it (or close it without sending anything, which I suppose would be morally equivalent).

Obviously, the asymmetry implies that the implementation will need to transpose a option<future<trailers>> to a future<option<trailers>> in some scenarios, but I expect that won't be difficult.

@dicej
Copy link
Collaborator

dicej commented Feb 19, 2025

Not to complicate things further, but we could even make body.finish return a future<trailers> and assume the write end will close the future without sending anything in the case there are no trailers. EDIT: Turns out this was an invalid suggestion based on my misunderstanding of when the owner of the write end of a future is allowed to close it without writing anything to it.

@vados-cosmonic
Copy link
Contributor Author

vados-cosmonic commented Feb 20, 2025

Hey @dicej waht do you think about actually changing the constructor for body to one without trailers and separating the ability to add/set trailers (either a different constructor or a setter are both fine).

Obviously, the asymmetry implies that the implementation will need to transpose a option<future> to a future<option> in some scenarios, but I expect that won't be difficult.

So I found this difficult to do in the impl without creating another future during finish -- transposing the Option into the FutureReader goes across the async boundary and the types don't quite permit me to change the Tto an Option<T> whether it's in the host machinery or otherwise. Maybe I was missing something obvious!

Not to complicate things further, but we could even make body.finish return a future and assume the write end will close the future without sending anything in the case there are no trailers.

This is certainly an interesting idea -- does this make it a little more difficult in that the write end has to remember to close the future? If this introduces the possibility of a hang due to incorrectly implemented writers it might increase the hurdle.

This also kind of makes me want to separate the common case and the with-trailers case into different functions, what do you think?

@pchickey if you want to chime in on the expanded interface changes (separating with trailers functions out), would appreciate it!

@dicej
Copy link
Collaborator

dicej commented Feb 20, 2025

So I found this difficult to do in the impl without creating another future during finish -- transposing the Option into the FutureReader goes across the async boundary and the types don't quite permit me to change the Tto an Option<T> whether it's in the host machinery or otherwise. Maybe I was missing something obvious!

Yeah, you'd need to create another future, although I think we can avoid that if finish returns future<trailers> instead of future<option<trailers>>.

This is certainly an interesting idea -- does this make it a little more difficult in that the write end has to remember to close the future?

They will either need to remember to send none or close the future, and I don't think the latter is more onerous than the former.

@dicej
Copy link
Collaborator

dicej commented Feb 20, 2025

BTW, I'm also fine with removing the trailers parameter from the constructor and e.g. adding a new new-with-trailers: static func(%stream: stream<u8>, trailers: future<trailers>) -> body; function.

@vados-cosmonic
Copy link
Contributor Author

vados-cosmonic commented Feb 20, 2025

Yeah, you'd need to create another future, although I think we can avoid that if finish returns future instead of future<option>.

I'm in favor of this!

BTW, I'm also fine with removing the trailers parameter from the constructor and e.g. adding a new new-with-trailers: static func(%stream: stream, trailers: future) -> body; function.

A tiny nit, but I personally like just with-trailers (but given that new is a thing, consistency is probably better here).

Will re-write the impl and make updates here with that in mind.

@dicej
Copy link
Collaborator

dicej commented Feb 26, 2025

Okay, turns out I was wrong about how future works. The owner of the write end of a future is in fact not allowed to close it without either writing a value to it or closing it with an error-context. So my suggestion that we could use future<trailers> and interpret a close-without-write as "there were no trailers" rather than "an error occurred" is invalid.

That means that we must make body.finish return either option<future<option<trailers>> or future<option<trailers>> if there's a chance that the implementation won't know for sure whether trailers are on their way by the time it returns a result. I.e. returning option<future<trailers>> would imply that implementations have read to the end of the content stream before returning, which I don't think is what we want (or if it is what we want, we can just go back to returning option<trailers> and assume finish will be called async). Does that make sense?

@vados-cosmonic vados-cosmonic force-pushed the refactor=require-independent-retrieval-of-body-trailers branch 2 times, most recently from 2c21aa7 to 22105a9 Compare March 3, 2025 19:04
This commit changes wasi:http/types#body.finish to forward the
optionally present trailers, rather than retrieve them with the
possibility of generating an error along the way.

With this change, callers of `body.finish` will have to retreive
trailers on their own, which *may* produce an efficiency gain in the
case where trailers are present but not needed, and avoiding work of
checking for trailers in the first place.

Signed-off-by: Victor Adossi <vadossi@cosmonic.com>
@vados-cosmonic vados-cosmonic force-pushed the refactor=require-independent-retrieval-of-body-trailers branch from 22105a9 to 218295d Compare March 3, 2025 19:05
@dicej dicej merged commit d8cca5c into WebAssembly:main Mar 10, 2025
1 check passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants