You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The async lifetime of a HttpSocket can often be handled by only 1 single callback at any given time. onAborted is only needed for when you neither wait for a body, nor wait for backpressure (that is, you are just waiting for something else).
onDataOrAborted & onWritableOrAborted, collectBodyOrAborted are decent names but they could need some thinking. Maybe they are too long. In any case, they would register the onAborted handler for you, and deliver abortion events to the onData or onWritable handler so you only need 1 callback at any given time.
onDataOrFail is an alternative, onDataV3 is another alternative onMaybeData is another
changed the title [-]onDataOrAbort & onWritableOrAbort, collectBodyOrAbort[/-][+]onDataOrAborted & onWritableOrAborted, collectBodyOrAborted[/+]on Oct 4, 2026
changed the title [-]onDataOrAborted & onWritableOrAborted, collectBodyOrAborted[/-][+]onDataOrAborted & onWritableOrAborted, maybeCollectBody[/+]on Oct 4, 2026
I like it, it is the onStream idea but one per event, and the C++ lib stay as it is. Some points to decide:
How the abort arrives. onDataV2 can use chunk === null, onWritable offset === -1 (return value ignored). collectBody already uses null for a body over maxSize, and there you can still answer 413, after an abort you cannot: so it needs another value, undefined or a second argument.
After the last chunk. The usual handler reads the body, awaits a db and answers. If the abort keeps going to the last ...OrAborted handler until the response ends, as you wrote in onAborted + onData -> new single onStream callback #1245 for onStream, that handler needs no onAborted at all. If not, it still needs onAborted for the await, and the gain is small.
An onAborted already set. The wrapper can keep one abort dispatcher that calls the current ...OrAborted handler and also the onAborted one, so nothing is overwritten (the problem of March in Add HTTP body reader helper #1159).
Ok for v21. Then onData, onDataV2, collectBody and onWritable install the abort themselves, and their handler gets undefined (chunk, body or offset).
Two questions:
A handler with both onData and onWritable (an upload while the response has backpressure): which one gets the abort? I would call both, once each.
After the last chunk, the abort still goes to onData until the response ends, right? That is what lets "read the body, await a db, answer" use one callback.
Most simple async cases will only use it to set an isAborted boolean.
Can't we just create that isAborted boolean
You are pressing on a fundamental difference between a GC language and a RAII language. In GC languages, you wouldn't even have isAborted you would just throw an exception if you tried to write to an aborted socket. In other words, GC languages are "poll based", you just do whatever you want and handle the exceptions when you "poll" their state. In Rust, C++ which are RAII languages, everything is "event based", you get the event that an object has been destructed, and from that point you cannot even store the isAborted because where do you store isAborted = true if the object has been destructed? So the claim that most simply set an isAborted is not really true - it's true in JS because JS is a script with GC, but in Rust, C++ you would handle the event of destruction by removing your async operation at that very point.
The problem here is that onAborted is very non-elegant because you would rather want it inside onData so you don't need to capture a shared_ptr from onAborted, also because you typically either wait for data, backpressure OR aborted
Activity
[-]onDataOrAbort & onWritableOrAbort, collectBodyOrAbort[/-][+]onDataOrAborted & onWritableOrAborted, collectBodyOrAborted[/+][-]onDataOrAborted & onWritableOrAborted, collectBodyOrAborted[/-][+]onDataOrAborted & onWritableOrAborted, maybeCollectBody[/+]nigrosimone commentedon Oct 4, 2026
I like it, it is the onStream idea but one per event, and the C++ lib stay as it is. Some points to decide:
chunk === null, onWritableoffset === -1(return value ignored). collectBody already usesnullfor a body over maxSize, and there you can still answer 413, after an abort you cannot: so it needs another value,undefinedor a second argument....OrAbortedhandler until the response ends, as you wrote in onAborted + onData -> new single onStream callback #1245 for onStream, that handler needs no onAborted at all. If not, it still needs onAborted for the await, and the gain is small....OrAbortedhandler and also the onAborted one, so nothing is overwritten (the problem of March in Add HTTP body reader helper #1159).onDataOrAbort,onWritableOrAbort,collectBodyOrAbort. collectJson (POC: collectJson, the body parsed by JSON::Parse, to a callback or a promise #1348) can follow the same rule.All of it is in the wrapper, nothing in C++. If you want, I can make a PR.
uNetworkingAB commentedon Oct 4, 2026
Regarding onStream, in v21 you could entirely remove onAborted and just pass optional chunk, or optional offset in onData or onWritable.
Then you just change the rule that you must attach onAborted handler to: you must attach either onData or onWritable if you go async.
uNetworkingAB commentedon Oct 4, 2026
So if you just want to wait, you would attach onData since that one maps well to "receiving FIN or RST" in Unix
nigrosimone commentedon Oct 4, 2026
Ok for v21. Then onData, onDataV2, collectBody and onWritable install the abort themselves, and their handler gets
undefined(chunk, body or offset).Two questions:
uNetworkingAB commentedon Oct 4, 2026
Both gets aborts and aborts are delivered even after isLast.
v21 is breaking change so it would not even have onDataV2, it would just be onData, onWritable.
You can even see the misdesign of onAborted in this example I made myself: https://github.com/uNetworking/uWebSockets/blob/e2ec2d7af011203420e782fb649d27934fc2bdea/examples/Crc32.cpp#L47
See how I need to allocate a shared_ptr for isAborted that's insane.
ValoChet commentedon Oct 6, 2026
Why is
res->onAborted()even mandatory?Most simple async cases will only use it to set an
isAbortedboolean.Can't we just create that
isAbortedboolean, orisFinished, and make theres->onAborted()optional, only used when we need to clear things up?In c++ this could be:
[](auto *res, auto *req, const bool *isAborted)using overloadsIn JS it would ideally be:
res.isAborteduNetworkingAB commentedon Oct 6, 2026
You are pressing on a fundamental difference between a GC language and a RAII language. In GC languages, you wouldn't even have isAborted you would just throw an exception if you tried to write to an aborted socket. In other words, GC languages are "poll based", you just do whatever you want and handle the exceptions when you "poll" their state. In Rust, C++ which are RAII languages, everything is "event based", you get the event that an object has been destructed, and from that point you cannot even store the isAborted because where do you store isAborted = true if the object has been destructed? So the claim that most simply set an isAborted is not really true - it's true in JS because JS is a script with GC, but in Rust, C++ you would handle the event of destruction by removing your async operation at that very point.
The problem here is that onAborted is very non-elegant because you would rather want it inside onData so you don't need to capture a shared_ptr from onAborted, also because you typically either wait for data, backpressure OR aborted