Skip to content

Commit d05c083

Browse files
authored
Add test coverage to packet stream and wire protocol modules (#429)
* add comment * validate content length * add comment closes #236 * add packet stream tests * avoid binary copying by using IO data avoid sending unneeded /r/n/r/n after body * add wire protocol tests * run formatter
1 parent 5b2d236 commit d05c083

19 files changed

+237
-9
lines changed

apps/elixir_ls_utils/lib/packet_stream.ex

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,18 @@ defmodule ElixirLS.Utils.PacketStream do
1212
fn -> :ok end,
1313
fn _acc ->
1414
case read_packet(pid) do
15-
:eof -> {:halt, :ok}
16-
{:error, reason} -> {:halt, {:error, reason}}
17-
{:ok, packet} -> {[packet], :ok}
15+
:eof ->
16+
{:halt, :ok}
17+
18+
{:error, reason} ->
19+
# jsonrpc 2.0 requires that server responds with
20+
# {"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}
21+
# when message fails to parse
22+
# instead we halt on any error - it's not woth to handle faulty clients
23+
{:halt, {:error, reason}}
24+
25+
{:ok, packet} ->
26+
{[packet], :ok}
1827
end
1928
end,
2029
fn
@@ -70,9 +79,22 @@ defmodule ElixirLS.Utils.PacketStream do
7079
case get_content_length(header) do
7180
{:ok, content_length} ->
7281
case IO.binread(pid, content_length) do
73-
:eof -> :eof
74-
{:error, reason} -> {:error, reason}
75-
body -> JasonVendored.decode(body)
82+
:eof ->
83+
:eof
84+
85+
{:error, reason} ->
86+
{:error, reason}
87+
88+
body ->
89+
case IO.iodata_length(body) do
90+
^content_length ->
91+
# though jason docs suggest using `strings: :copy` when parts of binary may be stored
92+
# processes/ets in our case it does not help (as of OTP 23)
93+
JasonVendored.decode(body)
94+
95+
_other ->
96+
{:error, :truncated}
97+
end
7698
end
7799

78100
other ->

apps/elixir_ls_utils/lib/wire_protocol.ex

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,24 @@ defmodule ElixirLS.Utils.WireProtocol do
44
"""
55
alias ElixirLS.Utils.{PacketStream, OutputDevice}
66

7+
@separator "\r\n\r\n"
8+
79
def send(packet) do
810
pid = io_dest()
9-
body = JasonVendored.encode!(packet) <> "\r\n\r\n"
10-
11-
case IO.binwrite(pid, "Content-Length: #{byte_size(body)}\r\n\r\n" <> body) do
11+
body = JasonVendored.encode_to_iodata!(packet)
12+
13+
case IO.binwrite(pid, [
14+
"Content-Length: ",
15+
IO.iodata_length(body) |> Integer.to_string(),
16+
@separator,
17+
body
18+
]) do
1219
:ok ->
1320
:ok
1421

1522
{:error, reason} ->
1623
IO.warn("Unable to write to the device: #{inspect(reason)}")
24+
:ok
1725
end
1826
end
1927

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Content-Length: 16
2+
3+
{"some":"value"}Content-Length: 16
4+
5+
{"some":"value1"Content-Length: 16
6+
7+
{"some":"value"}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Content-Length: 16
2+
3+
{"some":"val
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Content-Length: 16
2+
Content-Type: text/plain
3+
4+
{"some":"value"}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Content-Length: 15
2+
3+
{"some":"value}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Content-Length: 15
2+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Content-Length: 15
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Content-Length: 15
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Content-Len

0 commit comments

Comments
 (0)