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
Add handling for fragmented frames in WebSockets #7091
Add handling for fragmented frames in WebSockets #7091
Conversation
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 looking great so far, very nice work!
ember-server/jvm/src/test/scala/org/http4s/ember/server/EmberServerWebSocketSuite.scala
Outdated
Show resolved
Hide resolved
ember-server/shared/src/main/scala/org/http4s/ember/server/internal/WebSocketHelpers.scala
Outdated
Show resolved
Hide resolved
ember-server/shared/src/main/scala/org/http4s/ember/server/internal/WebSocketHelpers.scala
Outdated
Show resolved
Hide resolved
ember-server/shared/src/main/scala/org/http4s/ember/server/internal/WebSocketHelpers.scala
Outdated
Show resolved
Hide resolved
…erverWebSocketSuite.scala Co-authored-by: Arman Bilge <armanbilge@gmail.com>
aec3a7c
to
c6b94dc
Compare
cd9102a
to
8ffd86f
Compare
8ffd86f
to
0e1cf4b
Compare
Now I’m considering the configurable part, and I have a image like this: By default, we aggregate the incoming fragmented frame by using def httpApp[F[_]](wsBuilder: WebSocketBuilder2[F]): HttpApp[F] =
HttpRoutes
.of[F] {
case GET -> Root / “xxx” =>
...
wsBuilder
// User can provide their own WebSocketFrameAggregator implementation.
.withWebSocketFrameAggregator(CustomWebSocketFrameAggregator.apply[F]())
.build(sendReceive) So users who don’t want to implicitly aggregate fragments can provide a @armanbilge Of course, feedback on other parts is also welcome! |
I like the idea of adding this as a configuration on I am wondering if it is sufficient to just expose |
Oh, yes, I agree. Thank you, I will continue working on |
Hmm, it seems that adding a parameter to a |
server/shared/src/main/scala/org/http4s/server/websocket/WebSocketBuilder2.scala
Show resolved
Hide resolved
10a1a47
to
e35d735
Compare
It looks ok to me, so I've just changed the PR status :) |
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.
Everything is looking very nice, for happy-path 😄 I have some minor comments about naming and optimizations.
However, it's less clear how the not-so-happy path is handled e.g. if continuation frames are missing or of the wrong type. Actually I realized that maybe we don't need to consider this here: the server should be rejecting those anyway before they reach the defragmenter. If that's the case, then let's document on here that this defragmenter expects a valid sequence and will produce wrong results otherwise.
server/shared/src/main/scala/org/http4s/server/websocket/WebSocketBuilder2.scala
Outdated
Show resolved
Hide resolved
tests/shared/src/test/scala/org/http4s/websocket/WebSocketFrameAggregatorSuite.scala
Outdated
Show resolved
Hide resolved
tests/shared/src/test/scala/org/http4s/websocket/WebSocketFrameAggregatorSuite.scala
Outdated
Show resolved
Hide resolved
core/shared/src/main/scala/org/http4s/websocket/WebSocketFrameAggregator.scala
Outdated
Show resolved
Hide resolved
core/shared/src/main/scala/org/http4s/websocket/WebSocketFrameAggregator.scala
Outdated
Show resolved
Hide resolved
core/shared/src/main/scala/org/http4s/websocket/WebSocketFrameAggregator.scala
Outdated
Show resolved
Hide resolved
core/shared/src/main/scala/org/http4s/websocket/WebSocketFrameAggregator.scala
Outdated
Show resolved
Hide resolved
core/shared/src/main/scala/org/http4s/websocket/WebSocketFrameAggregator.scala
Outdated
Show resolved
Hide resolved
core/shared/src/main/scala/org/http4s/websocket/WebSocketFrameAggregator.scala
Outdated
Show resolved
Hide resolved
server/shared/src/main/scala/org/http4s/server/websocket/WebSocketBuilder2.scala
Outdated
Show resolved
Hide resolved
@armanbilge |
bf70fae
to
9c39fa6
Compare
Sorry, I misunderstood: is this "unified behavior" the current implementation in your PR (it seems like it?) or is this another possible implementation that you are proposing? In any case, I like what you've done here! Thanks for all your work on this :) |
@armanbilge Before receiving feedback from Arman, there were cases where there was no clear behavior for the operation when an invalid sequence was passed, e.g. some sequence pattern being defragmented and others not. Therefore, in the above commit, I tried to unified the behavior to not defragment if an invalid sequence related to fragmentation comes. Or am I missing something? |
Sorry for the slow review cycle on this. There is one more topic that I've been considering, from the linked RFC. I wonder if there is a good way to check for these conditions (reserved bit values or extensions) and automatically disable defragmentation in that case.
|
Good point, thank you.
Should these be addressed in a separate issue first? |
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.
Thanks for researching that! I agree, looks like it will need some follow-up work. Anyway, hopefully those are more rare usecases 🤔
In that case, everything here looks good to me! Nice work!
resolve #7073.
This PR adds handling for fragmented frames in WebSockets.
Currently, if a user wants to handle fragmented frames, they must explicitly include defragmentation logic in their receive handler. With this PR, the user can ensure that no fragmented frames arrive within the receive pipe they provide, and they no longer need to consider fragmentation.
This feature is enabled by default and can be turned off with
withDefragFrame
method.RFC 6455; 5.4. fragmentation