fix 4936: always transport request body (#5546)
This commit is contained in:
parent
95a8a61d2b
commit
979c076024
@ -73,7 +73,6 @@ func NewBeegoRequestWithCtx(ctx context.Context, rawurl, method string) *BeegoHT
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Error("%+v", berror.Wrapf(err, InvalidURLOrMethod, "invalid raw url or method: %s %s", rawurl, method))
|
logs.Error("%+v", berror.Wrapf(err, InvalidURLOrMethod, "invalid raw url or method: %s %s", rawurl, method))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &BeegoHTTPRequest{
|
return &BeegoHTTPRequest{
|
||||||
url: rawurl,
|
url: rawurl,
|
||||||
req: req,
|
req: req,
|
||||||
@ -81,6 +80,9 @@ func NewBeegoRequestWithCtx(ctx context.Context, rawurl, method string) *BeegoHT
|
|||||||
files: map[string]string{},
|
files: map[string]string{},
|
||||||
setting: defaultSetting,
|
setting: defaultSetting,
|
||||||
resp: &http.Response{},
|
resp: &http.Response{},
|
||||||
|
copyBody: func() io.ReadCloser {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +119,10 @@ type BeegoHTTPRequest struct {
|
|||||||
files map[string]string
|
files map[string]string
|
||||||
setting BeegoHTTPSettings
|
setting BeegoHTTPSettings
|
||||||
resp *http.Response
|
resp *http.Response
|
||||||
body []byte
|
// body the response body, not the request body
|
||||||
|
body []byte
|
||||||
|
// copyBody support retry strategy to avoid copy request body
|
||||||
|
copyBody func() io.ReadCloser
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRequest returns the request object
|
// GetRequest returns the request object
|
||||||
@ -281,25 +286,28 @@ func (b *BeegoHTTPRequest) PostFile(formname, filename string) *BeegoHTTPRequest
|
|||||||
func (b *BeegoHTTPRequest) Body(data interface{}) *BeegoHTTPRequest {
|
func (b *BeegoHTTPRequest) Body(data interface{}) *BeegoHTTPRequest {
|
||||||
switch t := data.(type) {
|
switch t := data.(type) {
|
||||||
case string:
|
case string:
|
||||||
bf := bytes.NewBufferString(t)
|
b.reqBody([]byte(t))
|
||||||
b.req.Body = io.NopCloser(bf)
|
|
||||||
b.req.GetBody = func() (io.ReadCloser, error) {
|
|
||||||
return io.NopCloser(bf), nil
|
|
||||||
}
|
|
||||||
b.req.ContentLength = int64(len(t))
|
|
||||||
case []byte:
|
case []byte:
|
||||||
bf := bytes.NewBuffer(t)
|
b.reqBody(t)
|
||||||
b.req.Body = io.NopCloser(bf)
|
|
||||||
b.req.GetBody = func() (io.ReadCloser, error) {
|
|
||||||
return io.NopCloser(bf), nil
|
|
||||||
}
|
|
||||||
b.req.ContentLength = int64(len(t))
|
|
||||||
default:
|
default:
|
||||||
logs.Error("%+v", berror.Errorf(UnsupportedBodyType, "unsupported body data type: %s", t))
|
logs.Error("%+v", berror.Errorf(UnsupportedBodyType, "unsupported body data type: %s", t))
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BeegoHTTPRequest) reqBody(data []byte) *BeegoHTTPRequest {
|
||||||
|
body := io.NopCloser(bytes.NewReader(data))
|
||||||
|
b.req.Body = body
|
||||||
|
b.req.GetBody = func() (io.ReadCloser, error) {
|
||||||
|
return body, nil
|
||||||
|
}
|
||||||
|
b.req.ContentLength = int64(len(data))
|
||||||
|
b.copyBody = func() io.ReadCloser {
|
||||||
|
return io.NopCloser(bytes.NewReader(data))
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
// XMLBody adds the request raw body encoded in XML.
|
// XMLBody adds the request raw body encoded in XML.
|
||||||
func (b *BeegoHTTPRequest) XMLBody(obj interface{}) (*BeegoHTTPRequest, error) {
|
func (b *BeegoHTTPRequest) XMLBody(obj interface{}) (*BeegoHTTPRequest, error) {
|
||||||
if b.req.Body == nil && obj != nil {
|
if b.req.Body == nil && obj != nil {
|
||||||
@ -307,11 +315,7 @@ func (b *BeegoHTTPRequest) XMLBody(obj interface{}) (*BeegoHTTPRequest, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return b, berror.Wrap(err, InvalidXMLBody, "obj could not be converted to XML data")
|
return b, berror.Wrap(err, InvalidXMLBody, "obj could not be converted to XML data")
|
||||||
}
|
}
|
||||||
b.req.Body = io.NopCloser(bytes.NewReader(byts))
|
b.reqBody(byts)
|
||||||
b.req.GetBody = func() (io.ReadCloser, error) {
|
|
||||||
return io.NopCloser(bytes.NewReader(byts)), nil
|
|
||||||
}
|
|
||||||
b.req.ContentLength = int64(len(byts))
|
|
||||||
b.req.Header.Set(contentTypeKey, "application/xml")
|
b.req.Header.Set(contentTypeKey, "application/xml")
|
||||||
}
|
}
|
||||||
return b, nil
|
return b, nil
|
||||||
@ -324,8 +328,7 @@ func (b *BeegoHTTPRequest) YAMLBody(obj interface{}) (*BeegoHTTPRequest, error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return b, berror.Wrap(err, InvalidYAMLBody, "obj could not be converted to YAML data")
|
return b, berror.Wrap(err, InvalidYAMLBody, "obj could not be converted to YAML data")
|
||||||
}
|
}
|
||||||
b.req.Body = io.NopCloser(bytes.NewReader(byts))
|
b.reqBody(byts)
|
||||||
b.req.ContentLength = int64(len(byts))
|
|
||||||
b.req.Header.Set(contentTypeKey, "application/x+yaml")
|
b.req.Header.Set(contentTypeKey, "application/x+yaml")
|
||||||
}
|
}
|
||||||
return b, nil
|
return b, nil
|
||||||
@ -338,8 +341,7 @@ func (b *BeegoHTTPRequest) JSONBody(obj interface{}) (*BeegoHTTPRequest, error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return b, berror.Wrap(err, InvalidJSONBody, "obj could not be converted to JSON body")
|
return b, berror.Wrap(err, InvalidJSONBody, "obj could not be converted to JSON body")
|
||||||
}
|
}
|
||||||
b.req.Body = io.NopCloser(bytes.NewReader(byts))
|
b.reqBody(byts)
|
||||||
b.req.ContentLength = int64(len(byts))
|
|
||||||
b.req.Header.Set(contentTypeKey, "application/json")
|
b.req.Header.Set(contentTypeKey, "application/json")
|
||||||
}
|
}
|
||||||
return b, nil
|
return b, nil
|
||||||
@ -493,7 +495,7 @@ func (b *BeegoHTTPRequest) doRequest(_ context.Context) (*http.Response, error)
|
|||||||
func (b *BeegoHTTPRequest) sendRequest(client *http.Client) (resp *http.Response, err error) {
|
func (b *BeegoHTTPRequest) sendRequest(client *http.Client) (resp *http.Response, err error) {
|
||||||
// retries default value is 0, it will run once.
|
// retries default value is 0, it will run once.
|
||||||
// retries equal to -1, it will run forever until success
|
// retries equal to -1, it will run forever until success
|
||||||
// retries is setted, it will retries fixed times.
|
// retries is set, it will retry fixed times.
|
||||||
// Sleeps for a 400ms between calls to reduce spam
|
// Sleeps for a 400ms between calls to reduce spam
|
||||||
for i := 0; b.setting.Retries == -1 || i <= b.setting.Retries; i++ {
|
for i := 0; b.setting.Retries == -1 || i <= b.setting.Retries; i++ {
|
||||||
resp, err = client.Do(b.req)
|
resp, err = client.Do(b.req)
|
||||||
@ -501,6 +503,7 @@ func (b *BeegoHTTPRequest) sendRequest(client *http.Client) (resp *http.Response
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
time.Sleep(b.setting.RetryDelay)
|
time.Sleep(b.setting.RetryDelay)
|
||||||
|
b.req.Body = b.copyBody()
|
||||||
}
|
}
|
||||||
return nil, berror.Wrap(err, SendRequestFailed, "sending request fail")
|
return nil, berror.Wrap(err, SendRequestFailed, "sending request fail")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -101,9 +101,17 @@ func (h *HttplibTestSuite) SetupSuite() {
|
|||||||
handler.HandleFunc("/redirect", func(writer http.ResponseWriter, request *http.Request) {
|
handler.HandleFunc("/redirect", func(writer http.ResponseWriter, request *http.Request) {
|
||||||
http.Redirect(writer, request, "redirect_dst", http.StatusTemporaryRedirect)
|
http.Redirect(writer, request, "redirect_dst", http.StatusTemporaryRedirect)
|
||||||
})
|
})
|
||||||
handler.HandleFunc("redirect_dst", func(writer http.ResponseWriter, request *http.Request) {
|
handler.HandleFunc("/redirect_dst", func(writer http.ResponseWriter, request *http.Request) {
|
||||||
_, _ = writer.Write([]byte("hello"))
|
_, _ = writer.Write([]byte("hello"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
handler.HandleFunc("/retry", func(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
body, err := io.ReadAll(request.Body)
|
||||||
|
require.NoError(h.T(), err)
|
||||||
|
assert.Equal(h.T(), []byte("retry body"), body)
|
||||||
|
panic("mock error")
|
||||||
|
})
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
_ = http.Serve(listener, handler)
|
_ = http.Serve(listener, handler)
|
||||||
}()
|
}()
|
||||||
@ -362,6 +370,34 @@ func (h *HttplibTestSuite) TestPut() {
|
|||||||
assert.Equal(t, "PUT", req.req.Method)
|
assert.Equal(t, "PUT", req.req.Method)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *HttplibTestSuite) TestRetry() {
|
||||||
|
defaultSetting.Retries = 2
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
req func(t *testing.T) *BeegoHTTPRequest
|
||||||
|
wantErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "retry_failed",
|
||||||
|
req: func(t *testing.T) *BeegoHTTPRequest {
|
||||||
|
req := NewBeegoRequest("http://localhost:8080/retry", http.MethodPost)
|
||||||
|
req.Body("retry body")
|
||||||
|
return req
|
||||||
|
},
|
||||||
|
wantErr: io.EOF,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
h.T().Run(tc.name, func(t *testing.T) {
|
||||||
|
req := tc.req(t)
|
||||||
|
resp, err := req.DoRequest()
|
||||||
|
assert.ErrorIs(t, err, tc.wantErr)
|
||||||
|
assert.Nil(t, resp)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewBeegoRequest(t *testing.T) {
|
func TestNewBeegoRequest(t *testing.T) {
|
||||||
req := NewBeegoRequest("http://beego.vip", "GET")
|
req := NewBeegoRequest("http://beego.vip", "GET")
|
||||||
assert.NotNil(t, req)
|
assert.NotNil(t, req)
|
||||||
@ -384,6 +420,7 @@ func TestNewBeegoRequestWithCtx(t *testing.T) {
|
|||||||
// bad method but still get request
|
// bad method but still get request
|
||||||
req = NewBeegoRequestWithCtx(context.Background(), "http://beego.vip", "G\tET")
|
req = NewBeegoRequestWithCtx(context.Background(), "http://beego.vip", "G\tET")
|
||||||
assert.NotNil(t, req)
|
assert.NotNil(t, req)
|
||||||
|
assert.NotNil(t, req.copyBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBeegoHTTPRequestSetProtocolVersion(t *testing.T) {
|
func TestBeegoHTTPRequestSetProtocolVersion(t *testing.T) {
|
||||||
@ -461,10 +498,6 @@ func TestBeegoHTTPRequestXMLBody(t *testing.T) {
|
|||||||
assert.NotNil(t, req.req.GetBody)
|
assert.NotNil(t, req.req.GetBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
|
||||||
func TestBeegoHTTPRequestResponseForValue(t *testing.T) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBeegoHTTPRequestJSONMarshal(t *testing.T) {
|
func TestBeegoHTTPRequestJSONMarshal(t *testing.T) {
|
||||||
req := Post("http://beego.vip")
|
req := Post("http://beego.vip")
|
||||||
req.SetEscapeHTML(false)
|
req.SetEscapeHTML(false)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user