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

No function clause error for some invocations of Mint.WebSocket.Frame.translate/1 #32

Closed
bfolkens opened this issue Mar 28, 2023 · 4 comments · Fixed by #33
Closed

No function clause error for some invocations of Mint.WebSocket.Frame.translate/1 #32

bfolkens opened this issue Mar 28, 2023 · 4 comments · Fixed by #33

Comments

@bfolkens
Copy link
Contributor

bfolkens commented Mar 28, 2023

I've been running across a no function clause error in lib/mint/web_socket/frame.ex:380 for Mint.WebSocket.Frame.translate/1 when connecting to some 3rd party services.

The triggering call is the following:

Mint.WebSocket.Frame.translate({:continuation, <<0::size(3)>>, nil, "[data here]", true)

It looks like an opcode <<0::size(3)>> is negatively matched against (below), but there are no other translate/1 functions to match <<0::size(3)>> in a continuation.

when reserved != <<0::size(3)>> do

Meanwhile, the spec (https://www.rfc-editor.org/rfc/rfc6455) Section 5.2 seems to only define opcode of size(4), which is successfully matched from @opcodes for all other requests.

I'm happy to submit a patch, but I'm not that familiar with the protocol to know the alternative. I'm assuming there's a good reason why the negative against <<0::size(3)>> is in place?

@the-mikedavis
Copy link
Collaborator

What is the backtrace for the function clause error you see? Also, if the data isn't sensitive, could you attach a packet capture using something like wireshark for a session that causes this?

translate/1 shouldn't be called on continuation frames or frames that haven't had their extensions decoded yet. The reserved bits are used by extensions to describe something about the frame specific to the extension. The block that matches on reserved != <<0::size(3)>> is for this part of the spec in section 5.2:

If a nonzero value is received and none of the negotiated extensions defines the meaning of such a nonzero value, the receiving endpoint MUST Fail the WebSocket Connection.

Once translate/1 is called by decode/2, the frame should have already been decoded by extensions and any continuation frames should be combined with their appropriate text or binary frame (see resolve_fragments/3).

@bfolkens
Copy link
Contributor Author

Thanks for the quick reply and added detail. That helps my understanding a bit - I'll spend some more time tracing this to see if I can figure out why this is happening.

Here's the stack trace:

Elixir.FunctionClauseError no function clause matches 
    lib/mint/web_socket/frame.ex:380 Mint.WebSocket.Frame.translate({:continuation, <<0::size(3)>>, nil, "[data here]" <> ..., true})
    lib/mint/web_socket/frame.ex:216 anonymous fn/2 in Mint.WebSocket.Frame.decode/2
    lib/enum.ex:2468 Enum."-reduce/3-lists^foldl/2-0-"/3
    lib/mint/web_socket/frame.ex:209 Mint.WebSocket.Frame.decode/2
    lib/connector.ex:166 Watchtower.Connector.handle_responses/2
    lib/connector.ex:121 Watchtower.Connector.handle_info/2
    gen_server.erl:1123 :gen_server.try_dispatch/4
    gen_server.erl:1200 :gen_server.handle_msg/6

I'll see if I can get the raw packets. Unfortunately, this happens intermittently in production, and since there's about 1-5 Mb/s of traffic being processed it may not be trivial to capture the problem sequence.

@the-mikedavis
Copy link
Collaborator

Even if this behavior is unexpected, raising a function clause error is really not ideal. We could add a clause to translate/1 that returns an error like {:error, :unexpected_continuation}.

One other thing to look for: in the code that calls Mint.WebSocket.decode/2 make sure to use the returned websocket value rather than the one passed in. Unfinished frames are stored in the Mint.WebSocket struct so re-using an old %Mint.WebSocket{} would discard an unfinished text or binary frame that the continuation could belong to.

@bfolkens
Copy link
Contributor Author

Yeah I agree - at least with an error tuple the developer can catch flaky websocket servers (which is what it appears like in my case). The problem server hasn't sent junk packets for over a week now, so although I won't be able to test this in production at the moment, I've included a test case with the patch that should cover the event above.

@the-mikedavis the-mikedavis linked a pull request Apr 16, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants