api/router: avoid unneeded loops and fix path match (#1594)

* api/router: avoid unneeded loops and fix path match

* if match found in google api path syntax, not try pcre loop
* if path is not ending via $ sign, append it to pcre to avoid matching other strings like
  /api/account/register can be matched to /api/account
* api: add tests and validations

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
Василий Толстов 2020-04-29 15:23:10 +03:00
parent ac3ce1ec16
commit 19a313ac4e
4 changed files with 178 additions and 15 deletions

View File

@ -33,6 +33,26 @@ func (s *testServer) HandleError(ctx context.Context, msg *pb.Request) error {
return fmt.Errorf("fake") return fmt.Errorf("fake")
} }
// TestHello implements helloworld.GreeterServer
func (s *testServer) CallPcre(ctx context.Context, req *pb.Request, rsp *pb.Response) error {
if req.Name == "Error" {
return &errors.Error{Id: "1", Code: 99, Detail: "detail"}
}
rsp.Msg = "Hello " + req.Name
return nil
}
// TestHello implements helloworld.GreeterServer
func (s *testServer) CallPcreInvalid(ctx context.Context, req *pb.Request, rsp *pb.Response) error {
if req.Name == "Error" {
return &errors.Error{Id: "1", Code: 99, Detail: "detail"}
}
rsp.Msg = "Hello " + req.Name
return nil
}
// TestHello implements helloworld.GreeterServer // TestHello implements helloworld.GreeterServer
func (s *testServer) Call(ctx context.Context, req *pb.Request, rsp *pb.Response) error { func (s *testServer) Call(ctx context.Context, req *pb.Request, rsp *pb.Response) error {
if req.Name == "Error" { if req.Name == "Error" {

View File

@ -119,20 +119,24 @@ func init() {
func init() { proto.RegisterFile("server/grpc/proto/test.proto", fileDescriptor_bb9c685b7640cf1e) } func init() { proto.RegisterFile("server/grpc/proto/test.proto", fileDescriptor_bb9c685b7640cf1e) }
var fileDescriptor_bb9c685b7640cf1e = []byte{ var fileDescriptor_bb9c685b7640cf1e = []byte{
// 198 bytes of a gzipped FileDescriptorProto // 261 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x29, 0x4e, 0x2d, 0x2a, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0xd0, 0xcf, 0x4a, 0x03, 0x31,
0x4b, 0x2d, 0xd2, 0x4f, 0x2f, 0x2a, 0x48, 0xd6, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0xd7, 0x2f, 0x49, 0x10, 0x06, 0x70, 0xb6, 0x2e, 0xba, 0xcd, 0x45, 0xc9, 0x69, 0x59, 0x56, 0x2c, 0xd1, 0x82, 0x54,
0x2d, 0x2e, 0xd1, 0x03, 0x33, 0xa5, 0x64, 0xd2, 0xf3, 0xf3, 0xd3, 0x73, 0x52, 0xf5, 0x13, 0x0b, 0xd8, 0xf1, 0xcf, 0xad, 0x97, 0x0a, 0x82, 0xe0, 0x4d, 0x56, 0xcf, 0x42, 0xdc, 0x0e, 0x4b, 0x20,
0x32, 0xf5, 0x13, 0xf3, 0xf2, 0xf2, 0x4b, 0x12, 0x4b, 0x32, 0xf3, 0xf3, 0x8a, 0x21, 0xb2, 0x4a, 0x4d, 0x62, 0x92, 0xdd, 0x8b, 0x78, 0xf1, 0x15, 0x7c, 0x34, 0x5f, 0x41, 0xdf, 0x43, 0x92, 0x6d,
0x86, 0x5c, 0xec, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x42, 0x42, 0x5c, 0x2c, 0xa5, 0xa5, 0x4f, 0xb6, 0xb7, 0x8f, 0x09, 0xdf, 0x6f, 0x86, 0x90, 0xd2, 0xa1, 0xed, 0xd1, 0x42, 0x6b, 0x4d,
0x99, 0x29, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x60, 0x36, 0x48, 0x2c, 0x2f, 0x31, 0x37, 0x03, 0xc6, 0x6a, 0xaf, 0xc1, 0xa3, 0xf3, 0x55, 0x8c, 0x45, 0xd9, 0x6a, 0xdd, 0x4a, 0x04, 0x6e,
0x55, 0x82, 0x09, 0x22, 0x06, 0x62, 0x2b, 0xc9, 0x70, 0x71, 0x04, 0xa5, 0x16, 0x17, 0xe4, 0xe7, 0x04, 0x70, 0xa5, 0xb4, 0xe7, 0x5e, 0x68, 0xe5, 0x86, 0x57, 0x76, 0x45, 0x0e, 0x6a, 0x7c, 0xeb,
0x15, 0xa7, 0x0a, 0x09, 0x70, 0x31, 0xe7, 0x16, 0xa7, 0x43, 0xb5, 0x80, 0x98, 0x46, 0x1e, 0x5c, 0xd0, 0x79, 0x4a, 0x49, 0xda, 0x75, 0x62, 0x99, 0x27, 0x93, 0xe4, 0x7c, 0x5c, 0xc7, 0x1c, 0x66,
0x2c, 0x21, 0x20, 0xd3, 0x1c, 0xb8, 0x58, 0x9c, 0x13, 0x73, 0x72, 0x84, 0x38, 0xf4, 0xa0, 0xe6, 0x8a, 0xaf, 0x30, 0x1f, 0x0d, 0xb3, 0x90, 0x59, 0x49, 0xb2, 0x1a, 0x9d, 0xd1, 0xca, 0x21, 0x3d,
0x4b, 0x71, 0xea, 0xc1, 0xb4, 0x29, 0x29, 0x37, 0x5d, 0x7e, 0x32, 0x99, 0x49, 0x56, 0x49, 0x02, 0x22, 0x7b, 0x2b, 0xd7, 0xae, 0x2b, 0x21, 0x5e, 0xff, 0x26, 0x24, 0x7d, 0x0e, 0xdc, 0x2d, 0x49,
0xec, 0xaa, 0x32, 0x03, 0xb0, 0x7b, 0xf5, 0x93, 0x13, 0x73, 0x72, 0xf4, 0xab, 0x41, 0xf6, 0xd6, 0xef, 0xb8, 0x94, 0x34, 0xab, 0xd6, 0x0b, 0x8a, 0x71, 0xb5, 0xe9, 0xb1, 0xd3, 0xcf, 0xef, 0x9f,
0x5a, 0x31, 0x6a, 0x25, 0xb1, 0x81, 0x5d, 0x68, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xd0, 0xc1, 0xaf, 0xd1, 0x31, 0xcb, 0xe3, 0x59, 0xfd, 0x65, 0x3c, 0x18, 0x1a, 0x2e, 0x25, 0xbc, 0x87, 0xc5,
0x00, 0x50, 0xdf, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0x64, 0x46, 0xef, 0x49, 0x16, 0x84, 0xc7, 0xc6, 0xe2, 0x76, 0x65, 0x1a, 0x95, 0x13,
0x56, 0xbc, 0xfc, 0x67, 0x4c, 0x63, 0x11, 0x16, 0x67, 0xc1, 0x79, 0x22, 0x87, 0x1b, 0xe7, 0x41,
0xf5, 0x5c, 0x8a, 0xe5, 0x76, 0xee, 0x22, 0x72, 0x53, 0x36, 0xd9, 0xa1, 0x89, 0xa1, 0x0c, 0x8b,
0x79, 0x32, 0x7b, 0xdd, 0x8f, 0xff, 0x77, 0xf3, 0x17, 0x00, 0x00, 0xff, 0xff, 0xd3, 0x25, 0x7a,
0x7d, 0x7d, 0x01, 0x00, 0x00,
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
@ -148,6 +152,8 @@ const _ = grpc.SupportPackageIsVersion4
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type TestClient interface { type TestClient interface {
Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
CallPcre(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
CallPcreInvalid(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
} }
type testClient struct { type testClient struct {
@ -167,9 +173,29 @@ func (c *testClient) Call(ctx context.Context, in *Request, opts ...grpc.CallOpt
return out, nil return out, nil
} }
func (c *testClient) CallPcre(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
out := new(Response)
err := c.cc.Invoke(ctx, "/Test/CallPcre", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *testClient) CallPcreInvalid(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
out := new(Response)
err := c.cc.Invoke(ctx, "/Test/CallPcreInvalid", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// TestServer is the server API for Test service. // TestServer is the server API for Test service.
type TestServer interface { type TestServer interface {
Call(context.Context, *Request) (*Response, error) Call(context.Context, *Request) (*Response, error)
CallPcre(context.Context, *Request) (*Response, error)
CallPcreInvalid(context.Context, *Request) (*Response, error)
} }
// UnimplementedTestServer can be embedded to have forward compatible implementations. // UnimplementedTestServer can be embedded to have forward compatible implementations.
@ -179,6 +205,12 @@ type UnimplementedTestServer struct {
func (*UnimplementedTestServer) Call(ctx context.Context, req *Request) (*Response, error) { func (*UnimplementedTestServer) Call(ctx context.Context, req *Request) (*Response, error) {
return nil, status.Errorf(codes.Unimplemented, "method Call not implemented") return nil, status.Errorf(codes.Unimplemented, "method Call not implemented")
} }
func (*UnimplementedTestServer) CallPcre(ctx context.Context, req *Request) (*Response, error) {
return nil, status.Errorf(codes.Unimplemented, "method CallPcre not implemented")
}
func (*UnimplementedTestServer) CallPcreInvalid(ctx context.Context, req *Request) (*Response, error) {
return nil, status.Errorf(codes.Unimplemented, "method CallPcreInvalid not implemented")
}
func RegisterTestServer(s *grpc.Server, srv TestServer) { func RegisterTestServer(s *grpc.Server, srv TestServer) {
s.RegisterService(&_Test_serviceDesc, srv) s.RegisterService(&_Test_serviceDesc, srv)
@ -202,6 +234,42 @@ func _Test_Call_Handler(srv interface{}, ctx context.Context, dec func(interface
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Test_CallPcre_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Request)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TestServer).CallPcre(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Test/CallPcre",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TestServer).CallPcre(ctx, req.(*Request))
}
return interceptor(ctx, in, info, handler)
}
func _Test_CallPcreInvalid_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Request)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TestServer).CallPcreInvalid(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Test/CallPcreInvalid",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TestServer).CallPcreInvalid(ctx, req.(*Request))
}
return interceptor(ctx, in, info, handler)
}
var _Test_serviceDesc = grpc.ServiceDesc{ var _Test_serviceDesc = grpc.ServiceDesc{
ServiceName: "Test", ServiceName: "Test",
HandlerType: (*TestServer)(nil), HandlerType: (*TestServer)(nil),
@ -210,6 +278,14 @@ var _Test_serviceDesc = grpc.ServiceDesc{
MethodName: "Call", MethodName: "Call",
Handler: _Test_Call_Handler, Handler: _Test_Call_Handler,
}, },
{
MethodName: "CallPcre",
Handler: _Test_CallPcre_Handler,
},
{
MethodName: "CallPcreInvalid",
Handler: _Test_CallPcreInvalid_Handler,
},
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{},
Metadata: "server/grpc/proto/test.proto", Metadata: "server/grpc/proto/test.proto",

View File

@ -45,6 +45,20 @@ func NewTestEndpoints() []*api.Endpoint {
Body: "*", Body: "*",
Handler: "rpc", Handler: "rpc",
}, },
&api.Endpoint{
Name: "Test.CallPcre",
Path: []string{"^/api/v0/test/call/pcre/?$"},
Method: []string{"POST"},
Body: "*",
Handler: "rpc",
},
&api.Endpoint{
Name: "Test.CallPcreInvalid",
Path: []string{"/api/v0/test/call/pcre/invalid/?"},
Method: []string{"POST"},
Body: "*",
Handler: "rpc",
},
} }
} }
@ -52,6 +66,8 @@ func NewTestEndpoints() []*api.Endpoint {
type TestService interface { type TestService interface {
Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
CallPcre(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
CallPcreInvalid(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
} }
type testService struct { type testService struct {
@ -76,15 +92,39 @@ func (c *testService) Call(ctx context.Context, in *Request, opts ...client.Call
return out, nil return out, nil
} }
func (c *testService) CallPcre(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
req := c.c.NewRequest(c.name, "Test.CallPcre", in)
out := new(Response)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *testService) CallPcreInvalid(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
req := c.c.NewRequest(c.name, "Test.CallPcreInvalid", in)
out := new(Response)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Test service // Server API for Test service
type TestHandler interface { type TestHandler interface {
Call(context.Context, *Request, *Response) error Call(context.Context, *Request, *Response) error
CallPcre(context.Context, *Request, *Response) error
CallPcreInvalid(context.Context, *Request, *Response) error
} }
func RegisterTestHandler(s server.Server, hdlr TestHandler, opts ...server.HandlerOption) error { func RegisterTestHandler(s server.Server, hdlr TestHandler, opts ...server.HandlerOption) error {
type test interface { type test interface {
Call(ctx context.Context, in *Request, out *Response) error Call(ctx context.Context, in *Request, out *Response) error
CallPcre(ctx context.Context, in *Request, out *Response) error
CallPcreInvalid(ctx context.Context, in *Request, out *Response) error
} }
type Test struct { type Test struct {
test test
@ -97,6 +137,20 @@ func RegisterTestHandler(s server.Server, hdlr TestHandler, opts ...server.Handl
Body: "*", Body: "*",
Handler: "rpc", Handler: "rpc",
})) }))
opts = append(opts, api.WithEndpoint(&api.Endpoint{
Name: "Test.CallPcre",
Path: []string{"^/api/v0/test/call/pcre/?$"},
Method: []string{"POST"},
Body: "*",
Handler: "rpc",
}))
opts = append(opts, api.WithEndpoint(&api.Endpoint{
Name: "Test.CallPcreInvalid",
Path: []string{"/api/v0/test/call/pcre/invalid/?"},
Method: []string{"POST"},
Body: "*",
Handler: "rpc",
}))
return s.Handle(s.NewHandler(&Test{h}, opts...)) return s.Handle(s.NewHandler(&Test{h}, opts...))
} }
@ -107,3 +161,11 @@ type testHandler struct {
func (h *testHandler) Call(ctx context.Context, in *Request, out *Response) error { func (h *testHandler) Call(ctx context.Context, in *Request, out *Response) error {
return h.TestHandler.Call(ctx, in, out) return h.TestHandler.Call(ctx, in, out)
} }
func (h *testHandler) CallPcre(ctx context.Context, in *Request, out *Response) error {
return h.TestHandler.CallPcre(ctx, in, out)
}
func (h *testHandler) CallPcreInvalid(ctx context.Context, in *Request, out *Response) error {
return h.TestHandler.CallPcreInvalid(ctx, in, out)
}

View File

@ -6,7 +6,12 @@ service Test {
rpc Call(Request) returns (Response) { rpc Call(Request) returns (Response) {
option (google.api.http) = { post: "/api/v0/test/call/{uuid}"; body:"*"; }; option (google.api.http) = { post: "/api/v0/test/call/{uuid}"; body:"*"; };
}; };
rpc CallPcre(Request) returns (Response) {
option (google.api.http) = { post: "^/api/v0/test/call/pcre/?$"; body:"*"; };
};
rpc CallPcreInvalid(Request) returns (Response) {
option (google.api.http) = { post: "^/api/v0/test/call/pcre/invalid/?"; body:"*"; };
};
} }
message Request { message Request {