| @@ -31,37 +31,37 @@ var _ context.Context | |||||||
| var _ client.Option | var _ client.Option | ||||||
| var _ server.Option | var _ server.Option | ||||||
|  |  | ||||||
| // Client API for Micro service | // Client API for Client service | ||||||
|  |  | ||||||
| type MicroService interface { | type ClientService interface { | ||||||
| 	// Call allows a single request to be made | 	// Call allows a single request to be made | ||||||
| 	Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) | 	Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) | ||||||
| 	// Stream is a bidirectional stream | 	// Stream is a bidirectional stream | ||||||
| 	Stream(ctx context.Context, opts ...client.CallOption) (Micro_StreamService, error) | 	Stream(ctx context.Context, opts ...client.CallOption) (Client_StreamService, error) | ||||||
| 	// Publish publishes a message and returns an empty Message | 	// Publish publishes a message and returns an empty Message | ||||||
| 	Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error) | 	Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error) | ||||||
| } | } | ||||||
|  |  | ||||||
| type microService struct { | type clientService struct { | ||||||
| 	c    client.Client | 	c    client.Client | ||||||
| 	name string | 	name string | ||||||
| } | } | ||||||
|  |  | ||||||
| func NewMicroService(name string, c client.Client) MicroService { | func NewClientService(name string, c client.Client) ClientService { | ||||||
| 	if c == nil { | 	if c == nil { | ||||||
| 		c = client.NewClient() | 		c = client.NewClient() | ||||||
| 	} | 	} | ||||||
| 	if len(name) == 0 { | 	if len(name) == 0 { | ||||||
| 		name = "go.micro.client" | 		name = "go.micro.client" | ||||||
| 	} | 	} | ||||||
| 	return µService{ | 	return &clientService{ | ||||||
| 		c:    c, | 		c:    c, | ||||||
| 		name: name, | 		name: name, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *microService) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) { | func (c *clientService) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) { | ||||||
| 	req := c.c.NewRequest(c.name, "Micro.Call", in) | 	req := c.c.NewRequest(c.name, "Client.Call", in) | ||||||
| 	out := new(Response) | 	out := new(Response) | ||||||
| 	err := c.c.Call(ctx, req, out, opts...) | 	err := c.c.Call(ctx, req, out, opts...) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -70,16 +70,16 @@ func (c *microService) Call(ctx context.Context, in *Request, opts ...client.Cal | |||||||
| 	return out, nil | 	return out, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *microService) Stream(ctx context.Context, opts ...client.CallOption) (Micro_StreamService, error) { | func (c *clientService) Stream(ctx context.Context, opts ...client.CallOption) (Client_StreamService, error) { | ||||||
| 	req := c.c.NewRequest(c.name, "Micro.Stream", &Request{}) | 	req := c.c.NewRequest(c.name, "Client.Stream", &Request{}) | ||||||
| 	stream, err := c.c.Stream(ctx, req, opts...) | 	stream, err := c.c.Stream(ctx, req, opts...) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	return µServiceStream{stream}, nil | 	return &clientServiceStream{stream}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| type Micro_StreamService interface { | type Client_StreamService interface { | ||||||
| 	SendMsg(interface{}) error | 	SendMsg(interface{}) error | ||||||
| 	RecvMsg(interface{}) error | 	RecvMsg(interface{}) error | ||||||
| 	Close() error | 	Close() error | ||||||
| @@ -87,27 +87,27 @@ type Micro_StreamService interface { | |||||||
| 	Recv() (*Response, error) | 	Recv() (*Response, error) | ||||||
| } | } | ||||||
|  |  | ||||||
| type microServiceStream struct { | type clientServiceStream struct { | ||||||
| 	stream client.Stream | 	stream client.Stream | ||||||
| } | } | ||||||
|  |  | ||||||
| func (x *microServiceStream) Close() error { | func (x *clientServiceStream) Close() error { | ||||||
| 	return x.stream.Close() | 	return x.stream.Close() | ||||||
| } | } | ||||||
|  |  | ||||||
| func (x *microServiceStream) SendMsg(m interface{}) error { | func (x *clientServiceStream) SendMsg(m interface{}) error { | ||||||
| 	return x.stream.Send(m) | 	return x.stream.Send(m) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (x *microServiceStream) RecvMsg(m interface{}) error { | func (x *clientServiceStream) RecvMsg(m interface{}) error { | ||||||
| 	return x.stream.Recv(m) | 	return x.stream.Recv(m) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (x *microServiceStream) Send(m *Request) error { | func (x *clientServiceStream) Send(m *Request) error { | ||||||
| 	return x.stream.Send(m) | 	return x.stream.Send(m) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (x *microServiceStream) Recv() (*Response, error) { | func (x *clientServiceStream) Recv() (*Response, error) { | ||||||
| 	m := new(Response) | 	m := new(Response) | ||||||
| 	err := x.stream.Recv(m) | 	err := x.stream.Recv(m) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -116,8 +116,8 @@ func (x *microServiceStream) Recv() (*Response, error) { | |||||||
| 	return m, nil | 	return m, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *microService) Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error) { | func (c *clientService) Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error) { | ||||||
| 	req := c.c.NewRequest(c.name, "Micro.Publish", in) | 	req := c.c.NewRequest(c.name, "Client.Publish", in) | ||||||
| 	out := new(Message) | 	out := new(Message) | ||||||
| 	err := c.c.Call(ctx, req, out, opts...) | 	err := c.c.Call(ctx, req, out, opts...) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -126,43 +126,43 @@ func (c *microService) Publish(ctx context.Context, in *Message, opts ...client. | |||||||
| 	return out, nil | 	return out, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // Server API for Micro service | // Server API for Client service | ||||||
|  |  | ||||||
| type MicroHandler interface { | type ClientHandler interface { | ||||||
| 	// Call allows a single request to be made | 	// Call allows a single request to be made | ||||||
| 	Call(context.Context, *Request, *Response) error | 	Call(context.Context, *Request, *Response) error | ||||||
| 	// Stream is a bidirectional stream | 	// Stream is a bidirectional stream | ||||||
| 	Stream(context.Context, Micro_StreamStream) error | 	Stream(context.Context, Client_StreamStream) error | ||||||
| 	// Publish publishes a message and returns an empty Message | 	// Publish publishes a message and returns an empty Message | ||||||
| 	Publish(context.Context, *Message, *Message) error | 	Publish(context.Context, *Message, *Message) error | ||||||
| } | } | ||||||
|  |  | ||||||
| func RegisterMicroHandler(s server.Server, hdlr MicroHandler, opts ...server.HandlerOption) error { | func RegisterClientHandler(s server.Server, hdlr ClientHandler, opts ...server.HandlerOption) error { | ||||||
| 	type micro interface { | 	type client interface { | ||||||
| 		Call(ctx context.Context, in *Request, out *Response) error | 		Call(ctx context.Context, in *Request, out *Response) error | ||||||
| 		Stream(ctx context.Context, stream server.Stream) error | 		Stream(ctx context.Context, stream server.Stream) error | ||||||
| 		Publish(ctx context.Context, in *Message, out *Message) error | 		Publish(ctx context.Context, in *Message, out *Message) error | ||||||
| 	} | 	} | ||||||
| 	type Micro struct { | 	type Client struct { | ||||||
| 		micro | 		client | ||||||
| 	} | 	} | ||||||
| 	h := µHandler{hdlr} | 	h := &clientHandler{hdlr} | ||||||
| 	return s.Handle(s.NewHandler(&Micro{h}, opts...)) | 	return s.Handle(s.NewHandler(&Client{h}, opts...)) | ||||||
| } | } | ||||||
|  |  | ||||||
| type microHandler struct { | type clientHandler struct { | ||||||
| 	MicroHandler | 	ClientHandler | ||||||
| } | } | ||||||
|  |  | ||||||
| func (h *microHandler) Call(ctx context.Context, in *Request, out *Response) error { | func (h *clientHandler) Call(ctx context.Context, in *Request, out *Response) error { | ||||||
| 	return h.MicroHandler.Call(ctx, in, out) | 	return h.ClientHandler.Call(ctx, in, out) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (h *microHandler) Stream(ctx context.Context, stream server.Stream) error { | func (h *clientHandler) Stream(ctx context.Context, stream server.Stream) error { | ||||||
| 	return h.MicroHandler.Stream(ctx, µStreamStream{stream}) | 	return h.ClientHandler.Stream(ctx, &clientStreamStream{stream}) | ||||||
| } | } | ||||||
|  |  | ||||||
| type Micro_StreamStream interface { | type Client_StreamStream interface { | ||||||
| 	SendMsg(interface{}) error | 	SendMsg(interface{}) error | ||||||
| 	RecvMsg(interface{}) error | 	RecvMsg(interface{}) error | ||||||
| 	Close() error | 	Close() error | ||||||
| @@ -170,27 +170,27 @@ type Micro_StreamStream interface { | |||||||
| 	Recv() (*Request, error) | 	Recv() (*Request, error) | ||||||
| } | } | ||||||
|  |  | ||||||
| type microStreamStream struct { | type clientStreamStream struct { | ||||||
| 	stream server.Stream | 	stream server.Stream | ||||||
| } | } | ||||||
|  |  | ||||||
| func (x *microStreamStream) Close() error { | func (x *clientStreamStream) Close() error { | ||||||
| 	return x.stream.Close() | 	return x.stream.Close() | ||||||
| } | } | ||||||
|  |  | ||||||
| func (x *microStreamStream) SendMsg(m interface{}) error { | func (x *clientStreamStream) SendMsg(m interface{}) error { | ||||||
| 	return x.stream.Send(m) | 	return x.stream.Send(m) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (x *microStreamStream) RecvMsg(m interface{}) error { | func (x *clientStreamStream) RecvMsg(m interface{}) error { | ||||||
| 	return x.stream.Recv(m) | 	return x.stream.Recv(m) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (x *microStreamStream) Send(m *Response) error { | func (x *clientStreamStream) Send(m *Response) error { | ||||||
| 	return x.stream.Send(m) | 	return x.stream.Send(m) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (x *microStreamStream) Recv() (*Request, error) { | func (x *clientStreamStream) Recv() (*Request, error) { | ||||||
| 	m := new(Request) | 	m := new(Request) | ||||||
| 	if err := x.stream.Recv(m); err != nil { | 	if err := x.stream.Recv(m); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @@ -198,6 +198,6 @@ func (x *microStreamStream) Recv() (*Request, error) { | |||||||
| 	return m, nil | 	return m, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (h *microHandler) Publish(ctx context.Context, in *Message, out *Message) error { | func (h *clientHandler) Publish(ctx context.Context, in *Message, out *Message) error { | ||||||
| 	return h.MicroHandler.Publish(ctx, in, out) | 	return h.ClientHandler.Publish(ctx, in, out) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -191,23 +191,23 @@ func init() { | |||||||
|  |  | ||||||
| var fileDescriptor_7d733ae29171347b = []byte{ | var fileDescriptor_7d733ae29171347b = []byte{ | ||||||
| 	// 270 bytes of a gzipped FileDescriptorProto | 	// 270 bytes of a gzipped FileDescriptorProto | ||||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x91, 0x3f, 0x4f, 0xc3, 0x30, | 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x91, 0x41, 0x4b, 0xc3, 0x40, | ||||||
| 	0x10, 0xc5, 0xeb, 0xfe, 0x4b, 0x39, 0x2a, 0x21, 0x9d, 0x18, 0x4c, 0x06, 0x54, 0x32, 0x65, 0xc1, | 	0x10, 0x85, 0xbb, 0x6d, 0x4c, 0xea, 0x58, 0x10, 0x06, 0x0f, 0x6b, 0x0e, 0x52, 0x73, 0xca, 0xc5, | ||||||
| 	0x45, 0x30, 0x23, 0x86, 0xce, 0x95, 0x50, 0x40, 0xac, 0x28, 0x71, 0x4f, 0xc1, 0x52, 0x6a, 0x9b, | 	0x54, 0xf4, 0x2c, 0x1e, 0x72, 0x16, 0x24, 0x8a, 0x57, 0x49, 0xb6, 0x43, 0x5c, 0x48, 0x77, 0xd7, | ||||||
| 	0xd8, 0xad, 0x94, 0xef, 0xc8, 0x87, 0x42, 0x38, 0x29, 0x45, 0xd0, 0x2e, 0x6c, 0xf7, 0xee, 0x67, | 	0xec, 0xb6, 0x90, 0x1f, 0xe9, 0x7f, 0x12, 0x36, 0xa9, 0x15, 0x6d, 0x2f, 0xbd, 0xcd, 0x9b, 0x6f, | ||||||
| 	0xbd, 0x3b, 0xbf, 0x83, 0x74, 0xad, 0x64, 0x6d, 0xe6, 0xa5, 0xb9, 0x6e, 0x0b, 0x59, 0x29, 0xd2, | 	0x79, 0x33, 0xfb, 0x06, 0xd2, 0x95, 0x14, 0xad, 0x5e, 0xd4, 0xfa, 0xa6, 0x2f, 0x44, 0x23, 0x49, | ||||||
| 	0x7e, 0x6e, 0x6b, 0xe3, 0x77, 0x42, 0x04, 0x81, 0x67, 0xa5, 0x11, 0xe1, 0x8d, 0x68, 0xdb, 0xc9, | 	0xb9, 0x85, 0x69, 0xb5, 0xdb, 0x8a, 0xcc, 0x0b, 0x3c, 0xaf, 0x75, 0xe6, 0xdf, 0x64, 0x7d, 0x3b, | ||||||
| 	0x16, 0xa2, 0x8c, 0xde, 0x37, 0xe4, 0x3c, 0x72, 0x88, 0x1c, 0xd5, 0x5b, 0x25, 0x89, 0xb3, 0x19, | 	0xd9, 0x40, 0x54, 0xd0, 0xe7, 0x9a, 0xac, 0x43, 0x0e, 0x91, 0xa5, 0x76, 0x23, 0x05, 0x71, 0x36, | ||||||
| 	0x4b, 0x4f, 0xb2, 0x9d, 0xc4, 0x18, 0x26, 0xa4, 0x57, 0xd6, 0x28, 0xed, 0x79, 0x3f, 0xa0, 0x6f, | 	0x67, 0xe9, 0x69, 0xb1, 0x95, 0x18, 0xc3, 0x94, 0xd4, 0xd2, 0x68, 0xa9, 0x1c, 0x1f, 0x7b, 0xf4, | ||||||
| 	0x8d, 0x57, 0x30, 0x95, 0x46, 0x7b, 0xd2, 0xfe, 0xd5, 0x37, 0x96, 0xf8, 0x20, 0xf0, 0xd3, 0xae, | 	0xa3, 0xf1, 0x1a, 0x66, 0x42, 0x2b, 0x47, 0xca, 0xbd, 0xbb, 0xce, 0x10, 0x9f, 0x78, 0x7e, 0x36, | ||||||
| 	0xf7, 0xdc, 0x58, 0x42, 0x84, 0x61, 0x61, 0x56, 0x0d, 0x1f, 0xce, 0x58, 0x3a, 0xcd, 0x42, 0x9d, | 	0xf4, 0x5e, 0x3b, 0x43, 0x88, 0x10, 0x54, 0x7a, 0xd9, 0xf1, 0x60, 0xce, 0xd2, 0x59, 0xe1, 0xeb, | ||||||
| 	0x5c, 0xc2, 0x24, 0x23, 0x67, 0x8d, 0x76, 0x7b, 0xce, 0x7e, 0xf0, 0x17, 0x88, 0x96, 0xe4, 0x5c, | 	0xe4, 0x0a, 0xa6, 0x05, 0x59, 0xa3, 0x95, 0xdd, 0x71, 0xf6, 0x8b, 0xbf, 0x41, 0xf4, 0x44, 0xd6, | ||||||
| 	0x5e, 0x12, 0x9e, 0xc3, 0xc8, 0x1b, 0xab, 0x64, 0xb7, 0x55, 0x2b, 0xfe, 0xcc, 0xed, 0x1f, 0x9f, | 	0x96, 0x35, 0xe1, 0x05, 0x9c, 0x38, 0x6d, 0xa4, 0x18, 0xb6, 0xea, 0xc5, 0xbf, 0xb9, 0xe3, 0xc3, | ||||||
| 	0x3b, 0xd8, 0xfb, 0xde, 0x7e, 0x30, 0x18, 0x2d, 0xbf, 0x02, 0xc0, 0x7b, 0x18, 0x2e, 0xf2, 0xaa, | 	0x73, 0x27, 0x3b, 0xdf, 0xbb, 0x2f, 0x06, 0x61, 0xee, 0xbf, 0x8e, 0x0f, 0x10, 0xe4, 0x65, 0xd3, | ||||||
| 	0x42, 0x2e, 0x7e, 0x65, 0x22, 0xba, 0x40, 0xe2, 0x8b, 0x03, 0xa4, 0x5d, 0x39, 0xe9, 0xe1, 0x02, | 	0x20, 0xcf, 0xfe, 0x84, 0x92, 0x0d, 0x89, 0xc4, 0x97, 0x7b, 0x48, 0xbf, 0x73, 0x32, 0xc2, 0x1c, | ||||||
| 	0xc6, 0x4f, 0xbe, 0xa6, 0x7c, 0xfd, 0x4f, 0x83, 0x94, 0xdd, 0x30, 0x7c, 0x80, 0xe8, 0x71, 0x53, | 	0xc2, 0x17, 0xd7, 0x52, 0xb9, 0x3a, 0xd2, 0x20, 0x65, 0xb7, 0x0c, 0x1f, 0x21, 0x7a, 0x5e, 0x57, | ||||||
| 	0x54, 0xca, 0xbd, 0x1d, 0x70, 0xe9, 0xfe, 0x1f, 0x1f, 0x25, 0x49, 0xaf, 0x18, 0x87, 0xb3, 0xde, | 	0x8d, 0xb4, 0x1f, 0x7b, 0x5c, 0x86, 0x00, 0xe2, 0x83, 0x24, 0x19, 0x55, 0xa1, 0xbf, 0xeb, 0xfd, | ||||||
| 	0x7d, 0x06, 0x00, 0x00, 0xff, 0xff, 0xd3, 0x63, 0x94, 0x1a, 0x02, 0x02, 0x00, 0x00, | 	0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0a, 0x76, 0x1f, 0x51, 0x03, 0x02, 0x00, 0x00, | ||||||
| } | } | ||||||
|  |  | ||||||
| // Reference imports to suppress errors if they are not otherwise used. | // Reference imports to suppress errors if they are not otherwise used. | ||||||
| @@ -218,59 +218,59 @@ var _ grpc.ClientConn | |||||||
| // is compatible with the grpc package it is being compiled against. | // is compatible with the grpc package it is being compiled against. | ||||||
| const _ = grpc.SupportPackageIsVersion4 | const _ = grpc.SupportPackageIsVersion4 | ||||||
|  |  | ||||||
| // MicroClient is the client API for Micro service. | // ClientClient is the client API for Client service. | ||||||
| // | // | ||||||
| // 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 MicroClient interface { | type ClientClient interface { | ||||||
| 	// Call allows a single request to be made | 	// Call allows a single request to be made | ||||||
| 	Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) | 	Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) | ||||||
| 	// Stream is a bidirectional stream | 	// Stream is a bidirectional stream | ||||||
| 	Stream(ctx context.Context, opts ...grpc.CallOption) (Micro_StreamClient, error) | 	Stream(ctx context.Context, opts ...grpc.CallOption) (Client_StreamClient, error) | ||||||
| 	// Publish publishes a message and returns an empty Message | 	// Publish publishes a message and returns an empty Message | ||||||
| 	Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) | 	Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) | ||||||
| } | } | ||||||
|  |  | ||||||
| type microClient struct { | type clientClient struct { | ||||||
| 	cc *grpc.ClientConn | 	cc *grpc.ClientConn | ||||||
| } | } | ||||||
|  |  | ||||||
| func NewMicroClient(cc *grpc.ClientConn) MicroClient { | func NewClientClient(cc *grpc.ClientConn) ClientClient { | ||||||
| 	return µClient{cc} | 	return &clientClient{cc} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *microClient) Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { | func (c *clientClient) Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { | ||||||
| 	out := new(Response) | 	out := new(Response) | ||||||
| 	err := c.cc.Invoke(ctx, "/go.micro.client.Micro/Call", in, out, opts...) | 	err := c.cc.Invoke(ctx, "/go.micro.client.Client/Call", in, out, opts...) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	return out, nil | 	return out, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *microClient) Stream(ctx context.Context, opts ...grpc.CallOption) (Micro_StreamClient, error) { | func (c *clientClient) Stream(ctx context.Context, opts ...grpc.CallOption) (Client_StreamClient, error) { | ||||||
| 	stream, err := c.cc.NewStream(ctx, &_Micro_serviceDesc.Streams[0], "/go.micro.client.Micro/Stream", opts...) | 	stream, err := c.cc.NewStream(ctx, &_Client_serviceDesc.Streams[0], "/go.micro.client.Client/Stream", opts...) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	x := µStreamClient{stream} | 	x := &clientStreamClient{stream} | ||||||
| 	return x, nil | 	return x, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| type Micro_StreamClient interface { | type Client_StreamClient interface { | ||||||
| 	Send(*Request) error | 	Send(*Request) error | ||||||
| 	Recv() (*Response, error) | 	Recv() (*Response, error) | ||||||
| 	grpc.ClientStream | 	grpc.ClientStream | ||||||
| } | } | ||||||
|  |  | ||||||
| type microStreamClient struct { | type clientStreamClient struct { | ||||||
| 	grpc.ClientStream | 	grpc.ClientStream | ||||||
| } | } | ||||||
|  |  | ||||||
| func (x *microStreamClient) Send(m *Request) error { | func (x *clientStreamClient) Send(m *Request) error { | ||||||
| 	return x.ClientStream.SendMsg(m) | 	return x.ClientStream.SendMsg(m) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (x *microStreamClient) Recv() (*Response, error) { | func (x *clientStreamClient) Recv() (*Response, error) { | ||||||
| 	m := new(Response) | 	m := new(Response) | ||||||
| 	if err := x.ClientStream.RecvMsg(m); err != nil { | 	if err := x.ClientStream.RecvMsg(m); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @@ -278,66 +278,66 @@ func (x *microStreamClient) Recv() (*Response, error) { | |||||||
| 	return m, nil | 	return m, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *microClient) Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) { | func (c *clientClient) Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) { | ||||||
| 	out := new(Message) | 	out := new(Message) | ||||||
| 	err := c.cc.Invoke(ctx, "/go.micro.client.Micro/Publish", in, out, opts...) | 	err := c.cc.Invoke(ctx, "/go.micro.client.Client/Publish", in, out, opts...) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	return out, nil | 	return out, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // MicroServer is the server API for Micro service. | // ClientServer is the server API for Client service. | ||||||
| type MicroServer interface { | type ClientServer interface { | ||||||
| 	// Call allows a single request to be made | 	// Call allows a single request to be made | ||||||
| 	Call(context.Context, *Request) (*Response, error) | 	Call(context.Context, *Request) (*Response, error) | ||||||
| 	// Stream is a bidirectional stream | 	// Stream is a bidirectional stream | ||||||
| 	Stream(Micro_StreamServer) error | 	Stream(Client_StreamServer) error | ||||||
| 	// Publish publishes a message and returns an empty Message | 	// Publish publishes a message and returns an empty Message | ||||||
| 	Publish(context.Context, *Message) (*Message, error) | 	Publish(context.Context, *Message) (*Message, error) | ||||||
| } | } | ||||||
|  |  | ||||||
| func RegisterMicroServer(s *grpc.Server, srv MicroServer) { | func RegisterClientServer(s *grpc.Server, srv ClientServer) { | ||||||
| 	s.RegisterService(&_Micro_serviceDesc, srv) | 	s.RegisterService(&_Client_serviceDesc, srv) | ||||||
| } | } | ||||||
|  |  | ||||||
| func _Micro_Call_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { | func _Client_Call_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { | ||||||
| 	in := new(Request) | 	in := new(Request) | ||||||
| 	if err := dec(in); err != nil { | 	if err := dec(in); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	if interceptor == nil { | 	if interceptor == nil { | ||||||
| 		return srv.(MicroServer).Call(ctx, in) | 		return srv.(ClientServer).Call(ctx, in) | ||||||
| 	} | 	} | ||||||
| 	info := &grpc.UnaryServerInfo{ | 	info := &grpc.UnaryServerInfo{ | ||||||
| 		Server:     srv, | 		Server:     srv, | ||||||
| 		FullMethod: "/go.micro.client.Micro/Call", | 		FullMethod: "/go.micro.client.Client/Call", | ||||||
| 	} | 	} | ||||||
| 	handler := func(ctx context.Context, req interface{}) (interface{}, error) { | 	handler := func(ctx context.Context, req interface{}) (interface{}, error) { | ||||||
| 		return srv.(MicroServer).Call(ctx, req.(*Request)) | 		return srv.(ClientServer).Call(ctx, req.(*Request)) | ||||||
| 	} | 	} | ||||||
| 	return interceptor(ctx, in, info, handler) | 	return interceptor(ctx, in, info, handler) | ||||||
| } | } | ||||||
|  |  | ||||||
| func _Micro_Stream_Handler(srv interface{}, stream grpc.ServerStream) error { | func _Client_Stream_Handler(srv interface{}, stream grpc.ServerStream) error { | ||||||
| 	return srv.(MicroServer).Stream(µStreamServer{stream}) | 	return srv.(ClientServer).Stream(&clientStreamServer{stream}) | ||||||
| } | } | ||||||
|  |  | ||||||
| type Micro_StreamServer interface { | type Client_StreamServer interface { | ||||||
| 	Send(*Response) error | 	Send(*Response) error | ||||||
| 	Recv() (*Request, error) | 	Recv() (*Request, error) | ||||||
| 	grpc.ServerStream | 	grpc.ServerStream | ||||||
| } | } | ||||||
|  |  | ||||||
| type microStreamServer struct { | type clientStreamServer struct { | ||||||
| 	grpc.ServerStream | 	grpc.ServerStream | ||||||
| } | } | ||||||
|  |  | ||||||
| func (x *microStreamServer) Send(m *Response) error { | func (x *clientStreamServer) Send(m *Response) error { | ||||||
| 	return x.ServerStream.SendMsg(m) | 	return x.ServerStream.SendMsg(m) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (x *microStreamServer) Recv() (*Request, error) { | func (x *clientStreamServer) Recv() (*Request, error) { | ||||||
| 	m := new(Request) | 	m := new(Request) | ||||||
| 	if err := x.ServerStream.RecvMsg(m); err != nil { | 	if err := x.ServerStream.RecvMsg(m); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @@ -345,41 +345,41 @@ func (x *microStreamServer) Recv() (*Request, error) { | |||||||
| 	return m, nil | 	return m, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func _Micro_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { | func _Client_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { | ||||||
| 	in := new(Message) | 	in := new(Message) | ||||||
| 	if err := dec(in); err != nil { | 	if err := dec(in); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	if interceptor == nil { | 	if interceptor == nil { | ||||||
| 		return srv.(MicroServer).Publish(ctx, in) | 		return srv.(ClientServer).Publish(ctx, in) | ||||||
| 	} | 	} | ||||||
| 	info := &grpc.UnaryServerInfo{ | 	info := &grpc.UnaryServerInfo{ | ||||||
| 		Server:     srv, | 		Server:     srv, | ||||||
| 		FullMethod: "/go.micro.client.Micro/Publish", | 		FullMethod: "/go.micro.client.Client/Publish", | ||||||
| 	} | 	} | ||||||
| 	handler := func(ctx context.Context, req interface{}) (interface{}, error) { | 	handler := func(ctx context.Context, req interface{}) (interface{}, error) { | ||||||
| 		return srv.(MicroServer).Publish(ctx, req.(*Message)) | 		return srv.(ClientServer).Publish(ctx, req.(*Message)) | ||||||
| 	} | 	} | ||||||
| 	return interceptor(ctx, in, info, handler) | 	return interceptor(ctx, in, info, handler) | ||||||
| } | } | ||||||
|  |  | ||||||
| var _Micro_serviceDesc = grpc.ServiceDesc{ | var _Client_serviceDesc = grpc.ServiceDesc{ | ||||||
| 	ServiceName: "go.micro.client.Micro", | 	ServiceName: "go.micro.client.Client", | ||||||
| 	HandlerType: (*MicroServer)(nil), | 	HandlerType: (*ClientServer)(nil), | ||||||
| 	Methods: []grpc.MethodDesc{ | 	Methods: []grpc.MethodDesc{ | ||||||
| 		{ | 		{ | ||||||
| 			MethodName: "Call", | 			MethodName: "Call", | ||||||
| 			Handler:    _Micro_Call_Handler, | 			Handler:    _Client_Call_Handler, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			MethodName: "Publish", | 			MethodName: "Publish", | ||||||
| 			Handler:    _Micro_Publish_Handler, | 			Handler:    _Client_Publish_Handler, | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| 	Streams: []grpc.StreamDesc{ | 	Streams: []grpc.StreamDesc{ | ||||||
| 		{ | 		{ | ||||||
| 			StreamName:    "Stream", | 			StreamName:    "Stream", | ||||||
| 			Handler:       _Micro_Stream_Handler, | 			Handler:       _Client_Stream_Handler, | ||||||
| 			ServerStreams: true, | 			ServerStreams: true, | ||||||
| 			ClientStreams: true, | 			ClientStreams: true, | ||||||
| 		}, | 		}, | ||||||
|   | |||||||
| @@ -2,8 +2,8 @@ syntax = "proto3"; | |||||||
|  |  | ||||||
| package go.micro.client; | package go.micro.client; | ||||||
|  |  | ||||||
| // Micro is the micro client interface | // Client is the micro client interface | ||||||
| service Micro { | service Client { | ||||||
| 	// Call allows a single request to be made | 	// Call allows a single request to be made | ||||||
| 	rpc Call(Request) returns (Response) {}; | 	rpc Call(Request) returns (Response) {}; | ||||||
| 	// Stream is a bidirectional stream | 	// Stream is a bidirectional stream | ||||||
|   | |||||||
| @@ -89,9 +89,22 @@ func (c *Codec) Write(m *codec.Message, b interface{}) error { | |||||||
| 		m.Header[":authority"] = m.Target | 		m.Header[":authority"] = m.Target | ||||||
| 		m.Header["content-type"] = c.ContentType | 		m.Header["content-type"] = c.ContentType | ||||||
| 	case codec.Response: | 	case codec.Response: | ||||||
| 		m.Header["Trailer"] = "grpc-status, grpc-message" | 		m.Header["Trailer"] = "grpc-status" //, grpc-message" | ||||||
|  | 		m.Header["content-type"] = c.ContentType | ||||||
|  | 		m.Header[":status"] = "200" | ||||||
| 		m.Header["grpc-status"] = "0" | 		m.Header["grpc-status"] = "0" | ||||||
| 		m.Header["grpc-message"] = "" | 		//		m.Header["grpc-message"] = "" | ||||||
|  | 	case codec.Error: | ||||||
|  | 		m.Header["Trailer"] = "grpc-status, grpc-message" | ||||||
|  | 		// micro end of stream | ||||||
|  | 		if m.Error == "EOS" { | ||||||
|  | 			m.Header["grpc-status"] = "0" | ||||||
|  | 		} else { | ||||||
|  | 			m.Header["grpc-message"] = m.Error | ||||||
|  | 			m.Header["grpc-status"] = "13" | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// marshal content | 	// marshal content | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| package config | package config | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| @@ -9,7 +8,6 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/google/uuid" |  | ||||||
| 	"github.com/micro/go-micro/config/source/env" | 	"github.com/micro/go-micro/config/source/env" | ||||||
| 	"github.com/micro/go-micro/config/source/file" | 	"github.com/micro/go-micro/config/source/file" | ||||||
| ) | ) | ||||||
| @@ -122,57 +120,3 @@ func TestConfigMerge(t *testing.T) { | |||||||
| 			actualHost) | 			actualHost) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestFileChange(t *testing.T) { |  | ||||||
| 	// create a temp file |  | ||||||
| 	fileName := uuid.New().String() + "testWatcher.json" |  | ||||||
| 	f, err := os.OpenFile("."+sep+fileName, os.O_WRONLY|os.O_CREATE, 0666) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Error(err) |  | ||||||
| 	} |  | ||||||
| 	defer f.Close() |  | ||||||
| 	defer os.Remove("." + sep + fileName) |  | ||||||
|  |  | ||||||
| 	// load the file |  | ||||||
| 	if err := Load(file.NewSource( |  | ||||||
| 		file.WithPath("." + sep + fileName), |  | ||||||
| 	)); err != nil { |  | ||||||
| 		t.Error(err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// watch changes |  | ||||||
| 	watcher, err := Watch() |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Error(err) |  | ||||||
| 	} |  | ||||||
| 	changeTimes := 0 |  | ||||||
| 	go func() { |  | ||||||
| 		for { |  | ||||||
| 			v, err := watcher.Next() |  | ||||||
| 			if err != nil { |  | ||||||
| 				t.Error(err) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			changeTimes++ |  | ||||||
| 			t.Logf("file change,%s", string(v.Bytes())) |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	content := map[int]string{} |  | ||||||
| 	// change the file |  | ||||||
| 	for i := 0; i < 5; i++ { |  | ||||||
| 		content[i] = time.Now().String() |  | ||||||
| 		bytes, _ := json.Marshal(content) |  | ||||||
| 		f.Truncate(0) |  | ||||||
| 		f.Seek(0, 0) |  | ||||||
| 		if _, err := f.Write(bytes); err != nil { |  | ||||||
| 			t.Error(err) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		time.Sleep(time.Second) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if changeTimes != 4 { |  | ||||||
| 		t.Error(fmt.Errorf("watcher error: change times %d is not enough", changeTimes)) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| package consul | package consul | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" |  | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/hashicorp/consul/api" | 	"github.com/hashicorp/consul/api" | ||||||
| @@ -80,7 +79,7 @@ func (w *watcher) Next() (*source.ChangeSet, error) { | |||||||
| 	case cs := <-w.ch: | 	case cs := <-w.ch: | ||||||
| 		return cs, nil | 		return cs, nil | ||||||
| 	case <-w.exit: | 	case <-w.exit: | ||||||
| 		return nil, errors.New("watcher stopped") | 		return nil, source.ErrWatcherStopped | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								config/source/env/env_test.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								config/source/env/env_test.go
									
									
									
									
										vendored
									
									
								
							| @@ -86,8 +86,8 @@ func TestEnvvar_Prefixes(t *testing.T) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func TestEnvvar_WatchNextNoOpsUntilStop(t *testing.T) { | func TestEnvvar_WatchNextNoOpsUntilStop(t *testing.T) { | ||||||
| 	source := NewSource(WithStrippedPrefix("GOMICRO_")) | 	src := NewSource(WithStrippedPrefix("GOMICRO_")) | ||||||
| 	w, err := source.Watch() | 	w, err := src.Watch() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Error(err) | 		t.Error(err) | ||||||
| 	} | 	} | ||||||
| @@ -97,7 +97,7 @@ func TestEnvvar_WatchNextNoOpsUntilStop(t *testing.T) { | |||||||
| 		w.Stop() | 		w.Stop() | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
| 	if _, err := w.Next(); err.Error() != "watcher stopped" { | 	if _, err := w.Next(); err != source.ErrWatcherStopped { | ||||||
| 		t.Errorf("expected watcher stopped error, got %v", err) | 		t.Errorf("expected watcher stopped error, got %v", err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								config/source/env/watcher.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								config/source/env/watcher.go
									
									
									
									
										vendored
									
									
								
							| @@ -1,8 +1,6 @@ | |||||||
| package env | package env | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" |  | ||||||
|  |  | ||||||
| 	"github.com/micro/go-micro/config/source" | 	"github.com/micro/go-micro/config/source" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -13,7 +11,7 @@ type watcher struct { | |||||||
| func (w *watcher) Next() (*source.ChangeSet, error) { | func (w *watcher) Next() (*source.ChangeSet, error) { | ||||||
| 	<-w.exit | 	<-w.exit | ||||||
|  |  | ||||||
| 	return nil, errors.New("watcher stopped") | 	return nil, source.ErrWatcherStopped | ||||||
| } | } | ||||||
|  |  | ||||||
| func (w *watcher) Stop() error { | func (w *watcher) Stop() error { | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ | |||||||
| package file | package file | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" |  | ||||||
| 	"os" | 	"os" | ||||||
|  |  | ||||||
| 	"github.com/fsnotify/fsnotify" | 	"github.com/fsnotify/fsnotify" | ||||||
| @@ -36,7 +35,7 @@ func (w *watcher) Next() (*source.ChangeSet, error) { | |||||||
| 	// is it closed? | 	// is it closed? | ||||||
| 	select { | 	select { | ||||||
| 	case <-w.exit: | 	case <-w.exit: | ||||||
| 		return nil, errors.New("watcher stopped") | 		return nil, source.ErrWatcherStopped | ||||||
| 	default: | 	default: | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -59,7 +58,7 @@ func (w *watcher) Next() (*source.ChangeSet, error) { | |||||||
| 	case err := <-w.fw.Errors: | 	case err := <-w.fw.Errors: | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	case <-w.exit: | 	case <-w.exit: | ||||||
| 		return nil, errors.New("watcher stopped") | 		return nil, source.ErrWatcherStopped | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ | |||||||
| package file | package file | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" |  | ||||||
| 	"os" | 	"os" | ||||||
|  |  | ||||||
| 	"github.com/fsnotify/fsnotify" | 	"github.com/fsnotify/fsnotify" | ||||||
| @@ -36,7 +35,7 @@ func (w *watcher) Next() (*source.ChangeSet, error) { | |||||||
| 	// is it closed? | 	// is it closed? | ||||||
| 	select { | 	select { | ||||||
| 	case <-w.exit: | 	case <-w.exit: | ||||||
| 		return nil, errors.New("watcher stopped") | 		return nil, source.ErrWatcherStopped | ||||||
| 	default: | 	default: | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -63,7 +62,7 @@ func (w *watcher) Next() (*source.ChangeSet, error) { | |||||||
| 	case err := <-w.fw.Errors: | 	case err := <-w.fw.Errors: | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	case <-w.exit: | 	case <-w.exit: | ||||||
| 		return nil, errors.New("watcher stopped") | 		return nil, source.ErrWatcherStopped | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,9 +2,15 @@ | |||||||
| package source | package source | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"errors" | ||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// ErrWatcherStopped is returned when source watcher has been stopped | ||||||
|  | 	ErrWatcherStopped = errors.New("watcher stopped") | ||||||
|  | ) | ||||||
|  |  | ||||||
| // Source is the source from which config is loaded | // Source is the source from which config is loaded | ||||||
| type Source interface { | type Source interface { | ||||||
| 	Read() (*ChangeSet, error) | 	Read() (*ChangeSet, error) | ||||||
|   | |||||||
							
								
								
									
										33
									
								
								network/resolver/static/static.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								network/resolver/static/static.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | // Package static is a static resolver | ||||||
|  | package registry | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/micro/go-micro/network/resolver" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Resolver returns a static list of nodes. In the event the node list | ||||||
|  | // is not present it will return the name of the network passed in. | ||||||
|  | type Resolver struct { | ||||||
|  | 	// A static list of nodes | ||||||
|  | 	Nodes []string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Resolve returns the list of nodes | ||||||
|  | func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) { | ||||||
|  | 	// if there are no nodes just return the name | ||||||
|  | 	if len(r.Nodes) == 0 { | ||||||
|  | 		return []*resolver.Record{ | ||||||
|  | 			{Address: name}, | ||||||
|  | 		}, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var records []*resolver.Record | ||||||
|  |  | ||||||
|  | 	for _, node := range r.Nodes { | ||||||
|  | 		records = append(records, &resolver.Record{ | ||||||
|  | 			Address: node, | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return records, nil | ||||||
|  | } | ||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"github.com/micro/go-micro/client/grpc" | 	"github.com/micro/go-micro/client/grpc" | ||||||
| 	"github.com/micro/go-micro/codec" | 	"github.com/micro/go-micro/codec" | ||||||
| 	"github.com/micro/go-micro/config/options" | 	"github.com/micro/go-micro/config/options" | ||||||
|  | 	"github.com/micro/go-micro/errors" | ||||||
| 	"github.com/micro/go-micro/proxy" | 	"github.com/micro/go-micro/proxy" | ||||||
| 	"github.com/micro/go-micro/server" | 	"github.com/micro/go-micro/server" | ||||||
| ) | ) | ||||||
| @@ -61,6 +62,10 @@ func readLoop(r server.Request, s client.Stream) error { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (p *Proxy) SendRequest(ctx context.Context, req client.Request, rsp client.Response) error { | ||||||
|  | 	return errors.InternalServerError("go.micro.proxy.grpc", "SendRequest is unsupported") | ||||||
|  | } | ||||||
|  |  | ||||||
| // ServeRequest honours the server.Proxy interface | // ServeRequest honours the server.Proxy interface | ||||||
| func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { | func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { | ||||||
| 	// set default client | 	// set default client | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"path" | 	"path" | ||||||
|  |  | ||||||
|  | 	"github.com/micro/go-micro/client" | ||||||
| 	"github.com/micro/go-micro/config/options" | 	"github.com/micro/go-micro/config/options" | ||||||
| 	"github.com/micro/go-micro/errors" | 	"github.com/micro/go-micro/errors" | ||||||
| 	"github.com/micro/go-micro/proxy" | 	"github.com/micro/go-micro/proxy" | ||||||
| @@ -44,6 +45,10 @@ func getEndpoint(hdr map[string]string) string { | |||||||
| 	return "" | 	return "" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (p *Proxy) SendRequest(ctx context.Context, req client.Request, rsp client.Response) error { | ||||||
|  | 	return errors.InternalServerError("go.micro.proxy.http", "SendRequest is unsupported") | ||||||
|  | } | ||||||
|  |  | ||||||
| // ServeRequest honours the server.Router interface | // ServeRequest honours the server.Router interface | ||||||
| func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { | func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { | ||||||
| 	if p.Endpoint == "" { | 	if p.Endpoint == "" { | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
|  | 	"sort" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
|  |  | ||||||
| @@ -12,6 +13,7 @@ import ( | |||||||
| 	"github.com/micro/go-micro/codec" | 	"github.com/micro/go-micro/codec" | ||||||
| 	"github.com/micro/go-micro/codec/bytes" | 	"github.com/micro/go-micro/codec/bytes" | ||||||
| 	"github.com/micro/go-micro/config/options" | 	"github.com/micro/go-micro/config/options" | ||||||
|  | 	"github.com/micro/go-micro/errors" | ||||||
| 	"github.com/micro/go-micro/proxy" | 	"github.com/micro/go-micro/proxy" | ||||||
| 	"github.com/micro/go-micro/router" | 	"github.com/micro/go-micro/router" | ||||||
| 	"github.com/micro/go-micro/server" | 	"github.com/micro/go-micro/server" | ||||||
| @@ -26,9 +28,12 @@ type Proxy struct { | |||||||
| 	// Endpoint specifies the fixed service endpoint to call. | 	// Endpoint specifies the fixed service endpoint to call. | ||||||
| 	Endpoint string | 	Endpoint string | ||||||
|  |  | ||||||
| 	// The client to use for outbound requests | 	// The client to use for outbound requests in the local network | ||||||
| 	Client client.Client | 	Client client.Client | ||||||
|  |  | ||||||
|  | 	// Links are used for outbound requests not in the local network | ||||||
|  | 	Links map[string]client.Client | ||||||
|  |  | ||||||
| 	// The router for routes | 	// The router for routes | ||||||
| 	Router router.Router | 	Router router.Router | ||||||
|  |  | ||||||
| @@ -76,7 +81,7 @@ func readLoop(r server.Request, s client.Stream) error { | |||||||
| } | } | ||||||
|  |  | ||||||
| // toNodes returns a list of node addresses from given routes | // toNodes returns a list of node addresses from given routes | ||||||
| func toNodes(routes map[uint64]router.Route) []string { | func toNodes(routes []router.Route) []string { | ||||||
| 	var nodes []string | 	var nodes []string | ||||||
| 	for _, node := range routes { | 	for _, node := range routes { | ||||||
| 		address := node.Address | 		address := node.Address | ||||||
| @@ -88,15 +93,37 @@ func toNodes(routes map[uint64]router.Route) []string { | |||||||
| 	return nodes | 	return nodes | ||||||
| } | } | ||||||
|  |  | ||||||
| func (p *Proxy) getRoute(service string) ([]string, error) { | func (p *Proxy) getLink(r router.Route) (client.Client, error) { | ||||||
|  | 	if r.Link == "local" || len(p.Links) == 0 { | ||||||
|  | 		return p.Client, nil | ||||||
|  | 	} | ||||||
|  | 	l, ok := p.Links[r.Link] | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil, errors.InternalServerError("go.micro.proxy", "link not found") | ||||||
|  | 	} | ||||||
|  | 	return l, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *Proxy) getRoute(service string) ([]router.Route, error) { | ||||||
|  | 	toSlice := func(r map[uint64]router.Route) []router.Route { | ||||||
|  | 		var routes []router.Route | ||||||
|  | 		for _, v := range r { | ||||||
|  | 			routes = append(routes, v) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// sort the routes in order of metric | ||||||
|  | 		sort.Slice(routes, func(i, j int) bool { return routes[i].Metric < routes[j].Metric }) | ||||||
|  |  | ||||||
|  | 		return routes | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// lookup the route cache first | 	// lookup the route cache first | ||||||
| 	p.Lock() | 	p.Lock() | ||||||
| 	routes, ok := p.Routes[service] | 	routes, ok := p.Routes[service] | ||||||
| 	if ok { | 	if ok { | ||||||
| 		p.Unlock() | 		p.Unlock() | ||||||
| 		return toNodes(routes), nil | 		return toSlice(routes), nil | ||||||
| 	} | 	} | ||||||
| 	p.Routes[service] = make(map[uint64]router.Route) |  | ||||||
| 	p.Unlock() | 	p.Unlock() | ||||||
|  |  | ||||||
| 	// lookup the routes in the router | 	// lookup the routes in the router | ||||||
| @@ -113,12 +140,16 @@ func (p *Proxy) getRoute(service string) ([]string, error) { | |||||||
| 	// update the proxy cache | 	// update the proxy cache | ||||||
| 	p.Lock() | 	p.Lock() | ||||||
| 	for _, route := range results { | 	for _, route := range results { | ||||||
|  | 		// create if does not exist | ||||||
|  | 		if _, ok := p.Routes[service]; !ok { | ||||||
|  | 			p.Routes[service] = make(map[uint64]router.Route) | ||||||
|  | 		} | ||||||
| 		p.Routes[service][route.Hash()] = route | 		p.Routes[service][route.Hash()] = route | ||||||
| 	} | 	} | ||||||
| 	routes = p.Routes[service] | 	routes = p.Routes[service] | ||||||
| 	p.Unlock() | 	p.Unlock() | ||||||
|  |  | ||||||
| 	return toNodes(routes), nil | 	return toSlice(routes), nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // manageRouteCache applies action on a given route to Proxy route cache | // manageRouteCache applies action on a given route to Proxy route cache | ||||||
| @@ -171,12 +202,27 @@ func (p *Proxy) watchRoutes() { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (p *Proxy) SendRequest(ctx context.Context, req client.Request, rsp client.Response) error { | ||||||
|  | 	return errors.InternalServerError("go.micro.proxy", "SendRequest is unsupported") | ||||||
|  | } | ||||||
|  |  | ||||||
| // ServeRequest honours the server.Router interface | // ServeRequest honours the server.Router interface | ||||||
| func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { | func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { | ||||||
| 	// service name | 	// determine if its local routing | ||||||
| 	service := req.Service() | 	var local bool | ||||||
| 	endpoint := req.Endpoint() | 	// address to call | ||||||
| 	var addresses []string | 	var addresses []string | ||||||
|  | 	// routes | ||||||
|  | 	var routes []router.Route | ||||||
|  | 	// service name to call | ||||||
|  | 	service := req.Service() | ||||||
|  | 	// endpoint to call | ||||||
|  | 	endpoint := req.Endpoint() | ||||||
|  |  | ||||||
|  | 	// are we network routing or local routing | ||||||
|  | 	if len(p.Links) == 0 { | ||||||
|  | 		local = true | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// call a specific backend endpoint either by name or address | 	// call a specific backend endpoint either by name or address | ||||||
| 	if len(p.Endpoint) > 0 { | 	if len(p.Endpoint) > 0 { | ||||||
| @@ -190,7 +236,7 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server | |||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
| 			// set the address | 			// set the address | ||||||
| 			addresses = addr | 			routes = addr | ||||||
| 			// set the name | 			// set the name | ||||||
| 			service = p.Endpoint | 			service = p.Endpoint | ||||||
| 		} | 		} | ||||||
| @@ -201,16 +247,66 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server | |||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 		addresses = addr | 		routes = addr | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var opts []client.CallOption | 	// if the address is already set just serve it | ||||||
|  | 	// TODO: figure it out if we should know to pick a link | ||||||
| 	// set address if available |  | ||||||
| 	if len(addresses) > 0 { | 	if len(addresses) > 0 { | ||||||
| 		opts = append(opts, client.WithAddress(addresses...)) | 		// serve the normal way | ||||||
|  | 		return p.serveRequest(ctx, p.Client, service, endpoint, req, rsp, client.WithAddress(addresses...)) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// there's no links e.g we're local routing then just serve it with addresses | ||||||
|  | 	if local { | ||||||
|  | 		var opts []client.CallOption | ||||||
|  |  | ||||||
|  | 		// set address if available via routes or specific endpoint | ||||||
|  | 		if len(routes) > 0 { | ||||||
|  | 			addresses := toNodes(routes) | ||||||
|  | 			opts = append(opts, client.WithAddress(addresses...)) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// serve the normal way | ||||||
|  | 		return p.serveRequest(ctx, p.Client, service, endpoint, req, rsp, opts...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var gerr error | ||||||
|  |  | ||||||
|  | 	// we're routing globally with multiple links | ||||||
|  | 	// so we need to pick a link per route | ||||||
|  | 	for _, route := range routes { | ||||||
|  | 		// pick the link or error out | ||||||
|  | 		link, err := p.getLink(route) | ||||||
|  | 		if err != nil { | ||||||
|  | 			// ok let's try again | ||||||
|  | 			gerr = err | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// set the address to call | ||||||
|  | 		addresses := toNodes([]router.Route{route}) | ||||||
|  |  | ||||||
|  | 		// do the request with the link | ||||||
|  | 		gerr = p.serveRequest(ctx, link, service, endpoint, req, rsp, client.WithAddress(addresses...)) | ||||||
|  | 		// return on no error since we succeeded | ||||||
|  | 		if gerr == nil { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// return where the context deadline was exceeded | ||||||
|  | 		if gerr == context.Canceled || gerr == context.DeadlineExceeded { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// otherwise attempt to do it all over again | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// if we got here something went really badly wrong | ||||||
|  | 	return gerr | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *Proxy) serveRequest(ctx context.Context, link client.Client, service, endpoint string, req server.Request, rsp server.Response, opts ...client.CallOption) error { | ||||||
| 	// read initial request | 	// read initial request | ||||||
| 	body, err := req.Read() | 	body, err := req.Read() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -218,14 +314,14 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// create new request with raw bytes body | 	// create new request with raw bytes body | ||||||
| 	creq := p.Client.NewRequest(service, endpoint, &bytes.Frame{body}, client.WithContentType(req.ContentType())) | 	creq := link.NewRequest(service, endpoint, &bytes.Frame{body}, client.WithContentType(req.ContentType())) | ||||||
|  |  | ||||||
| 	// not a stream so make a client.Call request | 	// not a stream so make a client.Call request | ||||||
| 	if !req.Stream() { | 	if !req.Stream() { | ||||||
| 		crsp := new(bytes.Frame) | 		crsp := new(bytes.Frame) | ||||||
|  |  | ||||||
| 		// make a call to the backend | 		// make a call to the backend | ||||||
| 		if err := p.Client.Call(ctx, creq, crsp, opts...); err != nil { | 		if err := link.Call(ctx, creq, crsp, opts...); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -238,7 +334,7 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// create new stream | 	// create new stream | ||||||
| 	stream, err := p.Client.Stream(ctx, creq, opts...) | 	stream, err := link.Stream(ctx, creq, opts...) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -300,6 +396,7 @@ func NewSingleHostProxy(endpoint string) *Proxy { | |||||||
| // NewProxy returns a new proxy which will route based on mucp headers | // NewProxy returns a new proxy which will route based on mucp headers | ||||||
| func NewProxy(opts ...options.Option) proxy.Proxy { | func NewProxy(opts ...options.Option) proxy.Proxy { | ||||||
| 	p := new(Proxy) | 	p := new(Proxy) | ||||||
|  | 	p.Links = map[string]client.Client{} | ||||||
| 	p.Options = options.NewOptions(opts...) | 	p.Options = options.NewOptions(opts...) | ||||||
| 	p.Options.Init(options.WithString("mucp")) | 	p.Options.Init(options.WithString("mucp")) | ||||||
|  |  | ||||||
| @@ -320,6 +417,12 @@ func NewProxy(opts ...options.Option) proxy.Proxy { | |||||||
| 		p.Client = client.DefaultClient | 		p.Client = client.DefaultClient | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// get client | ||||||
|  | 	links, ok := p.Options.Values().Get("proxy.links") | ||||||
|  | 	if ok { | ||||||
|  | 		p.Links = links.(map[string]client.Client) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// get router | 	// get router | ||||||
| 	r, ok := p.Options.Values().Get("proxy.router") | 	r, ok := p.Options.Values().Get("proxy.router") | ||||||
| 	if ok { | 	if ok { | ||||||
|   | |||||||
| @@ -13,6 +13,8 @@ import ( | |||||||
| // Proxy can be used as a proxy server for go-micro services | // Proxy can be used as a proxy server for go-micro services | ||||||
| type Proxy interface { | type Proxy interface { | ||||||
| 	options.Options | 	options.Options | ||||||
|  | 	// SendRequest honours the client.Router interface | ||||||
|  | 	SendRequest(context.Context, client.Request, client.Response) error | ||||||
| 	// ServeRequest honours the server.Router interface | 	// ServeRequest honours the server.Router interface | ||||||
| 	ServeRequest(context.Context, server.Request, server.Response) error | 	ServeRequest(context.Context, server.Request, server.Response) error | ||||||
| } | } | ||||||
| @@ -35,3 +37,20 @@ func WithClient(c client.Client) options.Option { | |||||||
| func WithRouter(r router.Router) options.Option { | func WithRouter(r router.Router) options.Option { | ||||||
| 	return options.WithValue("proxy.router", r) | 	return options.WithValue("proxy.router", r) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // WithLink sets a link for outbound requests | ||||||
|  | func WithLink(name string, c client.Client) options.Option { | ||||||
|  | 	return func(o *options.Values) error { | ||||||
|  | 		var links map[string]client.Client | ||||||
|  | 		v, ok := o.Get("proxy.links") | ||||||
|  | 		if ok { | ||||||
|  | 			links = v.(map[string]client.Client) | ||||||
|  | 		} else { | ||||||
|  | 			links = map[string]client.Client{} | ||||||
|  | 		} | ||||||
|  | 		links[name] = c | ||||||
|  | 		// save the links | ||||||
|  | 		o.Set("proxy.links", links) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -181,10 +181,6 @@ func (r *router) manageRegistryRoutes(reg registry.Registry, action string) erro | |||||||
| // watchRegistry watches registry and updates routing table based on the received events. | // watchRegistry watches registry and updates routing table based on the received events. | ||||||
| // It returns error if either the registry watcher fails with error or if the routing table update fails. | // It returns error if either the registry watcher fails with error or if the routing table update fails. | ||||||
| func (r *router) watchRegistry(w registry.Watcher) error { | func (r *router) watchRegistry(w registry.Watcher) error { | ||||||
| 	// wait in the background for the router to stop |  | ||||||
| 	// when the router stops, stop the watcher and exit |  | ||||||
| 	r.wg.Add(1) |  | ||||||
|  |  | ||||||
| 	exit := make(chan bool) | 	exit := make(chan bool) | ||||||
|  |  | ||||||
| 	defer func() { | 	defer func() { | ||||||
| @@ -193,6 +189,9 @@ func (r *router) watchRegistry(w registry.Watcher) error { | |||||||
| 		r.wg.Done() | 		r.wg.Done() | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
|  | 	// wait in the background for the router to stop | ||||||
|  | 	// when the router stops, stop the watcher and exit | ||||||
|  | 	r.wg.Add(1) | ||||||
| 	go func() { | 	go func() { | ||||||
| 		defer w.Stop() | 		defer w.Stop() | ||||||
|  |  | ||||||
| @@ -226,9 +225,6 @@ func (r *router) watchRegistry(w registry.Watcher) error { | |||||||
| // watchTable watches routing table entries and either adds or deletes locally registered service to/from network registry | // watchTable watches routing table entries and either adds or deletes locally registered service to/from network registry | ||||||
| // It returns error if the locally registered services either fails to be added/deleted to/from network registry. | // It returns error if the locally registered services either fails to be added/deleted to/from network registry. | ||||||
| func (r *router) watchTable(w Watcher) error { | func (r *router) watchTable(w Watcher) error { | ||||||
| 	// wait in the background for the router to stop |  | ||||||
| 	// when the router stops, stop the watcher and exit |  | ||||||
| 	r.wg.Add(1) |  | ||||||
| 	exit := make(chan bool) | 	exit := make(chan bool) | ||||||
|  |  | ||||||
| 	defer func() { | 	defer func() { | ||||||
| @@ -237,6 +233,9 @@ func (r *router) watchTable(w Watcher) error { | |||||||
| 		r.wg.Done() | 		r.wg.Done() | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
|  | 	// wait in the background for the router to stop | ||||||
|  | 	// when the router stops, stop the watcher and exit | ||||||
|  | 	r.wg.Add(1) | ||||||
| 	go func() { | 	go func() { | ||||||
| 		defer w.Stop() | 		defer w.Stop() | ||||||
|  |  | ||||||
| @@ -471,12 +470,38 @@ func (r *router) advertiseEvents() error { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // close closes exit channels | ||||||
|  | func (r *router) close() { | ||||||
|  | 	// notify all goroutines to finish | ||||||
|  | 	close(r.exit) | ||||||
|  |  | ||||||
|  | 	// drain the advertise channel only if advertising | ||||||
|  | 	if r.status.Code == Advertising { | ||||||
|  | 		// drain the event channel | ||||||
|  | 		for range r.eventChan { | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// close advert subscribers | ||||||
|  | 		for id, sub := range r.subscribers { | ||||||
|  | 			// close the channel | ||||||
|  | 			close(sub) | ||||||
|  |  | ||||||
|  | 			// delete the subscriber | ||||||
|  | 			delete(r.subscribers, id) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// mark the router as Stopped and set its Error to nil | ||||||
|  | 	r.status = Status{Code: Stopped, Error: nil} | ||||||
|  | } | ||||||
|  |  | ||||||
| // watchErrors watches router errors and takes appropriate actions | // watchErrors watches router errors and takes appropriate actions | ||||||
| func (r *router) watchErrors() { | func (r *router) watchErrors() { | ||||||
| 	var err error | 	var err error | ||||||
|  |  | ||||||
| 	select { | 	select { | ||||||
| 	case <-r.exit: | 	case <-r.exit: | ||||||
|  | 		return | ||||||
| 	case err = <-r.errChan: | 	case err = <-r.errChan: | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -484,22 +509,12 @@ func (r *router) watchErrors() { | |||||||
| 	defer r.Unlock() | 	defer r.Unlock() | ||||||
| 	// if the router is not stopped, stop it | 	// if the router is not stopped, stop it | ||||||
| 	if r.status.Code != Stopped { | 	if r.status.Code != Stopped { | ||||||
| 		// notify all goroutines to finish | 		// close all the channels | ||||||
| 		close(r.exit) | 		r.close() | ||||||
|  | 		// set the status error | ||||||
| 		// drain the advertise channel only if the router is advertising | 		if err != nil { | ||||||
| 		if r.status.Code == Advertising { | 			r.status.Error = err | ||||||
| 			// drain the event channel |  | ||||||
| 			for range r.eventChan { |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// mark the router as Stopped and set its Error to nil |  | ||||||
| 		r.status = Status{Code: Stopped, Error: nil} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err != nil { |  | ||||||
| 		r.status = Status{Code: Error, Error: err} |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -508,6 +523,11 @@ func (r *router) Start() error { | |||||||
| 	r.Lock() | 	r.Lock() | ||||||
| 	defer r.Unlock() | 	defer r.Unlock() | ||||||
|  |  | ||||||
|  | 	// only start if we're stopped | ||||||
|  | 	if r.status.Code != Stopped { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// add all local service routes into the routing table | 	// add all local service routes into the routing table | ||||||
| 	if err := r.manageRegistryRoutes(r.opts.Registry, "create"); err != nil { | 	if err := r.manageRegistryRoutes(r.opts.Registry, "create"); err != nil { | ||||||
| 		e := fmt.Errorf("failed adding registry routes: %s", err) | 		e := fmt.Errorf("failed adding registry routes: %s", err) | ||||||
| @@ -660,10 +680,12 @@ func (r *router) Process(a *Advert) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Lookup routes in the routing table | ||||||
| func (r *router) Lookup(q Query) ([]Route, error) { | func (r *router) Lookup(q Query) ([]Route, error) { | ||||||
| 	return r.table.Query(q) | 	return r.table.Query(q) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Watch routes | ||||||
| func (r *router) Watch(opts ...WatchOption) (Watcher, error) { | func (r *router) Watch(opts ...WatchOption) (Watcher, error) { | ||||||
| 	return r.table.Watch(opts...) | 	return r.table.Watch(opts...) | ||||||
| } | } | ||||||
| @@ -682,31 +704,15 @@ func (r *router) Status() Status { | |||||||
| // Stop stops the router | // Stop stops the router | ||||||
| func (r *router) Stop() error { | func (r *router) Stop() error { | ||||||
| 	r.Lock() | 	r.Lock() | ||||||
| 	// only close the channel if the router is running and/or advertising | 	defer r.Unlock() | ||||||
| 	if r.status.Code == Running || r.status.Code == Advertising { |  | ||||||
| 		// notify all goroutines to finish |  | ||||||
| 		close(r.exit) |  | ||||||
|  |  | ||||||
| 		// drain the advertise channel only if advertising | 	switch r.status.Code { | ||||||
| 		if r.status.Code == Advertising { | 	case Stopped, Error: | ||||||
| 			// drain the event channel | 		return r.status.Error | ||||||
| 			for range r.eventChan { | 	case Running, Advertising: | ||||||
| 			} | 		// close all the channels | ||||||
| 		} | 		r.close() | ||||||
|  |  | ||||||
| 		// close advert subscribers |  | ||||||
| 		for id, sub := range r.subscribers { |  | ||||||
| 			// close the channel |  | ||||||
| 			close(sub) |  | ||||||
|  |  | ||||||
| 			// delete the subscriber |  | ||||||
| 			delete(r.subscribers, id) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// mark the router as Stopped and set its Error to nil |  | ||||||
| 		r.status = Status{Code: Stopped, Error: nil} |  | ||||||
| 	} | 	} | ||||||
| 	r.Unlock() |  | ||||||
|  |  | ||||||
| 	// wait for all goroutines to finish | 	// wait for all goroutines to finish | ||||||
| 	r.wg.Wait() | 	r.wg.Wait() | ||||||
| @@ -716,5 +722,5 @@ func (r *router) Stop() error { | |||||||
|  |  | ||||||
| // String prints debugging information about router | // String prints debugging information about router | ||||||
| func (r *router) String() string { | func (r *router) String() string { | ||||||
| 	return "default" | 	return "memory" | ||||||
| } | } | ||||||
|   | |||||||
| @@ -53,6 +53,8 @@ type grpcServer struct { | |||||||
| 	opts        server.Options | 	opts        server.Options | ||||||
| 	handlers    map[string]server.Handler | 	handlers    map[string]server.Handler | ||||||
| 	subscribers map[*subscriber][]broker.Subscriber | 	subscribers map[*subscriber][]broker.Subscriber | ||||||
|  | 	// marks the serve as started | ||||||
|  | 	started bool | ||||||
| 	// used for first registration | 	// used for first registration | ||||||
| 	registered bool | 	registered bool | ||||||
| } | } | ||||||
| @@ -454,7 +456,10 @@ func (g *grpcServer) newCodec(contentType string) (codec.NewCodec, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (g *grpcServer) Options() server.Options { | func (g *grpcServer) Options() server.Options { | ||||||
|  | 	g.RLock() | ||||||
| 	opts := g.opts | 	opts := g.opts | ||||||
|  | 	g.RUnlock() | ||||||
|  |  | ||||||
| 	return opts | 	return opts | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -700,7 +705,14 @@ func (g *grpcServer) Deregister() error { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (g *grpcServer) Start() error { | func (g *grpcServer) Start() error { | ||||||
| 	config := g.opts | 	g.RLock() | ||||||
|  | 	if g.started { | ||||||
|  | 		g.RUnlock() | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	g.RUnlock() | ||||||
|  |  | ||||||
|  | 	config := g.Options() | ||||||
|  |  | ||||||
| 	// micro: config.Transport.Listen(config.Address) | 	// micro: config.Transport.Listen(config.Address) | ||||||
| 	ts, err := net.Listen("tcp", config.Address) | 	ts, err := net.Listen("tcp", config.Address) | ||||||
| @@ -781,13 +793,34 @@ func (g *grpcServer) Start() error { | |||||||
| 		config.Broker.Disconnect() | 		config.Broker.Disconnect() | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
|  | 	// mark the server as started | ||||||
|  | 	g.Lock() | ||||||
|  | 	g.started = true | ||||||
|  | 	g.Unlock() | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (g *grpcServer) Stop() error { | func (g *grpcServer) Stop() error { | ||||||
|  | 	g.RLock() | ||||||
|  | 	if !g.started { | ||||||
|  | 		g.RUnlock() | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	g.RUnlock() | ||||||
|  |  | ||||||
| 	ch := make(chan error) | 	ch := make(chan error) | ||||||
| 	g.exit <- ch | 	g.exit <- ch | ||||||
| 	return <-ch |  | ||||||
|  | 	var err error | ||||||
|  | 	select { | ||||||
|  | 	case err = <-ch: | ||||||
|  | 		g.Lock() | ||||||
|  | 		g.started = false | ||||||
|  | 		g.Unlock() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
| func (g *grpcServer) String() string { | func (g *grpcServer) String() string { | ||||||
|   | |||||||
| @@ -15,9 +15,10 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| type rpcCodec struct { | type rpcCodec struct { | ||||||
| 	socket transport.Socket | 	socket   transport.Socket | ||||||
| 	codec  codec.Codec | 	codec    codec.Codec | ||||||
| 	first  bool | 	first    bool | ||||||
|  | 	protocol string | ||||||
|  |  | ||||||
| 	req *transport.Message | 	req *transport.Message | ||||||
| 	buf *readWriteCloser | 	buf *readWriteCloser | ||||||
| @@ -157,12 +158,27 @@ func newRpcCodec(req *transport.Message, socket transport.Socket, c codec.NewCod | |||||||
| 		rbuf: bytes.NewBuffer(nil), | 		rbuf: bytes.NewBuffer(nil), | ||||||
| 		wbuf: bytes.NewBuffer(nil), | 		wbuf: bytes.NewBuffer(nil), | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	r := &rpcCodec{ | 	r := &rpcCodec{ | ||||||
| 		buf:    rwc, | 		buf:      rwc, | ||||||
| 		codec:  c(rwc), | 		codec:    c(rwc), | ||||||
| 		req:    req, | 		req:      req, | ||||||
| 		socket: socket, | 		socket:   socket, | ||||||
|  | 		protocol: "mucp", | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// if grpc pre-load the buffer | ||||||
|  | 	// TODO: remove this terrible hack | ||||||
|  | 	switch r.codec.String() { | ||||||
|  | 	case "grpc": | ||||||
|  | 		// set as first | ||||||
|  | 		r.first = true | ||||||
|  | 		// write the body | ||||||
|  | 		rwc.rbuf.Write(req.Body) | ||||||
|  | 		// set the protocol | ||||||
|  | 		r.protocol = "grpc" | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return r | 	return r | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -173,27 +189,33 @@ func (c *rpcCodec) ReadHeader(r *codec.Message, t codec.MessageType) error { | |||||||
| 		Body:   c.req.Body, | 		Body:   c.req.Body, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var tm transport.Message | 	// first message could be pre-loaded | ||||||
|  | 	if !c.first { | ||||||
|  | 		var tm transport.Message | ||||||
|  |  | ||||||
| 	// read off the socket | 		// read off the socket | ||||||
| 	if err := c.socket.Recv(&tm); err != nil { | 		if err := c.socket.Recv(&tm); err != nil { | ||||||
| 		return err | 			return err | ||||||
| 	} | 		} | ||||||
| 	// reset the read buffer | 		// reset the read buffer | ||||||
| 	c.buf.rbuf.Reset() | 		c.buf.rbuf.Reset() | ||||||
|  |  | ||||||
| 	// write the body to the buffer | 		// write the body to the buffer | ||||||
| 	if _, err := c.buf.rbuf.Write(tm.Body); err != nil { | 		if _, err := c.buf.rbuf.Write(tm.Body); err != nil { | ||||||
| 		return err | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// set the message header | ||||||
|  | 		m.Header = tm.Header | ||||||
|  | 		// set the message body | ||||||
|  | 		m.Body = tm.Body | ||||||
|  |  | ||||||
|  | 		// set req | ||||||
|  | 		c.req = &tm | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// set the message header | 	// disable first | ||||||
| 	m.Header = tm.Header | 	c.first = false | ||||||
| 	// set the message body |  | ||||||
| 	m.Body = tm.Body |  | ||||||
|  |  | ||||||
| 	// set req |  | ||||||
| 	c.req = &tm |  | ||||||
|  |  | ||||||
| 	// set some internal things | 	// set some internal things | ||||||
| 	getHeaders(&m) | 	getHeaders(&m) | ||||||
| @@ -293,5 +315,5 @@ func (c *rpcCodec) Close() error { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (c *rpcCodec) String() string { | func (c *rpcCodec) String() string { | ||||||
| 	return "rpc" | 	return c.protocol | ||||||
| } | } | ||||||
|   | |||||||
| @@ -30,6 +30,8 @@ type rpcServer struct { | |||||||
| 	opts        Options | 	opts        Options | ||||||
| 	handlers    map[string]Handler | 	handlers    map[string]Handler | ||||||
| 	subscribers map[*subscriber][]broker.Subscriber | 	subscribers map[*subscriber][]broker.Subscriber | ||||||
|  | 	// marks the serve as started | ||||||
|  | 	started bool | ||||||
| 	// used for first registration | 	// used for first registration | ||||||
| 	registered bool | 	registered bool | ||||||
| 	// graceful exit | 	// graceful exit | ||||||
| @@ -61,20 +63,33 @@ func (r rpcRouter) ServeRequest(ctx context.Context, req Request, rsp Response) | |||||||
|  |  | ||||||
| // ServeConn serves a single connection | // ServeConn serves a single connection | ||||||
| func (s *rpcServer) ServeConn(sock transport.Socket) { | func (s *rpcServer) ServeConn(sock transport.Socket) { | ||||||
|  | 	var wg sync.WaitGroup | ||||||
|  | 	var mtx sync.RWMutex | ||||||
|  | 	// streams are multiplexed on Micro-Stream or Micro-Id header | ||||||
|  | 	sockets := make(map[string]*socket.Socket) | ||||||
|  |  | ||||||
| 	defer func() { | 	defer func() { | ||||||
| 		// close socket | 		// wait till done | ||||||
|  | 		wg.Wait() | ||||||
|  |  | ||||||
|  | 		// close underlying socket | ||||||
| 		sock.Close() | 		sock.Close() | ||||||
|  |  | ||||||
|  | 		// close the sockets | ||||||
|  | 		mtx.Lock() | ||||||
|  | 		for id, psock := range sockets { | ||||||
|  | 			psock.Close() | ||||||
|  | 			delete(sockets, id) | ||||||
|  | 		} | ||||||
|  | 		mtx.Unlock() | ||||||
|  |  | ||||||
|  | 		// recover any panics | ||||||
| 		if r := recover(); r != nil { | 		if r := recover(); r != nil { | ||||||
| 			log.Log("panic recovered: ", r) | 			log.Log("panic recovered: ", r) | ||||||
| 			log.Log(string(debug.Stack())) | 			log.Log(string(debug.Stack())) | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
| 	// multiplex the streams on a single socket by Micro-Stream |  | ||||||
| 	var mtx sync.RWMutex |  | ||||||
| 	sockets := make(map[string]*socket.Socket) |  | ||||||
|  |  | ||||||
| 	for { | 	for { | ||||||
| 		var msg transport.Message | 		var msg transport.Message | ||||||
| 		if err := sock.Recv(&msg); err != nil { | 		if err := sock.Recv(&msg); err != nil { | ||||||
| @@ -92,6 +107,9 @@ func (s *rpcServer) ServeConn(sock transport.Socket) { | |||||||
| 			id = msg.Header["Micro-Id"] | 			id = msg.Header["Micro-Id"] | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		// we're starting processing | ||||||
|  | 		wg.Add(1) | ||||||
|  |  | ||||||
| 		// add to wait group if "wait" is opt-in | 		// add to wait group if "wait" is opt-in | ||||||
| 		if s.wg != nil { | 		if s.wg != nil { | ||||||
| 			s.wg.Add(1) | 			s.wg.Add(1) | ||||||
| @@ -117,6 +135,8 @@ func (s *rpcServer) ServeConn(sock transport.Socket) { | |||||||
| 				s.wg.Done() | 				s.wg.Done() | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			wg.Done() | ||||||
|  |  | ||||||
| 			// continue to the next message | 			// continue to the next message | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| @@ -134,28 +154,6 @@ func (s *rpcServer) ServeConn(sock transport.Socket) { | |||||||
| 		sockets[id] = psock | 		sockets[id] = psock | ||||||
| 		mtx.Unlock() | 		mtx.Unlock() | ||||||
|  |  | ||||||
| 		// process the outbound messages from the socket |  | ||||||
| 		go func(id string, psock *socket.Socket) { |  | ||||||
| 			defer psock.Close() |  | ||||||
|  |  | ||||||
| 			for { |  | ||||||
| 				// get the message from our internal handler/stream |  | ||||||
| 				m := new(transport.Message) |  | ||||||
| 				if err := psock.Process(m); err != nil { |  | ||||||
| 					// delete the socket |  | ||||||
| 					mtx.Lock() |  | ||||||
| 					delete(sockets, id) |  | ||||||
| 					mtx.Unlock() |  | ||||||
| 					return |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				// send the message back over the socket |  | ||||||
| 				if err := sock.Send(m); err != nil { |  | ||||||
| 					return |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		}(id, psock) |  | ||||||
|  |  | ||||||
| 		// now walk the usual path | 		// now walk the usual path | ||||||
|  |  | ||||||
| 		// we use this Timeout header to set a server deadline | 		// we use this Timeout header to set a server deadline | ||||||
| @@ -203,17 +201,23 @@ func (s *rpcServer) ServeConn(sock transport.Socket) { | |||||||
| 					}, | 					}, | ||||||
| 					Body: []byte(err.Error()), | 					Body: []byte(err.Error()), | ||||||
| 				}) | 				}) | ||||||
|  |  | ||||||
| 				if s.wg != nil { | 				if s.wg != nil { | ||||||
| 					s.wg.Done() | 					s.wg.Done() | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | 				wg.Done() | ||||||
|  |  | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		rcodec := newRpcCodec(&msg, psock, cf) | 		rcodec := newRpcCodec(&msg, psock, cf) | ||||||
|  | 		protocol := rcodec.String() | ||||||
|  |  | ||||||
| 		// check stream id | 		// check stream id | ||||||
| 		var stream bool | 		var stream bool | ||||||
|  |  | ||||||
| 		if v := getHeader("Micro-Stream", msg.Header); len(v) > 0 { | 		if v := getHeader("Micro-Stream", msg.Header); len(v) > 0 { | ||||||
| 			stream = true | 			stream = true | ||||||
| 		} | 		} | ||||||
| @@ -257,8 +261,44 @@ func (s *rpcServer) ServeConn(sock transport.Socket) { | |||||||
| 			r = rpcRouter{handler} | 			r = rpcRouter{handler} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		// wait for processing to exit | ||||||
|  | 		wg.Add(1) | ||||||
|  |  | ||||||
|  | 		// process the outbound messages from the socket | ||||||
|  | 		go func(id string, psock *socket.Socket) { | ||||||
|  | 			defer func() { | ||||||
|  | 				// TODO: don't hack this but if its grpc just break out of the stream | ||||||
|  | 				// We do this because the underlying connection is h2 and its a stream | ||||||
|  | 				switch protocol { | ||||||
|  | 				case "grpc": | ||||||
|  | 					sock.Close() | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				wg.Done() | ||||||
|  | 			}() | ||||||
|  |  | ||||||
|  | 			for { | ||||||
|  | 				// get the message from our internal handler/stream | ||||||
|  | 				m := new(transport.Message) | ||||||
|  | 				if err := psock.Process(m); err != nil { | ||||||
|  | 					// delete the socket | ||||||
|  | 					mtx.Lock() | ||||||
|  | 					delete(sockets, id) | ||||||
|  | 					mtx.Unlock() | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				// send the message back over the socket | ||||||
|  | 				if err := sock.Send(m); err != nil { | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}(id, psock) | ||||||
|  |  | ||||||
| 		// serve the request in a go routine as this may be a stream | 		// serve the request in a go routine as this may be a stream | ||||||
| 		go func(id string, psock *socket.Socket) { | 		go func(id string, psock *socket.Socket) { | ||||||
|  | 			defer psock.Close() | ||||||
|  |  | ||||||
| 			// serve the actual request using the request router | 			// serve the actual request using the request router | ||||||
| 			if err := r.ServeRequest(ctx, request, response); err != nil { | 			if err := r.ServeRequest(ctx, request, response); err != nil { | ||||||
| 				// write an error response | 				// write an error response | ||||||
| @@ -278,16 +318,14 @@ func (s *rpcServer) ServeConn(sock transport.Socket) { | |||||||
| 			delete(sockets, id) | 			delete(sockets, id) | ||||||
| 			mtx.Unlock() | 			mtx.Unlock() | ||||||
|  |  | ||||||
| 			// once done serving signal we're done | 			// signal we're done | ||||||
| 			if s.wg != nil { | 			if s.wg != nil { | ||||||
| 				s.wg.Done() | 				s.wg.Done() | ||||||
| 			} | 			} | ||||||
| 		}(id, psock) |  | ||||||
|  |  | ||||||
| 		// signal we're done | 			// done with this socket | ||||||
| 		if s.wg != nil { | 			wg.Done() | ||||||
| 			s.wg.Done() | 		}(id, psock) | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -587,6 +625,13 @@ func (s *rpcServer) Deregister() error { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (s *rpcServer) Start() error { | func (s *rpcServer) Start() error { | ||||||
|  | 	s.RLock() | ||||||
|  | 	if s.started { | ||||||
|  | 		s.RUnlock() | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	s.RUnlock() | ||||||
|  |  | ||||||
| 	config := s.Options() | 	config := s.Options() | ||||||
|  |  | ||||||
| 	// start listening on the transport | 	// start listening on the transport | ||||||
| @@ -711,13 +756,34 @@ func (s *rpcServer) Start() error { | |||||||
| 		s.Unlock() | 		s.Unlock() | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
|  | 	// mark the server as started | ||||||
|  | 	s.Lock() | ||||||
|  | 	s.started = true | ||||||
|  | 	s.Unlock() | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *rpcServer) Stop() error { | func (s *rpcServer) Stop() error { | ||||||
|  | 	s.RLock() | ||||||
|  | 	if !s.started { | ||||||
|  | 		s.RUnlock() | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	s.RUnlock() | ||||||
|  |  | ||||||
| 	ch := make(chan error) | 	ch := make(chan error) | ||||||
| 	s.exit <- ch | 	s.exit <- ch | ||||||
| 	return <-ch |  | ||||||
|  | 	var err error | ||||||
|  | 	select { | ||||||
|  | 	case err = <-ch: | ||||||
|  | 		s.Lock() | ||||||
|  | 		s.started = false | ||||||
|  | 		s.Unlock() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *rpcServer) String() string { | func (s *rpcServer) String() string { | ||||||
|   | |||||||
| @@ -3,9 +3,11 @@ package server | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"fmt" | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/signal" | 	"os/signal" | ||||||
| 	"syscall" | 	"syscall" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/google/uuid" | 	"github.com/google/uuid" | ||||||
| 	"github.com/micro/go-micro/codec" | 	"github.com/micro/go-micro/codec" | ||||||
| @@ -116,8 +118,8 @@ type Option func(*Options) | |||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	DefaultAddress              = ":0" | 	DefaultAddress              = ":0" | ||||||
| 	DefaultName                 = "server" | 	DefaultName                 = "go.micro.server" | ||||||
| 	DefaultVersion              = "latest" | 	DefaultVersion              = fmt.Sprintf("%d", time.Now().Unix()) | ||||||
| 	DefaultId                   = uuid.New().String() | 	DefaultId                   = uuid.New().String() | ||||||
| 	DefaultServer        Server = newRpcServer() | 	DefaultServer        Server = newRpcServer() | ||||||
| 	DefaultRouter               = newRpcRouter() | 	DefaultRouter               = newRpcRouter() | ||||||
|   | |||||||
| @@ -17,5 +17,5 @@ func NewService(opts ...micro.Option) micro.Service { | |||||||
|  |  | ||||||
| 	options = append(options, opts...) | 	options = append(options, opts...) | ||||||
|  |  | ||||||
| 	return micro.NewService(opts...) | 	return micro.NewService(options...) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -33,6 +33,8 @@ type httpTransportClient struct { | |||||||
| 	once     sync.Once | 	once     sync.Once | ||||||
|  |  | ||||||
| 	sync.RWMutex | 	sync.RWMutex | ||||||
|  |  | ||||||
|  | 	// request must be stored for response processing | ||||||
| 	r    chan *http.Request | 	r    chan *http.Request | ||||||
| 	bl   []*http.Request | 	bl   []*http.Request | ||||||
| 	buff *bufio.Reader | 	buff *bufio.Reader | ||||||
| @@ -48,10 +50,18 @@ type httpTransportSocket struct { | |||||||
| 	r  *http.Request | 	r  *http.Request | ||||||
| 	rw *bufio.ReadWriter | 	rw *bufio.ReadWriter | ||||||
|  |  | ||||||
|  | 	mtx sync.RWMutex | ||||||
|  |  | ||||||
|  | 	// the hijacked when using http 1 | ||||||
| 	conn net.Conn | 	conn net.Conn | ||||||
| 	// for the first request | 	// for the first request | ||||||
| 	ch chan *http.Request | 	ch chan *http.Request | ||||||
|  |  | ||||||
|  | 	// h2 things | ||||||
|  | 	buf *bufio.Reader | ||||||
|  | 	// indicate if socket is closed | ||||||
|  | 	closed chan bool | ||||||
|  |  | ||||||
| 	// local/remote ip | 	// local/remote ip | ||||||
| 	local  string | 	local  string | ||||||
| 	remote string | 	remote string | ||||||
| @@ -161,14 +171,13 @@ func (h *httpTransportClient) Recv(m *Message) error { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (h *httpTransportClient) Close() error { | func (h *httpTransportClient) Close() error { | ||||||
| 	err := h.conn.Close() |  | ||||||
| 	h.once.Do(func() { | 	h.once.Do(func() { | ||||||
| 		h.Lock() | 		h.Lock() | ||||||
| 		h.buff.Reset(nil) | 		h.buff.Reset(nil) | ||||||
| 		h.Unlock() | 		h.Unlock() | ||||||
| 		close(h.r) | 		close(h.r) | ||||||
| 	}) | 	}) | ||||||
| 	return err | 	return h.conn.Close() | ||||||
| } | } | ||||||
|  |  | ||||||
| func (h *httpTransportSocket) Local() string { | func (h *httpTransportSocket) Local() string { | ||||||
| @@ -232,14 +241,23 @@ func (h *httpTransportSocket) Recv(m *Message) error { | |||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// only process if the socket is open | ||||||
|  | 	select { | ||||||
|  | 	case <-h.closed: | ||||||
|  | 		return io.EOF | ||||||
|  | 	default: | ||||||
|  | 		// no op | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// processing http2 request | 	// processing http2 request | ||||||
| 	// read streaming body | 	// read streaming body | ||||||
|  |  | ||||||
| 	// set max buffer size | 	// set max buffer size | ||||||
| 	buf := make([]byte, 4*1024) | 	// TODO: adjustable buffer size | ||||||
|  | 	buf := make([]byte, 4*1024*1024) | ||||||
|  |  | ||||||
| 	// read the request body | 	// read the request body | ||||||
| 	n, err := h.r.Body.Read(buf) | 	n, err := h.buf.Read(buf) | ||||||
| 	// not an eof error | 	// not an eof error | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -290,7 +308,13 @@ func (h *httpTransportSocket) Send(m *Message) error { | |||||||
| 		return rsp.Write(h.conn) | 		return rsp.Write(h.conn) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// http2 request | 	// only process if the socket is open | ||||||
|  | 	select { | ||||||
|  | 	case <-h.closed: | ||||||
|  | 		return io.EOF | ||||||
|  | 	default: | ||||||
|  | 		// no op | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// set headers | 	// set headers | ||||||
| 	for k, v := range m.Header { | 	for k, v := range m.Header { | ||||||
| @@ -299,6 +323,10 @@ func (h *httpTransportSocket) Send(m *Message) error { | |||||||
|  |  | ||||||
| 	// write request | 	// write request | ||||||
| 	_, err := h.w.Write(m.Body) | 	_, err := h.w.Write(m.Body) | ||||||
|  |  | ||||||
|  | 	// flush the trailers | ||||||
|  | 	h.w.(http.Flusher).Flush() | ||||||
|  |  | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -321,13 +349,29 @@ func (h *httpTransportSocket) error(m *Message) error { | |||||||
|  |  | ||||||
| 		return rsp.Write(h.conn) | 		return rsp.Write(h.conn) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (h *httpTransportSocket) Close() error { | func (h *httpTransportSocket) Close() error { | ||||||
| 	if h.r.ProtoMajor == 1 { | 	h.mtx.Lock() | ||||||
| 		return h.conn.Close() | 	defer h.mtx.Unlock() | ||||||
|  | 	select { | ||||||
|  | 	case <-h.closed: | ||||||
|  | 		return nil | ||||||
|  | 	default: | ||||||
|  | 		// close the channel | ||||||
|  | 		close(h.closed) | ||||||
|  |  | ||||||
|  | 		// close the buffer | ||||||
|  | 		h.r.Body.Close() | ||||||
|  |  | ||||||
|  | 		// close the connection | ||||||
|  | 		if h.r.ProtoMajor == 1 { | ||||||
|  | 			return h.conn.Close() | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -374,20 +418,29 @@ func (h *httpTransportListener) Accept(fn func(Socket)) error { | |||||||
| 			con = conn | 			con = conn | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		// buffered reader | ||||||
|  | 		bufr := bufio.NewReader(r.Body) | ||||||
|  |  | ||||||
| 		// save the request | 		// save the request | ||||||
| 		ch := make(chan *http.Request, 1) | 		ch := make(chan *http.Request, 1) | ||||||
| 		ch <- r | 		ch <- r | ||||||
|  |  | ||||||
| 		fn(&httpTransportSocket{ | 		// create a new transport socket | ||||||
|  | 		sock := &httpTransportSocket{ | ||||||
| 			ht:     h.ht, | 			ht:     h.ht, | ||||||
| 			w:      w, | 			w:      w, | ||||||
| 			r:      r, | 			r:      r, | ||||||
| 			rw:     buf, | 			rw:     buf, | ||||||
|  | 			buf:    bufr, | ||||||
| 			ch:     ch, | 			ch:     ch, | ||||||
| 			conn:   con, | 			conn:   con, | ||||||
| 			local:  h.Addr(), | 			local:  h.Addr(), | ||||||
| 			remote: r.RemoteAddr, | 			remote: r.RemoteAddr, | ||||||
| 		}) | 			closed: make(chan bool), | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// execute the socket | ||||||
|  | 		fn(sock) | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| 	// get optional handlers | 	// get optional handlers | ||||||
|   | |||||||
							
								
								
									
										213
									
								
								tunnel/broker/broker.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								tunnel/broker/broker.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,213 @@ | |||||||
|  | // Package broker is a tunnel broker | ||||||
|  | package broker | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  |  | ||||||
|  | 	"github.com/micro/go-micro/broker" | ||||||
|  | 	"github.com/micro/go-micro/transport" | ||||||
|  | 	"github.com/micro/go-micro/tunnel" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type tunBroker struct { | ||||||
|  | 	opts   broker.Options | ||||||
|  | 	tunnel tunnel.Tunnel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type tunSubscriber struct { | ||||||
|  | 	topic   string | ||||||
|  | 	handler broker.Handler | ||||||
|  | 	opts    broker.SubscribeOptions | ||||||
|  |  | ||||||
|  | 	closed   chan bool | ||||||
|  | 	listener tunnel.Listener | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type tunEvent struct { | ||||||
|  | 	topic   string | ||||||
|  | 	message *broker.Message | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // used to access tunnel from options context | ||||||
|  | type tunnelKey struct{} | ||||||
|  | type tunnelAddr struct{} | ||||||
|  |  | ||||||
|  | func (t *tunBroker) Init(opts ...broker.Option) error { | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&t.opts) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *tunBroker) Options() broker.Options { | ||||||
|  | 	return t.opts | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *tunBroker) Address() string { | ||||||
|  | 	return t.tunnel.Address() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *tunBroker) Connect() error { | ||||||
|  | 	return t.tunnel.Connect() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *tunBroker) Disconnect() error { | ||||||
|  | 	return t.tunnel.Close() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *tunBroker) Publish(topic string, m *broker.Message, opts ...broker.PublishOption) error { | ||||||
|  | 	// TODO: this is probably inefficient, we might want to just maintain an open connection | ||||||
|  | 	// it may be easier to add broadcast to the tunnel | ||||||
|  | 	c, err := t.tunnel.Dial(topic) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer c.Close() | ||||||
|  |  | ||||||
|  | 	return c.Send(&transport.Message{ | ||||||
|  | 		Header: m.Header, | ||||||
|  | 		Body:   m.Body, | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *tunBroker) Subscribe(topic string, h broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) { | ||||||
|  | 	l, err := t.tunnel.Listen(topic) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var options broker.SubscribeOptions | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	tunSub := &tunSubscriber{ | ||||||
|  | 		topic:    topic, | ||||||
|  | 		handler:  h, | ||||||
|  | 		opts:     options, | ||||||
|  | 		closed:   make(chan bool), | ||||||
|  | 		listener: l, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// start processing | ||||||
|  | 	go tunSub.run() | ||||||
|  |  | ||||||
|  | 	return tunSub, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *tunBroker) String() string { | ||||||
|  | 	return "tunnel" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *tunSubscriber) run() { | ||||||
|  | 	for { | ||||||
|  | 		// accept a new connection | ||||||
|  | 		c, err := t.listener.Accept() | ||||||
|  | 		if err != nil { | ||||||
|  | 			select { | ||||||
|  | 			case <-t.closed: | ||||||
|  | 				return | ||||||
|  | 			default: | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// receive message | ||||||
|  | 		m := new(transport.Message) | ||||||
|  | 		if err := c.Recv(m); err != nil { | ||||||
|  | 			c.Close() | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// close the connection | ||||||
|  | 		c.Close() | ||||||
|  |  | ||||||
|  | 		// handle the message | ||||||
|  | 		go t.handler(&tunEvent{ | ||||||
|  | 			topic: t.topic, | ||||||
|  | 			message: &broker.Message{ | ||||||
|  | 				Header: m.Header, | ||||||
|  | 				Body:   m.Body, | ||||||
|  | 			}, | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *tunSubscriber) Options() broker.SubscribeOptions { | ||||||
|  | 	return t.opts | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *tunSubscriber) Topic() string { | ||||||
|  | 	return t.topic | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *tunSubscriber) Unsubscribe() error { | ||||||
|  | 	select { | ||||||
|  | 	case <-t.closed: | ||||||
|  | 		return nil | ||||||
|  | 	default: | ||||||
|  | 		close(t.closed) | ||||||
|  | 		return t.listener.Close() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *tunEvent) Topic() string { | ||||||
|  | 	return t.topic | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *tunEvent) Message() *broker.Message { | ||||||
|  | 	return t.message | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *tunEvent) Ack() error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewBroker(opts ...broker.Option) broker.Broker { | ||||||
|  | 	options := broker.Options{ | ||||||
|  | 		Context: context.Background(), | ||||||
|  | 	} | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&options) | ||||||
|  | 	} | ||||||
|  | 	t, ok := options.Context.Value(tunnelKey{}).(tunnel.Tunnel) | ||||||
|  | 	if !ok { | ||||||
|  | 		t = tunnel.NewTunnel() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	a, ok := options.Context.Value(tunnelAddr{}).(string) | ||||||
|  | 	if ok { | ||||||
|  | 		// initialise address | ||||||
|  | 		t.Init(tunnel.Address(a)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(options.Addrs) > 0 { | ||||||
|  | 		// initialise nodes | ||||||
|  | 		t.Init(tunnel.Nodes(options.Addrs...)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &tunBroker{ | ||||||
|  | 		opts:   options, | ||||||
|  | 		tunnel: t, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithAddress sets the tunnel address | ||||||
|  | func WithAddress(a string) broker.Option { | ||||||
|  | 	return func(o *broker.Options) { | ||||||
|  | 		if o.Context == nil { | ||||||
|  | 			o.Context = context.Background() | ||||||
|  | 		} | ||||||
|  | 		o.Context = context.WithValue(o.Context, tunnelAddr{}, a) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithTunnel sets the internal tunnel | ||||||
|  | func WithTunnel(t tunnel.Tunnel) broker.Option { | ||||||
|  | 	return func(o *broker.Options) { | ||||||
|  | 		if o.Context == nil { | ||||||
|  | 			o.Context = context.Background() | ||||||
|  | 		} | ||||||
|  | 		o.Context = context.WithValue(o.Context, tunnelKey{}, t) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -73,6 +73,8 @@ func newTunnel(opts ...Option) *tun { | |||||||
|  |  | ||||||
| // Init initializes tunnel options | // Init initializes tunnel options | ||||||
| func (t *tun) Init(opts ...Option) error { | func (t *tun) Init(opts ...Option) error { | ||||||
|  | 	t.Lock() | ||||||
|  | 	defer t.Unlock() | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&t.options) | 		o(&t.options) | ||||||
| 	} | 	} | ||||||
| @@ -170,6 +172,9 @@ func (t *tun) process() { | |||||||
| 				newMsg.Header[k] = v | 				newMsg.Header[k] = v | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			// set message head | ||||||
|  | 			newMsg.Header["Micro-Tunnel"] = "message" | ||||||
|  |  | ||||||
| 			// set the tunnel id on the outgoing message | 			// set the tunnel id on the outgoing message | ||||||
| 			newMsg.Header["Micro-Tunnel-Id"] = msg.id | 			newMsg.Header["Micro-Tunnel-Id"] = msg.id | ||||||
|  |  | ||||||
| @@ -227,8 +232,12 @@ func (t *tun) listen(link *link) { | |||||||
|  |  | ||||||
| 			// are we connecting to ourselves? | 			// are we connecting to ourselves? | ||||||
| 			if token == t.token { | 			if token == t.token { | ||||||
|  | 				t.Lock() | ||||||
| 				link.loopback = true | 				link.loopback = true | ||||||
|  | 				t.Unlock() | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			// nothing more to do | ||||||
| 			continue | 			continue | ||||||
| 		case "close": | 		case "close": | ||||||
| 			log.Debugf("Tunnel link %s closing connection", link.Remote()) | 			log.Debugf("Tunnel link %s closing connection", link.Remote()) | ||||||
| @@ -237,10 +246,21 @@ func (t *tun) listen(link *link) { | |||||||
| 			continue | 			continue | ||||||
| 		case "keepalive": | 		case "keepalive": | ||||||
| 			log.Debugf("Tunnel link %s received keepalive", link.Remote()) | 			log.Debugf("Tunnel link %s received keepalive", link.Remote()) | ||||||
|  | 			t.Lock() | ||||||
| 			link.lastKeepAlive = time.Now() | 			link.lastKeepAlive = time.Now() | ||||||
|  | 			t.Unlock() | ||||||
|  | 			continue | ||||||
|  | 		case "message": | ||||||
|  | 			// process message | ||||||
|  | 			log.Debugf("Received %+v from %s", msg, link.Remote()) | ||||||
|  | 		default: | ||||||
|  | 			// blackhole it | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		// strip message header | ||||||
|  | 		delete(msg.Header, "Micro-Tunnel") | ||||||
|  |  | ||||||
| 		// the tunnel id | 		// the tunnel id | ||||||
| 		id := msg.Header["Micro-Tunnel-Id"] | 		id := msg.Header["Micro-Tunnel-Id"] | ||||||
| 		delete(msg.Header, "Micro-Tunnel-Id") | 		delete(msg.Header, "Micro-Tunnel-Id") | ||||||
| @@ -249,6 +269,9 @@ func (t *tun) listen(link *link) { | |||||||
| 		session := msg.Header["Micro-Tunnel-Session"] | 		session := msg.Header["Micro-Tunnel-Session"] | ||||||
| 		delete(msg.Header, "Micro-Tunnel-Session") | 		delete(msg.Header, "Micro-Tunnel-Session") | ||||||
|  |  | ||||||
|  | 		// strip token header | ||||||
|  | 		delete(msg.Header, "Micro-Tunnel-Token") | ||||||
|  |  | ||||||
| 		// if the session id is blank there's nothing we can do | 		// if the session id is blank there's nothing we can do | ||||||
| 		// TODO: check this is the case, is there any reason | 		// TODO: check this is the case, is there any reason | ||||||
| 		// why we'd have a blank session? Is the tunnel | 		// why we'd have a blank session? Is the tunnel | ||||||
| @@ -260,8 +283,6 @@ func (t *tun) listen(link *link) { | |||||||
| 		var s *socket | 		var s *socket | ||||||
| 		var exists bool | 		var exists bool | ||||||
|  |  | ||||||
| 		log.Debugf("Received %+v from %s", msg, link.Remote()) |  | ||||||
|  |  | ||||||
| 		switch { | 		switch { | ||||||
| 		case link.loopback: | 		case link.loopback: | ||||||
| 			s, exists = t.getSocket(id, "listener") | 			s, exists = t.getSocket(id, "listener") | ||||||
| @@ -506,6 +527,17 @@ func (t *tun) close() error { | |||||||
| 	return t.listener.Close() | 	return t.listener.Close() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (t *tun) Address() string { | ||||||
|  | 	t.RLock() | ||||||
|  | 	defer t.RUnlock() | ||||||
|  |  | ||||||
|  | 	if !t.connected { | ||||||
|  | 		return t.options.Address | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return t.listener.Addr() | ||||||
|  | } | ||||||
|  |  | ||||||
| // Close the tunnel | // Close the tunnel | ||||||
| func (t *tun) Close() error { | func (t *tun) Close() error { | ||||||
| 	t.Lock() | 	t.Lock() | ||||||
| @@ -591,3 +623,7 @@ func (t *tun) Listen(addr string) (Listener, error) { | |||||||
| 	// return the listener | 	// return the listener | ||||||
| 	return tl, nil | 	return tl, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (t *tun) String() string { | ||||||
|  | 	return "mucp" | ||||||
|  | } | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import ( | |||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	// DefaultAddress is default tunnel bind address | 	// DefaultAddress is default tunnel bind address | ||||||
| 	DefaultAddress = ":9096" | 	DefaultAddress = ":0" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Option func(*Options) | type Option func(*Options) | ||||||
|   | |||||||
| @@ -11,6 +11,8 @@ import ( | |||||||
| // the address being requested. | // the address being requested. | ||||||
| type Tunnel interface { | type Tunnel interface { | ||||||
| 	Init(opts ...Option) error | 	Init(opts ...Option) error | ||||||
|  | 	// Address the tunnel is listening on | ||||||
|  | 	Address() string | ||||||
| 	// Connect connects the tunnel | 	// Connect connects the tunnel | ||||||
| 	Connect() error | 	Connect() error | ||||||
| 	// Close closes the tunnel | 	// Close closes the tunnel | ||||||
| @@ -19,6 +21,8 @@ type Tunnel interface { | |||||||
| 	Dial(addr string) (Conn, error) | 	Dial(addr string) (Conn, error) | ||||||
| 	// Accept connections | 	// Accept connections | ||||||
| 	Listen(addr string) (Listener, error) | 	Listen(addr string) (Listener, error) | ||||||
|  | 	// Name of the tunnel implementation | ||||||
|  | 	String() string | ||||||
| } | } | ||||||
|  |  | ||||||
| // The listener provides similar constructs to the transport.Listener | // The listener provides similar constructs to the transport.Listener | ||||||
|   | |||||||
| @@ -10,6 +10,8 @@ import ( | |||||||
|  |  | ||||||
| // testAccept will accept connections on the transport, create a new link and tunnel on top | // testAccept will accept connections on the transport, create a new link and tunnel on top | ||||||
| func testAccept(t *testing.T, tun Tunnel, wait chan bool, wg *sync.WaitGroup) { | func testAccept(t *testing.T, tun Tunnel, wait chan bool, wg *sync.WaitGroup) { | ||||||
|  | 	defer wg.Done() | ||||||
|  |  | ||||||
| 	// listen on some virtual address | 	// listen on some virtual address | ||||||
| 	tl, err := tun.Listen("test-tunnel") | 	tl, err := tun.Listen("test-tunnel") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -43,7 +45,8 @@ func testAccept(t *testing.T, tun Tunnel, wait chan bool, wg *sync.WaitGroup) { | |||||||
| 			t.Fatal(err) | 			t.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		wg.Done() | 		wait <- true | ||||||
|  |  | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -79,6 +82,8 @@ func testSend(t *testing.T, tun Tunnel, wait chan bool, wg *sync.WaitGroup) { | |||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	<-wait | ||||||
|  |  | ||||||
| 	if v := mr.Header["test"]; v != "accept" { | 	if v := mr.Header["test"]; v != "accept" { | ||||||
| 		t.Fatalf("Message not received from accepted side. Received: %s", v) | 		t.Fatalf("Message not received from accepted side. Received: %s", v) | ||||||
| 	} | 	} | ||||||
| @@ -140,6 +145,8 @@ func TestLoopbackTunnel(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 	defer tun.Close() | 	defer tun.Close() | ||||||
|  |  | ||||||
|  | 	time.Sleep(500 * time.Millisecond) | ||||||
|  |  | ||||||
| 	wait := make(chan bool) | 	wait := make(chan bool) | ||||||
|  |  | ||||||
| 	var wg sync.WaitGroup | 	var wg sync.WaitGroup | ||||||
|   | |||||||
| @@ -32,10 +32,10 @@ func (s *Socket) SetRemote(r string) { | |||||||
| // Accept passes a message to the socket which will be processed by the call to Recv | // Accept passes a message to the socket which will be processed by the call to Recv | ||||||
| func (s *Socket) Accept(m *transport.Message) error { | func (s *Socket) Accept(m *transport.Message) error { | ||||||
| 	select { | 	select { | ||||||
| 	case <-s.closed: |  | ||||||
| 		return io.EOF |  | ||||||
| 	case s.recv <- m: | 	case s.recv <- m: | ||||||
| 		return nil | 		return nil | ||||||
|  | 	case <-s.closed: | ||||||
|  | 		return io.EOF | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| @@ -43,10 +43,17 @@ func (s *Socket) Accept(m *transport.Message) error { | |||||||
| // Process takes the next message off the send queue created by a call to Send | // Process takes the next message off the send queue created by a call to Send | ||||||
| func (s *Socket) Process(m *transport.Message) error { | func (s *Socket) Process(m *transport.Message) error { | ||||||
| 	select { | 	select { | ||||||
| 	case <-s.closed: |  | ||||||
| 		return io.EOF |  | ||||||
| 	case msg := <-s.send: | 	case msg := <-s.send: | ||||||
| 		*m = *msg | 		*m = *msg | ||||||
|  | 	case <-s.closed: | ||||||
|  | 		// see if we need to drain | ||||||
|  | 		select { | ||||||
|  | 		case msg := <-s.send: | ||||||
|  | 			*m = *msg | ||||||
|  | 			return nil | ||||||
|  | 		default: | ||||||
|  | 			return io.EOF | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| @@ -60,13 +67,6 @@ func (s *Socket) Local() string { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (s *Socket) Send(m *transport.Message) error { | func (s *Socket) Send(m *transport.Message) error { | ||||||
| 	select { |  | ||||||
| 	case <-s.closed: |  | ||||||
| 		return io.EOF |  | ||||||
| 	default: |  | ||||||
| 		// no op |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// make copy | 	// make copy | ||||||
| 	msg := &transport.Message{ | 	msg := &transport.Message{ | ||||||
| 		Header: make(map[string]string), | 		Header: make(map[string]string), | ||||||
| @@ -92,13 +92,6 @@ func (s *Socket) Send(m *transport.Message) error { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (s *Socket) Recv(m *transport.Message) error { | func (s *Socket) Recv(m *transport.Message) error { | ||||||
| 	select { |  | ||||||
| 	case <-s.closed: |  | ||||||
| 		return io.EOF |  | ||||||
| 	default: |  | ||||||
| 		// no op |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// receive a message | 	// receive a message | ||||||
| 	select { | 	select { | ||||||
| 	case msg := <-s.recv: | 	case msg := <-s.recv: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user