Skip to content

Commit

Permalink
Update xhttp
Browse files Browse the repository at this point in the history
  • Loading branch information
onanying committed May 22, 2024
1 parent 1952801 commit f1d2827
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 68 deletions.
57 changes: 40 additions & 17 deletions src/xutil/xhttp/README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
> Produced by OpenMix: [https://openmix.org](https://openmix.org/mix-go)
## Mix XHttp
## Mix XHTTP

A highly efficient HTTP library.

## Installation

```
go get github.com/mix-go/xutil
go get github.com/mix-go/xhttp
```

## Functions

| Function | Description |
|----------------------------------------------------------------------------------|----------------------------------|
| xhttp.Request(method string, u string, opts ...RequestOption) (*Response, error) | Execute an http request. |
| xhttp.Do(req *http.Request, opts ...RequestOption) (*Response, error) | Execute an http request. |
| xhttp.WithBody(body Body) RequestOption | Set configuration item |
| xhttp.WithHeader(header http.Header) RequestOption | Set configuration item |
| xhttp.WithContentType(contentType string) RequestOption | Set configuration item |
| xhttp.WithTimeout(timeout time.Duration) RequestOption | Set configuration item |
| xhttp.WithDebugFunc(f DebugFunc) RequestOption | Set configuration item |
| xhttp.WithRetry(f RetryIfFunc, opts ...retry.Option) RequestOption | Set configuration item |
| xhttp.BuildJSON(v interface{}) Body | Generate json string |
| xhttp.BuildQuery(m map[string]string) Body | Generate urlencoded query string |
| Function | Description |
|-------------------------------------------------------------------------------------|----------------------------------|
| xhttp.NewRequest(method string, u string, opts ...RequestOption) (*Response, error) | Execute an http request. |
| xhttp.SendRequest(req *http.Request, opts ...RequestOption) (*Response, error) | Execute an http request. |
| xhttp.WithBody(body Body) RequestOption | Set configuration item |
| xhttp.WithHeader(header http.Header) RequestOption | Set configuration item |
| xhttp.WithContentType(contentType string) RequestOption | Set configuration item |
| xhttp.WithTimeout(timeout time.Duration) RequestOption | Set configuration item |
| xhttp.WithDebugFunc(f DebugFunc) RequestOption | Set configuration item |
| xhttp.WithRetry(f RetryIfFunc, opts ...retry.Option) RequestOption | Set configuration item |
| xhttp.WithMiddlewares(middlewares ...Middleware) RequestOption | Set configuration item |
| xhttp.BuildJSON(v interface{}) Body | Generate json string |
| xhttp.BuildQuery(m map[string]string) Body | Generate urlencoded query string |

## Debug Log

Expand Down Expand Up @@ -51,8 +52,8 @@ The log object contains the following fields
```go
type Log struct {
Duration time.Duration `json:"duration"`
Request *XRequest `json:"request"` // The XRequest.RetryAttempts field records the number of retry attempts
Response *XResponse `json:"response"` // If request error this field is equal to nil
Request *Request `json:"request"` // The Request.RetryAttempts field records the number of retry attempts
Response *Response `json:"response"` // If request error this field is equal to nil
Error error `json:"error"`
}
```
Expand All @@ -79,7 +80,7 @@ Network error, no retry.

```go
url := "https://aaaaa.com/"
retryIf := func(resp *xhttp.XResponse, err error) error {
retryIf := func(resp *xhttp.Response, err error) error {
if err != nil {
return errors.Join(err, xhttp.ErrAbortRetry)
}
Expand All @@ -91,6 +92,28 @@ retryIf := func(resp *xhttp.XResponse, err error) error {
resp, err := xhttp.Request("GET", url, xhttp.WithRetry(retryIf, retry.Attempts(2)))
```

## Middlewares

Middleware configuration before or after.

```go
logicMiddleware := func(next xhttp.HandlerFunc) xhttp.HandlerFunc {
return func(req *xhttp.Request, opts *xhttp.RequestOptions) (*xhttp.Response, error) {
// Before-logic
fmt.Printf("Before: %s %s\n", req.Method, req.URL)

// Call the next handler
resp, err := next(req, opts)

// After-logic
fmt.Printf("After: %s %s\n", req.Method, req.URL)

return resp, err
}
}
resp, err := xhttp.NewRequest("GET", "https://github.com/", xhttp.WithMiddlewares(logicMiddleware))
```

## License

Apache License Version 2.0, http://www.apache.org/licenses/
62 changes: 34 additions & 28 deletions src/xutil/xhttp/request.go → src/xutil/xhttp/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ var (
ErrShutdown = errors.New("xhttp: service is currently being shutdown and will no longer accept new requests")
)

type XRequest struct {
var DefaultClient = &Client{}

func NewRequest(method string, u string, opts ...RequestOption) (*Response, error) {
return DefaultClient.NewRequest(method, u, opts...)
}

type Request struct {
*http.Request

Body Body
Expand All @@ -25,8 +31,9 @@ type XRequest struct {
RetryAttempts int
}

type XResponse struct {
type Response struct {
*http.Response

Body Body
}

Expand All @@ -36,11 +43,15 @@ func (t Body) String() string {
return xconv.BytesToString(t)
}

func newXRequest(r *http.Request) *XRequest {
type Client struct {
http.Client
}

func newRequest(r *http.Request) *Request {
if r == nil {
return nil
}
req := &XRequest{
req := &Request{
Request: r,
}

Expand All @@ -58,11 +69,11 @@ func newXRequest(r *http.Request) *XRequest {
return req
}

func newXResponse(r *http.Response) *XResponse {
func newResponse(r *http.Response) *Response {
if r == nil {
return nil
}
resp := &XResponse{
resp := &Response{
Response: r,
}

Expand All @@ -79,7 +90,7 @@ func newXResponse(r *http.Response) *XResponse {
return resp
}

func Request(method string, u string, opts ...RequestOption) (*XResponse, error) {
func (t *Client) NewRequest(method string, u string, opts ...RequestOption) (*Response, error) {
o := mergeOptions(opts)
URL, err := url.Parse(u)
if err != nil {
Expand All @@ -95,46 +106,45 @@ func Request(method string, u string, opts ...RequestOption) (*XResponse, error)
}

if o.RetryOptions != nil {
xReq := newXRequest(req)
return doRetry(o, func() (*XResponse, error) {
xReq := newRequest(req)
return doRetry(o, func() (*Response, error) {
xReq.RetryAttempts++
return doRequest(xReq, o)
return t.doRequest(xReq, o)
})
}
return doRequest(newXRequest(req), o)
return t.doRequest(newRequest(req), o)
}

func Do(req *http.Request, opts ...RequestOption) (*XResponse, error) {
func (t *Client) SendRequest(req *http.Request, opts ...RequestOption) (*Response, error) {
o := mergeOptions(opts)

if o.RetryOptions != nil {
xReq := newXRequest(req)
return doRetry(o, func() (*XResponse, error) {
xReq := newRequest(req)
return doRetry(o, func() (*Response, error) {
xReq.RetryAttempts++
return doRequest(xReq, o)
return t.doRequest(xReq, o)
})
}
return doRequest(newXRequest(req), o)
return t.doRequest(newRequest(req), o)
}

func doRequest(xReq *XRequest, opts *RequestOptions) (*XResponse, error) {
var finalHandler HandlerFunc = func(xReq *XRequest, opts *RequestOptions) (*XResponse, error) {
func (t *Client) doRequest(xReq *Request, opts *RequestOptions) (*Response, error) {
var finalHandler HandlerFunc = func(xReq *Request, opts *RequestOptions) (*Response, error) {
if !shutdownController.BeginRequest() {
return nil, ErrShutdown
}
defer shutdownController.EndRequest()

cli := http.Client{
Timeout: opts.Timeout,
}
cli := t
cli.Timeout = opts.Timeout
startTime := time.Now()
r, err := cli.Do(xReq.Request)
if err != nil {
doDebug(opts, time.Now().Sub(startTime), xReq, nil, err)
t.doDebug(opts, time.Now().Sub(startTime), xReq, nil, err)
return nil, err
}
xResp := newXResponse(r)
doDebug(opts, time.Now().Sub(startTime), xReq, xResp, nil)
xResp := newResponse(r)
t.doDebug(opts, time.Now().Sub(startTime), xReq, xResp, nil)
return xResp, nil
}

Expand All @@ -144,7 +154,3 @@ func doRequest(xReq *XRequest, opts *RequestOptions) (*XResponse, error) {

return finalHandler(xReq, opts)
}

func Shutdown() {
shutdownController.InitiateShutdown()
}
32 changes: 16 additions & 16 deletions src/xutil/xhttp/request_test.go → src/xutil/xhttp/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import (
"testing"
)

func TestRequest(t *testing.T) {
func TestNewRequest(t *testing.T) {
a := assert.New(t)

url := "https://github.com/"
resp, err := xhttp.Request("GET", url)
resp, err := xhttp.NewRequest("GET", url)

a.Equal(resp.StatusCode, 200)
a.Nil(err)
Expand All @@ -24,7 +24,7 @@ func TestRequestPOST(t *testing.T) {
a := assert.New(t)

url := "https://github.com/"
resp, err := xhttp.Request("POST", url, xhttp.WithBodyString("abc"), xhttp.WithContentType("application/json"))
resp, err := xhttp.NewRequest("POST", url, xhttp.WithBodyString("abc"), xhttp.WithContentType("application/json"))

a.Equal(resp.StatusCode, 404)
a.Nil(err)
Expand All @@ -34,7 +34,7 @@ func TestRequestError(t *testing.T) {
a := assert.New(t)

url := "https://aaaaa.com/"
resp, err := xhttp.Request("GET", url)
resp, err := xhttp.NewRequest("GET", url)

a.Nil(resp)
a.NotNil(err)
Expand All @@ -50,7 +50,7 @@ func TestDebugAndRetryFail(t *testing.T) {
}

url := "https://aaaaa.com/"
retryIf := func(resp *xhttp.XResponse, err error) error {
retryIf := func(resp *xhttp.Response, err error) error {
if err != nil {
return err
}
Expand All @@ -59,7 +59,7 @@ func TestDebugAndRetryFail(t *testing.T) {
}
return nil
}
resp, err := xhttp.Request("GET", url, xhttp.WithRetry(retryIf, retry.Attempts(2)))
resp, err := xhttp.NewRequest("GET", url, xhttp.WithRetry(retryIf, retry.Attempts(2)))

a.Nil(resp)
a.NotNil(err)
Expand All @@ -77,13 +77,13 @@ func TestDebugAndRetrySuccess(t *testing.T) {
}

url := "https://aaaaa.com/"
retryIf := func(resp *xhttp.XResponse, err error) error {
retryIf := func(resp *xhttp.Response, err error) error {
if count == 1 {
return errors.New("the first request failed")
}
return nil
}
_, err := xhttp.Request("GET", url, xhttp.WithRetry(retryIf, retry.Attempts(3)))
_, err := xhttp.NewRequest("GET", url, xhttp.WithRetry(retryIf, retry.Attempts(3)))

a.Nil(err)
a.Equal(count, 2)
Expand All @@ -99,7 +99,7 @@ func TestDebugAndRetryAbort(t *testing.T) {
}

url := "https://aaaaa.com/"
retryIf := func(resp *xhttp.XResponse, err error) error {
retryIf := func(resp *xhttp.Response, err error) error {
if err != nil {
if count == 1 {
return err
Expand All @@ -111,7 +111,7 @@ func TestDebugAndRetryAbort(t *testing.T) {
}
return nil
}
resp, err := xhttp.Request("GET", url, xhttp.WithRetry(retryIf, retry.Attempts(3)))
resp, err := xhttp.NewRequest("GET", url, xhttp.WithRetry(retryIf, retry.Attempts(3)))

a.Nil(resp)
a.NotNil(err)
Expand All @@ -122,21 +122,21 @@ func TestDebugAndRetryAbort(t *testing.T) {
func TestMiddlewares(t *testing.T) {
a := assert.New(t)

logMiddleware := func(next xhttp.HandlerFunc) xhttp.HandlerFunc {
return func(xReq *xhttp.XRequest, opts *xhttp.RequestOptions) (*xhttp.XResponse, error) {
logicMiddleware := func(next xhttp.HandlerFunc) xhttp.HandlerFunc {
return func(req *xhttp.Request, opts *xhttp.RequestOptions) (*xhttp.Response, error) {
// Before-logic
fmt.Printf("Before: %s %s\n", xReq.Method, xReq.URL)
fmt.Printf("Before: %s %s\n", req.Method, req.URL)

// Call the next handler
resp, err := next(xReq, opts)
resp, err := next(req, opts)

// After-logic
fmt.Printf("After: %s %s\n", xReq.Method, xReq.URL)
fmt.Printf("After: %s %s\n", req.Method, req.URL)

return resp, err
}
}
resp, err := xhttp.Request("GET", "https://github.com/", xhttp.WithMiddlewares(logMiddleware))
resp, err := xhttp.NewRequest("GET", "https://github.com/", xhttp.WithMiddlewares(logicMiddleware))

a.Equal(resp.StatusCode, 200)
a.Nil(err)
Expand Down
6 changes: 3 additions & 3 deletions src/xutil/xhttp/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import (

type Log struct {
Duration time.Duration `json:"duration"`
Request *XRequest `json:"request"` // The XRequest.RetryAttempts field records the number of retry attempts
Response *XResponse `json:"response"` // If request error this field is equal to nil
Request *Request `json:"request"` // The Request.RetryAttempts field records the number of retry attempts
Response *Response `json:"response"` // If request error this field is equal to nil
Error error `json:"error"`
}

type DebugFunc func(l *Log)

func doDebug(opts *RequestOptions, duration time.Duration, req *XRequest, resp *XResponse, err error) {
func (t *Client) doDebug(opts *RequestOptions, duration time.Duration, req *Request, resp *Response, err error) {
if opts.DebugFunc == nil {
return
}
Expand Down
2 changes: 1 addition & 1 deletion src/xutil/xhttp/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ package xhttp

type Middleware func(next HandlerFunc) HandlerFunc

type HandlerFunc func(*XRequest, *RequestOptions) (*XResponse, error)
type HandlerFunc func(*Request, *RequestOptions) (*Response, error)
6 changes: 3 additions & 3 deletions src/xutil/xhttp/retry.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/avast/retry-go"
)

type RetryIfFunc func(*XResponse, error) error
type RetryIfFunc func(*Response, error) error

type Error []error

Expand All @@ -27,8 +27,8 @@ func (t Error) HasAbortRetry() bool {
return false
}

func doRetry(opts *RequestOptions, f func() (*XResponse, error)) (*XResponse, error) {
var resp *XResponse
func doRetry(opts *RequestOptions, f func() (*Response, error)) (*Response, error) {
var resp *Response
var err error
var errorLog Error
err = retry.Do(
Expand Down
4 changes: 4 additions & 0 deletions src/xutil/xhttp/shutdownctrl.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ func (sc *ShutdownController) InitiateShutdown() {
atomic.StoreInt32(&sc.shutdownFlag, 1)
sc.waitGroup.Wait()
}

func Shutdown() {
shutdownController.InitiateShutdown()
}

0 comments on commit f1d2827

Please sign in to comment.