Skip to content

Commit

Permalink
up: net - update and add some http client util functions
Browse files Browse the repository at this point in the history
  • Loading branch information
inhere committed Nov 26, 2022
1 parent 887ac5b commit 7f9bebc
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 0 deletions.
6 changes: 6 additions & 0 deletions netutil/httpreq/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ type DoerFunc func(req *http.Request) (*http.Response, error)
func (do DoerFunc) Do(req *http.Request) (*http.Response, error) {
return do(req)
}

// ReqLogger interface
type ReqLogger interface {
Infof(format string, args ...any)
Errorf(format string, args ...any)
}
122 changes: 122 additions & 0 deletions netutil/httpreq/response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package httpreq

import (
"bytes"
"encoding/json"
"errors"
"io"
"net/http"

"github.com/gookit/goutil/netutil/httpctype"
)

// Resp struct
type Resp struct {
*http.Response
// CostTime for a request-response
CostTime int64
}

// NewResp instance
func NewResp(hr *http.Response) *Resp {
return &Resp{Response: hr}
}

// IsFail check
func (r *Resp) IsFail() bool {
return r.StatusCode != http.StatusOK
}

// IsOk check
func (r *Resp) IsOk() bool {
return r.StatusCode == http.StatusOK
}

// IsSuccessful check
func (r *Resp) IsSuccessful() bool {
return IsSuccessful(r.StatusCode)
}

// IsEmptyBody check response body is empty
func (r *Resp) IsEmptyBody() bool {
return r.ContentLength <= 0
}

// ContentType get response content type
func (r *Resp) ContentType() string {
return r.Header.Get(httpctype.Key)
}

// BodyString get body as string.
func (r *Resp) String() string {
return ResponseToString(r.Response)
}

// BodyString get body as string.
func (r *Resp) BodyString() string {
return r.BodyBuffer().String()
}

// BodyBuffer read body to buffer.
//
// NOTICE: must close resp body.
func (r *Resp) BodyBuffer() *bytes.Buffer {
buf := &bytes.Buffer{}
// prof: assign memory before read
if r.ContentLength > bytes.MinRead {
buf.Grow(int(r.ContentLength) + 2)
}

// NOTICE: must close resp body.
defer r.QuiteCloseBody()
_, err := buf.ReadFrom(r.Body)
if err != nil {
panic(err)
}

return buf
}

// BindJsonOnOk body data on status is 200
//
// NOTICE: must close resp body.
func (r *Resp) BindJsonOnOk(ptr any) error {
// NOTICE: must close resp body.
defer r.QuiteCloseBody()

if r.IsFail() {
_, _ = io.Copy(io.Discard, r.Body) // <-- add this line
return errors.New("response status is not equals to 200")
}

if ptr == nil {
_, _ = io.Copy(io.Discard, r.Body) // <-- add this line
return nil
}
return json.NewDecoder(r.Body).Decode(ptr)
}

// BindJson body data to a ptr
//
// NOTICE: must close resp body.
func (r *Resp) BindJson(ptr any) error {
// NOTICE: must close resp body.
defer r.QuiteCloseBody()

if ptr == nil {
_, _ = io.Copy(io.Discard, r.Body) // <-- add this line
return nil
}

return json.NewDecoder(r.Body).Decode(ptr)
}

// CloseBody close resp body
func (r *Resp) CloseBody() error {
return r.Body.Close()
}

// QuiteCloseBody close resp body, ignore error
func (r *Resp) QuiteCloseBody() {
_ = r.Body.Close()
}
19 changes: 19 additions & 0 deletions netutil/httpreq/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"net/http"
"net/url"
"strings"

"github.com/gookit/goutil/strutil"
)

// IsOK check response status code is 200
Expand Down Expand Up @@ -63,6 +65,19 @@ func AddHeaders(req *http.Request, header http.Header) {
}
}

// HeaderToStringMap convert
func HeaderToStringMap(rh http.Header) map[string]string {
if len(rh) == 0 {
return nil
}

mp := make(map[string]string, len(rh))
for name, values := range rh {
mp[name] = strings.Join(values, "; ")
}
return mp
}

// ToQueryValues convert string-map to url.Values
func ToQueryValues(data any) url.Values {
// use url.Values directly if we have it
Expand All @@ -75,6 +90,10 @@ func ToQueryValues(data any) url.Values {
for k, v := range strMp {
uv.Add(k, v)
}
} else if kvMp, ok := data.(map[string]any); ok {
for k, v := range kvMp {
uv.Add(k, strutil.QuietString(v))
}
}

return uv
Expand Down
18 changes: 18 additions & 0 deletions netutil/httpreq/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ func TestAddHeaders(t *testing.T) {
assert.Eq(t, "val0", req.Header.Get("key0"))
}

func TestHeaderToStringMap(t *testing.T) {
assert.Nil(t, httpreq.HeaderToStringMap(nil))
assert.Nil(t, httpreq.HeaderToStringMap(http.Header{}))

want := map[string]string{"key": "value; more"}
assert.Eq(t, want, httpreq.HeaderToStringMap(http.Header{
"key": {"value", "more"},
}))
}

func TestToQueryValues(t *testing.T) {
vs := httpreq.ToQueryValues(map[string]string{"field1": "value1", "field2": "value2"})
assert.StrContains(t, vs.Encode(), "field=value1")

vs = httpreq.ToQueryValues(map[string]any{"field1": 234, "field2": "value2"})
assert.StrContains(t, vs.Encode(), "field=234")
}

func TestRequestToString(t *testing.T) {
req, err := http.NewRequest("GET", "inhere.xyz", nil)
assert.NoErr(t, err)
Expand Down

0 comments on commit 7f9bebc

Please sign in to comment.