From fd5479f6e7232809b0952a46177c54969163dbe4 Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Mon, 25 Oct 2021 19:59:37 +0300 Subject: [PATCH] add ability to send headers and cookies Signed-off-by: Vasiliy Tolstov --- README.md | 4 +- go.mod | 4 +- go.sum | 26 ++++------- http.go | 129 +++++++++++++++++++++++++++++++++++++-------------- http_test.go | 43 +++++++++++++---- message.go | 2 +- options.go | 25 +++++++++- request.go | 4 +- stream.go | 6 +-- util.go | 26 ++++++++--- util_test.go | 6 +-- 11 files changed, 192 insertions(+), 83 deletions(-) diff --git a/README.md b/README.md index f1b9480..564d4bd 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,14 @@ This plugin is a http client for micro. ## Overview The http client wraps `net/http` to provide a robust micro client with service discovery, load balancing and streaming. -It complies with the [micro.Client](https://godoc.org/github.com/unistack-org/micro-client-http#Client) interface. +It complies with the [micro.Client](https://godoc.org/go.unistack.org/micro-client-http/v3#Client) interface. ## Usage ### Use directly ```go -import "github.com/unistack-org/micro-client-http" +import "go.unistack.org/micro-client-http/v3" service := micro.NewService( micro.Name("my.service"), diff --git a/go.mod b/go.mod index da797c6..c8370d7 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ -module github.com/unistack-org/micro-client-http/v3 +module go.unistack.org/micro-client-http/v3 go 1.16 -require github.com/unistack-org/micro/v3 v3.7.1 +require go.unistack.org/micro/v3 v3.8.4 diff --git a/go.sum b/go.sum index 029f7ec..2e84097 100644 --- a/go.sum +++ b/go.sum @@ -1,34 +1,26 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.1.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/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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/silas/dag v0.0.0-20210121180416-41cf55125c34/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/unistack-org/micro-proto v0.0.9 h1:KrWLS4FUX7UAWNAilQf70uad6ZPf/0EudeddCXllRVc= -github.com/unistack-org/micro-proto v0.0.9/go.mod h1:Cckwmzd89gvS7ThxzZp9kQR/EOdksFQcsTAtDDyKwrg= -github.com/unistack-org/micro/v3 v3.7.1 h1:gjCon1U8i9upNgw9+iEgbZh2LCeBizDYotQ+THHV0lo= -github.com/unistack-org/micro/v3 v3.7.1/go.mod h1:gBoY6gvzeFiJTZ4FgDttGNSs4Y1+1PRg2cV1yTRMSlg= -golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +github.com/silas/dag v0.0.0-20210626123444-3804bac2d6d4/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I= +go.unistack.org/micro-proto/v3 v3.1.0 h1:q39FwjFiRZn+Ux/tt+d3bJTmDtsQQWa+3SLYVo1vLfA= +go.unistack.org/micro-proto/v3 v3.1.0/go.mod h1:DpRhYCBXlmSJ/AAXTmntvlh7kQkYU6eFvlmYAx4BQS8= +go.unistack.org/micro/v3 v3.8.4 h1:Nny6JT7TlDS0e5bPVmbbpKXrisMQ6q9cvao5uyaa3eo= +go.unistack.org/micro/v3 v3.8.4/go.mod h1:KMMmOmbgo/D52/rCAbqeKbBsgEEbSKM69he54J3ZIuA= +golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/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= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/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= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/http.go b/http.go index 6e92c79..b9f6e88 100644 --- a/http.go +++ b/http.go @@ -15,11 +15,12 @@ import ( "sync" "time" - "github.com/unistack-org/micro/v3/broker" - "github.com/unistack-org/micro/v3/client" - "github.com/unistack-org/micro/v3/codec" - "github.com/unistack-org/micro/v3/errors" - "github.com/unistack-org/micro/v3/metadata" + "go.unistack.org/micro/v3/broker" + "go.unistack.org/micro/v3/client" + "go.unistack.org/micro/v3/codec" + "go.unistack.org/micro/v3/errors" + "go.unistack.org/micro/v3/metadata" + rutil "go.unistack.org/micro/v3/util/reflect" ) var DefaultContentType = "application/json" @@ -40,6 +41,7 @@ type httpClient struct { func newRequest(ctx context.Context, addr string, req client.Request, ct string, cf codec.Codec, msg interface{}, opts client.CallOptions) (*http.Request, error) { var tags []string + var parameters map[string]map[string]string scheme := "http" method := http.MethodPost body := "*" // as like google api http annotation @@ -55,6 +57,7 @@ func newRequest(ctx context.Context, addr string, req client.Request, ct string, u = &url.URL{Scheme: scheme, Path: path, Host: host} } + // nolint: nestif if opts.Context != nil { if m, ok := opts.Context.Value(methodKey{}).(string); ok { method = m @@ -68,6 +71,32 @@ func newRequest(ctx context.Context, addr string, req client.Request, ct string, if t, ok := opts.Context.Value(structTagsKey{}).([]string); ok && len(t) > 0 { tags = t } + if k, ok := opts.Context.Value(headerKey{}).([]string); ok && len(k) > 0 { + if parameters == nil { + parameters = make(map[string]map[string]string) + } + m, ok := parameters["header"] + if !ok { + m = make(map[string]string) + parameters["header"] = m + } + for idx := 0; idx < len(k)/2; idx += 2 { + m[k[idx]] = k[idx+1] + } + } + if k, ok := opts.Context.Value(cookieKey{}).([]string); ok && len(k) > 0 { + if parameters == nil { + parameters = make(map[string]map[string]string) + } + m, ok := parameters["cookie"] + if !ok { + m = make(map[string]string) + parameters["cookie"] = m + } + for idx := 0; idx < len(k)/2; idx += 2 { + m[k[idx]] = k[idx+1] + } + } } if len(tags) == 0 { @@ -90,9 +119,9 @@ func newRequest(ctx context.Context, addr string, req client.Request, ct string, var nmsg interface{} if len(u.Query()) > 0 { - path, nmsg, err = newPathRequest(u.Path+"?"+u.RawQuery, method, body, msg, tags) + path, nmsg, err = newPathRequest(u.Path+"?"+u.RawQuery, method, body, msg, tags, parameters) } else { - path, nmsg, err = newPathRequest(u.Path, method, body, msg, tags) + path, nmsg, err = newPathRequest(u.Path, method, body, msg, tags, parameters) } if err != nil { @@ -104,6 +133,59 @@ func newRequest(ctx context.Context, addr string, req client.Request, ct string, return nil, errors.BadRequest("go.micro.client", err.Error()) } + var cookies []*http.Cookie + 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 != "" { + header.Set(metadata.HeaderAuthorization, opts.AuthToken) + } + + if md, ok := metadata.FromOutgoingContext(ctx); ok { + for k, v := range md { + header.Set(k, v) + } + } + + // set timeout in nanoseconds + if opts.StreamTimeout > time.Duration(0) { + header.Set(metadata.HeaderTimeout, fmt.Sprintf("%d", opts.StreamTimeout)) + } + if opts.RequestTimeout > time.Duration(0) { + header.Set(metadata.HeaderTimeout, fmt.Sprintf("%d", opts.RequestTimeout)) + } + + // set the content type for the request + header.Set(metadata.HeaderContentType, ct) + var v interface{} + + for km, vm := range parameters { + for k, required := range vm { + v, err = rutil.StructFieldByPath(nmsg, k) + if err != nil { + return nil, errors.BadRequest("go.micro.client", err.Error()) + } + if rutil.IsZero(v) { + if required == "true" { + return nil, errors.BadRequest("go.micro.client", fmt.Sprintf("required field %s not set", k)) + } + continue + } + + switch km { + case "header": + header.Set(k, fmt.Sprintf("%v", v)) + case "cookie": + cookies = append(cookies, &http.Cookie{Name: k, Value: fmt.Sprintf("%v", v)}) + } + } + } + b, err := cf.Marshal(nmsg) if err != nil { return nil, errors.BadRequest("go.micro.client", err.Error()) @@ -113,6 +195,7 @@ func newRequest(ctx context.Context, addr string, req client.Request, ct string, if len(b) > 0 { hreq, err = http.NewRequestWithContext(ctx, method, u.String(), ioutil.NopCloser(bytes.NewBuffer(b))) hreq.ContentLength = int64(len(b)) + header.Set("Content-Length", fmt.Sprintf("%d", hreq.ContentLength)) } else { hreq, err = http.NewRequestWithContext(ctx, method, u.String(), nil) } @@ -121,37 +204,11 @@ func newRequest(ctx context.Context, addr string, req client.Request, ct string, return nil, errors.BadRequest("go.micro.client", 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) - } - } + hreq.Header = header + for _, cookie := range cookies { + hreq.AddCookie(cookie) } - if opts.AuthToken != "" { - hreq.Header.Set(metadata.HeaderAuthorization, 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(metadata.HeaderTimeout, fmt.Sprintf("%d", opts.StreamTimeout)) - } - if opts.RequestTimeout > time.Duration(0) { - hreq.Header.Set(metadata.HeaderTimeout, fmt.Sprintf("%d", opts.RequestTimeout)) - } - - // set the content type for the request - hreq.Header.Set(metadata.HeaderContentType, ct) - return hreq, nil } diff --git a/http_test.go b/http_test.go index 2a925f5..cab635a 100644 --- a/http_test.go +++ b/http_test.go @@ -7,15 +7,41 @@ import ( ) type Request struct { - Name string `json:"name"` - Field1 string `json:"field1"` - Field2 string - Field3 int64 + Name string `json:"name"` + Field1 string `json:"field1"` + ClientID string + Field2 string + Field3 int64 +} + +func TestPathWithHeader(t *testing.T) { + req := &Request{Name: "vtolstov", Field1: "field1", ClientID: "1234567890"} + p, m, err := newPathRequest( + "/api/v1/test?Name={name}&Field1={field1}", + "POST", + "*", + req, + nil, + map[string]map[string]string{"header": {"ClientID": "true"}}, + ) + if err != nil { + t.Fatal(err) + } + u, err := url.Parse(p) + if err != nil { + t.Fatal(err) + } + if m != nil { + t.Fatal("new struct must be nil") + } + if u.Query().Get("Name") != "vtolstov" || u.Query().Get("Field1") != "field1" { + t.Fatalf("invalid values %v", u.Query()) + } } 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) + p, m, err := newPathRequest("/api/v1/test?Name={name}&Field1={field1}", "POST", "*", req, nil, nil) if err != nil { t.Fatal(err) } @@ -31,7 +57,7 @@ func TestPathValues(t *testing.T) { 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) + p, m, err := newPathRequest("/api/v1/{name}/list", "GET", "", req, nil, nil) if err != nil { t.Fatal(err) } @@ -48,9 +74,8 @@ func TestValidPath(t *testing.T) { func TestInvalidPath(t *testing.T) { req := &Request{Name: "vtolstov", Field1: "field1", Field2: "field2", Field3: 10} - p, m, err := newPathRequest("/api/v1/{xname}/list", "GET", "", req, nil) + _, _, err := newPathRequest("/api/v1/{xname}/list", "GET", "", req, nil, nil) if err == nil { - t.Fatalf("path param must not be filled") + t.Fatal("path param must not be filled") } - _, _ = p, m } diff --git a/message.go b/message.go index 899cfb8..ef69d9f 100644 --- a/message.go +++ b/message.go @@ -1,7 +1,7 @@ package http import ( - "github.com/unistack-org/micro/v3/client" + "go.unistack.org/micro/v3/client" ) type httpMessage struct { diff --git a/options.go b/options.go index 195d638..0e24695 100644 --- a/options.go +++ b/options.go @@ -4,8 +4,8 @@ import ( "net" "net/http" - "github.com/unistack-org/micro/v3/client" - "github.com/unistack-org/micro/v3/metadata" + "go.unistack.org/micro/v3/client" + "go.unistack.org/micro/v3/metadata" ) var ( @@ -57,6 +57,7 @@ func MaxSendMsgSize(s int) client.Option { type httpClientKey struct{} // nolint: golint +// HTTPClient pass http.Client option to client Call func HTTPClient(c *http.Client) client.Option { return client.SetOption(httpClientKey{}, c) } @@ -64,24 +65,28 @@ func HTTPClient(c *http.Client) client.Option { type httpDialerKey struct{} // nolint: golint +// HTTPDialer pass net.Dialer option to client func HTTPDialer(d *net.Dialer) client.Option { return client.SetOption(httpDialerKey{}, d) } type methodKey struct{} +// Method pass method option to client Call func Method(m string) client.CallOption { return client.SetCallOption(methodKey{}, m) } type pathKey struct{} +// Path spcecifies path option to client Call func Path(p string) client.CallOption { return client.SetCallOption(pathKey{}, p) } type bodyKey struct{} +// Body specifies body option to client Call func Body(b string) client.CallOption { return client.SetCallOption(bodyKey{}, b) } @@ -94,12 +99,28 @@ func ErrorMap(m map[string]interface{}) client.CallOption { type structTagsKey struct{} +// StructTags pass tags slice option to client Call func StructTags(tags []string) client.CallOption { return client.SetCallOption(structTagsKey{}, tags) } type metadataKey struct{} +// Metadata pass metadata to client Call func Metadata(md metadata.Metadata) client.CallOption { return client.SetCallOption(metadataKey{}, md) } + +type cookieKey struct{} + +// Cookie pass cookie to client Call +func Cookie(cookies ...string) client.CallOption { + return client.SetCallOption(cookieKey{}, cookies) +} + +type headerKey struct{} + +// Header pass cookie to client Call +func Header(headers ...string) client.CallOption { + return client.SetCallOption(headerKey{}, headers) +} diff --git a/request.go b/request.go index dd56684..00c23a4 100644 --- a/request.go +++ b/request.go @@ -1,8 +1,8 @@ package http import ( - "github.com/unistack-org/micro/v3/client" - "github.com/unistack-org/micro/v3/codec" + "go.unistack.org/micro/v3/client" + "go.unistack.org/micro/v3/codec" ) type httpRequest struct { diff --git a/stream.go b/stream.go index 0874232..7d2465c 100644 --- a/stream.go +++ b/stream.go @@ -9,9 +9,9 @@ import ( "net/http" "sync" - "github.com/unistack-org/micro/v3/client" - "github.com/unistack-org/micro/v3/codec" - "github.com/unistack-org/micro/v3/errors" + "go.unistack.org/micro/v3/client" + "go.unistack.org/micro/v3/codec" + "go.unistack.org/micro/v3/errors" ) // Implements the streamer interface diff --git a/util.go b/util.go index e800b4c..4785318 100644 --- a/util.go +++ b/util.go @@ -10,10 +10,10 @@ import ( "strings" "sync" - "github.com/unistack-org/micro/v3/client" - "github.com/unistack-org/micro/v3/errors" - "github.com/unistack-org/micro/v3/logger" - rutil "github.com/unistack-org/micro/v3/util/reflect" + "go.unistack.org/micro/v3/client" + "go.unistack.org/micro/v3/errors" + "go.unistack.org/micro/v3/logger" + rutil "go.unistack.org/micro/v3/util/reflect" ) var ( @@ -38,7 +38,7 @@ func GetError(err error) interface{} { 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, parameters map[string]map[string]string) (string, interface{}, error) { // parse via https://github.com/googleapis/googleapis/blob/master/google/api/http.proto definition tpl, err := newTemplate(path) if err != nil { @@ -117,10 +117,18 @@ func newPathRequest(path string, method string, body string, msg interface{}, ta } } - if t.name == "" { + cname := t.name + if cname == "" { + cname = fld.Name // fallback to lowercase t.name = strings.ToLower(fld.Name) } + if _, ok := parameters["header"][cname]; ok { + continue + } + if _, ok := parameters["cookie"][cname]; ok { + continue + } if !val.IsValid() || val.IsZero() { continue @@ -200,6 +208,12 @@ func newPathRequest(path string, method string, body string, msg interface{}, ta _, _ = b.WriteString(values.Encode()) } + /* + if err = rutil.ZeroFieldByPath(nmsg, k); err != nil { + return nil, errors.BadRequest("go.micro.client", err.Error()) + } + */ + if rutil.IsZero(nmsg) { return b.String(), nil, nil } diff --git a/util_test.go b/util_test.go index 03acf84..5d8b799 100644 --- a/util_test.go +++ b/util_test.go @@ -14,7 +14,7 @@ func TestParsing(t *testing.T) { for _, m := range []string{"POST"} { body := "" - path, nmsg, err := newPathRequest("/users/iin/{iin}/push-notifications", m, body, omsg, []string{"protobuf", "json"}) + path, nmsg, err := newPathRequest("/users/iin/{iin}/push-notifications", m, body, omsg, []string{"protobuf", "json"}, nil) if err != nil { t.Fatal(err) } @@ -44,7 +44,7 @@ func TestNewPathRequest(t *testing.T) { for _, m := range []string{"POST", "PUT", "PATCH", "GET", "DELETE"} { body := "" - path, nmsg, err := newPathRequest("/v1/test", m, body, omsg, []string{"protobuf", "json"}) + path, nmsg, err := newPathRequest("/v1/test", m, body, omsg, []string{"protobuf", "json"}, nil) if err != nil { t.Fatal(err) } @@ -74,7 +74,7 @@ func TestNewPathVarRequest(t *testing.T) { if m != "GET" { body = "*" } - path, nmsg, err := newPathRequest("/v1/test/{val1}", m, body, omsg, []string{"protobuf", "json"}) + path, nmsg, err := newPathRequest("/v1/test/{val1}", m, body, omsg, []string{"protobuf", "json"}, nil) if err != nil { t.Fatal(err) }