mirror of
https://gitee.com/johng/gf
synced 2026-06-06 02:25:47 +08:00
fix(net/gclient): panic when containing @file: parameter value in json post request (#3775)
This commit is contained in:
@ -167,7 +167,10 @@ func (c *Client) prepareRequest(ctx context.Context, method, url string, data ..
|
||||
if !gstr.ContainsI(url, httpProtocolName) {
|
||||
url = httpProtocolName + `://` + url
|
||||
}
|
||||
var params string
|
||||
var (
|
||||
params string
|
||||
allowFileUploading = true
|
||||
)
|
||||
if len(data) > 0 {
|
||||
switch c.header[httpHeaderContentType] {
|
||||
case httpHeaderContentTypeJson:
|
||||
@ -181,6 +184,7 @@ func (c *Client) prepareRequest(ctx context.Context, method, url string, data ..
|
||||
params = string(b)
|
||||
}
|
||||
}
|
||||
allowFileUploading = false
|
||||
|
||||
case httpHeaderContentTypeXml:
|
||||
switch data[0].(type) {
|
||||
@ -193,6 +197,8 @@ func (c *Client) prepareRequest(ctx context.Context, method, url string, data ..
|
||||
params = string(b)
|
||||
}
|
||||
}
|
||||
allowFileUploading = false
|
||||
|
||||
default:
|
||||
params = httputil.BuildParams(data[0], c.noUrlEncode)
|
||||
}
|
||||
@ -223,14 +229,18 @@ func (c *Client) prepareRequest(ctx context.Context, method, url string, data ..
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if strings.Contains(params, httpParamFileHolder) {
|
||||
if allowFileUploading && strings.Contains(params, httpParamFileHolder) {
|
||||
// File uploading request.
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
writer = multipart.NewWriter(buffer)
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
writer = multipart.NewWriter(buffer)
|
||||
isFileUploading = false
|
||||
)
|
||||
for _, item := range strings.Split(params, "&") {
|
||||
array := strings.Split(item, "=")
|
||||
if len(array) < 2 {
|
||||
continue
|
||||
}
|
||||
if len(array[1]) > 6 && strings.Compare(array[1][0:6], httpParamFileHolder) == 0 {
|
||||
path := array[1][6:]
|
||||
if !gfile.Exists(path) {
|
||||
@ -241,43 +251,50 @@ func (c *Client) prepareRequest(ctx context.Context, method, url string, data ..
|
||||
formFileName = gfile.Basename(path)
|
||||
formFieldName = array[0]
|
||||
)
|
||||
// it sets post content type as `application/octet-stream`
|
||||
if file, err = writer.CreateFormFile(formFieldName, formFileName); err != nil {
|
||||
err = gerror.Wrapf(err, `CreateFormFile failed with "%s", "%s"`, formFieldName, formFileName)
|
||||
return nil, err
|
||||
} else {
|
||||
var f *os.File
|
||||
if f, err = gfile.Open(path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = io.Copy(file, f); err != nil {
|
||||
err = gerror.Wrapf(err, `io.Copy failed from "%s" to form "%s"`, path, formFieldName)
|
||||
_ = f.Close()
|
||||
return nil, err
|
||||
}
|
||||
_ = f.Close()
|
||||
return nil, gerror.Wrapf(
|
||||
err, `CreateFormFile failed with "%s", "%s"`, formFieldName, formFileName,
|
||||
)
|
||||
}
|
||||
var f *os.File
|
||||
if f, err = gfile.Open(path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = io.Copy(file, f); err != nil {
|
||||
_ = f.Close()
|
||||
return nil, gerror.Wrapf(
|
||||
err, `io.Copy failed from "%s" to form "%s"`, path, formFieldName,
|
||||
)
|
||||
}
|
||||
if err = f.Close(); err != nil {
|
||||
return nil, gerror.Wrapf(err, `close file descriptor failed for "%s"`, path)
|
||||
}
|
||||
isFileUploading = true
|
||||
} else {
|
||||
var (
|
||||
fieldName = array[0]
|
||||
fieldValue = array[1]
|
||||
)
|
||||
if err = writer.WriteField(fieldName, fieldValue); err != nil {
|
||||
err = gerror.Wrapf(err, `write form field failed with "%s", "%s"`, fieldName, fieldValue)
|
||||
return nil, err
|
||||
return nil, gerror.Wrapf(
|
||||
err, `write form field failed with "%s", "%s"`, fieldName, fieldValue,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Close finishes the multipart message and writes the trailing
|
||||
// boundary end line to the output.
|
||||
if err = writer.Close(); err != nil {
|
||||
err = gerror.Wrapf(err, `form writer close failed`)
|
||||
return nil, err
|
||||
return nil, gerror.Wrapf(err, `form writer close failed`)
|
||||
}
|
||||
|
||||
if req, err = http.NewRequest(method, url, buffer); err != nil {
|
||||
err = gerror.Wrapf(err, `http.NewRequest failed for method "%s" and URL "%s"`, method, url)
|
||||
return nil, err
|
||||
} else {
|
||||
return nil, gerror.Wrapf(
|
||||
err, `http.NewRequest failed for method "%s" and URL "%s"`, method, url,
|
||||
)
|
||||
}
|
||||
if isFileUploading {
|
||||
req.Header.Set(httpHeaderContentType, writer.FormDataContentType())
|
||||
}
|
||||
} else {
|
||||
@ -286,18 +303,17 @@ func (c *Client) prepareRequest(ctx context.Context, method, url string, data ..
|
||||
if req, err = http.NewRequest(method, url, bytes.NewReader(paramBytes)); err != nil {
|
||||
err = gerror.Wrapf(err, `http.NewRequest failed for method "%s" and URL "%s"`, method, url)
|
||||
return nil, err
|
||||
} else {
|
||||
if v, ok := c.header[httpHeaderContentType]; ok {
|
||||
// Custom Content-Type.
|
||||
req.Header.Set(httpHeaderContentType, v)
|
||||
} else if len(paramBytes) > 0 {
|
||||
if (paramBytes[0] == '[' || paramBytes[0] == '{') && json.Valid(paramBytes) {
|
||||
// Auto-detecting and setting the post content format: JSON.
|
||||
req.Header.Set(httpHeaderContentType, httpHeaderContentTypeJson)
|
||||
} else if gregex.IsMatchString(httpRegexParamJson, params) {
|
||||
// If the parameters passed like "name=value", it then uses form type.
|
||||
req.Header.Set(httpHeaderContentType, httpHeaderContentTypeForm)
|
||||
}
|
||||
}
|
||||
if v, ok := c.header[httpHeaderContentType]; ok {
|
||||
// Custom Content-Type.
|
||||
req.Header.Set(httpHeaderContentType, v)
|
||||
} else if len(paramBytes) > 0 {
|
||||
if (paramBytes[0] == '[' || paramBytes[0] == '{') && json.Valid(paramBytes) {
|
||||
// Auto-detecting and setting the post content format: JSON.
|
||||
req.Header.Set(httpHeaderContentType, httpHeaderContentTypeJson)
|
||||
} else if gregex.IsMatchString(httpRegexParamJson, params) {
|
||||
// If the parameters passed like "name=value", it then uses form type.
|
||||
req.Header.Set(httpHeaderContentType, httpHeaderContentTypeForm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
82
net/gclient/gclient_z_unit_issue_test.go
Normal file
82
net/gclient/gclient_z_unit_issue_test.go
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gclient_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gclient"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
)
|
||||
|
||||
func Test_Issue3748(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write(
|
||||
r.GetBody(),
|
||||
)
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
clientHost := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := gclient.New()
|
||||
client.SetHeader("Content-Type", "application/json")
|
||||
data := map[string]interface{}{
|
||||
"name": "@file:",
|
||||
"value": "json",
|
||||
}
|
||||
client.SetPrefix(clientHost)
|
||||
content := client.PostContent(ctx, "/", data)
|
||||
t.Assert(content, `{"name":"@file:","value":"json"}`)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := gclient.New()
|
||||
client.SetHeader("Content-Type", "application/xml")
|
||||
data := map[string]interface{}{
|
||||
"name": "@file:",
|
||||
"value": "xml",
|
||||
}
|
||||
client.SetPrefix(clientHost)
|
||||
content := client.PostContent(ctx, "/", data)
|
||||
t.Assert(content, `<doc><name>@file:</name><value>xml</value></doc>`)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := gclient.New()
|
||||
client.SetHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||
data := map[string]interface{}{
|
||||
"name": "@file:",
|
||||
"value": "x-www-form-urlencoded",
|
||||
}
|
||||
client.SetPrefix(clientHost)
|
||||
content := client.PostContent(ctx, "/", data)
|
||||
t.Assert(strings.Contains(content, `Content-Disposition: form-data; name="value"`), true)
|
||||
t.Assert(strings.Contains(content, `Content-Disposition: form-data; name="name"`), true)
|
||||
t.Assert(strings.Contains(content, "\r\n@file:"), true)
|
||||
t.Assert(strings.Contains(content, "\r\nx-www-form-urlencoded"), true)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := gclient.New()
|
||||
data := "@file:"
|
||||
client.SetPrefix(clientHost)
|
||||
_, err := client.Post(ctx, "/", data)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user