Skip to content
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

Ember server fix frames #7059

Open
wants to merge 7 commits into
base: series/0.23
Choose a base branch
from

Conversation

acteek
Copy link

@acteek acteek commented Apr 12, 2023

Fixes #6639 Fixes #5128

  • Sometimes frames got corrupted when writing to a socket. This is because the frame headers and payload are not atomically written to the socket and the order can be changed.
  • Provide CloseFrame to user's handler

@mergify mergify bot added series/0.23 PRs targeting 0.23.x module:ember-server labels Apr 12, 2023
@acteek
Copy link
Author

acteek commented Apr 12, 2023

Fixes #6639 #5128

@armanbilge armanbilge self-requested a review April 12, 2023 14:41
@armanbilge
Copy link
Member

Thanks, this is great! Any chance we can add some test coverage for at least one of these fixes? 🙂

Copy link
Member

@danicheg danicheg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with @armanbilge, a dedicated test for the fixed case would be highly appreciated.

@acteek
Copy link
Author

acteek commented Apr 12, 2023

@danicheg @armanbilge I've added some tests

@acteek acteek requested a review from danicheg April 12, 2023 19:01
Comment on lines 119 to 123
def writeFrame(frame: WebSocketFrame): F[Unit] =
frameToBytes(frame).traverse_(c => timeoutMaybe(socket.write(c), idleTimeout))

def writeFrame(frame: WebSocketFrame): F[Unit] = {
val bytes = frameToBytes(frame)
timeoutMaybe(socket.write(bytes), idleTimeout)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes frames got corrupted when writing to a socket. This is because the frame headers and payload are not atomically written to the socket and the order can be changed.

Sorry, I'm having a hard time understanding this. How can the order be changed? Sequential calls to write should be sent sequentially on the socket. If that's not the case, I suspect that there is a very serious bug with the JDK/OS.

Can you provide a test that reproduces this issue? I tried running the test you added "do not corrupt frames if handle them concurrently" several times with the old code but it always passed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, do you mean that concurrent writes can be interleaved? I think this is different from an ordering problem: the order is correct, but concurrent writes are being mixed up with each other.

Array[ByteBuffer](buff)
} else {
buff.flip
Array[ByteBuffer](buff, in.data.toByteBuffer)

Now I see what you mean about the headers and the payload being separated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we just change the frame transcoder to copy the data instead the same, singular byte buffer, and return that? in.data.toByteBuffer is probably copying anyway, so might as well copy it into a single buffer.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@armanbilge Yes, you totally right - headers and payload for several frames can be mixed with each other.
For example:
Right case: [header1->payload1, header2->payload2]
Wrong case: [header1->header2, payload1->payload2]

Sure, we can change FrameTranscoder and return one byte buffer for frame in frameToBuffer.
I will add a commit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation @acteek!

Sorry, I'm actually still confused in what situation this can happen.

To create a WebSocket application, you use the WebSocketBuilder. Specifically, to send frames to the client you run a Stream. Because a Stream is sequential, there should never be concurrent writes to the socket.

I think this also explains why the test does not reproduce the issue.

Sometimes frames got corrupted when writing to a socket.

How did you discover this? It would be very helpful if we could see the original reproducer, so that we can solve the right problem.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@acteek oh, are you using Ember Server on Node.js?

Stream is sequential until we do not run async effect to write to something through IO. After that, we are not able to guarantee element sequential.

This sounds like a bug then that maybe we need to fix in FS2. Writing to a socket should always be sequential.

Copy link
Author

@acteek acteek Apr 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, are you using Ember Server on Node.js?

Oh, Sorry, I use a JVM server. But I think the main cause is the same - socket.write is an async function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@acteek got it, thanks for clarifying that!

But I think the main cause is the same - socket.write is an async function.

I'm not sure that's right—even though write is async, we backpressure until the write completes. But if that is right, then this is a serious issue in our Socket layer, because we rely on this assumption not just in websockets, but also in the rest of Ember, and also Skunk, and any other libraries using FS2 socket. So everything would break and we need to fix the root issue.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @armanbilge.
Do you have a plan for merging this solution for broken frames? Or are we gonna try to fix it on FS2 side? I'd like to avoid local fixies for my project and use it as a library solution.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @acteek, sorry for letting this hang. As I described above, the symptoms you are describing sounds like a serious problem that would affect all projects using FS2 sockets. But without a reproducer of the issue it's not clear to me what the actual problem is. Is there any way you would be able to help with that?

@mergify mergify bot added the module:core label Apr 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Handle WS close frames from user in ember server EmberWebSocketSuite: Unknown opcode 3
3 participants