Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: httpc package for easy to use #1643

Merged
merged 1 commit into from
Mar 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 4 additions & 57 deletions rest/httpc/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,72 +3,19 @@ package httpc
import (
"io"
"net/http"

"github.com/zeromicro/go-zero/core/breaker"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/rest/httpc/internal"
)

var interceptors = []internal.Interceptor{
internal.LogInterceptor,
}

type Option func(cli *http.Client)

// Do sends an HTTP request to the service assocated with the given key.
func Do(key string, r *http.Request, opts ...Option) (resp *http.Response, err error) {
var respHandlers []internal.ResponseHandler
for _, interceptor := range interceptors {
var h internal.ResponseHandler
r, h = interceptor(r)
respHandlers = append(respHandlers, h)
}

resp, err = doRequest(key, r, opts...)
if err != nil {
logx.Errorf("[HTTP] %s %s/%s - %v", r.Method, r.Host, r.RequestURI, err)
return
}

for i := len(respHandlers) - 1; i >= 0; i-- {
respHandlers[i](resp)
}

return
func Do(key string, r *http.Request, opts ...Option) (*http.Response, error) {
return NewService(key, opts...).Do(r)
}

// Get sends an HTTP GET request to the service assocated with the given key.
func Get(key, url string, opts ...Option) (*http.Response, error) {
r, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}

return Do(key, r, opts...)
return NewService(key, opts...).Get(url)
}

// Post sends an HTTP POST request to the service assocated with the given key.
func Post(key, url, contentType string, body io.Reader, opts ...Option) (*http.Response, error) {
r, err := http.NewRequest(http.MethodPost, url, body)
if err != nil {
return nil, err
}

return Do(key, r, opts...)
}

func doRequest(key string, r *http.Request, opts ...Option) (resp *http.Response, err error) {
brk := breaker.GetBreaker(key)
err = brk.DoWithAcceptable(func() error {
var cli http.Client
for _, opt := range opts {
opt(&cli)
}
resp, err = cli.Do(r)
return err
}, func(err error) bool {
return err == nil && resp.StatusCode < http.StatusInternalServerError
})

return
return NewService(key, opts...).Post(url, contentType, body)
}
97 changes: 85 additions & 12 deletions rest/httpc/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,110 @@ package httpc
import (
"io"
"net/http"

"github.com/zeromicro/go-zero/core/breaker"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/rest/httpc/internal"
)

// ContentType means Content-Type.
const ContentType = "Content-Type"

var interceptors = []internal.Interceptor{
internal.LogInterceptor,
}

type (
// Option is used to customize the *http.Client.
Option func(cli *http.Client)

// Service represents a remote HTTP service.
Service interface {
Do(r *http.Request, opts ...Option) (*http.Response, error)
Get(url string, opts ...Option) (*http.Response, error)
Post(url, contentType string, body io.Reader, opts ...Option) (*http.Response, error)
// Do sends an HTTP request to the service.
Do(r *http.Request) (*http.Response, error)
// Get sends an HTTP GET request to the service.
Get(url string) (*http.Response, error)
// Post sends an HTTP POST request to the service.
Post(url, contentType string, body io.Reader) (*http.Response, error)
}

namedService struct {
name string
opts []Option
cli *http.Client
}
)

// NewService returns a remote service with the given name.
// opts are used to customize the *http.Client.
func NewService(name string, opts ...Option) Service {
var cli *http.Client

if len(opts) == 0 {
cli = http.DefaultClient
} else {
cli = &http.Client{}
for _, opt := range opts {
opt(cli)
}
}

return namedService{
name: name,
opts: opts,
cli: cli,
}
}

func (s namedService) Do(r *http.Request, opts ...Option) (*http.Response, error) {
return Do(s.name, r, append(s.opts, opts...)...)
// Do sends an HTTP request to the service.
func (s namedService) Do(r *http.Request) (resp *http.Response, err error) {
var respHandlers []internal.ResponseHandler
for _, interceptor := range interceptors {
var h internal.ResponseHandler
r, h = interceptor(r)
respHandlers = append(respHandlers, h)
}

resp, err = s.doRequest(r)
if err != nil {
logx.Errorf("[HTTP] %s %s/%s - %v", r.Method, r.Host, r.RequestURI, err)
return
}

for i := len(respHandlers) - 1; i >= 0; i-- {
respHandlers[i](resp)
}

return
}

// Get sends an HTTP GET request to the service.
func (s namedService) Get(url string) (*http.Response, error) {
r, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}

return s.Do(r)
}

func (s namedService) Get(url string, opts ...Option) (*http.Response, error) {
return Get(s.name, url, append(s.opts, opts...)...)
// Post sends an HTTP POST request to the service.
func (s namedService) Post(url, contentType string, body io.Reader) (*http.Response, error) {
r, err := http.NewRequest(http.MethodPost, url, body)
if err != nil {
return nil, err
}

r.Header.Set(ContentType, contentType)
return s.Do(r)
}

func (s namedService) Post(url, contentType string, body io.Reader, opts ...Option) (
*http.Response, error) {
return Post(s.name, url, contentType, body, append(s.opts, opts...)...)
func (s namedService) doRequest(r *http.Request) (resp *http.Response, err error) {
brk := breaker.GetBreaker(s.name)
err = brk.DoWithAcceptable(func() error {
resp, err = s.cli.Do(r)
return err
}, func(err error) bool {
return err == nil && resp.StatusCode < http.StatusInternalServerError
})

return
}
4 changes: 2 additions & 2 deletions rest/httpc/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ func TestNamedService_Do(t *testing.T) {
func TestNamedService_Get(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
}))
service := NewService("foo")
resp, err := service.Get(svr.URL, func(cli *http.Client) {
service := NewService("foo", func(cli *http.Client) {
cli.Transport = http.DefaultTransport
})
resp, err := service.Get(svr.URL)
assert.Nil(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
}
Expand Down