Skip to content

Commit e8a343a

Browse files
authored
fix: ensure message id doesn't raise on non-map payloads (#1534)
1 parent c4ba2aa commit e8a343a

File tree

4 files changed

+88
-2
lines changed

4 files changed

+88
-2
lines changed

lib/realtime_web/channels/realtime_channel/message_dispatcher.ex

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ defmodule RealtimeWeb.RealtimeChannel.MessageDispatcher do
2525
# This reduce caches the serialization and bypasses the channel process going straight to the
2626
# transport process
2727

28-
message_id = msg.payload["meta"]["id"]
28+
message_id = message_id(msg.payload)
2929

3030
# Credo doesn't like that we don't use the result aggregation
3131
_ =
@@ -62,6 +62,9 @@ defmodule RealtimeWeb.RealtimeChannel.MessageDispatcher do
6262
:ok
6363
end
6464

65+
defp message_id(%{"meta" => %{"id" => id}}), do: id
66+
defp message_id(_), do: nil
67+
6568
defp already_replayed?(nil, _replayed_message_ids), do: false
6669
defp already_replayed?(message_id, replayed_message_ids), do: MapSet.member?(replayed_message_ids, message_id)
6770

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ defmodule Realtime.MixProject do
44
def project do
55
[
66
app: :realtime,
7-
version: "2.50.0",
7+
version: "2.50.1",
88
elixir: "~> 1.17.3",
99
elixirc_paths: elixirc_paths(Mix.env()),
1010
start_permanent: Mix.env() == :prod,

test/realtime_web/channels/realtime_channel/message_dispatcher_test.exs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,49 @@ defmodule RealtimeWeb.RealtimeChannel.MessageDispatcherTest do
112112
refute_receive _any
113113
end
114114

115+
test "payload is not a map" do
116+
parent = self()
117+
118+
subscriber_pid =
119+
spawn(fn ->
120+
loop = fn loop ->
121+
receive do
122+
msg ->
123+
send(parent, {:subscriber, msg})
124+
loop.(loop)
125+
end
126+
end
127+
128+
loop.(loop)
129+
end)
130+
131+
from_pid = :erlang.list_to_pid(~c'<0.2.1>')
132+
133+
subscribers = [
134+
{subscriber_pid, {:rc_fastlane, self(), TestSerializer, "realtime:topic", {:log, "tenant123"}, MapSet.new()}},
135+
{subscriber_pid, {:rc_fastlane, self(), TestSerializer, "realtime:topic", MapSet.new()}}
136+
]
137+
138+
msg = %Broadcast{topic: "some:other:topic", event: "event", payload: "not a map"}
139+
140+
log =
141+
capture_log(fn ->
142+
assert MessageDispatcher.dispatch(subscribers, from_pid, msg) == :ok
143+
end)
144+
145+
assert log =~ "Received message on realtime:topic with payload: #{inspect(msg, pretty: true)}"
146+
147+
assert_receive {:encoded, %Broadcast{event: "event", payload: "not a map", topic: "realtime:topic"}}
148+
assert_receive {:encoded, %Broadcast{event: "event", payload: "not a map", topic: "realtime:topic"}}
149+
150+
assert Agent.get(TestSerializer, & &1) == 1
151+
152+
assert_receive {:subscriber, :update_rate_counter}
153+
assert_receive {:subscriber, :update_rate_counter}
154+
155+
refute_receive _any
156+
end
157+
115158
test "dispatches messages to non fastlane subscribers" do
116159
from_pid = :erlang.list_to_pid(~c'<0.2.1>')
117160

test/realtime_web/channels/realtime_channel_test.exs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,46 @@ defmodule RealtimeWeb.RealtimeChannelTest do
3131
describe "broadcast" do
3232
@describetag policies: [:authenticated_all_topic_read]
3333

34+
test "broadcast map payload", %{tenant: tenant} do
35+
jwt = Generators.generate_jwt_token(tenant)
36+
{:ok, %Socket{} = socket} = connect(UserSocket, %{}, conn_opts(tenant, jwt))
37+
38+
config = %{
39+
"presence" => %{"enabled" => false},
40+
"broadcast" => %{"self" => true}
41+
}
42+
43+
assert {:ok, _, socket} = subscribe_and_join(socket, "realtime:test", %{"config" => config})
44+
45+
push(socket, "broadcast", %{"event" => "my_event", "payload" => %{"hello" => "world"}})
46+
47+
assert_receive %Phoenix.Socket.Message{
48+
topic: "realtime:test",
49+
event: "broadcast",
50+
payload: %{"event" => "my_event", "payload" => %{"hello" => "world"}}
51+
}
52+
end
53+
54+
test "broadcast non-map payload", %{tenant: tenant} do
55+
jwt = Generators.generate_jwt_token(tenant)
56+
{:ok, %Socket{} = socket} = connect(UserSocket, %{}, conn_opts(tenant, jwt))
57+
58+
config = %{
59+
"presence" => %{"enabled" => false},
60+
"broadcast" => %{"self" => true}
61+
}
62+
63+
assert {:ok, _, socket} = subscribe_and_join(socket, "realtime:test", %{"config" => config})
64+
65+
push(socket, "broadcast", "not a map")
66+
67+
assert_receive %Phoenix.Socket.Message{
68+
topic: "realtime:test",
69+
event: "broadcast",
70+
payload: "not a map"
71+
}
72+
end
73+
3474
test "wrong replay params", %{tenant: tenant} do
3575
jwt = Generators.generate_jwt_token(tenant)
3676
{:ok, %Socket{} = socket} = connect(UserSocket, %{"log_level" => "warning"}, conn_opts(tenant, jwt))

0 commit comments

Comments
 (0)