Skip to content

Commit ab81ad6

Browse files
author
hechenglong
committed
fix: Fixed functions of decodeJson do not return.
1 parent c639081 commit ab81ad6

File tree

3 files changed

+168
-4
lines changed

3 files changed

+168
-4
lines changed

response.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ func (r *Response) readAll() (err error) {
237237
} else {
238238
r.bodyBytes, err = ioReadAll(r.Body)
239239
closeq(r.Body)
240-
r.Body = &nopReadCloser{r: bytes.NewReader(r.bodyBytes)}
240+
r.Body = &nopReadCloser{r: bytes.NewReader(r.bodyBytes), resetOnEOF: true}
241241
}
242242
if err == io.ErrUnexpectedEOF {
243243
// content-encoding scenario's - empty/no response body from server
@@ -265,7 +265,7 @@ func (r *Response) wrapCopyReadCloser() {
265265
f: func(b *bytes.Buffer) {
266266
r.bodyBytes = append([]byte{}, b.Bytes()...)
267267
closeq(r.Body)
268-
r.Body = &nopReadCloser{r: bytes.NewReader(r.bodyBytes)}
268+
r.Body = &nopReadCloser{r: bytes.NewReader(r.bodyBytes), resetOnEOF: true}
269269
releaseBuffer(b)
270270
},
271271
}

stream.go

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,30 @@ func encodeJSONEscapeHTMLIndent(w io.Writer, v any, esc bool, indent string) err
5555

5656
func decodeJSON(r io.Reader, v any) error {
5757
dec := json.NewDecoder(r)
58+
59+
// Handle nopReadCloser specially to support multiple JSON objects
60+
// while preventing infinite loops
61+
if nrc, ok := r.(*nopReadCloser); ok {
62+
// Temporarily disable auto-reset to prevent infinite loops
63+
originalReset := nrc.resetOnEOF
64+
nrc.resetOnEOF = false
65+
defer func() { nrc.resetOnEOF = originalReset }()
66+
67+
// Decode all JSON objects in the data
68+
for {
69+
if err := dec.Decode(v); err == io.EOF {
70+
break
71+
} else if err != nil {
72+
return err
73+
}
74+
}
75+
76+
// After decoding, reset for future reads
77+
nrc.Reset()
78+
return nil
79+
}
80+
81+
// For other readers, decode multiple JSON objects as intended
5882
for {
5983
if err := dec.Decode(v); err == io.EOF {
6084
break
@@ -196,19 +220,25 @@ func (r *copyReadCloser) Close() error {
196220
var _ io.ReadCloser = (*nopReadCloser)(nil)
197221

198222
type nopReadCloser struct {
199-
r *bytes.Reader
223+
r *bytes.Reader
224+
resetOnEOF bool // Whether to reset on EOF
200225
}
201226

202227
func (r *nopReadCloser) Read(p []byte) (int, error) {
203228
n, err := r.r.Read(p)
204-
if err == io.EOF {
229+
if err == io.EOF && r.resetOnEOF {
205230
r.r.Seek(0, io.SeekStart)
206231
}
207232
return n, err
208233
}
209234

210235
func (r *nopReadCloser) Close() error { return nil }
211236

237+
// Reset allows manual reset of the reader position
238+
func (r *nopReadCloser) Reset() {
239+
r.r.Seek(0, io.SeekStart)
240+
}
241+
212242
var _ flate.Reader = (*nopReader)(nil)
213243

214244
type nopReader struct{}

stream_test.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package resty
2+
3+
import (
4+
"bytes"
5+
"io"
6+
"net/http"
7+
"net/http/httptest"
8+
"testing"
9+
)
10+
11+
func TestDecodeJSONWhenResponseBodyIsNull(t *testing.T) {
12+
r := &Response{
13+
Body: io.NopCloser(bytes.NewReader([]byte("null"))),
14+
}
15+
r.wrapCopyReadCloser()
16+
err := r.readAll()
17+
assertNil(t, err)
18+
19+
var result map[int]int
20+
err = decodeJSON(r.Body, &result)
21+
assertNil(t, err)
22+
assertNil(t, result)
23+
}
24+
25+
func TestGetMethodWhenResponseIsNull(t *testing.T) {
26+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
27+
w.Write([]byte("null"))
28+
}))
29+
30+
client := New().SetRetryCount(3).EnableGenerateCurlCmd()
31+
32+
var x any
33+
resp, err := client.R().SetBody("{}").
34+
SetHeader("Content-Type", "application/json; charset=utf-8").
35+
SetForceResponseContentType("application/json").
36+
SetAllowMethodGetPayload(true).
37+
SetResponseBodyUnlimitedReads(true).
38+
SetResult(&x).
39+
Get(server.URL + "/test")
40+
41+
assertNil(t, err)
42+
assertEqual(t, "null", resp.String())
43+
assertEqual(t, nil, x)
44+
}
45+
46+
func TestDecodeJSON(t *testing.T) {
47+
// Test single object
48+
jsonData := `{"name": "John", "age": 30}`
49+
reader := bytes.NewReader([]byte(jsonData))
50+
var result map[string]any
51+
err := decodeJSON(reader, &result)
52+
assertNil(t, err)
53+
assertEqual(t, "John", result["name"])
54+
assertEqual(t, float64(30), result["age"])
55+
56+
// Test multiple objects - should get the last one
57+
multipleJSON := `{"id": 1}
58+
{"id": 2}
59+
{"id": 3}`
60+
reader2 := bytes.NewReader([]byte(multipleJSON))
61+
var result2 map[string]any
62+
err = decodeJSON(reader2, &result2)
63+
assertNil(t, err)
64+
assertEqual(t, float64(3), result2["id"])
65+
66+
// Test malformed JSON
67+
malformedJSON := `{"name": "John", "age":}`
68+
reader3 := bytes.NewReader([]byte(malformedJSON))
69+
var result3 map[string]any
70+
err = decodeJSON(reader3, &result3)
71+
assertNotNil(t, err)
72+
}
73+
74+
func TestWrapCopyReadCloser(t *testing.T) {
75+
testData := "Hello, World!"
76+
r := &Response{
77+
Body: io.NopCloser(bytes.NewReader([]byte(testData))),
78+
}
79+
80+
// Before wrapping, bodyBytes should be empty
81+
assertEqual(t, 0, len(r.bodyBytes))
82+
83+
r.wrapCopyReadCloser()
84+
85+
// Read data - should trigger copy mechanism and transform to nopReadCloser
86+
data, err := io.ReadAll(r.Body)
87+
assertNil(t, err)
88+
assertEqual(t, testData, string(data))
89+
assertEqual(t, testData, string(r.bodyBytes))
90+
91+
// Should now be nopReadCloser for unlimited reads
92+
_, ok := r.Body.(*nopReadCloser)
93+
assertEqual(t, true, ok)
94+
95+
// Test unlimited reads
96+
data2, err := io.ReadAll(r.Body)
97+
assertNil(t, err)
98+
assertEqual(t, testData, string(data2))
99+
}
100+
101+
func TestMultipleJSONObjectsSupport(t *testing.T) {
102+
// Test multiple JSON objects with wrapCopyReadCloser
103+
jsonData := `{"first": 1}
104+
{"second": 2}
105+
{"third": 3}`
106+
107+
r := &Response{
108+
Body: io.NopCloser(bytes.NewReader([]byte(jsonData))),
109+
}
110+
r.wrapCopyReadCloser()
111+
112+
// Should process all objects and get the last one
113+
var result map[string]any
114+
err := decodeJSON(r.Body, &result)
115+
assertNil(t, err)
116+
assertEqual(t, float64(3), result["third"])
117+
118+
// Should support unlimited reads and decoding
119+
var result2 map[string]any
120+
err = decodeJSON(r.Body, &result2)
121+
assertNil(t, err)
122+
assertEqual(t, float64(3), result2["third"])
123+
124+
// Test direct nopReadCloser usage
125+
nopReader := &nopReadCloser{
126+
r: bytes.NewReader([]byte(jsonData)),
127+
resetOnEOF: true,
128+
}
129+
130+
var result3 map[string]any
131+
err = decodeJSON(nopReader, &result3)
132+
assertNil(t, err)
133+
assertEqual(t, float64(3), result3["third"])
134+
}

0 commit comments

Comments
 (0)