Skip to content

Commit 2dbddeb

Browse files
committed
Improve the errors returned from ReadJSON.
The JSON decoder returns io.EOF when a message is empty or all whitespace. Convert io.EOF return values from the JSON decoder to io.ErrUnexpectedEOF so that applications can distinguish between an error reading the JSON value and the connection closing.
1 parent ab5b3a6 commit 2dbddeb

File tree

2 files changed

+65
-1
lines changed

2 files changed

+65
-1
lines changed

json.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package websocket
66

77
import (
88
"encoding/json"
9+
"io"
910
)
1011

1112
// WriteJSON is deprecated, use c.WriteJSON instead.
@@ -45,5 +46,12 @@ func (c *Conn) ReadJSON(v interface{}) error {
4546
if err != nil {
4647
return err
4748
}
48-
return json.NewDecoder(r).Decode(v)
49+
err = json.NewDecoder(r).Decode(v)
50+
if err == io.EOF {
51+
// Decode returns io.EOF when the message is empty or all whitespace.
52+
// Convert to io.ErrUnexpectedEOF so that application can distinguish
53+
// between an error reading the JSON value and the connection closing.
54+
err = io.ErrUnexpectedEOF
55+
}
56+
return err
4957
}

json_test.go

+56
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ package websocket
66

77
import (
88
"bytes"
9+
"encoding/json"
10+
"io"
911
"reflect"
1012
"testing"
1113
)
@@ -36,6 +38,60 @@ func TestJSON(t *testing.T) {
3638
}
3739
}
3840

41+
func TestPartialJsonRead(t *testing.T) {
42+
var buf bytes.Buffer
43+
c := fakeNetConn{&buf, &buf}
44+
wc := newConn(c, true, 1024, 1024)
45+
rc := newConn(c, false, 1024, 1024)
46+
47+
var v struct {
48+
A int
49+
B string
50+
}
51+
v.A = 1
52+
v.B = "hello"
53+
54+
messageCount := 0
55+
56+
// Partial JSON values.
57+
58+
data, err := json.Marshal(v)
59+
if err != nil {
60+
t.Fatal(err)
61+
}
62+
for i := len(data) - 1; i >= 0; i-- {
63+
if err := wc.WriteMessage(TextMessage, data[:i]); err != nil {
64+
t.Fatal(err)
65+
}
66+
messageCount++
67+
}
68+
69+
// Whitespace.
70+
71+
if err := wc.WriteMessage(TextMessage, []byte(" ")); err != nil {
72+
t.Fatal(err)
73+
}
74+
messageCount++
75+
76+
// Close.
77+
78+
if err := wc.WriteMessage(CloseMessage, FormatCloseMessage(CloseNormalClosure, "")); err != nil {
79+
t.Fatal(err)
80+
}
81+
82+
for i := 0; i < messageCount; i++ {
83+
err := rc.ReadJSON(&v)
84+
if err != io.ErrUnexpectedEOF {
85+
t.Error("read", i, err)
86+
}
87+
}
88+
89+
err = rc.ReadJSON(&v)
90+
if err != io.EOF {
91+
t.Error("final", err)
92+
}
93+
}
94+
3995
func TestDeprecatedJSON(t *testing.T) {
4096
var buf bytes.Buffer
4197
c := fakeNetConn{&buf, &buf}

0 commit comments

Comments
 (0)