fix path template parsing
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
		
							
								
								
									
										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.5.2 | ||||
| require github.com/unistack-org/micro/v3 v3.5.4 | ||||
|   | ||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							| @@ -5,8 +5,8 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ | ||||
| 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.5.2 h1:8b9Mk4FLWRLp8SduBh5Xs6g/3xJ+ZIBOnH82eHuLWnw= | ||||
| github.com/unistack-org/micro/v3 v3.5.2/go.mod h1:1ZkwpEqpiHiVhM2hiF9DamtpsF04oFybFhEQ4zEMcro= | ||||
| github.com/unistack-org/micro/v3 v3.5.4 h1:6nIljqND355f+Fhc2mtCxYb5IRwer6nsMoAXpN8kka0= | ||||
| github.com/unistack-org/micro/v3 v3.5.4/go.mod h1:1ZkwpEqpiHiVhM2hiF9DamtpsF04oFybFhEQ4zEMcro= | ||||
| 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= | ||||
|   | ||||
							
								
								
									
										79
									
								
								http.go
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								http.go
									
									
									
									
									
								
							| @@ -532,50 +532,61 @@ 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) | ||||
| 	} | ||||
| 	md[metadata.HeaderContentType] = p.ContentType() | ||||
| 	md[metadata.HeaderTopic] = p.Topic() | ||||
|  | ||||
| 	cf, err := h.newCodec(p.ContentType()) | ||||
| 	if err != nil { | ||||
| 		return errors.InternalServerError("go.micro.client", err.Error()) | ||||
| 	// get proxy | ||||
| 	exchange := "" | ||||
| 	if v, ok := os.LookupEnv("MICRO_PROXY"); ok { | ||||
| 		exchange = v | ||||
| 	} | ||||
|  | ||||
| 	var body []byte | ||||
| 	msgs := make([]*broker.Message, 0, len(ps)) | ||||
|  | ||||
| 	// passed in raw data | ||||
| 	if d, ok := p.Payload().(*codec.Frame); ok { | ||||
| 		body = d.Data | ||||
| 	} else { | ||||
| 		b := bytes.NewBuffer(nil) | ||||
| 		if err := cf.Write(b, &codec.Message{Type: codec.Event}, p.Payload()); err != nil { | ||||
| 	for _, p := range ps { | ||||
| 		md, ok := metadata.FromOutgoingContext(ctx) | ||||
| 		if !ok { | ||||
| 			md = metadata.New(2) | ||||
| 		} | ||||
| 		md[metadata.HeaderContentType] = p.ContentType() | ||||
| 		md[metadata.HeaderTopic] = p.Topic() | ||||
|  | ||||
| 		cf, err := h.newCodec(p.ContentType()) | ||||
| 		if err != nil { | ||||
| 			return errors.InternalServerError("go.micro.client", err.Error()) | ||||
| 		} | ||||
| 		body = b.Bytes() | ||||
|  | ||||
| 		var body []byte | ||||
|  | ||||
| 		// passed in raw data | ||||
| 		if d, ok := p.Payload().(*codec.Frame); ok { | ||||
| 			body = d.Data | ||||
| 		} else { | ||||
| 			b := bytes.NewBuffer(nil) | ||||
| 			if err := cf.Write(b, &codec.Message{Type: codec.Event}, p.Payload()); err != nil { | ||||
| 				return errors.InternalServerError("go.micro.client", err.Error()) | ||||
| 			} | ||||
| 			body = b.Bytes() | ||||
| 		} | ||||
|  | ||||
| 		topic := p.Topic() | ||||
| 		if len(exchange) > 0 { | ||||
| 			topic = exchange | ||||
| 		} | ||||
|  | ||||
| 		md.Set(metadata.HeaderTopic, topic) | ||||
| 		msgs = append(msgs, &broker.Message{Header: md, Body: body}) | ||||
| 	} | ||||
|  | ||||
| 	topic := p.Topic() | ||||
|  | ||||
| 	// get proxy | ||||
| 	if prx := os.Getenv("MICRO_PROXY"); len(prx) > 0 { | ||||
| 		options.Exchange = prx | ||||
| 	} | ||||
|  | ||||
| 	// get the exchange | ||||
| 	if len(options.Exchange) > 0 { | ||||
| 		topic = options.Exchange | ||||
| 	} | ||||
|  | ||||
| 	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), | ||||
| 	) | ||||
|   | ||||
							
								
								
									
										38
									
								
								util.go
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								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,17 @@ 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 { | ||||
| 		if v[0] != '{' || v[len(v)-1] != '}' { | ||||
| 			continue | ||||
| 		} | ||||
| 		fieldsmap[v[1:len(v)-1]] = "" | ||||
| 	} | ||||
|  | ||||
| 	nmsg, err := rutil.Zero(msg) | ||||
| @@ -149,11 +151,15 @@ 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) | ||||
| 		if fld[0] == '{' && fld[len(fld)-1] == '}' { | ||||
| 			if v, ok := fieldsmap[fld[1:len(fld)-1]]; ok { | ||||
| 				if v != "" { | ||||
| 					_, _ = b.WriteString(v) | ||||
| 				} | ||||
| 			} else { | ||||
| 				_, _ = b.WriteString(fld) | ||||
| 			} | ||||
| 		} else { | ||||
| 			_, _ = b.WriteString(fld) | ||||
| @@ -172,7 +178,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 +190,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() | ||||
|   | ||||
							
								
								
									
										30
									
								
								util_test.go
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								util_test.go
									
									
									
									
									
								
							| @@ -5,13 +5,31 @@ import ( | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func TestTemplate(t *testing.T) { | ||||
| 	tpl, err := newTemplate("/v1/{ClientID}/list") | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 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) | ||||
| 		} | ||||
| 		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) | ||||
| 		} | ||||
| 	} | ||||
| 	_ = tpl | ||||
| 	//	fmt.Printf("%#+v\n", tpl.Pool) | ||||
| } | ||||
|  | ||||
| func TestNewPathRequest(t *testing.T) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user