Compare commits

...

7 Commits

Author SHA1 Message Date
e497b5fa89 rework newRequest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-09 16:27:49 +03:00
520dc29f89 fixup header filling after making new request
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-09 12:25:26 +03:00
59d6c26003 support metadata option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-09 11:00:19 +03:00
fade40754a update deps
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-05 16:23:00 +03:00
f39d449ca2 lint
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-05 16:10:38 +03:00
7cab3c18a7 add ability to wrap any struct to error interface
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-05 16:02:35 +03:00
7098c252dc allow to publish only body
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-06-11 15:21:36 +03:00
6 changed files with 130 additions and 95 deletions

2
go.mod
View File

@@ -2,4 +2,4 @@ module github.com/unistack-org/micro-client-http/v3
go 1.16 go 1.16
require github.com/unistack-org/micro/v3 v3.3.16 require github.com/unistack-org/micro/v3 v3.4.8

8
go.sum
View File

@@ -5,11 +5,11 @@ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I= github.com/silas/dag v0.0.0-20210121180416-41cf55125c34/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
github.com/unistack-org/micro/v3 v3.3.16 h1:v0h/oC0TO2n1djQJeOjD2jNEqKkiykwI6cpflEVTlQE= github.com/unistack-org/micro/v3 v3.4.8 h1:9+qGlNHgChC3aMuFrtTFUtG55PEAjneSvplg7phwoCI=
github.com/unistack-org/micro/v3 v3.3.16/go.mod h1:ETGcQQUcjxGaD44LUMX+0fgo8Loh7ExldfIPLvfUmDo= github.com/unistack-org/micro/v3 v3.4.8/go.mod h1:LXmPfbJnJNvL0kQs8HfnkV3Wya2Wb+C7keVq++RCZnk=
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

147
http.go
View File

@@ -32,39 +32,35 @@ func filterLabel(r []router.Route) []router.Route {
*/ */
type httpClient struct { type httpClient struct {
opts client.Options
httpcli *http.Client httpcli *http.Client
init bool opts client.Options
sync.RWMutex sync.RWMutex
init bool
} }
func newRequest(addr string, req client.Request, ct string, cf codec.Codec, msg interface{}, opts client.CallOptions) (*http.Request, error) { func newRequest(ctx context.Context, addr string, req client.Request, ct string, cf codec.Codec, msg interface{}, opts client.CallOptions) (*http.Request, error) {
hreq := &http.Request{Method: http.MethodPost}
body := "*" // as like google api http annotation
var tags []string var tags []string
var scheme string scheme := "http"
method := http.MethodPost
body := "*" // as like google api http annotation
host := addr
path := req.Endpoint()
u, err := url.Parse(addr) u, err := url.Parse(addr)
if err != nil { if err == nil {
hreq.URL = &url.URL{ scheme = u.Scheme
Scheme: "http", path = u.Path
Host: addr, host = u.Host
Path: req.Endpoint(), } else {
} u = &url.URL{Scheme: scheme, Path: path, Host: host}
hreq.Host = addr
scheme = "http"
} }
// nolint: nestif
if scheme == "" {
ep := req.Endpoint()
if opts.Context != nil { if opts.Context != nil {
if m, ok := opts.Context.Value(methodKey{}).(string); ok { if m, ok := opts.Context.Value(methodKey{}).(string); ok {
hreq.Method = m method = m
} }
if p, ok := opts.Context.Value(pathKey{}).(string); ok { if p, ok := opts.Context.Value(pathKey{}).(string); ok {
ep = p path += p
} }
if b, ok := opts.Context.Value(bodyKey{}).(string); ok { if b, ok := opts.Context.Value(bodyKey{}).(string); ok {
body = b body = b
@@ -73,11 +69,6 @@ func newRequest(addr string, req client.Request, ct string, cf codec.Codec, msg
tags = t tags = t
} }
} }
hreq.URL, err = u.Parse(ep)
if err != nil {
return nil, errors.BadRequest("go.micro.client", err.Error())
}
}
if len(tags) == 0 { if len(tags) == 0 {
switch ct { switch ct {
@@ -88,64 +79,93 @@ func newRequest(addr string, req client.Request, ct string, cf codec.Codec, msg
} }
} }
path, nmsg, err := newPathRequest(hreq.URL.Path, hreq.Method, body, msg, tags) if path == "" {
if err != nil { path = req.Endpoint()
return nil, errors.BadRequest("go.micro.client", err.Error())
} }
if scheme != "" { u, err = u.Parse(path)
hreq.URL, err = url.Parse(scheme + "://" + addr + path)
} else {
hreq.URL, err = url.Parse(addr + path)
}
if err != nil { if err != nil {
return nil, errors.BadRequest("go.micro.client", err.Error()) return nil, errors.BadRequest("go.micro.client_v", err.Error())
}
path, nmsg, err := newPathRequest(u.Path, method, body, msg, tags)
if err != nil {
return nil, errors.BadRequest("go.micro.client_a", err.Error())
}
u, err = url.Parse(fmt.Sprintf("%s://%s%s", scheme, host, path))
if err != nil {
return nil, errors.BadRequest("go.micro.client_t", err.Error())
} }
b, err := cf.Marshal(nmsg) b, err := cf.Marshal(nmsg)
if err != nil { if err != nil {
return nil, errors.BadRequest("go.micro.client", err.Error()) return nil, errors.BadRequest("go.micro.client_e", err.Error())
} }
var hreq *http.Request
if len(b) > 0 { if len(b) > 0 {
hreq.Body = ioutil.NopCloser(bytes.NewBuffer(b)) hreq, err = http.NewRequestWithContext(ctx, method, u.String(), ioutil.NopCloser(bytes.NewBuffer(b)))
hreq.ContentLength = int64(len(b)) hreq.ContentLength = int64(len(b))
} else {
hreq, err = http.NewRequestWithContext(ctx, method, u.String(), nil)
} }
if err != nil {
return nil, errors.BadRequest("go.micro.client_k", err.Error())
}
header := make(http.Header)
if opts.Context != nil {
if md, ok := opts.Context.Value(metadataKey{}).(metadata.Metadata); ok {
for k, v := range md {
header.Set(k, v)
}
}
}
if opts.AuthToken != "" {
hreq.Header.Set("Authorization", opts.AuthToken)
}
if md, ok := metadata.FromOutgoingContext(ctx); ok {
for k, v := range md {
hreq.Header.Set(k, v)
}
}
// set timeout in nanoseconds
if opts.StreamTimeout > time.Duration(0) {
hreq.Header.Set("Timeout", fmt.Sprintf("%d", opts.StreamTimeout))
}
if opts.RequestTimeout > time.Duration(0) {
hreq.Header.Set("Timeout", fmt.Sprintf("%d", opts.RequestTimeout))
}
// set the content type for the request
hreq.Header.Set("Content-Type", ct)
return hreq, nil return hreq, nil
} }
func (h *httpClient) call(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error { func (h *httpClient) call(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error {
header := make(http.Header, 2)
if md, ok := metadata.FromOutgoingContext(ctx); ok {
for k, v := range md {
header.Set(k, v)
}
}
ct := req.ContentType() ct := req.ContentType()
if len(opts.ContentType) > 0 { if len(opts.ContentType) > 0 {
ct = opts.ContentType ct = opts.ContentType
} }
// set timeout in nanoseconds
header.Set("Timeout", fmt.Sprintf("%d", opts.RequestTimeout))
// set the content type for the request
header.Set("Content-Type", ct)
cf, err := h.newCodec(ct) cf, err := h.newCodec(ct)
if err != nil { if err != nil {
return errors.InternalServerError("go.micro.client", err.Error()) return errors.InternalServerError("go.micro.client", err.Error())
} }
hreq, err := newRequest(addr, req, ct, cf, req.Body(), opts) hreq, err := newRequest(ctx, addr, req, ct, cf, req.Body(), opts)
if err != nil { if err != nil {
return err return err
} }
hreq.Header = header
// make the request // make the request
hrsp, err := h.httpcli.Do(hreq.WithContext(ctx)) hrsp, err := h.httpcli.Do(hreq)
if err != nil { if err != nil {
switch err := err.(type) { switch err := err.(type) {
case *url.Error: case *url.Error:
@@ -166,27 +186,10 @@ func (h *httpClient) call(ctx context.Context, addr string, req client.Request,
} }
func (h *httpClient) stream(ctx context.Context, addr string, req client.Request, opts client.CallOptions) (client.Stream, error) { func (h *httpClient) stream(ctx context.Context, addr string, req client.Request, opts client.CallOptions) (client.Stream, error) {
var header http.Header
if md, ok := metadata.FromOutgoingContext(ctx); ok {
header = make(http.Header, len(md)+2)
for k, v := range md {
header.Set(k, v)
}
} else {
header = make(http.Header, 2)
}
ct := req.ContentType() ct := req.ContentType()
if len(opts.ContentType) > 0 { if len(opts.ContentType) > 0 {
ct = opts.ContentType ct = opts.ContentType
} }
// set timeout in nanoseconds
if opts.StreamTimeout > time.Duration(0) {
header.Set("Timeout", fmt.Sprintf("%d", opts.StreamTimeout))
}
// set the content type for the request
header.Set("Content-Type", ct)
// get codec // get codec
cf, err := h.newCodec(ct) cf, err := h.newCodec(ct)
@@ -207,7 +210,6 @@ func (h *httpClient) stream(ctx context.Context, addr string, req client.Request
conn: cc, conn: cc,
ct: ct, ct: ct,
cf: cf, cf: cf,
header: header,
reader: bufio.NewReader(cc), reader: bufio.NewReader(cc),
request: req, request: req,
}, nil }, nil
@@ -573,7 +575,10 @@ func (h *httpClient) Publish(ctx context.Context, p client.Message, opts ...clie
return h.opts.Broker.Publish(ctx, topic, &broker.Message{ return h.opts.Broker.Publish(ctx, topic, &broker.Message{
Header: md, Header: md,
Body: body, Body: body,
}, broker.PublishContext(ctx)) },
broker.PublishContext(ctx),
broker.PublishBodyOnly(options.BodyOnly),
)
} }
func (h *httpClient) String() string { func (h *httpClient) String() string {

View File

@@ -5,6 +5,7 @@ import (
"net/http" "net/http"
"github.com/unistack-org/micro/v3/client" "github.com/unistack-org/micro/v3/client"
"github.com/unistack-org/micro/v3/metadata"
) )
var ( var (
@@ -96,3 +97,9 @@ type structTagsKey struct{}
func StructTags(tags []string) client.CallOption { func StructTags(tags []string) client.CallOption {
return client.SetCallOption(structTagsKey{}, tags) return client.SetCallOption(structTagsKey{}, tags)
} }
type metadataKey struct{}
func Metadata(md metadata.Metadata) client.CallOption {
return client.SetCallOption(metadataKey{}, md)
}

View File

@@ -21,7 +21,6 @@ type httpStream struct {
cf codec.Codec cf codec.Codec
context context.Context context context.Context
request client.Request request client.Request
header http.Header
closed chan bool closed chan bool
reader *bufio.Reader reader *bufio.Reader
address string address string
@@ -62,13 +61,11 @@ func (h *httpStream) Send(msg interface{}) error {
return errShutdown return errShutdown
} }
hreq, err := newRequest(h.address, h.request, h.ct, h.cf, msg, h.opts) hreq, err := newRequest(h.context, h.address, h.request, h.ct, h.cf, msg, h.opts)
if err != nil { if err != nil {
return err return err
} }
hreq.Header = h.header
return hreq.Write(h.conn) return hreq.Write(h.conn)
} }

38
util.go
View File

@@ -21,6 +21,23 @@ var (
mu sync.RWMutex mu sync.RWMutex
) )
// Error struct holds error
type Error struct {
err interface{}
}
// Error func for error interface
func (err *Error) Error() string {
return fmt.Sprintf("%v", err.err)
}
func GetError(err error) interface{} {
if rerr, ok := err.(*Error); ok {
return rerr.err
}
return err
}
func newPathRequest(path string, method string, body string, msg interface{}, tags []string) (string, interface{}, error) { func newPathRequest(path string, method string, body string, msg interface{}, tags []string) (string, interface{}, error) {
// parse via https://github.com/googleapis/googleapis/blob/master/google/api/http.proto definition // parse via https://github.com/googleapis/googleapis/blob/master/google/api/http.proto definition
tpl, err := newTemplate(path) tpl, err := newTemplate(path)
@@ -185,7 +202,6 @@ func (h *httpClient) parseRsp(ctx context.Context, hrsp *http.Response, rsp inte
if hrsp.StatusCode == http.StatusNoContent { if hrsp.StatusCode == http.StatusNoContent {
return nil return nil
} }
ct := DefaultContentType ct := DefaultContentType
if htype := hrsp.Header.Get("Content-Type"); htype != "" { if htype := hrsp.Header.Get("Content-Type"); htype != "" {
@@ -197,6 +213,7 @@ func (h *httpClient) parseRsp(ctx context.Context, hrsp *http.Response, rsp inte
return errors.InternalServerError("go.micro.client", cerr.Error()) return errors.InternalServerError("go.micro.client", cerr.Error())
} }
// succeseful response
if hrsp.StatusCode < 400 { if hrsp.StatusCode < 400 {
if err = cf.ReadBody(hrsp.Body, rsp); err != nil { if err = cf.ReadBody(hrsp.Body, rsp); err != nil {
return errors.InternalServerError("go.micro.client", err.Error()) return errors.InternalServerError("go.micro.client", err.Error())
@@ -204,13 +221,17 @@ func (h *httpClient) parseRsp(ctx context.Context, hrsp *http.Response, rsp inte
return nil return nil
} }
// response with error
var rerr interface{}
errmap, ok := opts.Context.Value(errorMapKey{}).(map[string]interface{}) errmap, ok := opts.Context.Value(errorMapKey{}).(map[string]interface{})
if ok && errmap != nil { if ok && errmap != nil {
if err, ok = errmap[fmt.Sprintf("%d", hrsp.StatusCode)].(error); !ok { rerr, ok = errmap[fmt.Sprintf("%d", hrsp.StatusCode)]
err, ok = errmap["default"].(error) if !ok {
rerr, ok = errmap["default"]
} }
} }
if !ok || err == nil {
if !ok || rerr == nil {
buf, rerr := io.ReadAll(hrsp.Body) buf, rerr := io.ReadAll(hrsp.Body)
if rerr != nil { if rerr != nil {
return errors.InternalServerError("go.micro.client", rerr.Error()) return errors.InternalServerError("go.micro.client", rerr.Error())
@@ -218,9 +239,14 @@ func (h *httpClient) parseRsp(ctx context.Context, hrsp *http.Response, rsp inte
return errors.New("go.micro.client", string(buf), int32(hrsp.StatusCode)) return errors.New("go.micro.client", string(buf), int32(hrsp.StatusCode))
} }
if cerr := cf.ReadBody(hrsp.Body, err); cerr != nil { if cerr := cf.ReadBody(hrsp.Body, rerr); cerr != nil {
err = errors.InternalServerError("go.micro.client", cerr.Error()) return errors.InternalServerError("go.micro.client", cerr.Error())
} }
if err, ok = rerr.(error); !ok {
err = &Error{rerr}
}
} }
return err return err