Skip to content

Commit 76783fd

Browse files
authored
fix(net/gclient): panic when containing @file: parameter value in json post request (#3775)
1 parent a1ce97e commit 76783fd

File tree

2 files changed

+132
-34
lines changed

2 files changed

+132
-34
lines changed

net/gclient/gclient_request.go

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,10 @@ func (c *Client) prepareRequest(ctx context.Context, method, url string, data ..
167167
if !gstr.ContainsI(url, httpProtocolName) {
168168
url = httpProtocolName + `://` + url
169169
}
170-
var params string
170+
var (
171+
params string
172+
allowFileUploading = true
173+
)
171174
if len(data) > 0 {
172175
switch c.header[httpHeaderContentType] {
173176
case httpHeaderContentTypeJson:
@@ -181,6 +184,7 @@ func (c *Client) prepareRequest(ctx context.Context, method, url string, data ..
181184
params = string(b)
182185
}
183186
}
187+
allowFileUploading = false
184188

185189
case httpHeaderContentTypeXml:
186190
switch data[0].(type) {
@@ -193,6 +197,8 @@ func (c *Client) prepareRequest(ctx context.Context, method, url string, data ..
193197
params = string(b)
194198
}
195199
}
200+
allowFileUploading = false
201+
196202
default:
197203
params = httputil.BuildParams(data[0], c.noUrlEncode)
198204
}
@@ -223,14 +229,18 @@ func (c *Client) prepareRequest(ctx context.Context, method, url string, data ..
223229
return nil, err
224230
}
225231
} else {
226-
if strings.Contains(params, httpParamFileHolder) {
232+
if allowFileUploading && strings.Contains(params, httpParamFileHolder) {
227233
// File uploading request.
228234
var (
229-
buffer = bytes.NewBuffer(nil)
230-
writer = multipart.NewWriter(buffer)
235+
buffer = bytes.NewBuffer(nil)
236+
writer = multipart.NewWriter(buffer)
237+
isFileUploading = false
231238
)
232239
for _, item := range strings.Split(params, "&") {
233240
array := strings.Split(item, "=")
241+
if len(array) < 2 {
242+
continue
243+
}
234244
if len(array[1]) > 6 && strings.Compare(array[1][0:6], httpParamFileHolder) == 0 {
235245
path := array[1][6:]
236246
if !gfile.Exists(path) {
@@ -241,43 +251,50 @@ func (c *Client) prepareRequest(ctx context.Context, method, url string, data ..
241251
formFileName = gfile.Basename(path)
242252
formFieldName = array[0]
243253
)
254+
// it sets post content type as `application/octet-stream`
244255
if file, err = writer.CreateFormFile(formFieldName, formFileName); err != nil {
245-
err = gerror.Wrapf(err, `CreateFormFile failed with "%s", "%s"`, formFieldName, formFileName)
256+
return nil, gerror.Wrapf(
257+
err, `CreateFormFile failed with "%s", "%s"`, formFieldName, formFileName,
258+
)
259+
}
260+
var f *os.File
261+
if f, err = gfile.Open(path); err != nil {
246262
return nil, err
247-
} else {
248-
var f *os.File
249-
if f, err = gfile.Open(path); err != nil {
250-
return nil, err
251-
}
252-
if _, err = io.Copy(file, f); err != nil {
253-
err = gerror.Wrapf(err, `io.Copy failed from "%s" to form "%s"`, path, formFieldName)
254-
_ = f.Close()
255-
return nil, err
256-
}
263+
}
264+
if _, err = io.Copy(file, f); err != nil {
257265
_ = f.Close()
266+
return nil, gerror.Wrapf(
267+
err, `io.Copy failed from "%s" to form "%s"`, path, formFieldName,
268+
)
258269
}
270+
if err = f.Close(); err != nil {
271+
return nil, gerror.Wrapf(err, `close file descriptor failed for "%s"`, path)
272+
}
273+
isFileUploading = true
259274
} else {
260275
var (
261276
fieldName = array[0]
262277
fieldValue = array[1]
263278
)
264279
if err = writer.WriteField(fieldName, fieldValue); err != nil {
265-
err = gerror.Wrapf(err, `write form field failed with "%s", "%s"`, fieldName, fieldValue)
266-
return nil, err
280+
return nil, gerror.Wrapf(
281+
err, `write form field failed with "%s", "%s"`, fieldName, fieldValue,
282+
)
267283
}
268284
}
269285
}
270286
// Close finishes the multipart message and writes the trailing
271287
// boundary end line to the output.
272288
if err = writer.Close(); err != nil {
273-
err = gerror.Wrapf(err, `form writer close failed`)
274-
return nil, err
289+
return nil, gerror.Wrapf(err, `form writer close failed`)
275290
}
276291

277292
if req, err = http.NewRequest(method, url, buffer); err != nil {
278-
err = gerror.Wrapf(err, `http.NewRequest failed for method "%s" and URL "%s"`, method, url)
279-
return nil, err
280-
} else {
293+
return nil, gerror.Wrapf(
294+
err, `http.NewRequest failed for method "%s" and URL "%s"`, method, url,
295+
)
296+
}
297+
if isFileUploading {
281298
req.Header.Set(httpHeaderContentType, writer.FormDataContentType())
282299
}
283300
} else {
@@ -286,18 +303,17 @@ func (c *Client) prepareRequest(ctx context.Context, method, url string, data ..
286303
if req, err = http.NewRequest(method, url, bytes.NewReader(paramBytes)); err != nil {
287304
err = gerror.Wrapf(err, `http.NewRequest failed for method "%s" and URL "%s"`, method, url)
288305
return nil, err
289-
} else {
290-
if v, ok := c.header[httpHeaderContentType]; ok {
291-
// Custom Content-Type.
292-
req.Header.Set(httpHeaderContentType, v)
293-
} else if len(paramBytes) > 0 {
294-
if (paramBytes[0] == '[' || paramBytes[0] == '{') && json.Valid(paramBytes) {
295-
// Auto-detecting and setting the post content format: JSON.
296-
req.Header.Set(httpHeaderContentType, httpHeaderContentTypeJson)
297-
} else if gregex.IsMatchString(httpRegexParamJson, params) {
298-
// If the parameters passed like "name=value", it then uses form type.
299-
req.Header.Set(httpHeaderContentType, httpHeaderContentTypeForm)
300-
}
306+
}
307+
if v, ok := c.header[httpHeaderContentType]; ok {
308+
// Custom Content-Type.
309+
req.Header.Set(httpHeaderContentType, v)
310+
} else if len(paramBytes) > 0 {
311+
if (paramBytes[0] == '[' || paramBytes[0] == '{') && json.Valid(paramBytes) {
312+
// Auto-detecting and setting the post content format: JSON.
313+
req.Header.Set(httpHeaderContentType, httpHeaderContentTypeJson)
314+
} else if gregex.IsMatchString(httpRegexParamJson, params) {
315+
// If the parameters passed like "name=value", it then uses form type.
316+
req.Header.Set(httpHeaderContentType, httpHeaderContentTypeForm)
301317
}
302318
}
303319
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
2+
//
3+
// This Source Code Form is subject to the terms of the MIT License.
4+
// If a copy of the MIT was not distributed with this file,
5+
// You can obtain one at https://github.com/gogf/gf.
6+
7+
package gclient_test
8+
9+
import (
10+
"fmt"
11+
"strings"
12+
"testing"
13+
"time"
14+
15+
"github.com/gogf/gf/v2/frame/g"
16+
"github.com/gogf/gf/v2/net/gclient"
17+
"github.com/gogf/gf/v2/net/ghttp"
18+
"github.com/gogf/gf/v2/test/gtest"
19+
"github.com/gogf/gf/v2/util/guid"
20+
)
21+
22+
func Test_Issue3748(t *testing.T) {
23+
s := g.Server(guid.S())
24+
s.BindHandler("/", func(r *ghttp.Request) {
25+
r.Response.Write(
26+
r.GetBody(),
27+
)
28+
})
29+
s.SetDumpRouterMap(false)
30+
s.Start()
31+
defer s.Shutdown()
32+
33+
clientHost := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
34+
time.Sleep(100 * time.Millisecond)
35+
36+
gtest.C(t, func(t *gtest.T) {
37+
client := gclient.New()
38+
client.SetHeader("Content-Type", "application/json")
39+
data := map[string]interface{}{
40+
"name": "@file:",
41+
"value": "json",
42+
}
43+
client.SetPrefix(clientHost)
44+
content := client.PostContent(ctx, "/", data)
45+
t.Assert(content, `{"name":"@file:","value":"json"}`)
46+
})
47+
48+
gtest.C(t, func(t *gtest.T) {
49+
client := gclient.New()
50+
client.SetHeader("Content-Type", "application/xml")
51+
data := map[string]interface{}{
52+
"name": "@file:",
53+
"value": "xml",
54+
}
55+
client.SetPrefix(clientHost)
56+
content := client.PostContent(ctx, "/", data)
57+
t.Assert(content, `<doc><name>@file:</name><value>xml</value></doc>`)
58+
})
59+
60+
gtest.C(t, func(t *gtest.T) {
61+
client := gclient.New()
62+
client.SetHeader("Content-Type", "application/x-www-form-urlencoded")
63+
data := map[string]interface{}{
64+
"name": "@file:",
65+
"value": "x-www-form-urlencoded",
66+
}
67+
client.SetPrefix(clientHost)
68+
content := client.PostContent(ctx, "/", data)
69+
t.Assert(strings.Contains(content, `Content-Disposition: form-data; name="value"`), true)
70+
t.Assert(strings.Contains(content, `Content-Disposition: form-data; name="name"`), true)
71+
t.Assert(strings.Contains(content, "\r\n@file:"), true)
72+
t.Assert(strings.Contains(content, "\r\nx-www-form-urlencoded"), true)
73+
})
74+
75+
gtest.C(t, func(t *gtest.T) {
76+
client := gclient.New()
77+
data := "@file:"
78+
client.SetPrefix(clientHost)
79+
_, err := client.Post(ctx, "/", data)
80+
t.AssertNil(err)
81+
})
82+
}

0 commit comments

Comments
 (0)