Skip to content

Commit 1efe6e3

Browse files
authored
fix: handle carriage return \r line terminators in SSE (#772)
fix: Handle carriage return \r line terminators in SSE
1 parent 6d3a189 commit 1efe6e3

File tree

2 files changed

+33
-3
lines changed

2 files changed

+33
-3
lines changed

lib/tesla/middleware/sse.ex

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ defmodule Tesla.Middleware.SSE do
4646
|> Stream.chunk_while(
4747
"",
4848
fn elem, acc ->
49-
{lines, [rest]} = (acc <> elem) |> String.split("\n\n") |> Enum.split(-1)
49+
{lines, [rest]} =
50+
(acc <> elem)
51+
|> String.split(double_linebreak_regex())
52+
|> Enum.split(-1)
53+
5054
{:cont, lines, rest}
5155
end,
5256
fn
@@ -61,14 +65,16 @@ defmodule Tesla.Middleware.SSE do
6165

6266
defp decode_body(binary, opts) when is_binary(binary) do
6367
binary
64-
|> String.split("\n\n")
68+
|> String.split(double_linebreak_regex())
6569
|> Enum.map(&decode_message/1)
6670
|> Enum.flat_map(&only(&1, opts[:only]))
6771
end
6872

73+
defp double_linebreak_regex(), do: ~r/((\r\n)|((?<!\r)\n)|(\r(?!\n))){2}/
74+
6975
defp decode_message(message) do
7076
message
71-
|> String.split("\n")
77+
|> String.split(["\r\n", "\n", "\r"])
7278
|> Enum.map(&decode_body/1)
7379
|> Enum.reduce(%{}, fn
7480
:empty, acc -> acc

test/tesla/middleware/sse_test.exs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,5 +120,29 @@ defmodule Tesla.Middleware.SSETest do
120120

121121
assert Enum.to_list(env.body) == [%{data: "data1"}, %{data: "data2"}, %{data: "data3"}]
122122
end
123+
124+
test "handle stream data with varying line terminators" do
125+
adapter = fn _env ->
126+
chunks = [
127+
~s|data: data1\n|,
128+
~s|\ndata: data2\r|,
129+
~s|\r\ndata: data3\r\revent: event4\r|,
130+
~s|\ndata: data4\n\n|
131+
]
132+
133+
stream = Stream.map(chunks, & &1)
134+
135+
{:ok, %{@env | body: stream}}
136+
end
137+
138+
assert {:ok, env} = Tesla.Middleware.SSE.call(%Tesla.Env{}, [{:fn, adapter}], [])
139+
140+
assert Enum.to_list(env.body) == [
141+
%{data: "data1"},
142+
%{data: "data2"},
143+
%{data: "data3"},
144+
%{event: "event4", data: "data4"}
145+
]
146+
end
123147
end
124148
end

0 commit comments

Comments
 (0)