Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
807b4dc41a | |||
a1f432fac9 | |||
dd7e872858 | |||
57c2a5b95f | |||
73fc3d34f0 | |||
09e7282b2b | |||
5f029fd432 |
2
go.mod
2
go.mod
@@ -2,4 +2,4 @@ module github.com/unistack-org/micro-client-http/v3
|
||||
|
||||
go 1.16
|
||||
|
||||
require github.com/unistack-org/micro/v3 v3.4.8
|
||||
require github.com/unistack-org/micro/v3 v3.6.2
|
||||
|
20
go.sum
20
go.sum
@@ -1,17 +1,27 @@
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg=
|
||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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/silas/dag v0.0.0-20210121180416-41cf55125c34/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
|
||||
github.com/unistack-org/micro/v3 v3.4.8 h1:9+qGlNHgChC3aMuFrtTFUtG55PEAjneSvplg7phwoCI=
|
||||
github.com/unistack-org/micro/v3 v3.4.8/go.mod h1:LXmPfbJnJNvL0kQs8HfnkV3Wya2Wb+C7keVq++RCZnk=
|
||||
github.com/unistack-org/micro-proto v0.0.5 h1:DIC97Hufa2nGjuvTsfToD9laEOKddWMRTzeCfBwJ1j8=
|
||||
github.com/unistack-org/micro-proto v0.0.5/go.mod h1:EuI7UlfGXmT1hy6WacULib9LbNgRnDYQvTCFoLgKM2I=
|
||||
github.com/unistack-org/micro/v3 v3.6.2 h1:hr/1GyDA+g9tcsSTFd1sJWZTQz5cBZD+3nD+6gCUjHI=
|
||||
github.com/unistack-org/micro/v3 v3.6.2/go.mod h1:X8UIH1JK4/jYN/CtvxJ6d+HrfqzmoVQc1no8Homy+ww=
|
||||
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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
61
http.go
61
http.go
@@ -88,7 +88,13 @@ func newRequest(ctx context.Context, addr string, req client.Request, ct string,
|
||||
return nil, errors.BadRequest("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
path, nmsg, err := newPathRequest(u.Path, method, body, msg, tags)
|
||||
var nmsg interface{}
|
||||
if len(u.Query()) > 0 {
|
||||
path, nmsg, err = newPathRequest(u.Path+"?"+u.Query().Encode(), method, body, msg, tags)
|
||||
} else {
|
||||
path, nmsg, err = newPathRequest(u.Path, method, body, msg, tags)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.BadRequest("go.micro.client", err.Error())
|
||||
}
|
||||
@@ -126,7 +132,7 @@ func newRequest(ctx context.Context, addr string, req client.Request, ct string,
|
||||
}
|
||||
|
||||
if opts.AuthToken != "" {
|
||||
hreq.Header.Set("Authorization", opts.AuthToken)
|
||||
hreq.Header.Set(metadata.HeaderAuthorization, opts.AuthToken)
|
||||
}
|
||||
|
||||
if md, ok := metadata.FromOutgoingContext(ctx); ok {
|
||||
@@ -137,14 +143,14 @@ func newRequest(ctx context.Context, addr string, req client.Request, ct string,
|
||||
|
||||
// set timeout in nanoseconds
|
||||
if opts.StreamTimeout > time.Duration(0) {
|
||||
hreq.Header.Set("Timeout", fmt.Sprintf("%d", opts.StreamTimeout))
|
||||
hreq.Header.Set(metadata.HeaderTimeout, fmt.Sprintf("%d", opts.StreamTimeout))
|
||||
}
|
||||
if opts.RequestTimeout > time.Duration(0) {
|
||||
hreq.Header.Set("Timeout", fmt.Sprintf("%d", opts.RequestTimeout))
|
||||
hreq.Header.Set(metadata.HeaderTimeout, fmt.Sprintf("%d", opts.RequestTimeout))
|
||||
}
|
||||
|
||||
// set the content type for the request
|
||||
hreq.Header.Set("Content-Type", ct)
|
||||
hreq.Header.Set(metadata.HeaderContentType, ct)
|
||||
|
||||
return hreq, nil
|
||||
}
|
||||
@@ -532,15 +538,34 @@ func (h *httpClient) Stream(ctx context.Context, req client.Request, opts ...cli
|
||||
return nil, grr
|
||||
}
|
||||
|
||||
func (h *httpClient) BatchPublish(ctx context.Context, p []client.Message, opts ...client.PublishOption) error {
|
||||
return h.publish(ctx, p, opts...)
|
||||
}
|
||||
|
||||
func (h *httpClient) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
|
||||
return h.publish(ctx, []client.Message{p}, opts...)
|
||||
}
|
||||
|
||||
func (h *httpClient) publish(ctx context.Context, ps []client.Message, opts ...client.PublishOption) error {
|
||||
options := client.NewPublishOptions(opts...)
|
||||
|
||||
md, ok := metadata.FromOutgoingContext(ctx)
|
||||
if !ok {
|
||||
md = metadata.New(2)
|
||||
// get proxy
|
||||
exchange := ""
|
||||
if v, ok := os.LookupEnv("MICRO_PROXY"); ok {
|
||||
exchange = v
|
||||
}
|
||||
md["Content-Type"] = p.ContentType()
|
||||
md["Micro-Topic"] = p.Topic()
|
||||
|
||||
omd, ok := metadata.FromOutgoingContext(ctx)
|
||||
if !ok {
|
||||
omd = metadata.New(2)
|
||||
}
|
||||
|
||||
msgs := make([]*broker.Message, 0, len(ps))
|
||||
|
||||
for _, p := range ps {
|
||||
md := metadata.Copy(omd)
|
||||
md[metadata.HeaderContentType] = p.ContentType()
|
||||
md[metadata.HeaderTopic] = p.Topic()
|
||||
|
||||
cf, err := h.newCodec(p.ContentType())
|
||||
if err != nil {
|
||||
@@ -561,21 +586,15 @@ func (h *httpClient) Publish(ctx context.Context, p client.Message, opts ...clie
|
||||
}
|
||||
|
||||
topic := p.Topic()
|
||||
|
||||
// get proxy
|
||||
if prx := os.Getenv("MICRO_PROXY"); len(prx) > 0 {
|
||||
options.Exchange = prx
|
||||
if len(exchange) > 0 {
|
||||
topic = exchange
|
||||
}
|
||||
|
||||
// get the exchange
|
||||
if len(options.Exchange) > 0 {
|
||||
topic = options.Exchange
|
||||
md.Set(metadata.HeaderTopic, topic)
|
||||
msgs = append(msgs, &broker.Message{Header: md, Body: body})
|
||||
}
|
||||
|
||||
return h.opts.Broker.Publish(ctx, topic, &broker.Message{
|
||||
Header: md,
|
||||
Body: body,
|
||||
},
|
||||
return h.opts.Broker.BatchPublish(ctx, msgs,
|
||||
broker.PublishContext(ctx),
|
||||
broker.PublishBodyOnly(options.BodyOnly),
|
||||
)
|
||||
|
18
http_test.go
18
http_test.go
@@ -8,11 +8,27 @@ import (
|
||||
|
||||
type Request struct {
|
||||
Name string `json:"name"`
|
||||
Field1 string
|
||||
Field1 string `json:"field1"`
|
||||
Field2 string
|
||||
Field3 int64
|
||||
}
|
||||
|
||||
func TestPathValues(t *testing.T) {
|
||||
req := &Request{Name: "vtolstov", Field1: "field1"}
|
||||
p, m, err := newPathRequest("/api/v1/test?Name={name}&Field1={field1}", "POST", "*", req, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
u, err := url.Parse(p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = m
|
||||
if u.Query().Get("Name") != "vtolstov" || u.Query().Get("Field1") != "field1" {
|
||||
t.Fatalf("invalid values %v", u.Query())
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidPath(t *testing.T) {
|
||||
req := &Request{Name: "vtolstov", Field1: "field1", Field2: "field2", Field3: 10}
|
||||
p, m, err := newPathRequest("/api/v1/{name}/list", "GET", "", req, nil)
|
||||
|
67
util.go
67
util.go
@@ -14,11 +14,10 @@ import (
|
||||
"github.com/unistack-org/micro/v3/errors"
|
||||
"github.com/unistack-org/micro/v3/logger"
|
||||
rutil "github.com/unistack-org/micro/v3/util/reflect"
|
||||
util "github.com/unistack-org/micro/v3/util/router"
|
||||
)
|
||||
|
||||
var (
|
||||
templateCache = make(map[string]util.Template)
|
||||
templateCache = make(map[string][]string)
|
||||
mu sync.RWMutex
|
||||
)
|
||||
|
||||
@@ -46,14 +45,27 @@ func newPathRequest(path string, method string, body string, msg interface{}, ta
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if len(tpl.Fields) > 0 && msg == nil {
|
||||
if len(tpl) > 0 && msg == nil {
|
||||
return "", nil, fmt.Errorf("nil message but path params requested: %v", path)
|
||||
}
|
||||
|
||||
fieldsmapskip := make(map[string]struct{})
|
||||
fieldsmap := make(map[string]string, len(tpl.Fields))
|
||||
for _, v := range tpl.Fields {
|
||||
fieldsmap[v] = ""
|
||||
fieldsmap := make(map[string]string, len(tpl))
|
||||
for _, v := range tpl {
|
||||
var vs, ve int
|
||||
for i := 0; i < len(v); i++ {
|
||||
switch v[i] {
|
||||
case '{':
|
||||
vs = i + 1
|
||||
case '}':
|
||||
ve = i
|
||||
}
|
||||
if ve != 0 {
|
||||
fieldsmap[v[vs:ve]] = ""
|
||||
vs = 0
|
||||
ve = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nmsg, err := rutil.Zero(msg)
|
||||
@@ -149,15 +161,38 @@ func newPathRequest(path string, method string, body string, msg interface{}, ta
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
for _, fld := range tpl.Pool {
|
||||
|
||||
for _, fld := range tpl {
|
||||
_, _ = b.WriteRune('/')
|
||||
if v, ok := fieldsmap[fld]; ok {
|
||||
if v != "" {
|
||||
_, _ = b.WriteString(v)
|
||||
// nolint: nestif
|
||||
var vs, ve, vf int
|
||||
var pholder bool
|
||||
for i := 0; i < len(fld); i++ {
|
||||
switch fld[i] {
|
||||
case '{':
|
||||
vs = i + 1
|
||||
case '}':
|
||||
ve = i
|
||||
}
|
||||
// nolint: nestif
|
||||
if vs > 0 && ve != 0 {
|
||||
if vm, ok := fieldsmap[fld[vs:ve]]; ok {
|
||||
if vm != "" {
|
||||
_, _ = b.WriteString(fld[vf : vs-1])
|
||||
_, _ = b.WriteString(vm)
|
||||
vf = ve + 1
|
||||
}
|
||||
} else {
|
||||
_, _ = b.WriteString(fld)
|
||||
}
|
||||
vs = 0
|
||||
ve = 0
|
||||
pholder = true
|
||||
}
|
||||
}
|
||||
if !pholder {
|
||||
_, _ = b.WriteString(fld)
|
||||
}
|
||||
}
|
||||
|
||||
if len(values) > 0 {
|
||||
@@ -172,7 +207,10 @@ func newPathRequest(path string, method string, body string, msg interface{}, ta
|
||||
return b.String(), nmsg, nil
|
||||
}
|
||||
|
||||
func newTemplate(path string) (util.Template, error) {
|
||||
func newTemplate(path string) ([]string, error) {
|
||||
if len(path) == 0 || path[0] != '/' {
|
||||
return nil, fmt.Errorf("path must starts with /")
|
||||
}
|
||||
mu.RLock()
|
||||
tpl, ok := templateCache[path]
|
||||
if ok {
|
||||
@@ -181,12 +219,7 @@ func newTemplate(path string) (util.Template, error) {
|
||||
}
|
||||
mu.RUnlock()
|
||||
|
||||
rule, err := util.Parse(path)
|
||||
if err != nil {
|
||||
return tpl, err
|
||||
}
|
||||
|
||||
tpl = rule.Compile()
|
||||
tpl = strings.Split(path[1:], "/")
|
||||
mu.Lock()
|
||||
templateCache[path] = tpl
|
||||
mu.Unlock()
|
||||
|
26
util_test.go
26
util_test.go
@@ -5,13 +5,31 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTemplate(t *testing.T) {
|
||||
tpl, err := newTemplate("/v1/{ClientID}/list")
|
||||
func TestParsing(t *testing.T) {
|
||||
type Message struct {
|
||||
IIN string `protobuf:"bytes,1,opt,name=iin,proto3" json:"iin"`
|
||||
}
|
||||
|
||||
omsg := &Message{IIN: "5555"}
|
||||
|
||||
for _, m := range []string{"POST"} {
|
||||
body := ""
|
||||
path, nmsg, err := newPathRequest("/users/iin/{iin}/push-notifications", m, body, omsg, []string{"protobuf", "json"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = tpl
|
||||
// fmt.Printf("%#+v\n", tpl.Pool)
|
||||
u, err := url.Parse(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = nmsg
|
||||
if u.Path != "/users/iin/5555/push-notifications" {
|
||||
t.Fatalf("newPathRequest invalid path %s", u.Path)
|
||||
}
|
||||
if nmsg != nil {
|
||||
t.Fatalf("new message must be nil: %v\n", nmsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPathRequest(t *testing.T) {
|
||||
|
Reference in New Issue
Block a user