Update grpc and protobufs dep (#243)

* Update grpc and protobufs dep

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

* Fix grpc error check

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2016-05-18 09:16:55 -07:00
parent c3da771256
commit 614df93b92
32 changed files with 2051 additions and 1053 deletions

View file

@ -849,6 +849,10 @@ func init() {
var _ context.Context var _ context.Context
var _ grpc.ClientConn var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion2
// Client API for API service // Client API for API service
type APIClient interface { type APIClient interface {
@ -1026,124 +1030,184 @@ func RegisterAPIServer(s *grpc.Server, srv APIServer) {
s.RegisterService(&_API_serviceDesc, srv) s.RegisterService(&_API_serviceDesc, srv)
} }
func _API_GetServerVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { func _API_GetServerVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetServerVersionRequest) in := new(GetServerVersionRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
out, err := srv.(APIServer).GetServerVersion(ctx, in) if interceptor == nil {
if err != nil { return srv.(APIServer).GetServerVersion(ctx, in)
return nil, err
} }
return out, nil info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/types.API/GetServerVersion",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(APIServer).GetServerVersion(ctx, req.(*GetServerVersionRequest))
}
return interceptor(ctx, in, info, handler)
} }
func _API_CreateContainer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { func _API_CreateContainer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateContainerRequest) in := new(CreateContainerRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
out, err := srv.(APIServer).CreateContainer(ctx, in) if interceptor == nil {
if err != nil { return srv.(APIServer).CreateContainer(ctx, in)
return nil, err
} }
return out, nil info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/types.API/CreateContainer",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(APIServer).CreateContainer(ctx, req.(*CreateContainerRequest))
}
return interceptor(ctx, in, info, handler)
} }
func _API_UpdateContainer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { func _API_UpdateContainer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateContainerRequest) in := new(UpdateContainerRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
out, err := srv.(APIServer).UpdateContainer(ctx, in) if interceptor == nil {
if err != nil { return srv.(APIServer).UpdateContainer(ctx, in)
return nil, err
} }
return out, nil info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/types.API/UpdateContainer",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(APIServer).UpdateContainer(ctx, req.(*UpdateContainerRequest))
}
return interceptor(ctx, in, info, handler)
} }
func _API_Signal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { func _API_Signal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SignalRequest) in := new(SignalRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
out, err := srv.(APIServer).Signal(ctx, in) if interceptor == nil {
if err != nil { return srv.(APIServer).Signal(ctx, in)
return nil, err
} }
return out, nil info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/types.API/Signal",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(APIServer).Signal(ctx, req.(*SignalRequest))
}
return interceptor(ctx, in, info, handler)
} }
func _API_UpdateProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { func _API_UpdateProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateProcessRequest) in := new(UpdateProcessRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
out, err := srv.(APIServer).UpdateProcess(ctx, in) if interceptor == nil {
if err != nil { return srv.(APIServer).UpdateProcess(ctx, in)
return nil, err
} }
return out, nil info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/types.API/UpdateProcess",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(APIServer).UpdateProcess(ctx, req.(*UpdateProcessRequest))
}
return interceptor(ctx, in, info, handler)
} }
func _API_AddProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { func _API_AddProcess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddProcessRequest) in := new(AddProcessRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
out, err := srv.(APIServer).AddProcess(ctx, in) if interceptor == nil {
if err != nil { return srv.(APIServer).AddProcess(ctx, in)
return nil, err
} }
return out, nil info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/types.API/AddProcess",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(APIServer).AddProcess(ctx, req.(*AddProcessRequest))
}
return interceptor(ctx, in, info, handler)
} }
func _API_CreateCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { func _API_CreateCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateCheckpointRequest) in := new(CreateCheckpointRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
out, err := srv.(APIServer).CreateCheckpoint(ctx, in) if interceptor == nil {
if err != nil { return srv.(APIServer).CreateCheckpoint(ctx, in)
return nil, err
} }
return out, nil info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/types.API/CreateCheckpoint",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(APIServer).CreateCheckpoint(ctx, req.(*CreateCheckpointRequest))
}
return interceptor(ctx, in, info, handler)
} }
func _API_DeleteCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { func _API_DeleteCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteCheckpointRequest) in := new(DeleteCheckpointRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
out, err := srv.(APIServer).DeleteCheckpoint(ctx, in) if interceptor == nil {
if err != nil { return srv.(APIServer).DeleteCheckpoint(ctx, in)
return nil, err
} }
return out, nil info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/types.API/DeleteCheckpoint",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(APIServer).DeleteCheckpoint(ctx, req.(*DeleteCheckpointRequest))
}
return interceptor(ctx, in, info, handler)
} }
func _API_ListCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { func _API_ListCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListCheckpointRequest) in := new(ListCheckpointRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
out, err := srv.(APIServer).ListCheckpoint(ctx, in) if interceptor == nil {
if err != nil { return srv.(APIServer).ListCheckpoint(ctx, in)
return nil, err
} }
return out, nil info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/types.API/ListCheckpoint",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(APIServer).ListCheckpoint(ctx, req.(*ListCheckpointRequest))
}
return interceptor(ctx, in, info, handler)
} }
func _API_State_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { func _API_State_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(StateRequest) in := new(StateRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
out, err := srv.(APIServer).State(ctx, in) if interceptor == nil {
if err != nil { return srv.(APIServer).State(ctx, in)
return nil, err
} }
return out, nil info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/types.API/State",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(APIServer).State(ctx, req.(*StateRequest))
}
return interceptor(ctx, in, info, handler)
} }
func _API_Events_Handler(srv interface{}, stream grpc.ServerStream) error { func _API_Events_Handler(srv interface{}, stream grpc.ServerStream) error {
@ -1167,16 +1231,22 @@ func (x *aPIEventsServer) Send(m *Event) error {
return x.ServerStream.SendMsg(m) return x.ServerStream.SendMsg(m)
} }
func _API_Stats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) { func _API_Stats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(StatsRequest) in := new(StatsRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
out, err := srv.(APIServer).Stats(ctx, in) if interceptor == nil {
if err != nil { return srv.(APIServer).Stats(ctx, in)
return nil, err
} }
return out, nil info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/types.API/Stats",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(APIServer).Stats(ctx, req.(*StatsRequest))
}
return interceptor(ctx, in, info, handler)
} }
var _API_serviceDesc = grpc.ServiceDesc{ var _API_serviceDesc = grpc.ServiceDesc{
@ -1238,148 +1308,123 @@ var _API_serviceDesc = grpc.ServiceDesc{
} }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 2285 bytes of a gzipped FileDescriptorProto // 1879 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xec, 0x19, 0xcb, 0x72, 0x1c, 0x49, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xd4, 0x58, 0x5b, 0x8f, 0x23, 0x47,
0xd1, 0xf3, 0x94, 0x26, 0xe7, 0x21, 0xa9, 0xfd, 0xd0, 0x78, 0x76, 0xed, 0x35, 0x1d, 0xc0, 0x1a, 0x15, 0x5e, 0xdf, 0xed, 0xe3, 0xcb, 0xec, 0xf4, 0xce, 0xc5, 0xeb, 0x90, 0xcd, 0xd2, 0x09, 0x64,
0x58, 0x84, 0x91, 0x77, 0x03, 0x07, 0x04, 0x44, 0xac, 0x65, 0xb3, 0x98, 0xb5, 0x16, 0xb9, 0x25, 0x05, 0xd1, 0x28, 0x78, 0xb9, 0x84, 0x20, 0x21, 0xc2, 0x24, 0x4a, 0x40, 0xb3, 0x8b, 0x33, 0xb3,
0xb1, 0x17, 0x22, 0x26, 0x5a, 0x33, 0xe5, 0x99, 0x46, 0x33, 0xdd, 0xbd, 0xdd, 0x35, 0xd2, 0xe8, 0x13, 0xc4, 0x93, 0xd5, 0xee, 0xae, 0xb5, 0x8b, 0x69, 0x77, 0x37, 0x55, 0xd5, 0x9e, 0x99, 0x1f,
0xc2, 0x11, 0x6e, 0xfc, 0x00, 0x11, 0x5c, 0xb8, 0x71, 0xe7, 0xc0, 0x17, 0xf0, 0x27, 0xc4, 0x5e, 0xc1, 0x7f, 0xe0, 0x1d, 0x09, 0xf1, 0xc4, 0x0f, 0xe0, 0xb7, 0xf0, 0x84, 0xc4, 0x7f, 0xe0, 0xd4,
0xb8, 0x73, 0x24, 0xab, 0x32, 0xbb, 0xba, 0x7a, 0x1e, 0xd2, 0x72, 0x20, 0xb8, 0xec, 0x65, 0xa2, 0xad, 0xdd, 0xdd, 0xbe, 0x0c, 0x12, 0xe2, 0x81, 0x17, 0xcb, 0x55, 0x75, 0xea, 0x3b, 0xe7, 0x7c,
0x32, 0x2b, 0x2b, 0x33, 0x2b, 0xdf, 0x5d, 0x03, 0x0d, 0x3f, 0x0e, 0xf6, 0xe2, 0x24, 0x92, 0x91, 0xe7, 0x52, 0x55, 0x0d, 0x1d, 0x2f, 0xa1, 0x67, 0x09, 0x8b, 0x45, 0xec, 0x34, 0xc4, 0x7d, 0x42,
0x53, 0x93, 0x57, 0xb1, 0x48, 0xdd, 0xfb, 0xb0, 0xfb, 0x89, 0x90, 0xc7, 0x22, 0xb9, 0x10, 0xc9, 0xb8, 0xfb, 0x14, 0x4e, 0xbf, 0x24, 0xe2, 0x8a, 0xb0, 0x15, 0x61, 0xdf, 0x10, 0xc6, 0x69, 0x1c,
0xaf, 0x45, 0x92, 0x06, 0x51, 0xe8, 0x89, 0x2f, 0x66, 0x22, 0x95, 0xee, 0x1c, 0xba, 0xcb, 0x5b, 0x5d, 0x92, 0x3f, 0xa4, 0x84, 0x0b, 0xf7, 0x77, 0x30, 0xdc, 0x5c, 0xe2, 0x49, 0x1c, 0x71, 0xe2,
0x69, 0x1c, 0x85, 0xa9, 0x70, 0xee, 0x40, 0x6d, 0xea, 0xff, 0x36, 0x4a, 0xba, 0xa5, 0x47, 0xa5, 0xf4, 0xa1, 0xb1, 0xf4, 0x7e, 0x1f, 0xb3, 0x61, 0xe5, 0x79, 0xe5, 0x45, 0x5f, 0x0d, 0x69, 0x84,
0xc7, 0x6d, 0x8f, 0x00, 0x8d, 0x0d, 0x42, 0xc4, 0x96, 0x19, 0xab, 0x00, 0x85, 0x8d, 0x7d, 0x39, 0xc3, 0xaa, 0x1d, 0x26, 0x9e, 0xf0, 0x17, 0xc3, 0x9a, 0x1a, 0x3e, 0x86, 0x36, 0x23, 0x2b, 0x2a,
0x18, 0x77, 0x2b, 0x84, 0xd5, 0x80, 0xd3, 0x83, 0xcd, 0x44, 0x5c, 0x04, 0x8a, 0x6b, 0xb7, 0x8a, 0x01, 0x86, 0x75, 0x9c, 0xe9, 0xb8, 0x33, 0x38, 0xba, 0x4e, 0x02, 0x4f, 0x90, 0x09, 0x8b, 0x7d,
0x1b, 0x0d, 0xcf, 0xc0, 0xee, 0xef, 0x4b, 0x70, 0xe7, 0x34, 0x1e, 0xfa, 0x52, 0x1c, 0x25, 0xd1, 0xc2, 0xb9, 0x51, 0xe9, 0x00, 0x54, 0x69, 0xa0, 0x30, 0x3b, 0x4e, 0x17, 0x6a, 0x09, 0x0e, 0xaa,
0x40, 0xa4, 0x29, 0xab, 0xe4, 0x74, 0xa0, 0x1c, 0x0c, 0xb5, 0xcc, 0x86, 0x87, 0x2b, 0x67, 0x1b, 0x6a, 0x80, 0x2b, 0x7e, 0x18, 0x73, 0x72, 0x25, 0x02, 0x1a, 0x29, 0xd8, 0xb6, 0xd4, 0x72, 0x4b,
0x2a, 0x31, 0x22, 0xca, 0x1a, 0xa1, 0x96, 0xce, 0x43, 0x80, 0xc1, 0x24, 0x4a, 0xc5, 0xb1, 0x1c, 0x03, 0xb1, 0x50, 0x98, 0x7d, 0x67, 0x00, 0xcd, 0x05, 0xa1, 0xf3, 0x85, 0x18, 0x36, 0xe4, 0xd8,
0x06, 0xa1, 0x96, 0xb8, 0xe9, 0x59, 0x18, 0xa5, 0xcc, 0x65, 0x30, 0x94, 0x63, 0x2d, 0x13, 0x95, 0x3d, 0x85, 0xe3, 0x92, 0x0e, 0x6d, 0xbb, 0xfb, 0xa7, 0x0a, 0x9c, 0x9c, 0x33, 0x82, 0x2b, 0xe7,
0xd1, 0x80, 0x73, 0x0f, 0xea, 0x63, 0x11, 0x8c, 0xc6, 0xb2, 0x5b, 0xd3, 0x68, 0x86, 0xdc, 0x5d, 0x71, 0x24, 0x3c, 0x1a, 0x11, 0xb6, 0x4d, 0x3f, 0x0e, 0x66, 0x69, 0x14, 0x84, 0x64, 0xe2, 0xa1,
0xb8, 0xbb, 0xa0, 0x07, 0xdd, 0xdf, 0xfd, 0xb2, 0x04, 0xf7, 0x0e, 0x12, 0x81, 0x3b, 0x07, 0x51, 0x8e, 0xb5, 0x19, 0x0b, 0xe2, 0xdf, 0x24, 0x31, 0x8d, 0x84, 0x32, 0xa3, 0x23, 0xcd, 0xe0, 0xca,
0x28, 0xfd, 0x20, 0x14, 0xc9, 0x3a, 0x1d, 0x51, 0xa3, 0xb3, 0x59, 0x38, 0x9c, 0x88, 0x23, 0x1f, 0x2a, 0xe5, 0x9a, 0x34, 0x03, 0x87, 0x71, 0xaa, 0xcd, 0xb0, 0x63, 0xc2, 0xd8, 0xb0, 0x69, 0xc7,
0xc5, 0x92, 0xaa, 0x16, 0x46, 0x6b, 0x3c, 0x16, 0x83, 0xf3, 0x38, 0x0a, 0x42, 0xa9, 0x35, 0xc6, 0xa1, 0x37, 0x23, 0x21, 0x1f, 0xb6, 0x9e, 0xd7, 0x70, 0xfc, 0x04, 0xba, 0x51, 0x3c, 0xa1, 0xab,
0xfd, 0x1c, 0xa3, 0x34, 0x4e, 0xf5, 0x65, 0xc8, 0x4a, 0x04, 0x28, 0x8d, 0x71, 0x11, 0xcd, 0x48, 0x58, 0x5c, 0xc6, 0xb1, 0x18, 0xb6, 0xa5, 0x6b, 0xee, 0xcf, 0xe1, 0x74, 0xc3, 0x42, 0xc3, 0xfc,
0xe3, 0x86, 0xc7, 0x10, 0xe3, 0x45, 0x92, 0x74, 0xeb, 0x06, 0x8f, 0x90, 0xc2, 0x4f, 0xfc, 0x33, 0xfb, 0xd0, 0xf1, 0xed, 0xa4, 0xb2, 0xb4, 0x3b, 0x7e, 0x7c, 0xa6, 0x62, 0x79, 0x96, 0x09, 0xbb,
0x31, 0x49, 0xbb, 0x1b, 0x8f, 0x2a, 0x0a, 0x4f, 0x90, 0xf3, 0x08, 0x9a, 0x61, 0x74, 0x14, 0x5c, 0x9f, 0x40, 0xff, 0x8a, 0xce, 0x23, 0x2f, 0x7c, 0x90, 0x58, 0x69, 0x9e, 0x92, 0xd4, 0xb1, 0x72,
0x44, 0xd2, 0x8b, 0x22, 0xd9, 0xdd, 0xd4, 0x06, 0xb3, 0x51, 0xee, 0x2b, 0xd8, 0x5d, 0xba, 0x29, 0x1f, 0xc3, 0xc0, 0xee, 0x34, 0x74, 0xfd, 0xa5, 0x0a, 0x87, 0x9f, 0x05, 0xc1, 0x9e, 0x48, 0x61,
0x47, 0xc1, 0x1e, 0x34, 0x06, 0x19, 0x52, 0xdf, 0xb8, 0xb9, 0xbf, 0xbd, 0xa7, 0xe3, 0x6a, 0x2f, 0x7c, 0x05, 0x61, 0x98, 0x00, 0x88, 0x52, 0x55, 0xa1, 0x79, 0x0a, 0xf5, 0x94, 0xa3, 0x7d, 0x35,
0x27, 0xce, 0x49, 0x90, 0x55, 0xfb, 0x38, 0x18, 0x85, 0xfe, 0xe4, 0xab, 0xfb, 0x53, 0xdd, 0x47, 0x65, 0x5f, 0xd7, 0xd8, 0x77, 0x8d, 0x53, 0x4e, 0x0f, 0xea, 0x1e, 0x9b, 0x73, 0x64, 0xab, 0xa6,
0x1f, 0xe1, 0xe8, 0x61, 0xc8, 0xdd, 0x86, 0x4e, 0xc6, 0x8a, 0x5d, 0xf2, 0xb7, 0x0a, 0xec, 0x7c, 0x6d, 0x21, 0xd1, 0x0a, 0xa9, 0x32, 0x03, 0xff, 0x36, 0x30, 0x3c, 0x19, 0x2b, 0x5b, 0x45, 0x8e,
0x3c, 0x1c, 0xde, 0x10, 0x31, 0x18, 0x76, 0x52, 0x24, 0x18, 0x98, 0xc8, 0xb1, 0xac, 0x2f, 0x6b, 0xdb, 0x25, 0x8e, 0x3b, 0x25, 0x8e, 0x41, 0x8d, 0x8f, 0xa0, 0xe7, 0x7b, 0x89, 0x37, 0xa3, 0x21,
0x60, 0xe7, 0x3d, 0xa8, 0xce, 0x52, 0xbc, 0x49, 0x45, 0xdf, 0xa4, 0xc9, 0x37, 0x39, 0x45, 0x94, 0x15, 0x94, 0xf0, 0x61, 0x57, 0xc1, 0x9f, 0xc2, 0x81, 0x97, 0x24, 0x1e, 0x5b, 0xc6, 0x0c, 0x9d,
0xa7, 0x37, 0x1c, 0x07, 0xaa, 0x7e, 0x32, 0x4a, 0xd1, 0x13, 0xca, 0x84, 0x7a, 0xad, 0x54, 0x16, 0x79, 0x4b, 0x43, 0x32, 0xec, 0x59, 0x71, 0x4e, 0x42, 0x1a, 0xa5, 0x77, 0x17, 0x32, 0x32, 0xc3,
0xe1, 0x05, 0x7a, 0x41, 0xa1, 0xd4, 0x52, 0x61, 0x06, 0x97, 0x43, 0xb6, 0xbf, 0x5a, 0x66, 0xd7, 0xbe, 0x9a, 0x45, 0xf1, 0x28, 0x7e, 0x4d, 0x6e, 0x27, 0x8c, 0xae, 0x50, 0x76, 0x8e, 0x38, 0x03,
0xda, 0xc8, 0xaf, 0x65, 0x9c, 0xba, 0xb9, 0xda, 0xa9, 0x8d, 0x35, 0x4e, 0x85, 0x82, 0x53, 0x5d, 0xe5, 0xdc, 0x33, 0x68, 0xb1, 0x90, 0x2e, 0xa9, 0xe0, 0xc3, 0x03, 0x04, 0xee, 0x8e, 0xfb, 0xc6,
0x68, 0x0d, 0xfc, 0xd8, 0x3f, 0x0b, 0x26, 0x81, 0x0c, 0x44, 0xda, 0x6d, 0x6a, 0x25, 0x0a, 0x38, 0xbf, 0x4b, 0x35, 0xeb, 0x8e, 0xa1, 0xa9, 0xff, 0x49, 0x5f, 0xe5, 0x8a, 0xa1, 0x09, 0x47, 0x3c,
0xe7, 0x31, 0x6c, 0xf9, 0x71, 0xec, 0x27, 0xd3, 0x28, 0x41, 0xd3, 0xbc, 0x0d, 0x26, 0xa2, 0xdb, 0x7e, 0x2b, 0x14, 0x45, 0x75, 0x39, 0x5a, 0x78, 0x2c, 0x50, 0x14, 0xd5, 0x31, 0x60, 0x75, 0xc5,
0xd2, 0x4c, 0x16, 0xd1, 0x8a, 0x5b, 0x2a, 0x26, 0x41, 0x38, 0x9b, 0xbf, 0x56, 0xb1, 0xd1, 0x6d, 0x0e, 0x7a, 0x9d, 0x1a, 0x5e, 0xfb, 0x72, 0x30, 0x37, 0x81, 0xea, 0x3b, 0x27, 0x30, 0xf0, 0x82,
0x6b, 0xb2, 0x02, 0x4e, 0x71, 0x0b, 0xa3, 0xcf, 0xc4, 0xe5, 0x51, 0x12, 0x5c, 0xe0, 0x99, 0x11, 0x00, 0xfd, 0x89, 0x91, 0xe6, 0x2f, 0x69, 0xc0, 0x71, 0x67, 0x0d, 0x03, 0x76, 0x04, 0x4e, 0x3e,
0x0a, 0xed, 0x68, 0x2b, 0x2e, 0xa2, 0x9d, 0xf7, 0x61, 0x23, 0x99, 0x04, 0xd3, 0x40, 0xa6, 0xdd, 0x3a, 0x26, 0x68, 0x17, 0x59, 0x02, 0x65, 0xe9, 0xba, 0x2d, 0x72, 0xdf, 0x29, 0xe4, 0x73, 0x55,
0x2d, 0x54, 0xab, 0xb9, 0xdf, 0x66, 0x7b, 0x7a, 0x1a, 0xeb, 0x65, 0xbb, 0xee, 0x0b, 0xa8, 0x13, 0x45, 0xeb, 0xd0, 0x66, 0x53, 0xb6, 0xe0, 0x8e, 0x60, 0xb8, 0x89, 0x66, 0x34, 0xbd, 0x84, 0xd3,
0x4a, 0x99, 0x57, 0x91, 0xb0, 0xb7, 0xf4, 0x5a, 0xe1, 0xd2, 0xe8, 0xad, 0xd4, 0xbe, 0xaa, 0x7a, 0xcf, 0x49, 0x48, 0x1e, 0xd2, 0x84, 0xee, 0x46, 0xde, 0x92, 0xe8, 0xac, 0x93, 0x80, 0x9b, 0x9b,
0x7a, 0xad, 0x70, 0x63, 0x3f, 0x19, 0x6a, 0x3f, 0x21, 0x4e, 0xad, 0x5d, 0x0f, 0xaa, 0xca, 0x51, 0x0c, 0xe0, 0xfb, 0x70, 0x7c, 0x41, 0xb9, 0xd8, 0x0b, 0x87, 0xbd, 0x09, 0xd6, 0x02, 0x19, 0x78,
0xca, 0xd4, 0x33, 0x76, 0x78, 0xdb, 0x53, 0x4b, 0x85, 0x19, 0x71, 0x4c, 0x21, 0x06, 0x97, 0xce, 0xa6, 0x8a, 0xdc, 0x51, 0x61, 0x52, 0x11, 0x49, 0x14, 0x7e, 0x62, 0x5a, 0x06, 0x16, 0x5b, 0x1a,
0xb7, 0xa1, 0xe3, 0x0f, 0x87, 0x68, 0x9e, 0x08, 0xbd, 0xfe, 0x49, 0x30, 0x4c, 0x91, 0x53, 0x05, 0xd1, 0xbb, 0xab, 0xd8, 0xbf, 0x21, 0x82, 0xab, 0x8a, 0x55, 0x7d, 0x84, 0x2f, 0x48, 0x18, 0xaa,
0x37, 0x17, 0xb0, 0xee, 0x1d, 0x70, 0xec, 0x80, 0xe2, 0x38, 0xfb, 0x8d, 0xc9, 0x07, 0x93, 0xa3, 0x82, 0x6d, 0xbb, 0xbf, 0x80, 0x93, 0xb2, 0x7e, 0x53, 0x7a, 0xdf, 0x85, 0xee, 0x9a, 0x2d, 0x8e,
0xeb, 0x82, 0xed, 0x87, 0x85, 0xd4, 0x2e, 0xeb, 0xb0, 0xda, 0xc9, 0x12, 0x24, 0x3f, 0x6d, 0x11, 0xda, 0x6a, 0xbb, 0xe8, 0xea, 0x5d, 0x09, 0x64, 0x6b, 0x9b, 0xe1, 0xcf, 0x61, 0x90, 0x95, 0xa9,
0xb9, 0x3d, 0xe8, 0x2e, 0x73, 0x67, 0xc9, 0x3f, 0x85, 0xdd, 0x17, 0x62, 0x22, 0xbe, 0x8a, 0x64, 0x12, 0xd2, 0xc9, 0xeb, 0x89, 0x94, 0x1b, 0x89, 0x3f, 0x57, 0xa1, 0x65, 0xc2, 0x69, 0x8b, 0xe0,
0x34, 0x51, 0xe8, 0x4f, 0x05, 0x67, 0x92, 0x5e, 0x2b, 0xd6, 0xcb, 0xc7, 0x99, 0xf5, 0xfb, 0x70, 0x7f, 0x58, 0x66, 0x87, 0xd0, 0xe1, 0xf7, 0x5c, 0x90, 0xe5, 0xc4, 0x14, 0x5b, 0xff, 0xff, 0xab,
0xf7, 0x75, 0x90, 0xca, 0x1b, 0x19, 0xbb, 0xbf, 0x03, 0xc8, 0x89, 0x8c, 0x98, 0x52, 0x2e, 0x46, 0xd8, 0xfe, 0x58, 0x81, 0x4e, 0x46, 0xe8, 0x83, 0xfd, 0xfb, 0xdb, 0xd0, 0x49, 0x34, 0xb5, 0x44,
0xe1, 0xc4, 0x3c, 0x90, 0x9c, 0x5d, 0x7a, 0xad, 0x7c, 0x20, 0x07, 0x31, 0x97, 0x63, 0xb5, 0x54, 0xd7, 0x4f, 0x77, 0x3c, 0x30, 0x78, 0x96, 0xf2, 0x75, 0x38, 0xea, 0xa5, 0x7e, 0xad, 0xd9, 0x43,
0x75, 0x67, 0x16, 0x06, 0xf3, 0xe3, 0x68, 0x70, 0x2e, 0x64, 0xaa, 0x6b, 0x1b, 0xd6, 0x1d, 0x0b, 0x62, 0x13, 0x59, 0x7d, 0x4d, 0x59, 0x7d, 0xce, 0x01, 0x9a, 0x97, 0x46, 0x82, 0x62, 0xf2, 0xa9,
0xa5, 0x53, 0x64, 0x2c, 0x26, 0x13, 0x5d, 0xe0, 0x36, 0x3d, 0x02, 0xdc, 0x43, 0xb8, 0xb7, 0xa8, 0x4e, 0xe5, 0x7e, 0x08, 0xad, 0x57, 0x9e, 0xbf, 0x40, 0x6b, 0xa4, 0xa4, 0x9f, 0x98, 0xb0, 0xaa,
0x28, 0x17, 0xa3, 0xa7, 0xd0, 0xcc, 0xed, 0x98, 0xa2, 0x4a, 0x95, 0xd5, 0xd6, 0xb6, 0xa9, 0xdc, 0xe3, 0x69, 0x49, 0x90, 0x8d, 0x7b, 0x5d, 0xff, 0xee, 0x37, 0xd8, 0xa2, 0x75, 0x92, 0x98, 0xec,
0x87, 0xd0, 0x3a, 0x96, 0x68, 0xed, 0x75, 0xd7, 0x7d, 0x0c, 0x1d, 0x53, 0xc9, 0x34, 0x21, 0xe5, 0xfa, 0x00, 0x6b, 0xd1, 0x3a, 0x62, 0x93, 0x6b, 0xa3, 0xb3, 0x3b, 0xef, 0x41, 0x6b, 0xa9, 0xf1,
0xa2, 0x2f, 0x67, 0x29, 0x53, 0x31, 0xe4, 0xfe, 0xbd, 0x02, 0x1b, 0x1c, 0x2a, 0x59, 0xbe, 0x97, 0x4d, 0xb9, 0x5a, 0xfb, 0x8d, 0x56, 0xf7, 0x06, 0x4e, 0xf4, 0xb1, 0xb7, 0xf7, 0x70, 0xdb, 0x38,
0xf2, 0x7c, 0xff, 0xbf, 0x94, 0x9d, 0x77, 0xa1, 0x91, 0x5e, 0xa5, 0x52, 0x4c, 0x8f, 0xb8, 0xf8, 0x03, 0xb4, 0xcb, 0xfa, 0x44, 0x7b, 0x01, 0x1d, 0x46, 0x78, 0x9c, 0x32, 0x24, 0x44, 0xb1, 0xd0,
0xb4, 0xbd, 0x1c, 0xf1, 0x75, 0x09, 0xca, 0x4b, 0xd0, 0x3f, 0x4a, 0xd0, 0x30, 0x6e, 0xfe, 0xaf, 0x1d, 0x1f, 0xdb, 0xdc, 0x52, 0xd0, 0x97, 0x66, 0xd5, 0xfd, 0x47, 0x05, 0x06, 0xc5, 0x29, 0x59,
0x1b, 0xf8, 0x07, 0xd0, 0x88, 0xc9, 0xf1, 0x82, 0x2a, 0x49, 0x73, 0xbf, 0xc3, 0x82, 0xb2, 0xda, 0x62, 0xb3, 0xf0, 0x86, 0xc6, 0xbf, 0xd5, 0x67, 0xb1, 0x76, 0x1e, 0xb3, 0x0c, 0xa9, 0xb8, 0xc2,
0x91, 0x13, 0x58, 0xf1, 0x53, 0xb5, 0xe3, 0xc7, 0x6a, 0xd0, 0xb5, 0x42, 0x83, 0x46, 0xe7, 0xc7, 0x86, 0x87, 0x88, 0xd5, 0xdc, 0xd4, 0x84, 0x30, 0x1a, 0x07, 0xeb, 0x7b, 0x02, 0x4e, 0x7d, 0x9d,
0xaa, 0x44, 0xd5, 0x75, 0x89, 0xd2, 0x6b, 0xa7, 0x8b, 0x17, 0x9b, 0x85, 0x32, 0xc0, 0xcc, 0xa3, 0xc6, 0xc2, 0x33, 0x67, 0xba, 0x3c, 0x6f, 0x91, 0x42, 0x22, 0xce, 0x25, 0x91, 0x8d, 0xec, 0x0c,
0x9e, 0x92, 0x81, 0xee, 0x47, 0xb0, 0x71, 0xe8, 0x0f, 0xc6, 0x78, 0x0f, 0x75, 0x70, 0x10, 0x73, 0x56, 0x73, 0xaf, 0xc8, 0x92, 0x9b, 0x2c, 0x46, 0xa5, 0x9a, 0xdc, 0x0b, 0x99, 0x14, 0x26, 0x8f,
0x98, 0xe2, 0x41, 0xb5, 0x56, 0x42, 0xa6, 0x02, 0xed, 0x7d, 0xc5, 0xf5, 0x94, 0x21, 0xf7, 0x1c, 0x51, 0x50, 0x4f, 0x5e, 0xdd, 0x7a, 0x89, 0x4a, 0xe6, 0x3e, 0x56, 0xcc, 0xa1, 0x9e, 0x43, 0x7b,
0x1b, 0x33, 0xa5, 0x01, 0x27, 0xd3, 0x13, 0xac, 0x5c, 0x99, 0x41, 0xb2, 0x5c, 0x5a, 0x6e, 0xed, 0xf1, 0x62, 0xe3, 0xc9, 0x76, 0xaa, 0xf2, 0x5a, 0x2d, 0xdd, 0x10, 0x16, 0x91, 0xf0, 0x55, 0x0e,
0x16, 0x0d, 0xba, 0x65, 0x63, 0x4a, 0x92, 0xb9, 0xd0, 0x65, 0x36, 0x60, 0x7d, 0xbc, 0x6c, 0xdb, 0x09, 0xd4, 0xa1, 0x88, 0x97, 0xa4, 0x0d, 0x4e, 0x4d, 0xb7, 0x72, 0xa1, 0xff, 0xc5, 0x8a, 0x60,
0xfd, 0x03, 0xce, 0x4e, 0x34, 0x55, 0xdd, 0x38, 0x3b, 0xad, 0x9e, 0x07, 0xc8, 0x7c, 0x95, 0x82, 0x3b, 0xb0, 0x2c, 0xa3, 0x5f, 0x32, 0x1d, 0x90, 0xd0, 0x65, 0xa2, 0xbc, 0xaf, 0xbb, 0x5f, 0x43,
0xf9, 0x9e, 0x42, 0x23, 0x11, 0x69, 0x34, 0x4b, 0xd0, 0xcc, 0xda, 0xb2, 0xcd, 0xfd, 0xbb, 0x59, 0x43, 0xc9, 0x94, 0xce, 0x03, 0x1d, 0x8f, 0x6d, 0x21, 0xe8, 0xdb, 0xf8, 0xd4, 0x6d, 0x8d, 0xae,
0x26, 0x69, 0x59, 0x1e, 0xef, 0x7a, 0x39, 0x9d, 0xfb, 0x65, 0x19, 0x3a, 0xc5, 0x5d, 0x55, 0x97, 0x21, 0x1b, 0x0a, 0xf2, 0x6f, 0x15, 0xe8, 0xbd, 0x26, 0xe2, 0x36, 0x66, 0x37, 0x32, 0x8b, 0x78,
0xce, 0x26, 0xe7, 0x41, 0xf4, 0x39, 0x8d, 0x83, 0x64, 0x3c, 0x1b, 0xa5, 0xb2, 0x0a, 0x6d, 0x79, 0xa9, 0x05, 0xca, 0x1b, 0xd7, 0xdd, 0x74, 0x76, 0x2f, 0x0c, 0xdd, 0x75, 0x49, 0x06, 0xce, 0x4c,
0x8c, 0x5d, 0x07, 0x25, 0x51, 0x57, 0xc9, 0x11, 0xbc, 0x7b, 0x24, 0x92, 0x20, 0x1a, 0xf2, 0xc8, 0x3c, 0xdd, 0xf8, 0xd4, 0xa1, 0x23, 0x71, 0x2f, 0xef, 0xa6, 0x58, 0xc9, 0x31, 0xd3, 0x71, 0x56,
0x92, 0x23, 0x54, 0x19, 0x40, 0xe0, 0xcd, 0x2c, 0x92, 0x3e, 0x0f, 0xa0, 0x06, 0xd6, 0x73, 0x20, 0x62, 0x38, 0x15, 0xb0, 0x38, 0x49, 0x48, 0xa0, 0x75, 0x49, 0xb0, 0x37, 0x16, 0xac, 0x69, 0xa5,
0xfa, 0x48, 0xc8, 0x03, 0xe5, 0xb5, 0x1a, 0xcf, 0x81, 0x06, 0x93, 0xef, 0x1f, 0x8a, 0x69, 0xca, 0x70, 0x26, 0x31, 0x60, 0x2d, 0x0b, 0xf6, 0x26, 0x03, 0x6b, 0xe7, 0xc4, 0x2c, 0x58, 0x47, 0x19,
0x69, 0x6e, 0x61, 0x94, 0xe6, 0xe4, 0xcd, 0xd7, 0x2a, 0xa8, 0x39, 0xdf, 0x6d, 0x94, 0xe2, 0x40, 0xbe, 0x84, 0x36, 0xc6, 0xf2, 0x9a, 0x7b, 0x73, 0x95, 0x2a, 0x02, 0x63, 0x1d, 0x4e, 0x53, 0x39,
0xe0, 0xf1, 0xa5, 0x1f, 0xeb, 0xb4, 0x6f, 0x7b, 0x16, 0x06, 0x03, 0x79, 0x87, 0x20, 0xb4, 0x06, 0xd4, 0x64, 0xc9, 0xfe, 0x90, 0x10, 0x86, 0x11, 0x36, 0xb3, 0x55, 0x2c, 0x84, 0xba, 0xf3, 0x0e,
0x4e, 0xfd, 0xbe, 0x6a, 0x85, 0xba, 0x0c, 0xb4, 0xbd, 0xe5, 0x0d, 0x45, 0x7d, 0x2e, 0x92, 0x50, 0x3c, 0x51, 0xc3, 0x29, 0x8d, 0xa6, 0x3a, 0x4a, 0xcb, 0x38, 0x20, 0xc6, 0x0f, 0x8c, 0x5c, 0xb6,
0x4c, 0x0e, 0x2d, 0xa9, 0x40, 0xd4, 0x4b, 0x1b, 0xea, 0x3b, 0x63, 0xc9, 0xe7, 0xdc, 0x7b, 0xbe, 0x28, 0xfb, 0xa1, 0x5a, 0x52, 0xfe, 0xb8, 0x6f, 0x60, 0xf0, 0x66, 0x81, 0xf7, 0x5d, 0x81, 0x1d,
0x0f, 0xed, 0x97, 0x17, 0x02, 0xab, 0x71, 0x16, 0x05, 0x68, 0x43, 0x15, 0xcc, 0xe8, 0xd9, 0x69, 0x67, 0xfe, 0xb9, 0x27, 0x3c, 0x59, 0xb1, 0x89, 0x4a, 0x3a, 0x6e, 0x14, 0xe2, 0x6e, 0xa1, 0x45,
0xac, 0x3d, 0x50, 0xf5, 0x72, 0x84, 0x9b, 0x42, 0x4d, 0x93, 0xaf, 0x1c, 0x17, 0x28, 0x80, 0xca, 0x48, 0x30, 0xb5, 0x4b, 0x9a, 0x34, 0x3c, 0x73, 0xd7, 0x4b, 0xaa, 0xc8, 0xf5, 0x69, 0x2d, 0x94,
0x26, 0x80, 0x8a, 0xe1, 0xd2, 0x36, 0xe1, 0xc2, 0x81, 0x55, 0xcd, 0x03, 0xab, 0x20, 0xb4, 0xb6, 0x13, 0x9a, 0x78, 0x57, 0xe5, 0x71, 0xce, 0x85, 0xee, 0xf8, 0xc0, 0x56, 0xad, 0x75, 0xf4, 0x0c,
0x28, 0xf4, 0x8f, 0x65, 0x68, 0x7d, 0x26, 0xe4, 0x65, 0x94, 0x9c, 0xab, 0x44, 0x49, 0x57, 0x76, 0x0e, 0x44, 0x66, 0xc5, 0x14, 0x13, 0xc9, 0x33, 0xc5, 0x6b, 0xcb, 0xaa, 0x64, 0xa3, 0xec, 0x91,
0xbe, 0xfb, 0xf8, 0x49, 0x33, 0xef, 0x9f, 0x5d, 0x49, 0x0e, 0x8c, 0x2a, 0xe6, 0xe5, 0xfc, 0xb9, 0xaa, 0x29, 0x1b, 0x58, 0xad, 0xf5, 0xfb, 0xd0, 0xc1, 0x26, 0xcd, 0xb5, 0x5a, 0x74, 0xc3, 0x4f,
0x02, 0x9d, 0x07, 0x00, 0xb8, 0x75, 0xe4, 0x53, 0xb7, 0xa3, 0xc1, 0xa5, 0x91, 0xcc, 0x19, 0xe1, 0x19, 0xc3, 0xac, 0x32, 0x6e, 0x60, 0xd7, 0x56, 0x1d, 0xd1, 0xb4, 0x97, 0xd7, 0x00, 0x3a, 0x8f,
0xbc, 0x03, 0x0d, 0x6f, 0xde, 0xc7, 0x7a, 0x1a, 0x25, 0x14, 0xbd, 0x55, 0xfc, 0x1a, 0x9a, 0xbf, 0x15, 0x20, 0x2e, 0xe6, 0x39, 0xc6, 0x58, 0x2d, 0xbd, 0xbb, 0x8c, 0x60, 0x39, 0x85, 0x78, 0x6f,
0xd4, 0xb0, 0x3a, 0x8b, 0x9b, 0xc3, 0x24, 0x8a, 0x63, 0x31, 0xcc, 0x54, 0x4b, 0xe6, 0x2f, 0x08, 0x3d, 0x1a, 0xfa, 0xe6, 0x5a, 0x9b, 0xc3, 0xd3, 0x44, 0xfe, 0xb3, 0x02, 0x5d, 0x0d, 0xa8, 0xf5,
0xa1, 0xa4, 0x9e, 0x64, 0x52, 0xeb, 0x24, 0x55, 0xe6, 0x52, 0x71, 0x2b, 0x66, 0xa9, 0x1b, 0x7c, 0xe3, 0xb2, 0x8f, 0x1d, 0xc7, 0x22, 0x3e, 0xb7, 0x0a, 0x8a, 0x77, 0x88, 0x9c, 0x09, 0x78, 0xd5,
0x29, 0x5b, 0xea, 0x89, 0x91, 0xba, 0x49, 0x52, 0xa5, 0x25, 0xf5, 0x24, 0x97, 0xda, 0xc8, 0xce, 0xe0, 0x58, 0x87, 0x39, 0x8f, 0xb6, 0x8a, 0x7d, 0x08, 0x3d, 0x1d, 0x5f, 0x23, 0x58, 0xdf, 0x25,
0xb2, 0x54, 0xf7, 0xaf, 0x25, 0xd8, 0xc4, 0xb0, 0x3c, 0x4d, 0xfd, 0x91, 0xc0, 0x0e, 0xd6, 0x94, 0xf8, 0x91, 0x3c, 0xa5, 0xd0, 0x12, 0xd5, 0x96, 0xbb, 0xe3, 0x77, 0x0b, 0x12, 0xca, 0xc6, 0x33,
0x18, 0xc2, 0x93, 0xfe, 0x4c, 0x81, 0xec, 0x32, 0xd0, 0x28, 0x22, 0xf8, 0x06, 0xb4, 0x62, 0x91, 0xf5, 0xfb, 0x45, 0x24, 0xd8, 0xfd, 0xe8, 0x23, 0x80, 0xf5, 0x48, 0x56, 0xd7, 0x0d, 0xb9, 0x37,
0x60, 0xb0, 0x32, 0x45, 0x19, 0x0b, 0x4a, 0xd5, 0x6b, 0x12, 0x8e, 0x48, 0xf6, 0xe0, 0xb6, 0xde, 0xb5, 0x82, 0x9e, 0xac, 0xbc, 0x30, 0x35, 0x44, 0x7c, 0x5a, 0xfd, 0xa4, 0xe2, 0xfe, 0x1a, 0x0e,
0xeb, 0x07, 0x61, 0x9f, 0xc2, 0x67, 0x1a, 0x0d, 0x05, 0x9b, 0x6a, 0x47, 0x6f, 0xbd, 0x0a, 0x3f, 0x7e, 0x29, 0x7b, 0x58, 0x6e, 0x4b, 0xe1, 0xc1, 0x53, 0x2f, 0x3e, 0x78, 0xea, 0xb2, 0x94, 0xe3,
0x35, 0x1b, 0xce, 0x77, 0x61, 0xc7, 0xd0, 0xab, 0x2e, 0xa9, 0xa9, 0xc9, 0x74, 0x5b, 0x4c, 0x7d, 0x64, 0xfd, 0x1e, 0xd0, 0x78, 0x9a, 0xb8, 0xbf, 0xd7, 0x00, 0xd6, 0x60, 0xce, 0xa7, 0x30, 0xa2,
0xca, 0x68, 0x1c, 0x5a, 0x3a, 0x27, 0x63, 0xfc, 0xea, 0x95, 0xd8, 0x46, 0x46, 0x2f, 0x7c, 0x4c, 0xf1, 0x54, 0xf6, 0x1e, 0xea, 0x13, 0x5d, 0x54, 0x53, 0x46, 0x30, 0x94, 0x9c, 0xae, 0x88, 0xe9,
0x36, 0xac, 0xa0, 0xb1, 0x4e, 0xc9, 0x94, 0xb5, 0xcd, 0x40, 0xe7, 0x7b, 0xb0, 0x23, 0x89, 0x56, 0xfa, 0x27, 0xc6, 0x97, 0xb2, 0x0d, 0x3f, 0x82, 0xe3, 0xf5, 0xde, 0x20, 0xb7, 0xad, 0xba, 0x77,
0x0c, 0xfb, 0x19, 0x0d, 0x79, 0x73, 0xdb, 0x6c, 0x1c, 0x31, 0xf1, 0xb7, 0xa0, 0x93, 0x13, 0xeb, 0xdb, 0x4b, 0x78, 0x82, 0xdb, 0xb0, 0x3b, 0xa5, 0x85, 0x4d, 0xb5, 0xbd, 0x9b, 0x7e, 0x0a, 0x4f,
0x7a, 0x4c, 0xfa, 0xb6, 0x0d, 0xf6, 0x44, 0x55, 0xe5, 0x3f, 0x91, 0xb1, 0x28, 0x72, 0x3e, 0xd0, 0x73, 0x76, 0xca, 0xdc, 0xcf, 0x6d, 0xad, 0xef, 0xdd, 0xfa, 0x63, 0x38, 0xc1, 0xad, 0xb7, 0x1e,
0x15, 0xc2, 0x32, 0x55, 0x73, 0x7f, 0x2b, 0xab, 0xac, 0x6c, 0x0c, 0x5d, 0x15, 0xc8, 0x2c, 0x3f, 0x15, 0xe5, 0x7d, 0x8d, 0xff, 0xc0, 0xce, 0x25, 0x61, 0xf3, 0x82, 0x9d, 0xcd, 0xbd, 0x9b, 0x7e,
0x83, 0x2d, 0x69, 0x54, 0xef, 0x63, 0x02, 0xf9, 0x5c, 0x5e, 0xb3, 0xea, 0x56, 0xbc, 0x98, 0xd7, 0x00, 0x87, 0xb8, 0xa9, 0xa4, 0xa7, 0xf5, 0xd0, 0x16, 0x4e, 0x7c, 0x81, 0x7d, 0x2a, 0xb7, 0xa5,
0x91, 0xc5, 0x8b, 0xa2, 0xe5, 0xa9, 0xe5, 0xb3, 0x40, 0xd2, 0xaf, 0x49, 0x38, 0x2d, 0xc2, 0xfd, 0xbd, 0x6f, 0x8b, 0x3b, 0x81, 0xde, 0x57, 0xe9, 0x9c, 0x88, 0x70, 0x96, 0x65, 0xff, 0x7f, 0x59,
0x09, 0x34, 0x70, 0x1e, 0x48, 0x49, 0x3b, 0x34, 0xcc, 0x60, 0x96, 0x24, 0x98, 0x5f, 0x99, 0x61, 0x4f, 0x7f, 0xad, 0x42, 0xf7, 0x7c, 0xce, 0xe2, 0x34, 0x29, 0xb4, 0x11, 0x9d, 0xd2, 0x1b, 0x6d,
0x18, 0x54, 0xf3, 0x82, 0x6e, 0x97, 0x6c, 0x0c, 0x02, 0xdc, 0x08, 0x80, 0xd2, 0x5c, 0x4b, 0x43, 0x44, 0xcb, 0xbc, 0x80, 0x9e, 0x3e, 0xbc, 0x8c, 0x98, 0xae, 0x35, 0x67, 0x33, 0xf3, 0xe5, 0x4d,
0x1a, 0x3b, 0x04, 0x08, 0x50, 0x71, 0x36, 0xf5, 0xe7, 0xc6, 0xf5, 0x3a, 0xce, 0x10, 0x41, 0x17, 0x55, 0x1d, 0xc2, 0x46, 0xb0, 0x58, 0x6d, 0xb9, 0x6c, 0xfc, 0x19, 0xf4, 0x17, 0xda, 0x2f, 0x23,
0x44, 0x81, 0x6f, 0xfd, 0x60, 0x32, 0xe0, 0x6f, 0x5f, 0x14, 0xc8, 0x60, 0x2e, 0xb0, 0x6a, 0x0b, 0xa9, 0x23, 0xfb, 0x81, 0xd5, 0xbc, 0x36, 0xf0, 0x2c, 0xef, 0xbf, 0xe6, 0x11, 0x2f, 0x2c, 0xf2,
0xfc, 0x4b, 0x19, 0x9a, 0x24, 0x91, 0x14, 0x46, 0xaa, 0x01, 0x36, 0x16, 0x23, 0x52, 0x03, 0xd8, 0x26, 0x34, 0xb5, 0x65, 0x98, 0x7f, 0x8a, 0x66, 0x8d, 0x6a, 0xf4, 0x15, 0x1c, 0x6e, 0x6e, 0x2d,
0xfa, 0x6b, 0xb9, 0xb8, 0x7c, 0x0c, 0xcc, 0x55, 0xcd, 0x74, 0xc3, 0x46, 0x97, 0x62, 0xed, 0xb3, 0x14, 0xa0, 0x9b, 0x2f, 0xc0, 0xee, 0xf8, 0x89, 0x81, 0xc8, 0xef, 0x52, 0x55, 0x79, 0xa7, 0x6f,
0xac, 0xb3, 0x92, 0xba, 0xa1, 0x88, 0x48, 0xe1, 0x0f, 0xa1, 0x45, 0xf1, 0xc9, 0x67, 0xaa, 0xeb, 0x4c, 0xd9, 0x23, 0xc7, 0xf9, 0x1e, 0xf4, 0x23, 0x7d, 0x06, 0x66, 0xbc, 0xd5, 0x72, 0x00, 0x85,
0xce, 0x34, 0x89, 0x8c, 0x4e, 0x3d, 0x55, 0xd3, 0x16, 0xea, 0xab, 0xbb, 0x7b, 0x73, 0xff, 0x41, 0xf3, 0x11, 0xb9, 0xf3, 0x95, 0x37, 0x5b, 0xb9, 0xcb, 0x47, 0xa2, 0x70, 0xda, 0xea, 0xce, 0x6b,
0x81, 0x5c, 0xdf, 0x64, 0x4f, 0xff, 0xbe, 0x0c, 0x25, 0x96, 0x59, 0xa2, 0xed, 0x3d, 0x03, 0xc8, 0x2e, 0xf4, 0xdb, 0x1e, 0xbf, 0xe3, 0x7f, 0x35, 0xa1, 0xf6, 0xd9, 0xe4, 0x57, 0xce, 0x35, 0x3c,
0x91, 0xaa, 0x66, 0x9d, 0x8b, 0xab, 0x6c, 0xaa, 0xc4, 0xa5, 0xba, 0xfb, 0x85, 0x3f, 0x99, 0x65, 0x2e, 0x7f, 0x2d, 0x71, 0x9e, 0x19, 0xf8, 0x1d, 0x5f, 0x58, 0x46, 0xef, 0xed, 0x5c, 0x37, 0xb7,
0x46, 0x25, 0xe0, 0xc7, 0xe5, 0x67, 0x25, 0x77, 0x00, 0x5b, 0xcf, 0x55, 0xcf, 0xb2, 0x8e, 0x17, 0x8b, 0x47, 0xce, 0x25, 0x1c, 0x94, 0xbe, 0x04, 0x38, 0xb6, 0xd5, 0x6d, 0xff, 0x86, 0x31, 0x7a,
0x9e, 0x6c, 0xaa, 0x2b, 0x9f, 0x6c, 0xaa, 0xd9, 0x93, 0x0d, 0x96, 0xd1, 0x28, 0xe6, 0x0e, 0x8b, 0xb6, 0x6b, 0x39, 0x8f, 0x59, 0xba, 0xce, 0x64, 0x98, 0xdb, 0xaf, 0x8e, 0x19, 0xe6, 0xae, 0x5b,
0xab, 0x5c, 0x50, 0xd5, 0x12, 0xe4, 0xfe, 0xb3, 0x0a, 0x90, 0x4b, 0x71, 0x8e, 0xa1, 0x17, 0x44, 0xd0, 0x23, 0xe7, 0x27, 0xd0, 0xd4, 0xdf, 0x0d, 0x9c, 0x23, 0x23, 0x5b, 0xf8, 0x00, 0x31, 0x3a,
0x7d, 0xd5, 0x20, 0x82, 0x81, 0xa0, 0x82, 0xd4, 0x4f, 0x04, 0x86, 0x4f, 0x1a, 0x5c, 0x08, 0x9e, 0x2e, 0xcd, 0x66, 0x1b, 0x2f, 0xa0, 0x5f, 0xf8, 0x4c, 0xe3, 0xbc, 0x53, 0xd0, 0x55, 0xfc, 0xec,
0x21, 0xee, 0xf1, 0xbd, 0x17, 0x94, 0xf3, 0x76, 0x11, 0xa2, 0x83, 0xba, 0x72, 0x79, 0xd9, 0x31, 0x30, 0xfa, 0xd6, 0xf6, 0xc5, 0x0c, 0xed, 0x1c, 0x60, 0xfd, 0x1a, 0x76, 0x86, 0x46, 0x7a, 0xe3,
0xe7, 0x97, 0x70, 0x37, 0x67, 0x3a, 0xb4, 0xf8, 0x95, 0xaf, 0xe5, 0x77, 0xdb, 0xf0, 0x1b, 0xe6, 0xf3, 0xc5, 0xe8, 0xe9, 0x96, 0x95, 0x0c, 0x04, 0x43, 0x59, 0x7e, 0xee, 0x3a, 0x25, 0x56, 0xcb,
0xbc, 0x7e, 0x0e, 0x88, 0xee, 0x63, 0x8f, 0x99, 0x15, 0x38, 0x55, 0xae, 0xe5, 0xb4, 0x13, 0x44, 0x8f, 0xd3, 0x2c, 0x94, 0x3b, 0xdf, 0xc9, 0x0a, 0xb6, 0xfc, 0xe8, 0xcd, 0x60, 0x77, 0x3c, 0xa1,
0x6f, 0xf4, 0x89, 0x9c, 0xcf, 0x1b, 0xb8, 0x6f, 0x5d, 0x54, 0xa5, 0xbd, 0xc5, 0xad, 0x7a, 0x2d, 0x33, 0xd8, 0x9d, 0xaf, 0xe5, 0x47, 0xce, 0x6f, 0x60, 0x50, 0x7c, 0xaf, 0x3a, 0x96, 0xa4, 0xad,
0xb7, 0x7b, 0x46, 0x2f, 0x55, 0x18, 0x72, 0x96, 0x9f, 0x02, 0xee, 0xf4, 0x2f, 0xfd, 0x40, 0x2e, 0xcf, 0xe8, 0xd1, 0xbb, 0x3b, 0x56, 0x33, 0xc0, 0x1f, 0x42, 0x43, 0xbf, 0x4c, 0x6d, 0x21, 0xe5,
0xf2, 0xab, 0xdd, 0x74, 0xcf, 0xcf, 0xf1, 0x50, 0x91, 0x19, 0xdd, 0x73, 0x2a, 0x92, 0x51, 0xe1, 0x1f, 0xb3, 0xa3, 0xa3, 0xe2, 0x64, 0xb6, 0xeb, 0x63, 0x68, 0xea, 0x8b, 0x70, 0x96, 0x00, 0x85,
0x9e, 0xf5, 0x9b, 0xee, 0x79, 0xa8, 0x4f, 0xe4, 0x7c, 0x9e, 0x03, 0x22, 0x17, 0xf5, 0xd9, 0xb8, 0x7b, 0xf1, 0xa8, 0x97, 0x9f, 0x75, 0x1f, 0x7d, 0x5c, 0xb1, 0x7a, 0x78, 0x41, 0x0f, 0xdf, 0xa6,
0x96, 0xcb, 0x56, 0x10, 0x15, 0x75, 0x39, 0x80, 0x9d, 0x54, 0x0c, 0x24, 0x76, 0x14, 0x8b, 0xc7, 0x27, 0x17, 0x9c, 0x59, 0x53, 0x7d, 0xbe, 0x7c, 0xf9, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0x65,
0xe6, 0xb5, 0x3c, 0xb6, 0xf9, 0x80, 0x61, 0xe2, 0x7e, 0x01, 0xad, 0x5f, 0xcc, 0x46, 0x42, 0x4e, 0x85, 0xda, 0xbd, 0xcb, 0x14, 0x00, 0x00,
0xce, 0x4c, 0xce, 0xff, 0xaf, 0xcb, 0xcc, 0xbf, 0xb1, 0xcc, 0x1c, 0x8c, 0x92, 0x68, 0x16, 0x17,
0xaa, 0x36, 0xe5, 0xf0, 0x52, 0xd5, 0xd6, 0x34, 0xba, 0x6a, 0x13, 0xf5, 0x47, 0xd0, 0xa2, 0x81,
0x89, 0x0f, 0x50, 0x15, 0x72, 0x96, 0x93, 0x3e, 0x1b, 0xd0, 0xe8, 0xd8, 0x3e, 0x0f, 0x9f, 0x7c,
0xaa, 0x58, 0x8d, 0x72, 0x33, 0xe1, 0xd7, 0x47, 0x9e, 0x75, 0xaf, 0xa0, 0x3d, 0x26, 0xdb, 0xf0,
0x29, 0x0a, 0xc0, 0x6f, 0x66, 0xca, 0xe5, 0x77, 0xd8, 0xb3, 0x6d, 0x48, 0xa6, 0x6e, 0x8d, 0x6d,
0xb3, 0xfe, 0x00, 0x40, 0x7d, 0x5e, 0xf4, 0xb3, 0x42, 0x65, 0xbf, 0xe7, 0x99, 0x0e, 0x81, 0xdf,
0x32, 0xd9, 0xb2, 0x77, 0x02, 0x3b, 0x4b, 0x3c, 0x57, 0x94, 0xa9, 0xef, 0xd8, 0x65, 0xaa, 0xb9,
0x7f, 0x9b, 0x59, 0xda, 0x47, 0xed, 0xda, 0xf5, 0xe7, 0x12, 0x7d, 0x8d, 0x98, 0x27, 0x17, 0xe7,
0x19, 0xb4, 0x43, 0x1a, 0xbe, 0x8c, 0x03, 0x2a, 0x16, 0x23, 0x7b, 0x30, 0xf3, 0x5a, 0xa1, 0x3d,
0xa6, 0xa1, 0x23, 0x06, 0xda, 0x02, 0x2b, 0x1d, 0x61, 0x19, 0xc7, 0x6b, 0x0e, 0x2c, 0x6f, 0x17,
0x86, 0xc1, 0xca, 0xe2, 0x30, 0xc8, 0x8f, 0x06, 0xeb, 0xde, 0x18, 0xf7, 0xff, 0x55, 0x87, 0xca,
0xc7, 0x47, 0xaf, 0x9c, 0x53, 0xd8, 0x5e, 0x7c, 0x40, 0x77, 0x1e, 0xb2, 0xe8, 0x35, 0x8f, 0xee,
0xbd, 0xf7, 0xd6, 0xee, 0xf3, 0xb4, 0x7c, 0xcb, 0xf1, 0x60, 0x6b, 0xe1, 0x41, 0xd6, 0xc9, 0xda,
0xc9, 0xea, 0x27, 0xe9, 0xde, 0xc3, 0x75, 0xdb, 0x36, 0xcf, 0x85, 0xf1, 0xdc, 0xf0, 0x5c, 0xfd,
0xa9, 0x66, 0x78, 0xae, 0x9b, 0xea, 0x6f, 0x39, 0x3f, 0x82, 0x3a, 0x3d, 0xd1, 0x3a, 0x77, 0x98,
0xb6, 0xf0, 0xf8, 0xdb, 0xbb, 0xbb, 0x80, 0x35, 0x07, 0x5f, 0x43, 0xbb, 0xf0, 0xea, 0xee, 0xbc,
0x53, 0x90, 0x55, 0x7c, 0xe1, 0xed, 0xbd, 0xbb, 0x7a, 0xd3, 0x70, 0x3b, 0x00, 0xc8, 0x5f, 0xf1,
0x9c, 0x2e, 0x53, 0x2f, 0xbd, 0x14, 0xf7, 0xee, 0xaf, 0xd8, 0x31, 0x4c, 0xd0, 0x95, 0x8b, 0xcf,
0x72, 0xce, 0x82, 0x55, 0x17, 0x9f, 0xce, 0x8c, 0x2b, 0xd7, 0xbe, 0xe7, 0x69, 0xb6, 0x8b, 0x4f,
0x72, 0x86, 0xed, 0x9a, 0xa7, 0x3e, 0xc3, 0x76, 0xed, 0x5b, 0xde, 0x2d, 0xe7, 0x57, 0xd0, 0x29,
0x3e, 0x92, 0x39, 0x99, 0x91, 0x56, 0x3e, 0xf2, 0xf5, 0x1e, 0xac, 0xd9, 0x35, 0x0c, 0x3f, 0x84,
0x1a, 0xbd, 0x7e, 0x65, 0x29, 0x67, 0x3f, 0x9a, 0xf5, 0xee, 0x14, 0x91, 0xe6, 0xd4, 0x13, 0xa8,
0xd3, 0x87, 0x9d, 0x09, 0x80, 0xc2, 0x77, 0x5e, 0xaf, 0x65, 0x63, 0xdd, 0x5b, 0x4f, 0x4a, 0x99,
0x9c, 0xb4, 0x20, 0x27, 0x5d, 0x25, 0xc7, 0x72, 0xce, 0x59, 0x5d, 0xff, 0xa3, 0xf5, 0xf4, 0x3f,
0x01, 0x00, 0x00, 0xff, 0xff, 0xf0, 0xa3, 0xf6, 0xb8, 0xde, 0x1a, 0x00, 0x00,
} }

View file

@ -13,7 +13,7 @@ clone git github.com/docker/docker 2f6e3b0ba027b558adabd41344fee59db4441011
clone git github.com/docker/go-units 5d2041e26a699eaca682e2ea41c8f891e1060444 clone git github.com/docker/go-units 5d2041e26a699eaca682e2ea41c8f891e1060444
clone git github.com/godbus/dbus e2cf28118e66a6a63db46cf6088a35d2054d3bb0 clone git github.com/godbus/dbus e2cf28118e66a6a63db46cf6088a35d2054d3bb0
clone git github.com/golang/glog 23def4e6c14b4da8ac2ed8007337bc5eb5007998 clone git github.com/golang/glog 23def4e6c14b4da8ac2ed8007337bc5eb5007998
clone git github.com/golang/protobuf 8d92cf5fc15a4382f8964b08e1f42a75c0591aa3 clone git github.com/golang/protobuf 3c84672111d91bb5ac31719e112f9f7126a0e26e
clone git github.com/opencontainers/runc d49ece5a83da3dcb820121d6850e2b61bd0a5fbe clone git github.com/opencontainers/runc d49ece5a83da3dcb820121d6850e2b61bd0a5fbe
clone git github.com/opencontainers/runtime-spec f955d90e70a98ddfb886bd930ffd076da9b67998 clone git github.com/opencontainers/runtime-spec f955d90e70a98ddfb886bd930ffd076da9b67998
clone git github.com/rcrowley/go-metrics eeba7bd0dd01ace6e690fa833b3f22aaec29af43 clone git github.com/rcrowley/go-metrics eeba7bd0dd01ace6e690fa833b3f22aaec29af43
@ -22,7 +22,7 @@ clone git github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd85
clone git github.com/vishvananda/netlink adb0f53af689dd38f1443eba79489feaacf0b22e clone git github.com/vishvananda/netlink adb0f53af689dd38f1443eba79489feaacf0b22e
clone git github.com/Azure/go-ansiterm 70b2c90b260171e829f1ebd7c17f600c11858dbe clone git github.com/Azure/go-ansiterm 70b2c90b260171e829f1ebd7c17f600c11858dbe
clone git golang.org/x/net 991d3e32f76f19ee6d9caadb3a22eae8d23315f7 https://github.com/golang/net.git clone git golang.org/x/net 991d3e32f76f19ee6d9caadb3a22eae8d23315f7 https://github.com/golang/net.git
clone git google.golang.org/grpc a22b6611561e9f0a3e0919690dd2caf48f14c517 https://github.com/grpc/grpc-go.git clone git google.golang.org/grpc ab0be5212fb225475f2087566eded7da5d727960 https://github.com/grpc/grpc-go.git
clone git github.com/seccomp/libseccomp-golang 1b506fc7c24eec5a3693cdcbed40d9c226cfc6a1 clone git github.com/seccomp/libseccomp-golang 1b506fc7c24eec5a3693cdcbed40d9c226cfc6a1
clone git github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3 clone git github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3

View file

@ -9,6 +9,7 @@ import (
"github.com/docker/containerd/api/grpc/types" "github.com/docker/containerd/api/grpc/types"
"github.com/docker/docker/pkg/integration/checker" "github.com/docker/docker/pkg/integration/checker"
"github.com/go-check/check" "github.com/go-check/check"
"google.golang.org/grpc"
) )
func (cs *ContainerdSuite) TestStartBusyboxLsSlash(t *check.C) { func (cs *ContainerdSuite) TestStartBusyboxLsSlash(t *check.C) {
@ -44,14 +45,14 @@ var
} }
func (cs *ContainerdSuite) TestStartBusyboxNoSuchFile(t *check.C) { func (cs *ContainerdSuite) TestStartBusyboxNoSuchFile(t *check.C) {
expectedOutput := `oci runtime error: exec: \"NoSuchFile\": executable file not found in $PATH` expectedOutput := `oci runtime error: exec: "NoSuchFile": executable file not found in $PATH`
if err := CreateBusyboxBundle("busybox-no-such-file", []string{"NoSuchFile"}); err != nil { if err := CreateBusyboxBundle("busybox-no-such-file", []string{"NoSuchFile"}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, err := cs.RunContainer("NoSuchFile", "busybox-no-such-file") _, err := cs.RunContainer("NoSuchFile", "busybox-no-such-file")
t.Assert(err.Error(), checker.Contains, expectedOutput) t.Assert(grpc.ErrorDesc(err), checker.Contains, expectedOutput)
} }
func (cs *ContainerdSuite) TestStartBusyboxTop(t *check.C) { func (cs *ContainerdSuite) TestStartBusyboxTop(t *check.C) {

View file

@ -39,5 +39,5 @@ test: install generate-test-pbs
generate-test-pbs: generate-test-pbs:
make install make install
make -C testdata make -C testdata
protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata,Mgoogle/protobuf/any.proto=github.com/golang/protobuf/ptypes/any:. proto3_proto/proto3.proto protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata:. proto3_proto/proto3.proto
make make

View file

@ -768,11 +768,10 @@ func (o *Buffer) dec_new_map(p *Properties, base structPointer) error {
} }
} }
keyelem, valelem := keyptr.Elem(), valptr.Elem() keyelem, valelem := keyptr.Elem(), valptr.Elem()
if !keyelem.IsValid() { if !keyelem.IsValid() || !valelem.IsValid() {
keyelem = reflect.Zero(p.mtype.Key()) // We did not decode the key or the value in the map entry.
} // Either way, it's an invalid map entry.
if !valelem.IsValid() { return fmt.Errorf("proto: bad map data: missing key/val")
valelem = reflect.Zero(p.mtype.Elem())
} }
v.SetMapIndex(keyelem, valelem) v.SetMapIndex(keyelem, valelem)

View file

@ -64,10 +64,6 @@ var (
// a struct with a repeated field containing a nil element. // a struct with a repeated field containing a nil element.
errRepeatedHasNil = errors.New("proto: repeated field has nil element") errRepeatedHasNil = errors.New("proto: repeated field has nil element")
// errOneofHasNil is the error returned if Marshal is called with
// a struct with a oneof field containing a nil element.
errOneofHasNil = errors.New("proto: oneof field has nil value")
// ErrNil is the error returned if Marshal is called with nil. // ErrNil is the error returned if Marshal is called with nil.
ErrNil = errors.New("proto: Marshal called with nil") ErrNil = errors.New("proto: Marshal called with nil")
) )
@ -1226,9 +1222,7 @@ func (o *Buffer) enc_struct(prop *StructProperties, base structPointer) error {
// Do oneof fields. // Do oneof fields.
if prop.oneofMarshaler != nil { if prop.oneofMarshaler != nil {
m := structPointer_Interface(base, prop.stype).(Message) m := structPointer_Interface(base, prop.stype).(Message)
if err := prop.oneofMarshaler(m, o); err == ErrNil { if err := prop.oneofMarshaler(m, o); err != nil {
return errOneofHasNil
} else if err != nil {
return err return err
} }
} }

View file

@ -175,93 +175,7 @@ type raw interface {
Bytes() []byte Bytes() []byte
} }
func requiresQuotes(u string) bool { func writeStruct(w *textWriter, sv reflect.Value) error {
// When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted.
for _, ch := range u {
switch {
case ch == '.' || ch == '/' || ch == '_':
continue
case '0' <= ch && ch <= '9':
continue
case 'A' <= ch && ch <= 'Z':
continue
case 'a' <= ch && ch <= 'z':
continue
default:
return true
}
}
return false
}
// isAny reports whether sv is a google.protobuf.Any message
func isAny(sv reflect.Value) bool {
type wkt interface {
XXX_WellKnownType() string
}
t, ok := sv.Addr().Interface().(wkt)
return ok && t.XXX_WellKnownType() == "Any"
}
// writeProto3Any writes an expanded google.protobuf.Any message.
//
// It returns (false, nil) if sv value can't be unmarshaled (e.g. because
// required messages are not linked in).
//
// It returns (true, error) when sv was written in expanded format or an error
// was encountered.
func (tm *TextMarshaler) writeProto3Any(w *textWriter, sv reflect.Value) (bool, error) {
turl := sv.FieldByName("TypeUrl")
val := sv.FieldByName("Value")
if !turl.IsValid() || !val.IsValid() {
return true, errors.New("proto: invalid google.protobuf.Any message")
}
b, ok := val.Interface().([]byte)
if !ok {
return true, errors.New("proto: invalid google.protobuf.Any message")
}
parts := strings.Split(turl.String(), "/")
mt := MessageType(parts[len(parts)-1])
if mt == nil {
return false, nil
}
m := reflect.New(mt.Elem())
if err := Unmarshal(b, m.Interface().(Message)); err != nil {
return false, nil
}
w.Write([]byte("["))
u := turl.String()
if requiresQuotes(u) {
writeString(w, u)
} else {
w.Write([]byte(u))
}
if w.compact {
w.Write([]byte("]:<"))
} else {
w.Write([]byte("]: <\n"))
w.ind++
}
if err := tm.writeStruct(w, m.Elem()); err != nil {
return true, err
}
if w.compact {
w.Write([]byte("> "))
} else {
w.ind--
w.Write([]byte(">\n"))
}
return true, nil
}
func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
if tm.ExpandAny && isAny(sv) {
if canExpand, err := tm.writeProto3Any(w, sv); canExpand {
return err
}
}
st := sv.Type() st := sv.Type()
sprops := GetProperties(st) sprops := GetProperties(st)
for i := 0; i < sv.NumField(); i++ { for i := 0; i < sv.NumField(); i++ {
@ -313,7 +227,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
} }
continue continue
} }
if err := tm.writeAny(w, v, props); err != nil { if err := writeAny(w, v, props); err != nil {
return err return err
} }
if err := w.WriteByte('\n'); err != nil { if err := w.WriteByte('\n'); err != nil {
@ -355,7 +269,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
return err return err
} }
} }
if err := tm.writeAny(w, key, props.mkeyprop); err != nil { if err := writeAny(w, key, props.mkeyprop); err != nil {
return err return err
} }
if err := w.WriteByte('\n'); err != nil { if err := w.WriteByte('\n'); err != nil {
@ -372,7 +286,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
return err return err
} }
} }
if err := tm.writeAny(w, val, props.mvalprop); err != nil { if err := writeAny(w, val, props.mvalprop); err != nil {
return err return err
} }
if err := w.WriteByte('\n'); err != nil { if err := w.WriteByte('\n'); err != nil {
@ -444,7 +358,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
} }
// Enums have a String method, so writeAny will work fine. // Enums have a String method, so writeAny will work fine.
if err := tm.writeAny(w, fv, props); err != nil { if err := writeAny(w, fv, props); err != nil {
return err return err
} }
@ -456,7 +370,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
// Extensions (the XXX_extensions field). // Extensions (the XXX_extensions field).
pv := sv.Addr() pv := sv.Addr()
if pv.Type().Implements(extendableProtoType) { if pv.Type().Implements(extendableProtoType) {
if err := tm.writeExtensions(w, pv); err != nil { if err := writeExtensions(w, pv); err != nil {
return err return err
} }
} }
@ -486,7 +400,7 @@ func writeRaw(w *textWriter, b []byte) error {
} }
// writeAny writes an arbitrary field. // writeAny writes an arbitrary field.
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error { func writeAny(w *textWriter, v reflect.Value, props *Properties) error {
v = reflect.Indirect(v) v = reflect.Indirect(v)
// Floats have special cases. // Floats have special cases.
@ -535,15 +449,15 @@ func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Propert
} }
} }
w.indent() w.indent()
if etm, ok := v.Interface().(encoding.TextMarshaler); ok { if tm, ok := v.Interface().(encoding.TextMarshaler); ok {
text, err := etm.MarshalText() text, err := tm.MarshalText()
if err != nil { if err != nil {
return err return err
} }
if _, err = w.Write(text); err != nil { if _, err = w.Write(text); err != nil {
return err return err
} }
} else if err := tm.writeStruct(w, v); err != nil { } else if err := writeStruct(w, v); err != nil {
return err return err
} }
w.unindent() w.unindent()
@ -687,7 +601,7 @@ func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// writeExtensions writes all the extensions in pv. // writeExtensions writes all the extensions in pv.
// pv is assumed to be a pointer to a protocol message struct that is extendable. // pv is assumed to be a pointer to a protocol message struct that is extendable.
func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error { func writeExtensions(w *textWriter, pv reflect.Value) error {
emap := extensionMaps[pv.Type().Elem()] emap := extensionMaps[pv.Type().Elem()]
ep := pv.Interface().(extendableProto) ep := pv.Interface().(extendableProto)
@ -722,13 +636,13 @@ func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error
// Repeated extensions will appear as a slice. // Repeated extensions will appear as a slice.
if !desc.repeated() { if !desc.repeated() {
if err := tm.writeExtension(w, desc.Name, pb); err != nil { if err := writeExtension(w, desc.Name, pb); err != nil {
return err return err
} }
} else { } else {
v := reflect.ValueOf(pb) v := reflect.ValueOf(pb)
for i := 0; i < v.Len(); i++ { for i := 0; i < v.Len(); i++ {
if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil { if err := writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
return err return err
} }
} }
@ -737,7 +651,7 @@ func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error
return nil return nil
} }
func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error { func writeExtension(w *textWriter, name string, pb interface{}) error {
if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil { if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil {
return err return err
} }
@ -746,7 +660,7 @@ func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface
return err return err
} }
} }
if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil { if err := writeAny(w, reflect.ValueOf(pb), nil); err != nil {
return err return err
} }
if err := w.WriteByte('\n'); err != nil { if err := w.WriteByte('\n'); err != nil {
@ -773,13 +687,12 @@ func (w *textWriter) writeIndent() {
// TextMarshaler is a configurable text format marshaler. // TextMarshaler is a configurable text format marshaler.
type TextMarshaler struct { type TextMarshaler struct {
Compact bool // use compact text format (one line). Compact bool // use compact text format (one line).
ExpandAny bool // expand google.protobuf.Any messages of known types
} }
// Marshal writes a given protocol buffer in text format. // Marshal writes a given protocol buffer in text format.
// The only errors returned are from w. // The only errors returned are from w.
func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error { func (m *TextMarshaler) Marshal(w io.Writer, pb Message) error {
val := reflect.ValueOf(pb) val := reflect.ValueOf(pb)
if pb == nil || val.IsNil() { if pb == nil || val.IsNil() {
w.Write([]byte("<nil>")) w.Write([]byte("<nil>"))
@ -794,11 +707,11 @@ func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error {
aw := &textWriter{ aw := &textWriter{
w: ww, w: ww,
complete: true, complete: true,
compact: tm.Compact, compact: m.Compact,
} }
if etm, ok := pb.(encoding.TextMarshaler); ok { if tm, ok := pb.(encoding.TextMarshaler); ok {
text, err := etm.MarshalText() text, err := tm.MarshalText()
if err != nil { if err != nil {
return err return err
} }
@ -812,7 +725,7 @@ func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error {
} }
// Dereference the received pointer so we don't have outer < and >. // Dereference the received pointer so we don't have outer < and >.
v := reflect.Indirect(val) v := reflect.Indirect(val)
if err := tm.writeStruct(aw, v); err != nil { if err := writeStruct(aw, v); err != nil {
return err return err
} }
if bw != nil { if bw != nil {
@ -822,9 +735,9 @@ func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error {
} }
// Text is the same as Marshal, but returns the string directly. // Text is the same as Marshal, but returns the string directly.
func (tm *TextMarshaler) Text(pb Message) string { func (m *TextMarshaler) Text(pb Message) string {
var buf bytes.Buffer var buf bytes.Buffer
tm.Marshal(&buf, pb) m.Marshal(&buf, pb)
return buf.String() return buf.String()
} }

View file

@ -163,7 +163,7 @@ func (p *textParser) advance() {
p.cur.offset, p.cur.line = p.offset, p.line p.cur.offset, p.cur.line = p.offset, p.line
p.cur.unquoted = "" p.cur.unquoted = ""
switch p.s[0] { switch p.s[0] {
case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/': case '<', '>', '{', '}', ':', '[', ']', ';', ',':
// Single symbol // Single symbol
p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)] p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)]
case '"', '\'': case '"', '\'':
@ -451,10 +451,7 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
fieldSet := make(map[string]bool) fieldSet := make(map[string]bool)
// A struct is a sequence of "name: value", terminated by one of // A struct is a sequence of "name: value", terminated by one of
// '>' or '}', or the end of the input. A name may also be // '>' or '}', or the end of the input. A name may also be
// "[extension]" or "[type/url]". // "[extension]".
//
// The whole struct can also be an expanded Any message, like:
// [type/url] < ... struct contents ... >
for { for {
tok := p.next() tok := p.next()
if tok.err != nil { if tok.err != nil {
@ -464,66 +461,33 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
break break
} }
if tok.value == "[" { if tok.value == "[" {
// Looks like an extension or an Any. // Looks like an extension.
// //
// TODO: Check whether we need to handle // TODO: Check whether we need to handle
// namespace rooted names (e.g. ".something.Foo"). // namespace rooted names (e.g. ".something.Foo").
extName, err := p.consumeExtName() tok = p.next()
if err != nil { if tok.err != nil {
return err return tok.err
} }
if s := strings.LastIndex(extName, "/"); s >= 0 {
// If it contains a slash, it's an Any type URL.
messageName := extName[s+1:]
mt := MessageType(messageName)
if mt == nil {
return p.errorf("unrecognized message %q in google.protobuf.Any", messageName)
}
tok = p.next()
if tok.err != nil {
return tok.err
}
// consume an optional colon
if tok.value == ":" {
tok = p.next()
if tok.err != nil {
return tok.err
}
}
var terminator string
switch tok.value {
case "<":
terminator = ">"
case "{":
terminator = "}"
default:
return p.errorf("expected '{' or '<', found %q", tok.value)
}
v := reflect.New(mt.Elem())
if pe := p.readStruct(v.Elem(), terminator); pe != nil {
return pe
}
b, err := Marshal(v.Interface().(Message))
if err != nil {
return p.errorf("failed to marshal message of type %q: %v", messageName, err)
}
sv.FieldByName("TypeUrl").SetString(extName)
sv.FieldByName("Value").SetBytes(b)
continue
}
var desc *ExtensionDesc var desc *ExtensionDesc
// This could be faster, but it's functional. // This could be faster, but it's functional.
// TODO: Do something smarter than a linear scan. // TODO: Do something smarter than a linear scan.
for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) { for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) {
if d.Name == extName { if d.Name == tok.value {
desc = d desc = d
break break
} }
} }
if desc == nil { if desc == nil {
return p.errorf("unrecognized extension %q", extName) return p.errorf("unrecognized extension %q", tok.value)
}
// Check the extension terminator.
tok = p.next()
if tok.err != nil {
return tok.err
}
if tok.value != "]" {
return p.errorf("unrecognized extension terminator %q", tok.value)
} }
props := &Properties{} props := &Properties{}
@ -679,35 +643,6 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
return reqFieldErr return reqFieldErr
} }
// consumeExtName consumes extension name or expanded Any type URL and the
// following ']'. It returns the name or URL consumed.
func (p *textParser) consumeExtName() (string, error) {
tok := p.next()
if tok.err != nil {
return "", tok.err
}
// If extension name or type url is quoted, it's a single token.
if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] {
name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0]))
if err != nil {
return "", err
}
return name, p.consumeToken("]")
}
// Consume everything up to "]"
var parts []string
for tok.value != "]" {
parts = append(parts, tok.value)
tok = p.next()
if tok.err != nil {
return "", p.errorf("unrecognized type_url or extension name: %s", tok.err)
}
}
return strings.Join(parts, ""), nil
}
// consumeOptionalSeparator consumes an optional semicolon or comma. // consumeOptionalSeparator consumes an optional semicolon or comma.
// It is used in readStruct to provide backward compatibility. // It is used in readStruct to provide backward compatibility.
func (p *textParser) consumeOptionalSeparator() error { func (p *textParser) consumeOptionalSeparator() error {

View file

@ -1,5 +1,9 @@
language: go language: go
go:
- 1.5.3
- 1.6
before_install: before_install:
- go get github.com/axw/gocov/gocov - go get github.com/axw/gocov/gocov
- go get github.com/mattn/goveralls - go get github.com/mattn/goveralls
@ -11,4 +15,3 @@ install:
script: script:
- make test testrace - make test testrace
- make coverage

View file

@ -1,16 +1,39 @@
# How to contribute # How to contribute
We definitely welcome patches and contribution to grpc! Here is some guideline We definitely welcome patches and contribution to grpc! Here are some guidelines
and information about how to do so. and information about how to do so.
## Getting started ## Sending patches
### Legal requirements ### Getting started
1. Check out the code:
$ go get google.golang.org/grpc
$ cd $GOPATH/src/google.golang.org/grpc
1. Create a fork of the grpc-go repository.
1. Add your fork as a remote:
$ git remote add fork git@github.com:$YOURGITHUBUSERNAME/grpc-go.git
1. Make changes, commit them.
1. Run the test suite:
$ make test
1. Push your changes to your fork:
$ git push fork ...
1. Open a pull request.
## Legal requirements
In order to protect both you and ourselves, you will need to sign the In order to protect both you and ourselves, you will need to sign the
[Contributor License Agreement](https://cla.developers.google.com/clas). [Contributor License Agreement](https://cla.developers.google.com/clas).
### Filing Issues ## Filing Issues
When filing an issue, make sure to answer these five questions: When filing an issue, make sure to answer these five questions:
1. What version of Go are you using (`go version`)? 1. What version of Go are you using (`go version`)?

View file

@ -1,15 +1,3 @@
.PHONY: \
all \
deps \
updatedeps \
testdeps \
updatetestdeps \
build \
proto \
test \
testrace \
clean \
all: test testrace all: test testrace
deps: deps:
@ -32,7 +20,7 @@ proto:
echo "error: protoc not installed" >&2; \ echo "error: protoc not installed" >&2; \
exit 1; \ exit 1; \
fi fi
go get -v github.com/golang/protobuf/protoc-gen-go go get -u -v github.com/golang/protobuf/protoc-gen-go
for file in $$(git ls-files '*.proto'); do \ for file in $$(git ls-files '*.proto'); do \
protoc -I $$(dirname $$file) --go_out=plugins=grpc:$$(dirname $$file) $$file; \ protoc -I $$(dirname $$file) --go_out=plugins=grpc:$$(dirname $$file) $$file; \
done done
@ -44,7 +32,20 @@ testrace: testdeps
go test -v -race -cpu 1,4 google.golang.org/grpc/... go test -v -race -cpu 1,4 google.golang.org/grpc/...
clean: clean:
go clean google.golang.org/grpc/... go clean -i google.golang.org/grpc/...
coverage: testdeps coverage: testdeps
./coverage.sh --coveralls ./coverage.sh --coveralls
.PHONY: \
all \
deps \
updatedeps \
testdeps \
updatetestdeps \
build \
proto \
test \
testrace \
clean \
coverage

View file

@ -7,7 +7,7 @@ The Go implementation of [gRPC](http://www.grpc.io/): A high performance, open s
Installation Installation
------------ ------------
To install this package, you need to install Go 1.4 or above and setup your Go workspace on your computer. The simplest way to install the library is to run: To install this package, you need to install Go and setup your Go workspace on your computer. The simplest way to install the library is to run:
``` ```
$ go get google.golang.org/grpc $ go get google.golang.org/grpc
@ -16,7 +16,7 @@ $ go get google.golang.org/grpc
Prerequisites Prerequisites
------------- -------------
This requires Go 1.4 or above. This requires Go 1.5 or later .
Constraints Constraints
----------- -----------

View file

@ -0,0 +1,80 @@
package grpc
import (
"math/rand"
"time"
)
// DefaultBackoffConfig uses values specified for backoff in
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
var (
DefaultBackoffConfig = BackoffConfig{
MaxDelay: 120 * time.Second,
baseDelay: 1.0 * time.Second,
factor: 1.6,
jitter: 0.2,
}
)
// backoffStrategy defines the methodology for backing off after a grpc
// connection failure.
//
// This is unexported until the GRPC project decides whether or not to allow
// alternative backoff strategies. Once a decision is made, this type and its
// method may be exported.
type backoffStrategy interface {
// backoff returns the amount of time to wait before the next retry given
// the number of consecutive failures.
backoff(retries int) time.Duration
}
// BackoffConfig defines the parameters for the default GRPC backoff strategy.
type BackoffConfig struct {
// MaxDelay is the upper bound of backoff delay.
MaxDelay time.Duration
// TODO(stevvooe): The following fields are not exported, as allowing
// changes would violate the current GRPC specification for backoff. If
// GRPC decides to allow more interesting backoff strategies, these fields
// may be opened up in the future.
// baseDelay is the amount of time to wait before retrying after the first
// failure.
baseDelay time.Duration
// factor is applied to the backoff after each retry.
factor float64
// jitter provides a range to randomize backoff delays.
jitter float64
}
func setDefaults(bc *BackoffConfig) {
md := bc.MaxDelay
*bc = DefaultBackoffConfig
if md > 0 {
bc.MaxDelay = md
}
}
func (bc BackoffConfig) backoff(retries int) (t time.Duration) {
if retries == 0 {
return bc.baseDelay
}
backoff, max := float64(bc.baseDelay), float64(bc.MaxDelay)
for backoff < max && retries > 0 {
backoff *= bc.factor
retries--
}
if backoff > max {
backoff = max
}
// Randomize backoff delays so that if a cluster of requests start at
// the same time, they won't operate in lockstep.
backoff *= 1 + bc.jitter*(rand.Float64()*2-1)
if backoff < 0 {
return 0
}
return time.Duration(backoff)
}

View file

@ -34,13 +34,13 @@
package grpc package grpc
import ( import (
"bytes"
"io" "io"
"time" "time"
"golang.org/x/net/context" "golang.org/x/net/context"
"golang.org/x/net/trace" "golang.org/x/net/trace"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/transport" "google.golang.org/grpc/transport"
) )
@ -48,16 +48,16 @@ import (
// On error, it returns the error and indicates whether the call should be retried. // On error, it returns the error and indicates whether the call should be retried.
// //
// TODO(zhaoq): Check whether the received message sequence is valid. // TODO(zhaoq): Check whether the received message sequence is valid.
func recvResponse(codec Codec, t transport.ClientTransport, c *callInfo, stream *transport.Stream, reply interface{}) error { func recvResponse(dopts dialOptions, t transport.ClientTransport, c *callInfo, stream *transport.Stream, reply interface{}) error {
// Try to acquire header metadata from the server if there is any. // Try to acquire header metadata from the server if there is any.
var err error var err error
c.headerMD, err = stream.Header() c.headerMD, err = stream.Header()
if err != nil { if err != nil {
return err return err
} }
p := &parser{s: stream} p := &parser{r: stream}
for { for {
if err = recv(p, codec, reply); err != nil { if err = recv(p, dopts.codec, stream, dopts.dc, reply); err != nil {
if err == io.EOF { if err == io.EOF {
break break
} }
@ -69,7 +69,7 @@ func recvResponse(codec Codec, t transport.ClientTransport, c *callInfo, stream
} }
// sendRequest writes out various information of an RPC such as Context and Message. // sendRequest writes out various information of an RPC such as Context and Message.
func sendRequest(ctx context.Context, codec Codec, callHdr *transport.CallHdr, t transport.ClientTransport, args interface{}, opts *transport.Options) (_ *transport.Stream, err error) { func sendRequest(ctx context.Context, codec Codec, compressor Compressor, callHdr *transport.CallHdr, t transport.ClientTransport, args interface{}, opts *transport.Options) (_ *transport.Stream, err error) {
stream, err := t.NewStream(ctx, callHdr) stream, err := t.NewStream(ctx, callHdr)
if err != nil { if err != nil {
return nil, err return nil, err
@ -81,8 +81,11 @@ func sendRequest(ctx context.Context, codec Codec, callHdr *transport.CallHdr, t
} }
} }
}() }()
// TODO(zhaoq): Support compression. var cbuf *bytes.Buffer
outBuf, err := encode(codec, args, compressionNone) if compressor != nil {
cbuf = new(bytes.Buffer)
}
outBuf, err := encode(codec, args, compressor, cbuf)
if err != nil { if err != nil {
return nil, transport.StreamErrorf(codes.Internal, "grpc: %v", err) return nil, transport.StreamErrorf(codes.Internal, "grpc: %v", err)
} }
@ -94,16 +97,9 @@ func sendRequest(ctx context.Context, codec Codec, callHdr *transport.CallHdr, t
return stream, nil return stream, nil
} }
// callInfo contains all related configuration and information about an RPC. // Invoke sends the RPC request on the wire and returns after response is received.
type callInfo struct { // Invoke is called by generated code. Also users can call Invoke directly when it
failFast bool // is really needed in their use cases.
headerMD metadata.MD
trailerMD metadata.MD
traceInfo traceInfo // in trace.go
}
// Invoke is called by the generated code. It sends the RPC request on the
// wire and returns after response is received.
func Invoke(ctx context.Context, method string, args, reply interface{}, cc *ClientConn, opts ...CallOption) (err error) { func Invoke(ctx context.Context, method string, args, reply interface{}, cc *ClientConn, opts ...CallOption) (err error) {
var c callInfo var c callInfo
for _, o := range opts { for _, o := range opts {
@ -153,6 +149,9 @@ func Invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
Host: cc.authority, Host: cc.authority,
Method: method, Method: method,
} }
if cc.dopts.cp != nil {
callHdr.SendCompress = cc.dopts.cp.Type()
}
t, err = cc.dopts.picker.Pick(ctx) t, err = cc.dopts.picker.Pick(ctx)
if err != nil { if err != nil {
if lastErr != nil { if lastErr != nil {
@ -164,7 +163,7 @@ func Invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
if c.traceInfo.tr != nil { if c.traceInfo.tr != nil {
c.traceInfo.tr.LazyLog(&payload{sent: true, msg: args}, true) c.traceInfo.tr.LazyLog(&payload{sent: true, msg: args}, true)
} }
stream, err = sendRequest(ctx, cc.dopts.codec, callHdr, t, args, topts) stream, err = sendRequest(ctx, cc.dopts.codec, cc.dopts.cp, callHdr, t, args, topts)
if err != nil { if err != nil {
if _, ok := err.(transport.ConnectionError); ok { if _, ok := err.(transport.ConnectionError); ok {
lastErr = err lastErr = err
@ -176,7 +175,7 @@ func Invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
return toRPCErr(err) return toRPCErr(err)
} }
// Receive the response // Receive the response
lastErr = recvResponse(cc.dopts.codec, t, &c, stream, reply) lastErr = recvResponse(cc.dopts, t, &c, stream, reply)
if _, ok := lastErr.(transport.ConnectionError); ok { if _, ok := lastErr.(transport.ConnectionError); ok {
continue continue
} }
@ -187,6 +186,6 @@ func Invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
if lastErr != nil { if lastErr != nil {
return toRPCErr(lastErr) return toRPCErr(lastErr)
} }
return Errorf(stream.StatusCode(), stream.StatusDesc()) return Errorf(stream.StatusCode(), "%s", stream.StatusDesc())
} }
} }

View file

@ -52,10 +52,10 @@ var (
// ErrUnspecTarget indicates that the target address is unspecified. // ErrUnspecTarget indicates that the target address is unspecified.
ErrUnspecTarget = errors.New("grpc: target is unspecified") ErrUnspecTarget = errors.New("grpc: target is unspecified")
// ErrNoTransportSecurity indicates that there is no transport security // ErrNoTransportSecurity indicates that there is no transport security
// being set for ClientConn. Users should either set one or explicityly // being set for ClientConn. Users should either set one or explicitly
// call WithInsecure DialOption to disable security. // call WithInsecure DialOption to disable security.
ErrNoTransportSecurity = errors.New("grpc: no transport security set (use grpc.WithInsecure() explicitly or set credentials)") ErrNoTransportSecurity = errors.New("grpc: no transport security set (use grpc.WithInsecure() explicitly or set credentials)")
// ErrCredentialsMisuse indicates that users want to transmit security infomation // ErrCredentialsMisuse indicates that users want to transmit security information
// (e.g., oauth2 token) which requires secure connection on an insecure // (e.g., oauth2 token) which requires secure connection on an insecure
// connection. // connection.
ErrCredentialsMisuse = errors.New("grpc: the credentials require transport level security (use grpc.WithTransportAuthenticator() to set)") ErrCredentialsMisuse = errors.New("grpc: the credentials require transport level security (use grpc.WithTransportAuthenticator() to set)")
@ -73,6 +73,9 @@ var (
// values passed to Dial. // values passed to Dial.
type dialOptions struct { type dialOptions struct {
codec Codec codec Codec
cp Compressor
dc Decompressor
bs backoffStrategy
picker Picker picker Picker
block bool block bool
insecure bool insecure bool
@ -89,12 +92,57 @@ func WithCodec(c Codec) DialOption {
} }
} }
// WithCompressor returns a DialOption which sets a CompressorGenerator for generating message
// compressor.
func WithCompressor(cp Compressor) DialOption {
return func(o *dialOptions) {
o.cp = cp
}
}
// WithDecompressor returns a DialOption which sets a DecompressorGenerator for generating
// message decompressor.
func WithDecompressor(dc Decompressor) DialOption {
return func(o *dialOptions) {
o.dc = dc
}
}
// WithPicker returns a DialOption which sets a picker for connection selection.
func WithPicker(p Picker) DialOption { func WithPicker(p Picker) DialOption {
return func(o *dialOptions) { return func(o *dialOptions) {
o.picker = p o.picker = p
} }
} }
// WithBackoffMaxDelay configures the dialer to use the provided maximum delay
// when backing off after failed connection attempts.
func WithBackoffMaxDelay(md time.Duration) DialOption {
return WithBackoffConfig(BackoffConfig{MaxDelay: md})
}
// WithBackoffConfig configures the dialer to use the provided backoff
// parameters after connection failures.
//
// Use WithBackoffMaxDelay until more parameters on BackoffConfig are opened up
// for use.
func WithBackoffConfig(b BackoffConfig) DialOption {
// Set defaults to ensure that provided BackoffConfig is valid and
// unexported fields get default values.
setDefaults(&b)
return withBackoff(b)
}
// withBackoff sets the backoff strategy used for retries after a
// failed connection attempt.
//
// This can be exported if arbitrary backoff strategies are allowed by GRPC.
func withBackoff(bs backoffStrategy) DialOption {
return func(o *dialOptions) {
o.bs = bs
}
}
// WithBlock returns a DialOption which makes caller of Dial blocks until the underlying // WithBlock returns a DialOption which makes caller of Dial blocks until the underlying
// connection is up. Without this, Dial returns immediately and connecting the server // connection is up. Without this, Dial returns immediately and connecting the server
// happens in background. // happens in background.
@ -104,6 +152,8 @@ func WithBlock() DialOption {
} }
} }
// WithInsecure returns a DialOption which disables transport security for this ClientConn.
// Note that transport security is required unless WithInsecure is set.
func WithInsecure() DialOption { func WithInsecure() DialOption {
return func(o *dialOptions) { return func(o *dialOptions) {
o.insecure = true o.insecure = true
@ -159,6 +209,11 @@ func Dial(target string, opts ...DialOption) (*ClientConn, error) {
// Set the default codec. // Set the default codec.
cc.dopts.codec = protoCodec{} cc.dopts.codec = protoCodec{}
} }
if cc.dopts.bs == nil {
cc.dopts.bs = DefaultBackoffConfig
}
if cc.dopts.picker == nil { if cc.dopts.picker == nil {
cc.dopts.picker = &unicastPicker{ cc.dopts.picker = &unicastPicker{
target: target, target: target,
@ -267,10 +322,9 @@ func NewConn(cc *ClientConn) (*Conn, error) {
if !c.dopts.insecure { if !c.dopts.insecure {
var ok bool var ok bool
for _, cd := range c.dopts.copts.AuthOptions { for _, cd := range c.dopts.copts.AuthOptions {
if _, ok := cd.(credentials.TransportAuthenticator); !ok { if _, ok = cd.(credentials.TransportAuthenticator); ok {
continue break
} }
ok = true
} }
if !ok { if !ok {
return nil, ErrNoTransportSecurity return nil, ErrNoTransportSecurity
@ -395,7 +449,7 @@ func (cc *Conn) resetTransport(closeTransport bool) error {
return ErrClientConnTimeout return ErrClientConnTimeout
} }
} }
sleepTime := backoff(retries) sleepTime := cc.dopts.bs.backoff(retries)
timeout := sleepTime timeout := sleepTime
if timeout < minConnectTimeout { if timeout < minConnectTimeout {
timeout = minConnectTimeout timeout = minConnectTimeout
@ -518,8 +572,9 @@ func (cc *Conn) Wait(ctx context.Context) (transport.ClientTransport, error) {
cc.mu.Unlock() cc.mu.Unlock()
return nil, ErrClientConnClosing return nil, ErrClientConnClosing
case cc.state == Ready: case cc.state == Ready:
ct := cc.transport
cc.mu.Unlock() cc.mu.Unlock()
return cc.transport, nil return ct, nil
default: default:
ready := cc.ready ready := cc.ready
if ready == nil { if ready == nil {

View file

@ -4,15 +4,20 @@ set -e
workdir=.cover workdir=.cover
profile="$workdir/cover.out" profile="$workdir/cover.out"
mode=count mode=set
end2endtest="google.golang.org/grpc/test"
generate_cover_data() { generate_cover_data() {
rm -rf "$workdir" rm -rf "$workdir"
mkdir "$workdir" mkdir "$workdir"
for pkg in "$@"; do for pkg in "$@"; do
f="$workdir/$(echo $pkg | tr / -).cover" if [ $pkg == "google.golang.org/grpc" -o $pkg == "google.golang.org/grpc/transport" -o $pkg == "google.golang.org/grpc/metadata" -o $pkg == "google.golang.org/grpc/credentials" ]
go test -covermode="$mode" -coverprofile="$f" "$pkg" then
f="$workdir/$(echo $pkg | tr / -)"
go test -covermode="$mode" -coverprofile="$f.cover" "$pkg"
go test -covermode="$mode" -coverpkg "$pkg" -coverprofile="$f.e2e.cover" "$end2endtest"
fi
done done
echo "mode: $mode" >"$profile" echo "mode: $mode" >"$profile"
@ -32,6 +37,8 @@ show_cover_report func
case "$1" in case "$1" in
"") "")
;; ;;
--html)
show_cover_report html ;;
--coveralls) --coveralls)
push_to_coveralls ;; push_to_coveralls ;;
*) *)

View file

@ -87,19 +87,6 @@ type AuthInfo interface {
AuthType() string AuthType() string
} }
type authInfoKey struct{}
// NewContext creates a new context with authInfo attached.
func NewContext(ctx context.Context, authInfo AuthInfo) context.Context {
return context.WithValue(ctx, authInfoKey{}, authInfo)
}
// FromContext returns the authInfo in ctx if it exists.
func FromContext(ctx context.Context) (authInfo AuthInfo, ok bool) {
authInfo, ok = ctx.Value(authInfoKey{}).(AuthInfo)
return
}
// TransportAuthenticator defines the common interface for all the live gRPC wire // TransportAuthenticator defines the common interface for all the live gRPC wire
// protocols and supported transport security protocols (e.g., TLS, SSL). // protocols and supported transport security protocols (e.g., TLS, SSL).
type TransportAuthenticator interface { type TransportAuthenticator interface {

View file

@ -1,6 +1,6 @@
/* /*
Package grpc implements an RPC system called gRPC. Package grpc implements an RPC system called gRPC.
See https://github.com/grpc/grpc for more information about gRPC. See www.grpc.io for more information about gRPC.
*/ */
package grpc // import "google.golang.org/grpc" package grpc // import "google.golang.org/grpc"

View file

@ -42,6 +42,8 @@ import (
) )
// Use golang's standard logger by default. // Use golang's standard logger by default.
// Access is not mutex-protected: do not modify except in init()
// functions.
var logger Logger = log.New(os.Stderr, "", log.LstdFlags) var logger Logger = log.New(os.Stderr, "", log.LstdFlags)
// Logger mimics golang's standard Logger as an interface. // Logger mimics golang's standard Logger as an interface.
@ -54,7 +56,8 @@ type Logger interface {
Println(args ...interface{}) Println(args ...interface{})
} }
// SetLogger sets the logger that is used in grpc. // SetLogger sets the logger that is used in grpc. Call only from
// init() functions.
func SetLogger(l Logger) { func SetLogger(l Logger) {
logger = l logger = l
} }

View file

@ -0,0 +1,74 @@
/*
*
* Copyright 2016, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package grpc
import (
"golang.org/x/net/context"
)
// UnaryServerInfo consists of various information about a unary RPC on
// server side. All per-rpc information may be mutated by the interceptor.
type UnaryServerInfo struct {
// Server is the service implementation the user provides. This is read-only.
Server interface{}
// FullMethod is the full RPC method string, i.e., /package.service/method.
FullMethod string
}
// UnaryHandler defines the handler invoked by UnaryServerInterceptor to complete the normal
// execution of a unary RPC.
type UnaryHandler func(ctx context.Context, req interface{}) (interface{}, error)
// UnaryServerInterceptor provides a hook to intercept the execution of a unary RPC on the server. info
// contains all the information of this RPC the interceptor can operate on. And handler is the wrapper
// of the service method implementation. It is the responsibility of the interceptor to invoke handler
// to complete the RPC.
type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)
// StreamServerInfo consists of various information about a streaming RPC on
// server side. All per-rpc information may be mutated by the interceptor.
type StreamServerInfo struct {
// FullMethod is the full RPC method string, i.e., /package.service/method.
FullMethod string
// IsClientStream indicates whether the RPC is a client streaming RPC.
IsClientStream bool
// IsServerStream indicates whether the RPC is a server streaming RPC.
IsServerStream bool
}
// StreamServerInterceptor provides a hook to intercept the execution of a streaming RPC on the server.
// info contains all the information of this RPC the interceptor can operate on. And handler is the
// service method implementation. It is the responsibility of the interceptor to invoke handler to
// complete the RPC.
type StreamServerInterceptor func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error

View file

@ -0,0 +1,49 @@
/*
* Copyright 2016, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// Package internal contains gRPC-internal code for testing, to avoid polluting
// the godoc of the top-level grpc package.
package internal
// TestingCloseConns closes all existing transports but keeps
// grpcServer.lis accepting new connections.
//
// The provided grpcServer must be of type *grpc.Server. It is untyped
// for circular dependency reasons.
var TestingCloseConns func(grpcServer interface{})
// TestingUseHandlerImpl enables the http.Handler-based server implementation.
// It must be called before Serve and requires TLS credentials.
//
// The provided grpcServer must be of type *grpc.Server. It is untyped
// for circular dependency reasons.
var TestingUseHandlerImpl func(grpcServer interface{})

View file

@ -0,0 +1,65 @@
/*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// Package peer defines various peer information associated with RPCs and
// corresponding utils.
package peer
import (
"net"
"golang.org/x/net/context"
"google.golang.org/grpc/credentials"
)
// Peer contains the information of the peer for an RPC.
type Peer struct {
// Addr is the peer address.
Addr net.Addr
// AuthInfo is the authentication information of the transport.
// It is nil if there is no transport security being used.
AuthInfo credentials.AuthInfo
}
type peerKey struct{}
// NewContext creates a new context with peer information attached.
func NewContext(ctx context.Context, p *Peer) context.Context {
return context.WithValue(ctx, peerKey{}, p)
}
// FromContext returns the peer information in ctx if it exists.
func FromContext(ctx context.Context) (p *Peer, ok bool) {
p, ok = ctx.Value(peerKey{}).(*Peer)
return
}

View file

@ -172,7 +172,7 @@ func (p *unicastNamingPicker) processUpdates() error {
} }
p.mu.Unlock() p.mu.Unlock()
default: default:
grpclog.Println("Unknown update.Op %d", update.Op) grpclog.Println("Unknown update.Op ", update.Op)
} }
} }
return nil return nil

View file

@ -34,13 +34,14 @@
package grpc package grpc
import ( import (
"bytes"
"compress/gzip"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"math" "math"
"math/rand"
"os" "os"
"time"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -75,6 +76,71 @@ func (protoCodec) String() string {
return "proto" return "proto"
} }
// Compressor defines the interface gRPC uses to compress a message.
type Compressor interface {
// Do compresses p into w.
Do(w io.Writer, p []byte) error
// Type returns the compression algorithm the Compressor uses.
Type() string
}
// NewGZIPCompressor creates a Compressor based on GZIP.
func NewGZIPCompressor() Compressor {
return &gzipCompressor{}
}
type gzipCompressor struct {
}
func (c *gzipCompressor) Do(w io.Writer, p []byte) error {
z := gzip.NewWriter(w)
if _, err := z.Write(p); err != nil {
return err
}
return z.Close()
}
func (c *gzipCompressor) Type() string {
return "gzip"
}
// Decompressor defines the interface gRPC uses to decompress a message.
type Decompressor interface {
// Do reads the data from r and uncompress them.
Do(r io.Reader) ([]byte, error)
// Type returns the compression algorithm the Decompressor uses.
Type() string
}
type gzipDecompressor struct {
}
// NewGZIPDecompressor creates a Decompressor based on GZIP.
func NewGZIPDecompressor() Decompressor {
return &gzipDecompressor{}
}
func (d *gzipDecompressor) Do(r io.Reader) ([]byte, error) {
z, err := gzip.NewReader(r)
if err != nil {
return nil, err
}
defer z.Close()
return ioutil.ReadAll(z)
}
func (d *gzipDecompressor) Type() string {
return "gzip"
}
// callInfo contains all related configuration and information about an RPC.
type callInfo struct {
failFast bool
headerMD metadata.MD
trailerMD metadata.MD
traceInfo traceInfo // in trace.go
}
// CallOption configures a Call before it starts or extracts information from // CallOption configures a Call before it starts or extracts information from
// a Call after it completes. // a Call after it completes.
type CallOption interface { type CallOption interface {
@ -118,36 +184,49 @@ type payloadFormat uint8
const ( const (
compressionNone payloadFormat = iota // no compression compressionNone payloadFormat = iota // no compression
compressionFlate compressionMade
// More formats
) )
// parser reads complelete gRPC messages from the underlying reader. // parser reads complelete gRPC messages from the underlying reader.
type parser struct { type parser struct {
s io.Reader // r is the underlying reader.
} // See the comment on recvMsg for the permissible
// error types.
r io.Reader
// recvMsg is to read a complete gRPC message from the stream. It is blocking if
// the message has not been complete yet. It returns the message and its type,
// EOF is returned with nil msg and 0 pf if the entire stream is done. Other
// non-nil error is returned if something is wrong on reading.
func (p *parser) recvMsg() (pf payloadFormat, msg []byte, err error) {
// The header of a gRPC message. Find more detail // The header of a gRPC message. Find more detail
// at http://www.grpc.io/docs/guides/wire.html. // at http://www.grpc.io/docs/guides/wire.html.
var buf [5]byte header [5]byte
}
if _, err := io.ReadFull(p.s, buf[:]); err != nil { // recvMsg reads a complete gRPC message from the stream.
//
// It returns the message and its payload (compression/encoding)
// format. The caller owns the returned msg memory.
//
// If there is an error, possible values are:
// * io.EOF, when no messages remain
// * io.ErrUnexpectedEOF
// * of type transport.ConnectionError
// * of type transport.StreamError
// No other error values or types must be returned, which also means
// that the underlying io.Reader must not return an incompatible
// error.
func (p *parser) recvMsg() (pf payloadFormat, msg []byte, err error) {
if _, err := io.ReadFull(p.r, p.header[:]); err != nil {
return 0, nil, err return 0, nil, err
} }
pf = payloadFormat(buf[0]) pf = payloadFormat(p.header[0])
length := binary.BigEndian.Uint32(buf[1:]) length := binary.BigEndian.Uint32(p.header[1:])
if length == 0 { if length == 0 {
return pf, nil, nil return pf, nil, nil
} }
// TODO(bradfitz,zhaoq): garbage. reuse buffer after proto decoding instead
// of making it for each message:
msg = make([]byte, int(length)) msg = make([]byte, int(length))
if _, err := io.ReadFull(p.s, msg); err != nil { if _, err := io.ReadFull(p.r, msg); err != nil {
if err == io.EOF { if err == io.EOF {
err = io.ErrUnexpectedEOF err = io.ErrUnexpectedEOF
} }
@ -158,7 +237,7 @@ func (p *parser) recvMsg() (pf payloadFormat, msg []byte, err error) {
// encode serializes msg and prepends the message header. If msg is nil, it // encode serializes msg and prepends the message header. If msg is nil, it
// generates the message header of 0 message length. // generates the message header of 0 message length.
func encode(c Codec, msg interface{}, pf payloadFormat) ([]byte, error) { func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer) ([]byte, error) {
var b []byte var b []byte
var length uint var length uint
if msg != nil { if msg != nil {
@ -168,6 +247,12 @@ func encode(c Codec, msg interface{}, pf payloadFormat) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if cp != nil {
if err := cp.Do(cbuf, b); err != nil {
return nil, err
}
b = cbuf.Bytes()
}
length = uint(len(b)) length = uint(len(b))
} }
if length > math.MaxUint32 { if length > math.MaxUint32 {
@ -182,7 +267,11 @@ func encode(c Codec, msg interface{}, pf payloadFormat) ([]byte, error) {
var buf = make([]byte, payloadLen+sizeLen+len(b)) var buf = make([]byte, payloadLen+sizeLen+len(b))
// Write payload format // Write payload format
buf[0] = byte(pf) if cp == nil {
buf[0] = byte(compressionNone)
} else {
buf[0] = byte(compressionMade)
}
// Write length of b into buf // Write length of b into buf
binary.BigEndian.PutUint32(buf[1:], uint32(length)) binary.BigEndian.PutUint32(buf[1:], uint32(length))
// Copy encoded msg to buf // Copy encoded msg to buf
@ -191,22 +280,38 @@ func encode(c Codec, msg interface{}, pf payloadFormat) ([]byte, error) {
return buf, nil return buf, nil
} }
func recv(p *parser, c Codec, m interface{}) error { func checkRecvPayload(pf payloadFormat, recvCompress string, dc Decompressor) error {
switch pf {
case compressionNone:
case compressionMade:
if recvCompress == "" {
return transport.StreamErrorf(codes.InvalidArgument, "grpc: invalid grpc-encoding %q with compression enabled", recvCompress)
}
if dc == nil || recvCompress != dc.Type() {
return transport.StreamErrorf(codes.InvalidArgument, "grpc: Decompressor is not installed for grpc-encoding %q", recvCompress)
}
default:
return transport.StreamErrorf(codes.InvalidArgument, "grpc: received unexpected payload format %d", pf)
}
return nil
}
func recv(p *parser, c Codec, s *transport.Stream, dc Decompressor, m interface{}) error {
pf, d, err := p.recvMsg() pf, d, err := p.recvMsg()
if err != nil { if err != nil {
return err return err
} }
switch pf { if err := checkRecvPayload(pf, s.RecvCompress(), dc); err != nil {
case compressionNone: return err
if err := c.Unmarshal(d, m); err != nil { }
if rErr, ok := err.(rpcError); ok { if pf == compressionMade {
return rErr d, err = dc.Do(bytes.NewReader(d))
} else { if err != nil {
return Errorf(codes.Internal, "grpc: %v", err) return transport.StreamErrorf(codes.Internal, "grpc: failed to decompress the received message %v", err)
}
} }
default: }
return Errorf(codes.Internal, "gprc: compression is not supported yet.") if err := c.Unmarshal(d, m); err != nil {
return transport.StreamErrorf(codes.Internal, "grpc: failed to unmarshal the received message %v", err)
} }
return nil return nil
} }
@ -218,7 +323,7 @@ type rpcError struct {
} }
func (e rpcError) Error() string { func (e rpcError) Error() string {
return fmt.Sprintf("rpc error: code = %d desc = %q", e.code, e.desc) return fmt.Sprintf("rpc error: code = %d desc = %s", e.code, e.desc)
} }
// Code returns the error code for err if it was produced by the rpc system. // Code returns the error code for err if it was produced by the rpc system.
@ -304,34 +409,10 @@ func convertCode(err error) codes.Code {
return codes.Unknown return codes.Unknown
} }
const ( // SupportPackageIsVersion2 is referenced from generated protocol buffer files
// how long to wait after the first failure before retrying // to assert that that code is compatible with this version of the grpc package.
baseDelay = 1.0 * time.Second //
// upper bound of backoff delay // This constant may be renamed in the future if a change in the generated code
maxDelay = 120 * time.Second // requires a synchronised update of grpc-go and protoc-gen-go. This constant
// backoff increases by this factor on each retry // should not be referenced from any other code.
backoffFactor = 1.6 const SupportPackageIsVersion2 = true
// backoff is randomized downwards by this factor
backoffJitter = 0.2
)
func backoff(retries int) (t time.Duration) {
if retries == 0 {
return baseDelay
}
backoff, max := float64(baseDelay), float64(maxDelay)
for backoff < max && retries > 0 {
backoff *= backoffFactor
retries--
}
if backoff > max {
backoff = max
}
// Randomize backoff delays so that if a cluster of requests start at
// the same time, they won't operate in lockstep.
backoff *= 1 + backoffJitter*(rand.Float64()*2-1)
if backoff < 0 {
return 0
}
return time.Duration(backoff)
}

View file

@ -34,10 +34,12 @@
package grpc package grpc
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"net" "net"
"net/http"
"reflect" "reflect"
"runtime" "runtime"
"strings" "strings"
@ -45,15 +47,17 @@ import (
"time" "time"
"golang.org/x/net/context" "golang.org/x/net/context"
"golang.org/x/net/http2"
"golang.org/x/net/trace" "golang.org/x/net/trace"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/transport" "google.golang.org/grpc/transport"
) )
type methodHandler func(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) type methodHandler func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor UnaryServerInterceptor) (interface{}, error)
// MethodDesc represents an RPC service's method specification. // MethodDesc represents an RPC service's method specification.
type MethodDesc struct { type MethodDesc struct {
@ -81,10 +85,11 @@ type service struct {
// Server is a gRPC server to serve RPC requests. // Server is a gRPC server to serve RPC requests.
type Server struct { type Server struct {
opts options opts options
mu sync.Mutex
mu sync.Mutex // guards following
lis map[net.Listener]bool lis map[net.Listener]bool
conns map[transport.ServerTransport]bool conns map[io.Closer]bool
m map[string]*service // service name -> service info m map[string]*service // service name -> service info
events trace.EventLog events trace.EventLog
} }
@ -92,7 +97,12 @@ type Server struct {
type options struct { type options struct {
creds credentials.Credentials creds credentials.Credentials
codec Codec codec Codec
cp Compressor
dc Decompressor
unaryInt UnaryServerInterceptor
streamInt StreamServerInterceptor
maxConcurrentStreams uint32 maxConcurrentStreams uint32
useHandlerImpl bool // use http.Handler-based server
} }
// A ServerOption sets options. // A ServerOption sets options.
@ -105,6 +115,18 @@ func CustomCodec(codec Codec) ServerOption {
} }
} }
func RPCCompressor(cp Compressor) ServerOption {
return func(o *options) {
o.cp = cp
}
}
func RPCDecompressor(dc Decompressor) ServerOption {
return func(o *options) {
o.dc = dc
}
}
// MaxConcurrentStreams returns a ServerOption that will apply a limit on the number // MaxConcurrentStreams returns a ServerOption that will apply a limit on the number
// of concurrent streams to each ServerTransport. // of concurrent streams to each ServerTransport.
func MaxConcurrentStreams(n uint32) ServerOption { func MaxConcurrentStreams(n uint32) ServerOption {
@ -120,6 +142,29 @@ func Creds(c credentials.Credentials) ServerOption {
} }
} }
// UnaryInterceptor returns a ServerOption that sets the UnaryServerInterceptor for the
// server. Only one unary interceptor can be installed. The construction of multiple
// interceptors (e.g., chaining) can be implemented at the caller.
func UnaryInterceptor(i UnaryServerInterceptor) ServerOption {
return func(o *options) {
if o.unaryInt != nil {
panic("The unary server interceptor has been set.")
}
o.unaryInt = i
}
}
// StreamInterceptor returns a ServerOption that sets the StreamServerInterceptor for the
// server. Only one stream interceptor can be installed.
func StreamInterceptor(i StreamServerInterceptor) ServerOption {
return func(o *options) {
if o.streamInt != nil {
panic("The stream server interceptor has been set.")
}
o.streamInt = i
}
}
// NewServer creates a gRPC server which has no service registered and has not // NewServer creates a gRPC server which has no service registered and has not
// started to accept requests yet. // started to accept requests yet.
func NewServer(opt ...ServerOption) *Server { func NewServer(opt ...ServerOption) *Server {
@ -134,7 +179,7 @@ func NewServer(opt ...ServerOption) *Server {
s := &Server{ s := &Server{
lis: make(map[net.Listener]bool), lis: make(map[net.Listener]bool),
opts: opts, opts: opts,
conns: make(map[transport.ServerTransport]bool), conns: make(map[io.Closer]bool),
m: make(map[string]*service), m: make(map[string]*service),
} }
if EnableTracing { if EnableTracing {
@ -201,9 +246,17 @@ var (
ErrServerStopped = errors.New("grpc: the server has been stopped") ErrServerStopped = errors.New("grpc: the server has been stopped")
) )
func (s *Server) useTransportAuthenticator(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
creds, ok := s.opts.creds.(credentials.TransportAuthenticator)
if !ok {
return rawConn, nil, nil
}
return creds.ServerHandshake(rawConn)
}
// Serve accepts incoming connections on the listener lis, creating a new // Serve accepts incoming connections on the listener lis, creating a new
// ServerTransport and service goroutine for each. The service goroutines // ServerTransport and service goroutine for each. The service goroutines
// read gRPC request and then call the registered handlers to reply to them. // read gRPC requests and then call the registered handlers to reply to them.
// Service returns when lis.Accept fails. // Service returns when lis.Accept fails.
func (s *Server) Serve(lis net.Listener) error { func (s *Server) Serve(lis net.Listener) error {
s.mu.Lock() s.mu.Lock()
@ -221,74 +274,167 @@ func (s *Server) Serve(lis net.Listener) error {
s.mu.Unlock() s.mu.Unlock()
}() }()
for { for {
c, err := lis.Accept() rawConn, err := lis.Accept()
if err != nil { if err != nil {
s.mu.Lock() s.mu.Lock()
s.printf("done serving; Accept = %v", err) s.printf("done serving; Accept = %v", err)
s.mu.Unlock() s.mu.Unlock()
return err return err
} }
var authInfo credentials.AuthInfo // Start a new goroutine to deal with rawConn
if creds, ok := s.opts.creds.(credentials.TransportAuthenticator); ok { // so we don't stall this Accept loop goroutine.
var conn net.Conn go s.handleRawConn(rawConn)
conn, authInfo, err = creds.ServerHandshake(c)
if err != nil {
s.mu.Lock()
s.errorf("ServerHandshake(%q) failed: %v", c.RemoteAddr(), err)
s.mu.Unlock()
grpclog.Println("grpc: Server.Serve failed to complete security handshake.")
continue
}
c = conn
}
s.mu.Lock()
if s.conns == nil {
s.mu.Unlock()
c.Close()
return nil
}
st, err := transport.NewServerTransport("http2", c, s.opts.maxConcurrentStreams, authInfo)
if err != nil {
s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
s.mu.Unlock()
c.Close()
grpclog.Println("grpc: Server.Serve failed to create ServerTransport: ", err)
continue
}
s.conns[st] = true
s.mu.Unlock()
go func() {
var wg sync.WaitGroup
st.HandleStreams(func(stream *transport.Stream) {
var trInfo *traceInfo
if EnableTracing {
trInfo = &traceInfo{
tr: trace.New("grpc.Recv."+methodFamily(stream.Method()), stream.Method()),
}
trInfo.firstLine.client = false
trInfo.firstLine.remoteAddr = st.RemoteAddr()
stream.TraceContext(trInfo.tr)
if dl, ok := stream.Context().Deadline(); ok {
trInfo.firstLine.deadline = dl.Sub(time.Now())
}
}
wg.Add(1)
go func() {
s.handleStream(st, stream, trInfo)
wg.Done()
}()
})
wg.Wait()
s.mu.Lock()
delete(s.conns, st)
s.mu.Unlock()
}()
} }
} }
func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, pf payloadFormat, opts *transport.Options) error { // handleRawConn is run in its own goroutine and handles a just-accepted
p, err := encode(s.opts.codec, msg, pf) // connection that has not had any I/O performed on it yet.
func (s *Server) handleRawConn(rawConn net.Conn) {
conn, authInfo, err := s.useTransportAuthenticator(rawConn)
if err != nil {
s.mu.Lock()
s.errorf("ServerHandshake(%q) failed: %v", rawConn.RemoteAddr(), err)
s.mu.Unlock()
grpclog.Printf("grpc: Server.Serve failed to complete security handshake from %q: %v", rawConn.RemoteAddr(), err)
rawConn.Close()
return
}
s.mu.Lock()
if s.conns == nil {
s.mu.Unlock()
conn.Close()
return
}
s.mu.Unlock()
if s.opts.useHandlerImpl {
s.serveUsingHandler(conn)
} else {
s.serveNewHTTP2Transport(conn, authInfo)
}
}
// serveNewHTTP2Transport sets up a new http/2 transport (using the
// gRPC http2 server transport in transport/http2_server.go) and
// serves streams on it.
// This is run in its own goroutine (it does network I/O in
// transport.NewServerTransport).
func (s *Server) serveNewHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) {
st, err := transport.NewServerTransport("http2", c, s.opts.maxConcurrentStreams, authInfo)
if err != nil {
s.mu.Lock()
s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
s.mu.Unlock()
c.Close()
grpclog.Println("grpc: Server.Serve failed to create ServerTransport: ", err)
return
}
if !s.addConn(st) {
st.Close()
return
}
s.serveStreams(st)
}
func (s *Server) serveStreams(st transport.ServerTransport) {
defer s.removeConn(st)
defer st.Close()
var wg sync.WaitGroup
st.HandleStreams(func(stream *transport.Stream) {
wg.Add(1)
go func() {
defer wg.Done()
s.handleStream(st, stream, s.traceInfo(st, stream))
}()
})
wg.Wait()
}
var _ http.Handler = (*Server)(nil)
// serveUsingHandler is called from handleRawConn when s is configured
// to handle requests via the http.Handler interface. It sets up a
// net/http.Server to handle the just-accepted conn. The http.Server
// is configured to route all incoming requests (all HTTP/2 streams)
// to ServeHTTP, which creates a new ServerTransport for each stream.
// serveUsingHandler blocks until conn closes.
//
// This codepath is only used when Server.TestingUseHandlerImpl has
// been configured. This lets the end2end tests exercise the ServeHTTP
// method as one of the environment types.
//
// conn is the *tls.Conn that's already been authenticated.
func (s *Server) serveUsingHandler(conn net.Conn) {
if !s.addConn(conn) {
conn.Close()
return
}
defer s.removeConn(conn)
h2s := &http2.Server{
MaxConcurrentStreams: s.opts.maxConcurrentStreams,
}
h2s.ServeConn(conn, &http2.ServeConnOpts{
Handler: s,
})
}
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
st, err := transport.NewServerHandlerTransport(w, r)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if !s.addConn(st) {
st.Close()
return
}
defer s.removeConn(st)
s.serveStreams(st)
}
// traceInfo returns a traceInfo and associates it with stream, if tracing is enabled.
// If tracing is not enabled, it returns nil.
func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Stream) (trInfo *traceInfo) {
if !EnableTracing {
return nil
}
trInfo = &traceInfo{
tr: trace.New("grpc.Recv."+methodFamily(stream.Method()), stream.Method()),
}
trInfo.firstLine.client = false
trInfo.firstLine.remoteAddr = st.RemoteAddr()
stream.TraceContext(trInfo.tr)
if dl, ok := stream.Context().Deadline(); ok {
trInfo.firstLine.deadline = dl.Sub(time.Now())
}
return trInfo
}
func (s *Server) addConn(c io.Closer) bool {
s.mu.Lock()
defer s.mu.Unlock()
if s.conns == nil {
return false
}
s.conns[c] = true
return true
}
func (s *Server) removeConn(c io.Closer) {
s.mu.Lock()
defer s.mu.Unlock()
if s.conns != nil {
delete(s.conns, c)
}
}
func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, cp Compressor, opts *transport.Options) error {
var cbuf *bytes.Buffer
if cp != nil {
cbuf = new(bytes.Buffer)
}
p, err := encode(s.opts.codec, msg, cp, cbuf)
if err != nil { if err != nil {
// This typically indicates a fatal issue (e.g., memory // This typically indicates a fatal issue (e.g., memory
// corruption or hardware faults) the application program // corruption or hardware faults) the application program
@ -314,97 +460,130 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
} }
}() }()
} }
p := &parser{s: stream} p := &parser{r: stream}
for { for {
pf, req, err := p.recvMsg() pf, req, err := p.recvMsg()
if err == io.EOF { if err == io.EOF {
// The entire stream is done (for unary RPC only). // The entire stream is done (for unary RPC only).
return err return err
} }
if err == io.ErrUnexpectedEOF {
err = transport.StreamError{Code: codes.Internal, Desc: "io.ErrUnexpectedEOF"}
}
if err != nil { if err != nil {
switch err := err.(type) { switch err := err.(type) {
case transport.ConnectionError: case transport.ConnectionError:
// Nothing to do here. // Nothing to do here.
case transport.StreamError: case transport.StreamError:
if err := t.WriteStatus(stream, err.Code, err.Desc); err != nil { if err := t.WriteStatus(stream, err.Code, err.Desc); err != nil {
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err) grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
} }
default: default:
panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", err, err)) panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", err, err))
} }
return err return err
} }
switch pf {
case compressionNone: if err := checkRecvPayload(pf, stream.RecvCompress(), s.opts.dc); err != nil {
statusCode := codes.OK switch err := err.(type) {
statusDesc := "" case transport.StreamError:
df := func(v interface{}) error { if err := t.WriteStatus(stream, err.Code, err.Desc); err != nil {
if err := s.opts.codec.Unmarshal(req, v); err != nil { grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
return err
} }
if trInfo != nil { default:
trInfo.tr.LazyLog(&payload{sent: false, msg: v}, true) if err := t.WriteStatus(stream, codes.Internal, err.Error()); err != nil {
} grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
return nil
}
reply, appErr := md.Handler(srv.server, stream.Context(), df)
if appErr != nil {
if err, ok := appErr.(rpcError); ok {
statusCode = err.code
statusDesc = err.desc
} else {
statusCode = convertCode(appErr)
statusDesc = appErr.Error()
}
if trInfo != nil && statusCode != codes.OK {
trInfo.tr.LazyLog(stringer(statusDesc), true)
trInfo.tr.SetError()
} }
if err := t.WriteStatus(stream, statusCode, statusDesc); err != nil { }
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err) return err
}
statusCode := codes.OK
statusDesc := ""
df := func(v interface{}) error {
if pf == compressionMade {
var err error
req, err = s.opts.dc.Do(bytes.NewReader(req))
if err != nil {
if err := t.WriteStatus(stream, codes.Internal, err.Error()); err != nil {
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
}
return err return err
} }
return nil
} }
if trInfo != nil { if err := s.opts.codec.Unmarshal(req, v); err != nil {
trInfo.tr.LazyLog(stringer("OK"), false)
}
opts := &transport.Options{
Last: true,
Delay: false,
}
if err := s.sendResponse(t, stream, reply, compressionNone, opts); err != nil {
switch err := err.(type) {
case transport.ConnectionError:
// Nothing to do here.
case transport.StreamError:
statusCode = err.Code
statusDesc = err.Desc
default:
statusCode = codes.Unknown
statusDesc = err.Error()
}
return err return err
} }
if trInfo != nil { if trInfo != nil {
trInfo.tr.LazyLog(&payload{sent: true, msg: reply}, true) trInfo.tr.LazyLog(&payload{sent: false, msg: v}, true)
} }
return t.WriteStatus(stream, statusCode, statusDesc) return nil
default:
panic(fmt.Sprintf("payload format to be supported: %d", pf))
} }
reply, appErr := md.Handler(srv.server, stream.Context(), df, s.opts.unaryInt)
if appErr != nil {
if err, ok := appErr.(rpcError); ok {
statusCode = err.code
statusDesc = err.desc
} else {
statusCode = convertCode(appErr)
statusDesc = appErr.Error()
}
if trInfo != nil && statusCode != codes.OK {
trInfo.tr.LazyLog(stringer(statusDesc), true)
trInfo.tr.SetError()
}
if err := t.WriteStatus(stream, statusCode, statusDesc); err != nil {
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
return err
}
return nil
}
if trInfo != nil {
trInfo.tr.LazyLog(stringer("OK"), false)
}
opts := &transport.Options{
Last: true,
Delay: false,
}
if s.opts.cp != nil {
stream.SetSendCompress(s.opts.cp.Type())
}
if err := s.sendResponse(t, stream, reply, s.opts.cp, opts); err != nil {
switch err := err.(type) {
case transport.ConnectionError:
// Nothing to do here.
case transport.StreamError:
statusCode = err.Code
statusDesc = err.Desc
default:
statusCode = codes.Unknown
statusDesc = err.Error()
}
return err
}
if trInfo != nil {
trInfo.tr.LazyLog(&payload{sent: true, msg: reply}, true)
}
return t.WriteStatus(stream, statusCode, statusDesc)
} }
} }
func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc, trInfo *traceInfo) (err error) { func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc, trInfo *traceInfo) (err error) {
if s.opts.cp != nil {
stream.SetSendCompress(s.opts.cp.Type())
}
ss := &serverStream{ ss := &serverStream{
t: t, t: t,
s: stream, s: stream,
p: &parser{s: stream}, p: &parser{r: stream},
codec: s.opts.codec, codec: s.opts.codec,
cp: s.opts.cp,
dc: s.opts.dc,
trInfo: trInfo, trInfo: trInfo,
} }
if ss.cp != nil {
ss.cbuf = new(bytes.Buffer)
}
if trInfo != nil { if trInfo != nil {
trInfo.tr.LazyLog(&trInfo.firstLine, false) trInfo.tr.LazyLog(&trInfo.firstLine, false)
defer func() { defer func() {
@ -418,10 +597,24 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
ss.mu.Unlock() ss.mu.Unlock()
}() }()
} }
if appErr := sd.Handler(srv.server, ss); appErr != nil { var appErr error
if s.opts.streamInt == nil {
appErr = sd.Handler(srv.server, ss)
} else {
info := &StreamServerInfo{
FullMethod: stream.Method(),
IsClientStream: sd.ClientStreams,
IsServerStream: sd.ServerStreams,
}
appErr = s.opts.streamInt(srv.server, ss, info, sd.Handler)
}
if appErr != nil {
if err, ok := appErr.(rpcError); ok { if err, ok := appErr.(rpcError); ok {
ss.statusCode = err.code ss.statusCode = err.code
ss.statusDesc = err.desc ss.statusDesc = err.desc
} else if err, ok := appErr.(transport.StreamError); ok {
ss.statusCode = err.Code
ss.statusDesc = err.Desc
} else { } else {
ss.statusCode = convertCode(appErr) ss.statusCode = convertCode(appErr)
ss.statusDesc = appErr.Error() ss.statusDesc = appErr.Error()
@ -509,8 +702,11 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
} }
} }
// Stop stops the gRPC server. Once Stop returns, the server stops accepting // Stop stops the gRPC server. It immediately closes all open
// connection requests and closes all the connected connections. // connections and listeners.
// It cancels all active RPCs on the server side and the corresponding
// pending RPCs on the client side will get notified by connection
// errors.
func (s *Server) Stop() { func (s *Server) Stop() {
s.mu.Lock() s.mu.Lock()
listeners := s.lis listeners := s.lis
@ -518,12 +714,14 @@ func (s *Server) Stop() {
cs := s.conns cs := s.conns
s.conns = nil s.conns = nil
s.mu.Unlock() s.mu.Unlock()
for lis := range listeners { for lis := range listeners {
lis.Close() lis.Close()
} }
for c := range cs { for c := range cs {
c.Close() c.Close()
} }
s.mu.Lock() s.mu.Lock()
if s.events != nil { if s.events != nil {
s.events.Finish() s.events.Finish()
@ -532,14 +730,23 @@ func (s *Server) Stop() {
s.mu.Unlock() s.mu.Unlock()
} }
// TestingCloseConns closes all exiting transports but keeps s.lis accepting new func init() {
// connections. This is for test only now. internal.TestingCloseConns = func(arg interface{}) {
func (s *Server) TestingCloseConns() { arg.(*Server).testingCloseConns()
}
internal.TestingUseHandlerImpl = func(arg interface{}) {
arg.(*Server).opts.useHandlerImpl = true
}
}
// testingCloseConns closes all existing transports but keeps s.lis
// accepting new connections.
func (s *Server) testingCloseConns() {
s.mu.Lock() s.mu.Lock()
for c := range s.conns { for c := range s.conns {
c.Close() c.Close()
delete(s.conns, c)
} }
s.conns = make(map[transport.ServerTransport]bool)
s.mu.Unlock() s.mu.Unlock()
} }

View file

@ -34,6 +34,7 @@
package grpc package grpc
import ( import (
"bytes"
"errors" "errors"
"io" "io"
"sync" "sync"
@ -46,12 +47,14 @@ import (
"google.golang.org/grpc/transport" "google.golang.org/grpc/transport"
) )
type streamHandler func(srv interface{}, stream ServerStream) error // StreamHandler defines the handler called by gRPC server to complete the
// execution of a streaming RPC.
type StreamHandler func(srv interface{}, stream ServerStream) error
// StreamDesc represents a streaming RPC service's method specification. // StreamDesc represents a streaming RPC service's method specification.
type StreamDesc struct { type StreamDesc struct {
StreamName string StreamName string
Handler streamHandler Handler StreamHandler
// At least one of these is true. // At least one of these is true.
ServerStreams bool ServerStreams bool
@ -66,18 +69,19 @@ type Stream interface {
// breaks. // breaks.
// On error, it aborts the stream and returns an RPC status on client // On error, it aborts the stream and returns an RPC status on client
// side. On server side, it simply returns the error to the caller. // side. On server side, it simply returns the error to the caller.
// SendMsg is called by generated code. // SendMsg is called by generated code. Also Users can call SendMsg
// directly when it is really needed in their use cases.
SendMsg(m interface{}) error SendMsg(m interface{}) error
// RecvMsg blocks until it receives a message or the stream is // RecvMsg blocks until it receives a message or the stream is
// done. On client side, it returns io.EOF when the stream is done. On // done. On client side, it returns io.EOF when the stream is done. On
// any other error, it aborts the streama nd returns an RPC status. On // any other error, it aborts the stream and returns an RPC status. On
// server side, it simply returns the error to the caller. // server side, it simply returns the error to the caller.
RecvMsg(m interface{}) error RecvMsg(m interface{}) error
} }
// ClientStream defines the interface a client stream has to satify. // ClientStream defines the interface a client stream has to satify.
type ClientStream interface { type ClientStream interface {
// Header returns the header metedata received from the server if there // Header returns the header metadata received from the server if there
// is any. It blocks if the metadata is not ready to read. // is any. It blocks if the metadata is not ready to read.
Header() (metadata.MD, error) Header() (metadata.MD, error)
// Trailer returns the trailer metadata from the server. It must be called // Trailer returns the trailer metadata from the server. It must be called
@ -108,12 +112,22 @@ func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
callHdr := &transport.CallHdr{ callHdr := &transport.CallHdr{
Host: cc.authority, Host: cc.authority,
Method: method, Method: method,
Flush: desc.ServerStreams && desc.ClientStreams,
}
if cc.dopts.cp != nil {
callHdr.SendCompress = cc.dopts.cp.Type()
} }
cs := &clientStream{ cs := &clientStream{
desc: desc, desc: desc,
codec: cc.dopts.codec, codec: cc.dopts.codec,
cp: cc.dopts.cp,
dc: cc.dopts.dc,
tracing: EnableTracing, tracing: EnableTracing,
} }
if cc.dopts.cp != nil {
callHdr.SendCompress = cc.dopts.cp.Type()
cs.cbuf = new(bytes.Buffer)
}
if cs.tracing { if cs.tracing {
cs.trInfo.tr = trace.New("grpc.Sent."+methodFamily(method), method) cs.trInfo.tr = trace.New("grpc.Sent."+methodFamily(method), method)
cs.trInfo.firstLine.client = true cs.trInfo.firstLine.client = true
@ -125,16 +139,23 @@ func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
} }
s, err := t.NewStream(ctx, callHdr) s, err := t.NewStream(ctx, callHdr)
if err != nil { if err != nil {
cs.finish(err)
return nil, toRPCErr(err) return nil, toRPCErr(err)
} }
cs.t = t cs.t = t
cs.s = s cs.s = s
cs.p = &parser{s: s} cs.p = &parser{r: s}
// Listen on ctx.Done() to detect cancellation when there is no pending // Listen on ctx.Done() to detect cancellation when there is no pending
// I/O operations on this stream. // I/O operations on this stream.
go func() { go func() {
<-s.Context().Done() select {
cs.closeTransportStream(transport.ContextErr(s.Context().Err())) case <-t.Error():
// Incur transport error, simply exit.
case <-s.Context().Done():
err := s.Context().Err()
cs.finish(err)
cs.closeTransportStream(transport.ContextErr(err))
}
}() }()
return cs, nil return cs, nil
} }
@ -146,6 +167,9 @@ type clientStream struct {
p *parser p *parser
desc *StreamDesc desc *StreamDesc
codec Codec codec Codec
cp Compressor
cbuf *bytes.Buffer
dc Decompressor
tracing bool // set to EnableTracing when the clientStream is created. tracing bool // set to EnableTracing when the clientStream is created.
@ -183,6 +207,9 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
cs.mu.Unlock() cs.mu.Unlock()
} }
defer func() { defer func() {
if err != nil {
cs.finish(err)
}
if err == nil || err == io.EOF { if err == nil || err == io.EOF {
return return
} }
@ -191,7 +218,12 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
} }
err = toRPCErr(err) err = toRPCErr(err)
}() }()
out, err := encode(cs.codec, m, compressionNone) out, err := encode(cs.codec, m, cs.cp, cs.cbuf)
defer func() {
if cs.cbuf != nil {
cs.cbuf.Reset()
}
}()
if err != nil { if err != nil {
return transport.StreamErrorf(codes.Internal, "grpc: %v", err) return transport.StreamErrorf(codes.Internal, "grpc: %v", err)
} }
@ -199,7 +231,7 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
} }
func (cs *clientStream) RecvMsg(m interface{}) (err error) { func (cs *clientStream) RecvMsg(m interface{}) (err error) {
err = recv(cs.p, cs.codec, m) err = recv(cs.p, cs.codec, cs.s, cs.dc, m)
defer func() { defer func() {
// err != nil indicates the termination of the stream. // err != nil indicates the termination of the stream.
if err != nil { if err != nil {
@ -218,16 +250,17 @@ func (cs *clientStream) RecvMsg(m interface{}) (err error) {
return return
} }
// Special handling for client streaming rpc. // Special handling for client streaming rpc.
err = recv(cs.p, cs.codec, m) err = recv(cs.p, cs.codec, cs.s, cs.dc, m)
cs.closeTransportStream(err) cs.closeTransportStream(err)
if err == nil { if err == nil {
return toRPCErr(errors.New("grpc: client streaming protocol violation: get <nil>, want <EOF>")) return toRPCErr(errors.New("grpc: client streaming protocol violation: get <nil>, want <EOF>"))
} }
if err == io.EOF { if err == io.EOF {
if cs.s.StatusCode() == codes.OK { if cs.s.StatusCode() == codes.OK {
cs.finish(err)
return nil return nil
} }
return Errorf(cs.s.StatusCode(), cs.s.StatusDesc()) return Errorf(cs.s.StatusCode(), "%s", cs.s.StatusDesc())
} }
return toRPCErr(err) return toRPCErr(err)
} }
@ -239,13 +272,18 @@ func (cs *clientStream) RecvMsg(m interface{}) (err error) {
// Returns io.EOF to indicate the end of the stream. // Returns io.EOF to indicate the end of the stream.
return return
} }
return Errorf(cs.s.StatusCode(), cs.s.StatusDesc()) return Errorf(cs.s.StatusCode(), "%s", cs.s.StatusDesc())
} }
return toRPCErr(err) return toRPCErr(err)
} }
func (cs *clientStream) CloseSend() (err error) { func (cs *clientStream) CloseSend() (err error) {
err = cs.t.Write(cs.s, nil, &transport.Options{Last: true}) err = cs.t.Write(cs.s, nil, &transport.Options{Last: true})
defer func() {
if err != nil {
cs.finish(err)
}
}()
if err == nil || err == io.EOF { if err == nil || err == io.EOF {
return return
} }
@ -303,6 +341,9 @@ type serverStream struct {
s *transport.Stream s *transport.Stream
p *parser p *parser
codec Codec codec Codec
cp Compressor
dc Decompressor
cbuf *bytes.Buffer
statusCode codes.Code statusCode codes.Code
statusDesc string statusDesc string
trInfo *traceInfo trInfo *traceInfo
@ -341,7 +382,12 @@ func (ss *serverStream) SendMsg(m interface{}) (err error) {
ss.mu.Unlock() ss.mu.Unlock()
} }
}() }()
out, err := encode(ss.codec, m, compressionNone) out, err := encode(ss.codec, m, ss.cp, ss.cbuf)
defer func() {
if ss.cbuf != nil {
ss.cbuf.Reset()
}
}()
if err != nil { if err != nil {
err = transport.StreamErrorf(codes.Internal, "grpc: %v", err) err = transport.StreamErrorf(codes.Internal, "grpc: %v", err)
return err return err
@ -364,5 +410,5 @@ func (ss *serverStream) RecvMsg(m interface{}) (err error) {
ss.mu.Unlock() ss.mu.Unlock()
} }
}() }()
return recv(ss.p, ss.codec, m) return recv(ss.p, ss.codec, ss.s, ss.dc, m)
} }

View file

@ -56,43 +56,33 @@ type windowUpdate struct {
increment uint32 increment uint32
} }
func (windowUpdate) isItem() bool { func (*windowUpdate) item() {}
return true
}
type settings struct { type settings struct {
ack bool ack bool
ss []http2.Setting ss []http2.Setting
} }
func (settings) isItem() bool { func (*settings) item() {}
return true
}
type resetStream struct { type resetStream struct {
streamID uint32 streamID uint32
code http2.ErrCode code http2.ErrCode
} }
func (resetStream) isItem() bool { func (*resetStream) item() {}
return true
}
type flushIO struct { type flushIO struct {
} }
func (flushIO) isItem() bool { func (*flushIO) item() {}
return true
}
type ping struct { type ping struct {
ack bool ack bool
data [8]byte data [8]byte
} }
func (ping) isItem() bool { func (*ping) item() {}
return true
}
// quotaPool is a pool which accumulates the quota and sends it to acquire() // quotaPool is a pool which accumulates the quota and sends it to acquire()
// when it is available. // when it is available.
@ -172,10 +162,6 @@ func (qb *quotaPool) acquire() <-chan int {
type inFlow struct { type inFlow struct {
// The inbound flow control limit for pending data. // The inbound flow control limit for pending data.
limit uint32 limit uint32
// conn points to the shared connection-level inFlow that is shared
// by all streams on that conn. It is nil for the inFlow on the conn
// directly.
conn *inFlow
mu sync.Mutex mu sync.Mutex
// pendingData is the overall data which have been received but not been // pendingData is the overall data which have been received but not been
@ -186,75 +172,39 @@ type inFlow struct {
pendingUpdate uint32 pendingUpdate uint32
} }
// onData is invoked when some data frame is received. It increments not only its // onData is invoked when some data frame is received. It updates pendingData.
// own pendingData but also that of the associated connection-level flow.
func (f *inFlow) onData(n uint32) error { func (f *inFlow) onData(n uint32) error {
if n == 0 {
return nil
}
f.mu.Lock() f.mu.Lock()
defer f.mu.Unlock() defer f.mu.Unlock()
if f.pendingData+f.pendingUpdate+n > f.limit {
return fmt.Errorf("recieved %d-bytes data exceeding the limit %d bytes", f.pendingData+f.pendingUpdate+n, f.limit)
}
if f.conn != nil {
if err := f.conn.onData(n); err != nil {
return ConnectionErrorf("%v", err)
}
}
f.pendingData += n f.pendingData += n
if f.pendingData+f.pendingUpdate > f.limit {
return fmt.Errorf("received %d-bytes data exceeding the limit %d bytes", f.pendingData+f.pendingUpdate, f.limit)
}
return nil return nil
} }
// connOnRead updates the connection level states when the application consumes data. // onRead is invoked when the application reads the data. It returns the window size
func (f *inFlow) connOnRead(n uint32) uint32 { // to be sent to the peer.
if n == 0 || f.conn != nil { func (f *inFlow) onRead(n uint32) uint32 {
return 0
}
f.mu.Lock() f.mu.Lock()
defer f.mu.Unlock() defer f.mu.Unlock()
if f.pendingData == 0 {
return 0
}
f.pendingData -= n f.pendingData -= n
f.pendingUpdate += n f.pendingUpdate += n
if f.pendingUpdate >= f.limit/4 { if f.pendingUpdate >= f.limit/4 {
ret := f.pendingUpdate wu := f.pendingUpdate
f.pendingUpdate = 0 f.pendingUpdate = 0
return ret return wu
} }
return 0 return 0
} }
// onRead is invoked when the application reads the data. It returns the window updates func (f *inFlow) resetPendingData() uint32 {
// for both stream and connection level.
func (f *inFlow) onRead(n uint32) (swu, cwu uint32) {
if n == 0 {
return
}
f.mu.Lock()
defer f.mu.Unlock()
if f.pendingData == 0 {
// pendingData has been adjusted by restoreConn.
return
}
f.pendingData -= n
f.pendingUpdate += n
if f.pendingUpdate >= f.limit/4 {
swu = f.pendingUpdate
f.pendingUpdate = 0
}
cwu = f.conn.connOnRead(n)
return
}
// restoreConn is invoked when a stream is terminated. It removes its stake in
// the connection-level flow and resets its own state.
func (f *inFlow) restoreConn() uint32 {
if f.conn == nil {
return 0
}
f.mu.Lock() f.mu.Lock()
defer f.mu.Unlock() defer f.mu.Unlock()
n := f.pendingData n := f.pendingData
f.pendingData = 0 f.pendingData = 0
f.pendingUpdate = 0 return n
return f.conn.connOnRead(n)
} }

View file

@ -0,0 +1,383 @@
/*
* Copyright 2016, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// This file is the implementation of a gRPC server using HTTP/2 which
// uses the standard Go http2 Server implementation (via the
// http.Handler interface), rather than speaking low-level HTTP/2
// frames itself. It is the implementation of *grpc.Server.ServeHTTP.
package transport
import (
"errors"
"fmt"
"io"
"net"
"net/http"
"strings"
"sync"
"time"
"golang.org/x/net/context"
"golang.org/x/net/http2"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
)
// NewServerHandlerTransport returns a ServerTransport handling gRPC
// from inside an http.Handler. It requires that the http Server
// supports HTTP/2.
func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request) (ServerTransport, error) {
if r.ProtoMajor != 2 {
return nil, errors.New("gRPC requires HTTP/2")
}
if r.Method != "POST" {
return nil, errors.New("invalid gRPC request method")
}
if !strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
return nil, errors.New("invalid gRPC request content-type")
}
if _, ok := w.(http.Flusher); !ok {
return nil, errors.New("gRPC requires a ResponseWriter supporting http.Flusher")
}
if _, ok := w.(http.CloseNotifier); !ok {
return nil, errors.New("gRPC requires a ResponseWriter supporting http.CloseNotifier")
}
st := &serverHandlerTransport{
rw: w,
req: r,
closedCh: make(chan struct{}),
writes: make(chan func()),
}
if v := r.Header.Get("grpc-timeout"); v != "" {
to, err := timeoutDecode(v)
if err != nil {
return nil, StreamErrorf(codes.Internal, "malformed time-out: %v", err)
}
st.timeoutSet = true
st.timeout = to
}
var metakv []string
for k, vv := range r.Header {
k = strings.ToLower(k)
if isReservedHeader(k) {
continue
}
for _, v := range vv {
if k == "user-agent" {
// user-agent is special. Copying logic of http_util.go.
if i := strings.LastIndex(v, " "); i == -1 {
// There is no application user agent string being set
continue
} else {
v = v[:i]
}
}
metakv = append(metakv, k, v)
}
}
st.headerMD = metadata.Pairs(metakv...)
return st, nil
}
// serverHandlerTransport is an implementation of ServerTransport
// which replies to exactly one gRPC request (exactly one HTTP request),
// using the net/http.Handler interface. This http.Handler is guaranteed
// at this point to be speaking over HTTP/2, so it's able to speak valid
// gRPC.
type serverHandlerTransport struct {
rw http.ResponseWriter
req *http.Request
timeoutSet bool
timeout time.Duration
didCommonHeaders bool
headerMD metadata.MD
closeOnce sync.Once
closedCh chan struct{} // closed on Close
// writes is a channel of code to run serialized in the
// ServeHTTP (HandleStreams) goroutine. The channel is closed
// when WriteStatus is called.
writes chan func()
}
func (ht *serverHandlerTransport) Close() error {
ht.closeOnce.Do(ht.closeCloseChanOnce)
return nil
}
func (ht *serverHandlerTransport) closeCloseChanOnce() { close(ht.closedCh) }
func (ht *serverHandlerTransport) RemoteAddr() net.Addr { return strAddr(ht.req.RemoteAddr) }
// strAddr is a net.Addr backed by either a TCP "ip:port" string, or
// the empty string if unknown.
type strAddr string
func (a strAddr) Network() string {
if a != "" {
// Per the documentation on net/http.Request.RemoteAddr, if this is
// set, it's set to the IP:port of the peer (hence, TCP):
// https://golang.org/pkg/net/http/#Request
//
// If we want to support Unix sockets later, we can
// add our own grpc-specific convention within the
// grpc codebase to set RemoteAddr to a different
// format, or probably better: we can attach it to the
// context and use that from serverHandlerTransport.RemoteAddr.
return "tcp"
}
return ""
}
func (a strAddr) String() string { return string(a) }
// do runs fn in the ServeHTTP goroutine.
func (ht *serverHandlerTransport) do(fn func()) error {
select {
case ht.writes <- fn:
return nil
case <-ht.closedCh:
return ErrConnClosing
}
}
func (ht *serverHandlerTransport) WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error {
err := ht.do(func() {
ht.writeCommonHeaders(s)
// And flush, in case no header or body has been sent yet.
// This forces a separation of headers and trailers if this is the
// first call (for example, in end2end tests's TestNoService).
ht.rw.(http.Flusher).Flush()
h := ht.rw.Header()
h.Set("Grpc-Status", fmt.Sprintf("%d", statusCode))
if statusDesc != "" {
h.Set("Grpc-Message", statusDesc)
}
if md := s.Trailer(); len(md) > 0 {
for k, vv := range md {
for _, v := range vv {
// http2 ResponseWriter mechanism to
// send undeclared Trailers after the
// headers have possibly been written.
h.Add(http2.TrailerPrefix+k, v)
}
}
}
})
close(ht.writes)
return err
}
// writeCommonHeaders sets common headers on the first write
// call (Write, WriteHeader, or WriteStatus).
func (ht *serverHandlerTransport) writeCommonHeaders(s *Stream) {
if ht.didCommonHeaders {
return
}
ht.didCommonHeaders = true
h := ht.rw.Header()
h["Date"] = nil // suppress Date to make tests happy; TODO: restore
h.Set("Content-Type", "application/grpc")
// Predeclare trailers we'll set later in WriteStatus (after the body).
// This is a SHOULD in the HTTP RFC, and the way you add (known)
// Trailers per the net/http.ResponseWriter contract.
// See https://golang.org/pkg/net/http/#ResponseWriter
// and https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
h.Add("Trailer", "Grpc-Status")
h.Add("Trailer", "Grpc-Message")
if s.sendCompress != "" {
h.Set("Grpc-Encoding", s.sendCompress)
}
}
func (ht *serverHandlerTransport) Write(s *Stream, data []byte, opts *Options) error {
return ht.do(func() {
ht.writeCommonHeaders(s)
ht.rw.Write(data)
if !opts.Delay {
ht.rw.(http.Flusher).Flush()
}
})
}
func (ht *serverHandlerTransport) WriteHeader(s *Stream, md metadata.MD) error {
return ht.do(func() {
ht.writeCommonHeaders(s)
h := ht.rw.Header()
for k, vv := range md {
for _, v := range vv {
h.Add(k, v)
}
}
ht.rw.WriteHeader(200)
ht.rw.(http.Flusher).Flush()
})
}
func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream)) {
// With this transport type there will be exactly 1 stream: this HTTP request.
var ctx context.Context
var cancel context.CancelFunc
if ht.timeoutSet {
ctx, cancel = context.WithTimeout(context.Background(), ht.timeout)
} else {
ctx, cancel = context.WithCancel(context.Background())
}
// requestOver is closed when either the request's context is done
// or the status has been written via WriteStatus.
requestOver := make(chan struct{})
// clientGone receives a single value if peer is gone, either
// because the underlying connection is dead or because the
// peer sends an http2 RST_STREAM.
clientGone := ht.rw.(http.CloseNotifier).CloseNotify()
go func() {
select {
case <-requestOver:
return
case <-ht.closedCh:
case <-clientGone:
}
cancel()
}()
req := ht.req
s := &Stream{
id: 0, // irrelevant
windowHandler: func(int) {}, // nothing
cancel: cancel,
buf: newRecvBuffer(),
st: ht,
method: req.URL.Path,
recvCompress: req.Header.Get("grpc-encoding"),
}
pr := &peer.Peer{
Addr: ht.RemoteAddr(),
}
if req.TLS != nil {
pr.AuthInfo = credentials.TLSInfo{*req.TLS}
}
ctx = metadata.NewContext(ctx, ht.headerMD)
ctx = peer.NewContext(ctx, pr)
s.ctx = newContextWithStream(ctx, s)
s.dec = &recvBufferReader{ctx: s.ctx, recv: s.buf}
// readerDone is closed when the Body.Read-ing goroutine exits.
readerDone := make(chan struct{})
go func() {
defer close(readerDone)
// TODO: minimize garbage, optimize recvBuffer code/ownership
const readSize = 8196
for buf := make([]byte, readSize); ; {
n, err := req.Body.Read(buf)
if n > 0 {
s.buf.put(&recvMsg{data: buf[:n:n]})
buf = buf[n:]
}
if err != nil {
s.buf.put(&recvMsg{err: mapRecvMsgError(err)})
return
}
if len(buf) == 0 {
buf = make([]byte, readSize)
}
}
}()
// startStream is provided by the *grpc.Server's serveStreams.
// It starts a goroutine serving s and exits immediately.
// The goroutine that is started is the one that then calls
// into ht, calling WriteHeader, Write, WriteStatus, Close, etc.
startStream(s)
ht.runStream()
close(requestOver)
// Wait for reading goroutine to finish.
req.Body.Close()
<-readerDone
}
func (ht *serverHandlerTransport) runStream() {
for {
select {
case fn, ok := <-ht.writes:
if !ok {
return
}
fn()
case <-ht.closedCh:
return
}
}
}
// mapRecvMsgError returns the non-nil err into the appropriate
// error value as expected by callers of *grpc.parser.recvMsg.
// In particular, in can only be:
// * io.EOF
// * io.ErrUnexpectedEOF
// * of type transport.ConnectionError
// * of type transport.StreamError
func mapRecvMsgError(err error) error {
if err == io.EOF || err == io.ErrUnexpectedEOF {
return err
}
if se, ok := err.(http2.StreamError); ok {
if code, ok := http2ErrConvTab[se.Code]; ok {
return StreamError{
Code: code,
Desc: se.Error(),
}
}
}
return ConnectionError{Desc: err.Error()}
}

View file

@ -50,6 +50,7 @@ import (
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
) )
// http2Client implements the ClientTransport interface with HTTP2. // http2Client implements the ClientTransport interface with HTTP2.
@ -139,29 +140,6 @@ func newHTTP2Client(addr string, opts *ConnectOptions) (_ ClientTransport, err e
conn.Close() conn.Close()
} }
}() }()
// Send connection preface to server.
n, err := conn.Write(clientPreface)
if err != nil {
return nil, ConnectionErrorf("transport: %v", err)
}
if n != len(clientPreface) {
return nil, ConnectionErrorf("transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface))
}
framer := newFramer(conn)
if initialWindowSize != defaultWindowSize {
err = framer.writeSettings(true, http2.Setting{http2.SettingInitialWindowSize, uint32(initialWindowSize)})
} else {
err = framer.writeSettings(true)
}
if err != nil {
return nil, ConnectionErrorf("transport: %v", err)
}
// Adjust the connection flow control window if needed.
if delta := uint32(initialConnWindowSize - defaultWindowSize); delta > 0 {
if err := framer.writeWindowUpdate(true, 0, delta); err != nil {
return nil, ConnectionErrorf("transport: %v", err)
}
}
ua := primaryUA ua := primaryUA
if opts.UserAgent != "" { if opts.UserAgent != "" {
ua = opts.UserAgent + " " + ua ua = opts.UserAgent + " " + ua
@ -177,7 +155,7 @@ func newHTTP2Client(addr string, opts *ConnectOptions) (_ ClientTransport, err e
writableChan: make(chan int, 1), writableChan: make(chan int, 1),
shutdownChan: make(chan struct{}), shutdownChan: make(chan struct{}),
errorChan: make(chan struct{}), errorChan: make(chan struct{}),
framer: framer, framer: newFramer(conn),
hBuf: &buf, hBuf: &buf,
hEnc: hpack.NewEncoder(&buf), hEnc: hpack.NewEncoder(&buf),
controlBuf: newRecvBuffer(), controlBuf: newRecvBuffer(),
@ -190,27 +168,49 @@ func newHTTP2Client(addr string, opts *ConnectOptions) (_ ClientTransport, err e
maxStreams: math.MaxInt32, maxStreams: math.MaxInt32,
streamSendQuota: defaultWindowSize, streamSendQuota: defaultWindowSize,
} }
// Start the reader goroutine for incoming message. Each transport has
// a dedicated goroutine which reads HTTP2 frame from network. Then it
// dispatches the frame to the corresponding stream entity.
go t.reader()
// Send connection preface to server.
n, err := t.conn.Write(clientPreface)
if err != nil {
t.Close()
return nil, ConnectionErrorf("transport: %v", err)
}
if n != len(clientPreface) {
t.Close()
return nil, ConnectionErrorf("transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface))
}
if initialWindowSize != defaultWindowSize {
err = t.framer.writeSettings(true, http2.Setting{http2.SettingInitialWindowSize, uint32(initialWindowSize)})
} else {
err = t.framer.writeSettings(true)
}
if err != nil {
t.Close()
return nil, ConnectionErrorf("transport: %v", err)
}
// Adjust the connection flow control window if needed.
if delta := uint32(initialConnWindowSize - defaultWindowSize); delta > 0 {
if err := t.framer.writeWindowUpdate(true, 0, delta); err != nil {
t.Close()
return nil, ConnectionErrorf("transport: %v", err)
}
}
go t.controller() go t.controller()
t.writableChan <- 0 t.writableChan <- 0
// Start the reader goroutine for incoming message. The threading model
// on receiving is that each transport has a dedicated goroutine which
// reads HTTP2 frame from network. Then it dispatches the frame to the
// corresponding stream entity.
go t.reader()
return t, nil return t, nil
} }
func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream { func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
fc := &inFlow{
limit: initialWindowSize,
conn: t.fc,
}
// TODO(zhaoq): Handle uint32 overflow of Stream.id. // TODO(zhaoq): Handle uint32 overflow of Stream.id.
s := &Stream{ s := &Stream{
id: t.nextID, id: t.nextID,
method: callHdr.Method, method: callHdr.Method,
sendCompress: callHdr.SendCompress,
buf: newRecvBuffer(), buf: newRecvBuffer(),
fc: fc, fc: &inFlow{limit: initialWindowSize},
sendQuotaPool: newQuotaPool(int(t.streamSendQuota)), sendQuotaPool: newQuotaPool(int(t.streamSendQuota)),
headerChan: make(chan struct{}), headerChan: make(chan struct{}),
} }
@ -234,14 +234,20 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
var timeout time.Duration var timeout time.Duration
if dl, ok := ctx.Deadline(); ok { if dl, ok := ctx.Deadline(); ok {
timeout = dl.Sub(time.Now()) timeout = dl.Sub(time.Now())
if timeout <= 0 { }
return nil, ContextErr(context.DeadlineExceeded) select {
} case <-ctx.Done():
return nil, ContextErr(ctx.Err())
default:
}
pr := &peer.Peer{
Addr: t.conn.RemoteAddr(),
} }
// Attach Auth info if there is any. // Attach Auth info if there is any.
if t.authInfo != nil { if t.authInfo != nil {
ctx = credentials.NewContext(ctx, t.authInfo) pr.AuthInfo = t.authInfo
} }
ctx = peer.NewContext(ctx, pr)
authData := make(map[string]string) authData := make(map[string]string)
for _, c := range t.authCreds { for _, c := range t.authCreds {
// Construct URI required to get auth request metadata. // Construct URI required to get auth request metadata.
@ -317,10 +323,15 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
t.hEnc.WriteField(hpack.HeaderField{Name: "user-agent", Value: t.userAgent}) t.hEnc.WriteField(hpack.HeaderField{Name: "user-agent", Value: t.userAgent})
t.hEnc.WriteField(hpack.HeaderField{Name: "te", Value: "trailers"}) t.hEnc.WriteField(hpack.HeaderField{Name: "te", Value: "trailers"})
if callHdr.SendCompress != "" {
t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-encoding", Value: callHdr.SendCompress})
}
if timeout > 0 { if timeout > 0 {
t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-timeout", Value: timeoutEncode(timeout)}) t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-timeout", Value: timeoutEncode(timeout)})
} }
for k, v := range authData { for k, v := range authData {
// Capital header names are illegal in HTTP/2.
k = strings.ToLower(k)
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: v}) t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: v})
} }
var ( var (
@ -344,6 +355,10 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
} else { } else {
endHeaders = true endHeaders = true
} }
var flush bool
if endHeaders && (hasMD || callHdr.Flush) {
flush = true
}
if first { if first {
// Sends a HeadersFrame to server to start a new stream. // Sends a HeadersFrame to server to start a new stream.
p := http2.HeadersFrameParam{ p := http2.HeadersFrameParam{
@ -355,11 +370,11 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
// Do a force flush for the buffered frames iff it is the last headers frame // Do a force flush for the buffered frames iff it is the last headers frame
// and there is header metadata to be sent. Otherwise, there is flushing until // and there is header metadata to be sent. Otherwise, there is flushing until
// the corresponding data frame is written. // the corresponding data frame is written.
err = t.framer.writeHeaders(hasMD && endHeaders, p) err = t.framer.writeHeaders(flush, p)
first = false first = false
} else { } else {
// Sends Continuation frames for the leftover headers. // Sends Continuation frames for the leftover headers.
err = t.framer.writeContinuation(hasMD && endHeaders, s.id, endHeaders, t.hBuf.Next(size)) err = t.framer.writeContinuation(flush, s.id, endHeaders, t.hBuf.Next(size))
} }
if err != nil { if err != nil {
t.notifyError(err) t.notifyError(err)
@ -389,8 +404,10 @@ func (t *http2Client) CloseStream(s *Stream, err error) {
// other goroutines. // other goroutines.
s.cancel() s.cancel()
s.mu.Lock() s.mu.Lock()
if q := s.fc.restoreConn(); q > 0 { if q := s.fc.resetPendingData(); q > 0 {
t.controlBuf.put(&windowUpdate{0, q}) if n := t.fc.onRead(q); n > 0 {
t.controlBuf.put(&windowUpdate{0, n})
}
} }
if s.state == streamDone { if s.state == streamDone {
s.mu.Unlock() s.mu.Unlock()
@ -412,6 +429,9 @@ func (t *http2Client) CloseStream(s *Stream, err error) {
// accessed any more. // accessed any more.
func (t *http2Client) Close() (err error) { func (t *http2Client) Close() (err error) {
t.mu.Lock() t.mu.Lock()
if t.state == reachable {
close(t.errorChan)
}
if t.state == closing { if t.state == closing {
t.mu.Unlock() t.mu.Unlock()
return errors.New("transport: Close() was already called") return errors.New("transport: Close() was already called")
@ -490,6 +510,10 @@ func (t *http2Client) Write(s *Stream, data []byte, opts *Options) error {
t.framer.adjustNumWriters(1) t.framer.adjustNumWriters(1)
// Got some quota. Try to acquire writing privilege on the transport. // Got some quota. Try to acquire writing privilege on the transport.
if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil { if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
if _, ok := err.(StreamError); ok {
// Return the connection quota back.
t.sendQuotaPool.add(len(p))
}
if t.framer.adjustNumWriters(-1) == 0 { if t.framer.adjustNumWriters(-1) == 0 {
// This writer is the last one in this batch and has the // This writer is the last one in this batch and has the
// responsibility to flush the buffered frames. It queues // responsibility to flush the buffered frames. It queues
@ -499,6 +523,16 @@ func (t *http2Client) Write(s *Stream, data []byte, opts *Options) error {
} }
return err return err
} }
select {
case <-s.ctx.Done():
t.sendQuotaPool.add(len(p))
if t.framer.adjustNumWriters(-1) == 0 {
t.controlBuf.put(&flushIO{})
}
t.writableChan <- 0
return ContextErr(s.ctx.Err())
default:
}
if r.Len() == 0 && t.framer.adjustNumWriters(0) == 1 { if r.Len() == 0 && t.framer.adjustNumWriters(0) == 1 {
// Do a force flush iff this is last frame for the entire gRPC message // Do a force flush iff this is last frame for the entire gRPC message
// and the caller is the only writer at this moment. // and the caller is the only writer at this moment.
@ -537,47 +571,52 @@ func (t *http2Client) Write(s *Stream, data []byte, opts *Options) error {
func (t *http2Client) getStream(f http2.Frame) (*Stream, bool) { func (t *http2Client) getStream(f http2.Frame) (*Stream, bool) {
t.mu.Lock() t.mu.Lock()
defer t.mu.Unlock() defer t.mu.Unlock()
if t.activeStreams == nil { s, ok := t.activeStreams[f.Header().StreamID]
// The transport is closing. return s, ok
return nil, false
}
if s, ok := t.activeStreams[f.Header().StreamID]; ok {
return s, true
}
return nil, false
} }
// updateWindow adjusts the inbound quota for the stream and the transport. // updateWindow adjusts the inbound quota for the stream and the transport.
// Window updates will deliver to the controller for sending when // Window updates will deliver to the controller for sending when
// the cumulative quota exceeds the corresponding threshold. // the cumulative quota exceeds the corresponding threshold.
func (t *http2Client) updateWindow(s *Stream, n uint32) { func (t *http2Client) updateWindow(s *Stream, n uint32) {
swu, cwu := s.fc.onRead(n) s.mu.Lock()
if swu > 0 { defer s.mu.Unlock()
t.controlBuf.put(&windowUpdate{s.id, swu}) if s.state == streamDone {
return
} }
if cwu > 0 { if w := t.fc.onRead(n); w > 0 {
t.controlBuf.put(&windowUpdate{0, cwu}) t.controlBuf.put(&windowUpdate{0, w})
}
if w := s.fc.onRead(n); w > 0 {
t.controlBuf.put(&windowUpdate{s.id, w})
} }
} }
func (t *http2Client) handleData(f *http2.DataFrame) { func (t *http2Client) handleData(f *http2.DataFrame) {
size := len(f.Data())
if err := t.fc.onData(uint32(size)); err != nil {
t.notifyError(ConnectionErrorf("%v", err))
return
}
// Select the right stream to dispatch. // Select the right stream to dispatch.
s, ok := t.getStream(f) s, ok := t.getStream(f)
if !ok { if !ok {
if w := t.fc.onRead(uint32(size)); w > 0 {
t.controlBuf.put(&windowUpdate{0, w})
}
return return
} }
size := len(f.Data())
if size > 0 { if size > 0 {
s.mu.Lock()
if s.state == streamDone {
s.mu.Unlock()
// The stream has been closed. Release the corresponding quota.
if w := t.fc.onRead(uint32(size)); w > 0 {
t.controlBuf.put(&windowUpdate{0, w})
}
return
}
if err := s.fc.onData(uint32(size)); err != nil { if err := s.fc.onData(uint32(size)); err != nil {
if _, ok := err.(ConnectionError); ok {
t.notifyError(err)
return
}
s.mu.Lock()
if s.state == streamDone {
s.mu.Unlock()
return
}
s.state = streamDone s.state = streamDone
s.statusCode = codes.Internal s.statusCode = codes.Internal
s.statusDesc = err.Error() s.statusDesc = err.Error()
@ -586,6 +625,7 @@ func (t *http2Client) handleData(f *http2.DataFrame) {
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeFlowControl}) t.controlBuf.put(&resetStream{s.id, http2.ErrCodeFlowControl})
return return
} }
s.mu.Unlock()
// TODO(bradfitz, zhaoq): A copy is required here because there is no // TODO(bradfitz, zhaoq): A copy is required here because there is no
// guarantee f.Data() is consumed before the arrival of next frame. // guarantee f.Data() is consumed before the arrival of next frame.
// Can this copy be eliminated? // Can this copy be eliminated?
@ -624,9 +664,10 @@ func (t *http2Client) handleRSTStream(f *http2.RSTStreamFrame) {
close(s.headerChan) close(s.headerChan)
s.headerDone = true s.headerDone = true
} }
s.statusCode, ok = http2RSTErrConvTab[http2.ErrCode(f.ErrCode)] s.statusCode, ok = http2ErrConvTab[http2.ErrCode(f.ErrCode)]
if !ok { if !ok {
grpclog.Println("transport: http2Client.handleRSTStream found no mapped gRPC status for the received http2 error ", f.ErrCode) grpclog.Println("transport: http2Client.handleRSTStream found no mapped gRPC status for the received http2 error ", f.ErrCode)
s.statusCode = codes.Unknown
} }
s.mu.Unlock() s.mu.Unlock()
s.write(recvMsg{err: io.EOF}) s.write(recvMsg{err: io.EOF})
@ -667,52 +708,59 @@ func (t *http2Client) handleWindowUpdate(f *http2.WindowUpdateFrame) {
} }
} }
// operateHeader takes action on the decoded headers. It returns the current // operateHeaders takes action on the decoded headers.
// stream if there are remaining headers on the wire (in the following func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
// Continuation frame). s, ok := t.getStream(frame)
func (t *http2Client) operateHeaders(hDec *hpackDecoder, s *Stream, frame headerFrame, endStream bool) (pendingStream *Stream) { if !ok {
defer func() { return
if pendingStream == nil {
hDec.state = decodeState{}
}
}()
endHeaders, err := hDec.decodeClientHTTP2Headers(frame)
if s == nil {
// s has been closed.
return nil
} }
if err != nil { var state decodeState
s.write(recvMsg{err: err}) for _, hf := range frame.Fields {
state.processHeaderField(hf)
}
if state.err != nil {
s.write(recvMsg{err: state.err})
// Something wrong. Stops reading even when there is remaining. // Something wrong. Stops reading even when there is remaining.
return nil return
}
if !endHeaders {
return s
} }
endStream := frame.StreamEnded()
s.mu.Lock() s.mu.Lock()
if !endStream {
s.recvCompress = state.encoding
}
if !s.headerDone { if !s.headerDone {
if !endStream && len(hDec.state.mdata) > 0 { if !endStream && len(state.mdata) > 0 {
s.header = hDec.state.mdata s.header = state.mdata
} }
close(s.headerChan) close(s.headerChan)
s.headerDone = true s.headerDone = true
} }
if !endStream || s.state == streamDone { if !endStream || s.state == streamDone {
s.mu.Unlock() s.mu.Unlock()
return nil return
} }
if len(hDec.state.mdata) > 0 { if len(state.mdata) > 0 {
s.trailer = hDec.state.mdata s.trailer = state.mdata
} }
s.state = streamDone s.state = streamDone
s.statusCode = hDec.state.statusCode s.statusCode = state.statusCode
s.statusDesc = hDec.state.statusDesc s.statusDesc = state.statusDesc
s.mu.Unlock() s.mu.Unlock()
s.write(recvMsg{err: io.EOF}) s.write(recvMsg{err: io.EOF})
return nil }
func handleMalformedHTTP2(s *Stream, err error) {
s.mu.Lock()
if !s.headerDone {
close(s.headerChan)
s.headerDone = true
}
s.mu.Unlock()
s.write(recvMsg{err: err})
} }
// reader runs as a separate goroutine in charge of reading data from network // reader runs as a separate goroutine in charge of reading data from network
@ -735,25 +783,31 @@ func (t *http2Client) reader() {
} }
t.handleSettings(sf) t.handleSettings(sf)
hDec := newHPACKDecoder()
var curStream *Stream
// loop to keep reading incoming messages on this transport. // loop to keep reading incoming messages on this transport.
for { for {
frame, err := t.framer.readFrame() frame, err := t.framer.readFrame()
if err != nil { if err != nil {
t.notifyError(err) // Abort an active stream if the http2.Framer returns a
return // http2.StreamError. This can happen only if the server's response
// is malformed http2.
if se, ok := err.(http2.StreamError); ok {
t.mu.Lock()
s := t.activeStreams[se.StreamID]
t.mu.Unlock()
if s != nil {
// use error detail to provide better err message
handleMalformedHTTP2(s, StreamErrorf(http2ErrConvTab[se.Code], "%v", t.framer.errorDetail()))
}
continue
} else {
// Transport error.
t.notifyError(err)
return
}
} }
switch frame := frame.(type) { switch frame := frame.(type) {
case *http2.HeadersFrame: case *http2.MetaHeadersFrame:
// operateHeaders has to be invoked regardless the value of curStream t.operateHeaders(frame)
// because the HPACK decoder needs to be updated using the received
// headers.
curStream, _ = t.getStream(frame)
endStream := frame.Header().Flags.Has(http2.FlagHeadersEndStream)
curStream = t.operateHeaders(hDec, curStream, frame, endStream)
case *http2.ContinuationFrame:
curStream = t.operateHeaders(hDec, curStream, frame, frame.HeadersEnded())
case *http2.DataFrame: case *http2.DataFrame:
t.handleData(frame) t.handleData(frame)
case *http2.RSTStreamFrame: case *http2.RSTStreamFrame:

View file

@ -49,6 +49,7 @@ import (
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
) )
// ErrIllegalHeaderWrite indicates that setting header is illegal because of // ErrIllegalHeaderWrite indicates that setting header is illegal because of
@ -61,8 +62,8 @@ type http2Server struct {
maxStreamID uint32 // max stream ID ever seen maxStreamID uint32 // max stream ID ever seen
authInfo credentials.AuthInfo // auth info about the connection authInfo credentials.AuthInfo // auth info about the connection
// writableChan synchronizes write access to the transport. // writableChan synchronizes write access to the transport.
// A writer acquires the write lock by sending a value on writableChan // A writer acquires the write lock by receiving a value on writableChan
// and releases it by receiving from writableChan. // and releases it by sending on writableChan.
writableChan chan int writableChan chan int
// shutdownChan is closed when Close is called. // shutdownChan is closed when Close is called.
// Blocking operations should select on shutdownChan to avoid // Blocking operations should select on shutdownChan to avoid
@ -135,66 +136,69 @@ func newHTTP2Server(conn net.Conn, maxStreams uint32, authInfo credentials.AuthI
return t, nil return t, nil
} }
// operateHeader takes action on the decoded headers. It returns the current // operateHeader takes action on the decoded headers.
// stream if there are remaining headers on the wire (in the following func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream)) {
// Continuation frame). buf := newRecvBuffer()
func (t *http2Server) operateHeaders(hDec *hpackDecoder, s *Stream, frame headerFrame, endStream bool, handle func(*Stream)) (pendingStream *Stream) { s := &Stream{
defer func() { id: frame.Header().StreamID,
if pendingStream == nil { st: t,
hDec.state = decodeState{} buf: buf,
} fc: &inFlow{limit: initialWindowSize},
}()
endHeaders, err := hDec.decodeServerHTTP2Headers(frame)
if s == nil {
// s has been closed.
return nil
} }
if err != nil {
grpclog.Printf("transport: http2Server.operateHeader found %v", err) var state decodeState
for _, hf := range frame.Fields {
state.processHeaderField(hf)
}
if err := state.err; err != nil {
if se, ok := err.(StreamError); ok { if se, ok := err.(StreamError); ok {
t.controlBuf.put(&resetStream{s.id, statusCodeConvTab[se.Code]}) t.controlBuf.put(&resetStream{s.id, statusCodeConvTab[se.Code]})
} }
return nil return
} }
if endStream {
if frame.StreamEnded() {
// s is just created by the caller. No lock needed. // s is just created by the caller. No lock needed.
s.state = streamReadDone s.state = streamReadDone
} }
if !endHeaders { s.recvCompress = state.encoding
return s if state.timeoutSet {
} s.ctx, s.cancel = context.WithTimeout(context.TODO(), state.timeout)
if hDec.state.timeoutSet {
s.ctx, s.cancel = context.WithTimeout(context.TODO(), hDec.state.timeout)
} else { } else {
s.ctx, s.cancel = context.WithCancel(context.TODO()) s.ctx, s.cancel = context.WithCancel(context.TODO())
} }
pr := &peer.Peer{
Addr: t.conn.RemoteAddr(),
}
// Attach Auth info if there is any. // Attach Auth info if there is any.
if t.authInfo != nil { if t.authInfo != nil {
s.ctx = credentials.NewContext(s.ctx, t.authInfo) pr.AuthInfo = t.authInfo
} }
s.ctx = peer.NewContext(s.ctx, pr)
// Cache the current stream to the context so that the server application // Cache the current stream to the context so that the server application
// can find out. Required when the server wants to send some metadata // can find out. Required when the server wants to send some metadata
// back to the client (unary call only). // back to the client (unary call only).
s.ctx = newContextWithStream(s.ctx, s) s.ctx = newContextWithStream(s.ctx, s)
// Attach the received metadata to the context. // Attach the received metadata to the context.
if len(hDec.state.mdata) > 0 { if len(state.mdata) > 0 {
s.ctx = metadata.NewContext(s.ctx, hDec.state.mdata) s.ctx = metadata.NewContext(s.ctx, state.mdata)
} }
s.dec = &recvBufferReader{ s.dec = &recvBufferReader{
ctx: s.ctx, ctx: s.ctx,
recv: s.buf, recv: s.buf,
} }
s.method = hDec.state.method s.recvCompress = state.encoding
s.method = state.method
t.mu.Lock() t.mu.Lock()
if t.state != reachable { if t.state != reachable {
t.mu.Unlock() t.mu.Unlock()
return nil return
} }
if uint32(len(t.activeStreams)) >= t.maxStreams { if uint32(len(t.activeStreams)) >= t.maxStreams {
t.mu.Unlock() t.mu.Unlock()
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeRefusedStream}) t.controlBuf.put(&resetStream{s.id, http2.ErrCodeRefusedStream})
return nil return
} }
s.sendQuotaPool = newQuotaPool(int(t.streamSendQuota)) s.sendQuotaPool = newQuotaPool(int(t.streamSendQuota))
t.activeStreams[s.id] = s t.activeStreams[s.id] = s
@ -203,7 +207,6 @@ func (t *http2Server) operateHeaders(hDec *hpackDecoder, s *Stream, frame header
t.updateWindow(s, uint32(n)) t.updateWindow(s, uint32(n))
} }
handle(s) handle(s)
return nil
} }
// HandleStreams receives incoming streams using the given handler. This is // HandleStreams receives incoming streams using the given handler. This is
@ -236,16 +239,24 @@ func (t *http2Server) HandleStreams(handle func(*Stream)) {
} }
t.handleSettings(sf) t.handleSettings(sf)
hDec := newHPACKDecoder()
var curStream *Stream
for { for {
frame, err := t.framer.readFrame() frame, err := t.framer.readFrame()
if err != nil { if err != nil {
if se, ok := err.(http2.StreamError); ok {
t.mu.Lock()
s := t.activeStreams[se.StreamID]
t.mu.Unlock()
if s != nil {
t.closeStream(s)
}
t.controlBuf.put(&resetStream{se.StreamID, se.Code})
continue
}
t.Close() t.Close()
return return
} }
switch frame := frame.(type) { switch frame := frame.(type) {
case *http2.HeadersFrame: case *http2.MetaHeadersFrame:
id := frame.Header().StreamID id := frame.Header().StreamID
if id%2 != 1 || id <= t.maxStreamID { if id%2 != 1 || id <= t.maxStreamID {
// illegal gRPC stream id. // illegal gRPC stream id.
@ -254,21 +265,7 @@ func (t *http2Server) HandleStreams(handle func(*Stream)) {
break break
} }
t.maxStreamID = id t.maxStreamID = id
buf := newRecvBuffer() t.operateHeaders(frame, handle)
fc := &inFlow{
limit: initialWindowSize,
conn: t.fc,
}
curStream = &Stream{
id: frame.Header().StreamID,
st: t,
buf: buf,
fc: fc,
}
endStream := frame.Header().Flags.Has(http2.FlagHeadersEndStream)
curStream = t.operateHeaders(hDec, curStream, frame, endStream, handle)
case *http2.ContinuationFrame:
curStream = t.operateHeaders(hDec, curStream, frame, frame.HeadersEnded(), handle)
case *http2.DataFrame: case *http2.DataFrame:
t.handleData(frame) t.handleData(frame)
case *http2.RSTStreamFrame: case *http2.RSTStreamFrame:
@ -306,33 +303,51 @@ func (t *http2Server) getStream(f http2.Frame) (*Stream, bool) {
// Window updates will deliver to the controller for sending when // Window updates will deliver to the controller for sending when
// the cumulative quota exceeds the corresponding threshold. // the cumulative quota exceeds the corresponding threshold.
func (t *http2Server) updateWindow(s *Stream, n uint32) { func (t *http2Server) updateWindow(s *Stream, n uint32) {
swu, cwu := s.fc.onRead(n) s.mu.Lock()
if swu > 0 { defer s.mu.Unlock()
t.controlBuf.put(&windowUpdate{s.id, swu}) if s.state == streamDone {
return
} }
if cwu > 0 { if w := t.fc.onRead(n); w > 0 {
t.controlBuf.put(&windowUpdate{0, cwu}) t.controlBuf.put(&windowUpdate{0, w})
}
if w := s.fc.onRead(n); w > 0 {
t.controlBuf.put(&windowUpdate{s.id, w})
} }
} }
func (t *http2Server) handleData(f *http2.DataFrame) { func (t *http2Server) handleData(f *http2.DataFrame) {
size := len(f.Data())
if err := t.fc.onData(uint32(size)); err != nil {
grpclog.Printf("transport: http2Server %v", err)
t.Close()
return
}
// Select the right stream to dispatch. // Select the right stream to dispatch.
s, ok := t.getStream(f) s, ok := t.getStream(f)
if !ok { if !ok {
if w := t.fc.onRead(uint32(size)); w > 0 {
t.controlBuf.put(&windowUpdate{0, w})
}
return return
} }
size := len(f.Data())
if size > 0 { if size > 0 {
if err := s.fc.onData(uint32(size)); err != nil { s.mu.Lock()
if _, ok := err.(ConnectionError); ok { if s.state == streamDone {
grpclog.Printf("transport: http2Server %v", err) s.mu.Unlock()
t.Close() // The stream has been closed. Release the corresponding quota.
return if w := t.fc.onRead(uint32(size)); w > 0 {
t.controlBuf.put(&windowUpdate{0, w})
} }
return
}
if err := s.fc.onData(uint32(size)); err != nil {
s.mu.Unlock()
t.closeStream(s) t.closeStream(s)
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeFlowControl}) t.controlBuf.put(&resetStream{s.id, http2.ErrCodeFlowControl})
return return
} }
s.mu.Unlock()
// TODO(bradfitz, zhaoq): A copy is required here because there is no // TODO(bradfitz, zhaoq): A copy is required here because there is no
// guarantee f.Data() is consumed before the arrival of next frame. // guarantee f.Data() is consumed before the arrival of next frame.
// Can this copy be eliminated? // Can this copy be eliminated?
@ -441,6 +456,9 @@ func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
t.hBuf.Reset() t.hBuf.Reset()
t.hEnc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"}) t.hEnc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"}) t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"})
if s.sendCompress != "" {
t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress})
}
for k, v := range md { for k, v := range md {
for _, entry := range v { for _, entry := range v {
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry}) t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
@ -503,6 +521,10 @@ func (t *http2Server) Write(s *Stream, data []byte, opts *Options) error {
// TODO(zhaoq): Support multi-writers for a single stream. // TODO(zhaoq): Support multi-writers for a single stream.
var writeHeaderFrame bool var writeHeaderFrame bool
s.mu.Lock() s.mu.Lock()
if s.state == streamDone {
s.mu.Unlock()
return StreamErrorf(codes.Unknown, "the stream has been done")
}
if !s.headerOk { if !s.headerOk {
writeHeaderFrame = true writeHeaderFrame = true
s.headerOk = true s.headerOk = true
@ -515,6 +537,9 @@ func (t *http2Server) Write(s *Stream, data []byte, opts *Options) error {
t.hBuf.Reset() t.hBuf.Reset()
t.hEnc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"}) t.hEnc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"}) t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"})
if s.sendCompress != "" {
t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress})
}
p := http2.HeadersFrameParam{ p := http2.HeadersFrameParam{
StreamID: s.id, StreamID: s.id,
BlockFragment: t.hBuf.Bytes(), BlockFragment: t.hBuf.Bytes(),
@ -567,6 +592,10 @@ func (t *http2Server) Write(s *Stream, data []byte, opts *Options) error {
// Got some quota. Try to acquire writing privilege on the // Got some quota. Try to acquire writing privilege on the
// transport. // transport.
if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil { if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
if _, ok := err.(StreamError); ok {
// Return the connection quota back.
t.sendQuotaPool.add(ps)
}
if t.framer.adjustNumWriters(-1) == 0 { if t.framer.adjustNumWriters(-1) == 0 {
// This writer is the last one in this batch and has the // This writer is the last one in this batch and has the
// responsibility to flush the buffered frames. It queues // responsibility to flush the buffered frames. It queues
@ -576,6 +605,16 @@ func (t *http2Server) Write(s *Stream, data []byte, opts *Options) error {
} }
return err return err
} }
select {
case <-s.ctx.Done():
t.sendQuotaPool.add(ps)
if t.framer.adjustNumWriters(-1) == 0 {
t.controlBuf.put(&flushIO{})
}
t.writableChan <- 0
return ContextErr(s.ctx.Err())
default:
}
var forceFlush bool var forceFlush bool
if r.Len() == 0 && t.framer.adjustNumWriters(0) == 1 && !opts.Last { if r.Len() == 0 && t.framer.adjustNumWriters(0) == 1 && !opts.Last {
forceFlush = true forceFlush = true
@ -673,20 +712,22 @@ func (t *http2Server) closeStream(s *Stream) {
t.mu.Lock() t.mu.Lock()
delete(t.activeStreams, s.id) delete(t.activeStreams, s.id)
t.mu.Unlock() t.mu.Unlock()
if q := s.fc.restoreConn(); q > 0 { // In case stream sending and receiving are invoked in separate
t.controlBuf.put(&windowUpdate{0, q}) // goroutines (e.g., bi-directional streaming), cancel needs to be
} // called to interrupt the potential blocking on other goroutines.
s.cancel()
s.mu.Lock() s.mu.Lock()
if q := s.fc.resetPendingData(); q > 0 {
if w := t.fc.onRead(q); w > 0 {
t.controlBuf.put(&windowUpdate{0, w})
}
}
if s.state == streamDone { if s.state == streamDone {
s.mu.Unlock() s.mu.Unlock()
return return
} }
s.state = streamDone s.state = streamDone
s.mu.Unlock() s.mu.Unlock()
// In case stream sending and receiving are invoked in separate
// goroutines (e.g., bi-directional streaming), cancel needs to be
// called to interrupt the potential blocking on other goroutines.
s.cancel()
} }
func (t *http2Server) RemoteAddr() net.Addr { func (t *http2Server) RemoteAddr() net.Addr {

View file

@ -62,13 +62,14 @@ const (
) )
var ( var (
clientPreface = []byte(http2.ClientPreface) clientPreface = []byte(http2.ClientPreface)
http2RSTErrConvTab = map[http2.ErrCode]codes.Code{ http2ErrConvTab = map[http2.ErrCode]codes.Code{
http2.ErrCodeNo: codes.Internal, http2.ErrCodeNo: codes.Internal,
http2.ErrCodeProtocol: codes.Internal, http2.ErrCodeProtocol: codes.Internal,
http2.ErrCodeInternal: codes.Internal, http2.ErrCodeInternal: codes.Internal,
http2.ErrCodeFlowControl: codes.ResourceExhausted, http2.ErrCodeFlowControl: codes.ResourceExhausted,
http2.ErrCodeSettingsTimeout: codes.Internal, http2.ErrCodeSettingsTimeout: codes.Internal,
http2.ErrCodeStreamClosed: codes.Internal,
http2.ErrCodeFrameSize: codes.Internal, http2.ErrCodeFrameSize: codes.Internal,
http2.ErrCodeRefusedStream: codes.Unavailable, http2.ErrCodeRefusedStream: codes.Unavailable,
http2.ErrCodeCancel: codes.Canceled, http2.ErrCodeCancel: codes.Canceled,
@ -76,6 +77,7 @@ var (
http2.ErrCodeConnect: codes.Internal, http2.ErrCodeConnect: codes.Internal,
http2.ErrCodeEnhanceYourCalm: codes.ResourceExhausted, http2.ErrCodeEnhanceYourCalm: codes.ResourceExhausted,
http2.ErrCodeInadequateSecurity: codes.PermissionDenied, http2.ErrCodeInadequateSecurity: codes.PermissionDenied,
http2.ErrCodeHTTP11Required: codes.FailedPrecondition,
} }
statusCodeConvTab = map[codes.Code]http2.ErrCode{ statusCodeConvTab = map[codes.Code]http2.ErrCode{
codes.Internal: http2.ErrCodeInternal, codes.Internal: http2.ErrCodeInternal,
@ -89,6 +91,9 @@ var (
// Records the states during HPACK decoding. Must be reset once the // Records the states during HPACK decoding. Must be reset once the
// decoding of the entire headers are finished. // decoding of the entire headers are finished.
type decodeState struct { type decodeState struct {
err error // first error encountered decoding
encoding string
// statusCode caches the stream status received from the trailer // statusCode caches the stream status received from the trailer
// the server sent. Client side only. // the server sent. Client side only.
statusCode codes.Code statusCode codes.Code
@ -101,25 +106,11 @@ type decodeState struct {
mdata map[string][]string mdata map[string][]string
} }
// An hpackDecoder decodes HTTP2 headers which may span multiple frames.
type hpackDecoder struct {
h *hpack.Decoder
state decodeState
err error // The err when decoding
}
// A headerFrame is either a http2.HeaderFrame or http2.ContinuationFrame.
type headerFrame interface {
Header() http2.FrameHeader
HeaderBlockFragment() []byte
HeadersEnded() bool
}
// isReservedHeader checks whether hdr belongs to HTTP2 headers // isReservedHeader checks whether hdr belongs to HTTP2 headers
// reserved by gRPC protocol. Any other headers are classified as the // reserved by gRPC protocol. Any other headers are classified as the
// user-specified metadata. // user-specified metadata.
func isReservedHeader(hdr string) bool { func isReservedHeader(hdr string) bool {
if hdr[0] == ':' { if hdr != "" && hdr[0] == ':' {
return true return true
} }
switch hdr { switch hdr {
@ -136,98 +127,62 @@ func isReservedHeader(hdr string) bool {
} }
} }
func newHPACKDecoder() *hpackDecoder { func (d *decodeState) setErr(err error) {
d := &hpackDecoder{} if d.err == nil {
d.h = hpack.NewDecoder(http2InitHeaderTableSize, func(f hpack.HeaderField) { d.err = err
switch f.Name { }
case "content-type": }
if !strings.Contains(f.Value, "application/grpc") {
d.err = StreamErrorf(codes.FailedPrecondition, "transport: received the unexpected header") func (d *decodeState) processHeaderField(f hpack.HeaderField) {
return switch f.Name {
} case "content-type":
case "grpc-status": if !strings.Contains(f.Value, "application/grpc") {
code, err := strconv.Atoi(f.Value) d.setErr(StreamErrorf(codes.FailedPrecondition, "transport: received the unexpected content-type %q", f.Value))
if err != nil { return
d.err = StreamErrorf(codes.Internal, "transport: malformed grpc-status: %v", err) }
return case "grpc-encoding":
} d.encoding = f.Value
d.state.statusCode = codes.Code(code) case "grpc-status":
case "grpc-message": code, err := strconv.Atoi(f.Value)
d.state.statusDesc = f.Value if err != nil {
case "grpc-timeout": d.setErr(StreamErrorf(codes.Internal, "transport: malformed grpc-status: %v", err))
d.state.timeoutSet = true return
var err error }
d.state.timeout, err = timeoutDecode(f.Value) d.statusCode = codes.Code(code)
if err != nil { case "grpc-message":
d.err = StreamErrorf(codes.Internal, "transport: malformed time-out: %v", err) d.statusDesc = f.Value
return case "grpc-timeout":
} d.timeoutSet = true
case ":path": var err error
d.state.method = f.Value d.timeout, err = timeoutDecode(f.Value)
default: if err != nil {
if !isReservedHeader(f.Name) { d.setErr(StreamErrorf(codes.Internal, "transport: malformed time-out: %v", err))
if f.Name == "user-agent" { return
i := strings.LastIndex(f.Value, " ") }
if i == -1 { case ":path":
// There is no application user agent string being set. d.method = f.Value
return default:
} if !isReservedHeader(f.Name) {
// Extract the application user agent string. if f.Name == "user-agent" {
f.Value = f.Value[:i] i := strings.LastIndex(f.Value, " ")
} if i == -1 {
if d.state.mdata == nil { // There is no application user agent string being set.
d.state.mdata = make(map[string][]string)
}
k, v, err := metadata.DecodeKeyValue(f.Name, f.Value)
if err != nil {
grpclog.Printf("Failed to decode (%q, %q): %v", f.Name, f.Value, err)
return return
} }
d.state.mdata[k] = append(d.state.mdata[k], v) // Extract the application user agent string.
f.Value = f.Value[:i]
} }
if d.mdata == nil {
d.mdata = make(map[string][]string)
}
k, v, err := metadata.DecodeKeyValue(f.Name, f.Value)
if err != nil {
grpclog.Printf("Failed to decode (%q, %q): %v", f.Name, f.Value, err)
return
}
d.mdata[k] = append(d.mdata[k], v)
} }
})
return d
}
func (d *hpackDecoder) decodeClientHTTP2Headers(frame headerFrame) (endHeaders bool, err error) {
d.err = nil
_, err = d.h.Write(frame.HeaderBlockFragment())
if err != nil {
err = StreamErrorf(codes.Internal, "transport: HPACK header decode error: %v", err)
} }
if frame.HeadersEnded() {
if closeErr := d.h.Close(); closeErr != nil && err == nil {
err = StreamErrorf(codes.Internal, "transport: HPACK decoder close error: %v", closeErr)
}
endHeaders = true
}
if err == nil && d.err != nil {
err = d.err
}
return
}
func (d *hpackDecoder) decodeServerHTTP2Headers(frame headerFrame) (endHeaders bool, err error) {
d.err = nil
_, err = d.h.Write(frame.HeaderBlockFragment())
if err != nil {
err = StreamErrorf(codes.Internal, "transport: HPACK header decode error: %v", err)
}
if frame.HeadersEnded() {
if closeErr := d.h.Close(); closeErr != nil && err == nil {
err = StreamErrorf(codes.Internal, "transport: HPACK decoder close error: %v", closeErr)
}
endHeaders = true
}
if err == nil && d.err != nil {
err = d.err
}
return
} }
type timeoutUnit uint8 type timeoutUnit uint8
@ -318,10 +273,11 @@ type framer struct {
func newFramer(conn net.Conn) *framer { func newFramer(conn net.Conn) *framer {
f := &framer{ f := &framer{
reader: conn, reader: bufio.NewReaderSize(conn, http2IOBufSize),
writer: bufio.NewWriterSize(conn, http2IOBufSize), writer: bufio.NewWriterSize(conn, http2IOBufSize),
} }
f.fr = http2.NewFramer(f.writer, f.reader) f.fr = http2.NewFramer(f.writer, f.reader)
f.fr.ReadMetaHeaders = hpack.NewDecoder(http2InitHeaderTableSize, nil)
return f return f
} }
@ -449,3 +405,7 @@ func (f *framer) flushWrite() error {
func (f *framer) readFrame() (http2.Frame, error) { func (f *framer) readFrame() (http2.Frame, error) {
return f.fr.ReadFrame() return f.fr.ReadFrame()
} }
func (f *framer) errorDetail() error {
return f.fr.ErrorDetail()
}

View file

@ -63,13 +63,11 @@ type recvMsg struct {
err error err error
} }
func (recvMsg) isItem() bool { func (*recvMsg) item() {}
return true
}
// All items in an out of a recvBuffer should be the same type. // All items in an out of a recvBuffer should be the same type.
type item interface { type item interface {
isItem() bool item()
} }
// recvBuffer is an unbounded channel of item. // recvBuffer is an unbounded channel of item.
@ -89,12 +87,14 @@ func newRecvBuffer() *recvBuffer {
func (b *recvBuffer) put(r item) { func (b *recvBuffer) put(r item) {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
b.backlog = append(b.backlog, r) if len(b.backlog) == 0 {
select { select {
case b.c <- b.backlog[0]: case b.c <- r:
b.backlog = b.backlog[1:] return
default: default:
}
} }
b.backlog = append(b.backlog, r)
} }
func (b *recvBuffer) load() { func (b *recvBuffer) load() {
@ -170,11 +170,13 @@ type Stream struct {
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
// method records the associated RPC method of the stream. // method records the associated RPC method of the stream.
method string method string
buf *recvBuffer recvCompress string
dec io.Reader sendCompress string
fc *inFlow buf *recvBuffer
recvQuota uint32 dec io.Reader
fc *inFlow
recvQuota uint32
// The accumulated inbound quota pending for window update. // The accumulated inbound quota pending for window update.
updateQuota uint32 updateQuota uint32
// The handler to control the window update procedure for both this // The handler to control the window update procedure for both this
@ -201,6 +203,17 @@ type Stream struct {
statusDesc string statusDesc string
} }
// RecvCompress returns the compression algorithm applied to the inbound
// message. It is empty string if there is no compression applied.
func (s *Stream) RecvCompress() string {
return s.recvCompress
}
// SetSendCompress sets the compression algorithm to the stream.
func (s *Stream) SetSendCompress(str string) {
s.sendCompress = str
}
// Header acquires the key-value pairs of header metadata once it // Header acquires the key-value pairs of header metadata once it
// is available. It blocks until i) the metadata is ready or ii) there is no // is available. It blocks until i) the metadata is ready or ii) there is no
// header metadata or iii) the stream is cancelled/expired. // header metadata or iii) the stream is cancelled/expired.
@ -286,20 +299,18 @@ func (s *Stream) Read(p []byte) (n int, err error) {
return return
} }
type key int
// The key to save transport.Stream in the context. // The key to save transport.Stream in the context.
const streamKey = key(0) type streamKey struct{}
// newContextWithStream creates a new context from ctx and attaches stream // newContextWithStream creates a new context from ctx and attaches stream
// to it. // to it.
func newContextWithStream(ctx context.Context, stream *Stream) context.Context { func newContextWithStream(ctx context.Context, stream *Stream) context.Context {
return context.WithValue(ctx, streamKey, stream) return context.WithValue(ctx, streamKey{}, stream)
} }
// StreamFromContext returns the stream saved in ctx. // StreamFromContext returns the stream saved in ctx.
func StreamFromContext(ctx context.Context) (s *Stream, ok bool) { func StreamFromContext(ctx context.Context) (s *Stream, ok bool) {
s, ok = ctx.Value(streamKey).(*Stream) s, ok = ctx.Value(streamKey{}).(*Stream)
return return
} }
@ -339,20 +350,40 @@ func NewClientTransport(target string, opts *ConnectOptions) (ClientTransport, e
// Options provides additional hints and information for message // Options provides additional hints and information for message
// transmission. // transmission.
type Options struct { type Options struct {
// Indicate whether it is the last piece for this stream. // Last indicates whether this write is the last piece for
// this stream.
Last bool Last bool
// The hint to transport impl whether the data could be buffered for
// batching write. Transport impl can feel free to ignore it. // Delay is a hint to the transport implementation for whether
// the data could be buffered for a batching write. The
// Transport implementation may ignore the hint.
Delay bool Delay bool
} }
// CallHdr carries the information of a particular RPC. // CallHdr carries the information of a particular RPC.
type CallHdr struct { type CallHdr struct {
Host string // peer host // Host specifies the peer's host.
Method string // the operation to perform on the specified host Host string
// Method specifies the operation to perform.
Method string
// RecvCompress specifies the compression algorithm applied on
// inbound messages.
RecvCompress string
// SendCompress specifies the compression algorithm applied on
// outbound message.
SendCompress string
// Flush indicates whether a new stream command should be sent
// to the peer without waiting for the first data. This is
// only a hint. The transport may modify the flush decision
// for performance purposes.
Flush bool
} }
// ClientTransport is the common interface for all gRPC client side transport // ClientTransport is the common interface for all gRPC client-side transport
// implementations. // implementations.
type ClientTransport interface { type ClientTransport interface {
// Close tears down this transport. Once it returns, the transport // Close tears down this transport. Once it returns, the transport
@ -381,21 +412,33 @@ type ClientTransport interface {
Error() <-chan struct{} Error() <-chan struct{}
} }
// ServerTransport is the common interface for all gRPC server side transport // ServerTransport is the common interface for all gRPC server-side transport
// implementations. // implementations.
//
// Methods may be called concurrently from multiple goroutines, but
// Write methods for a given Stream will be called serially.
type ServerTransport interface { type ServerTransport interface {
// WriteStatus sends the status of a stream to the client.
WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error
// Write sends the data for the given stream.
Write(s *Stream, data []byte, opts *Options) error
// WriteHeader sends the header metedata for the given stream.
WriteHeader(s *Stream, md metadata.MD) error
// HandleStreams receives incoming streams using the given handler. // HandleStreams receives incoming streams using the given handler.
HandleStreams(func(*Stream)) HandleStreams(func(*Stream))
// WriteHeader sends the header metadata for the given stream.
// WriteHeader may not be called on all streams.
WriteHeader(s *Stream, md metadata.MD) error
// Write sends the data for the given stream.
// Write may not be called on all streams.
Write(s *Stream, data []byte, opts *Options) error
// WriteStatus sends the status of a stream to the client.
// WriteStatus is the final call made on a stream and always
// occurs.
WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error
// Close tears down the transport. Once it is called, the transport // Close tears down the transport. Once it is called, the transport
// should not be accessed any more. All the pending streams and their // should not be accessed any more. All the pending streams and their
// handlers will be terminated asynchronously. // handlers will be terminated asynchronously.
Close() error Close() error
// RemoteAddr returns the remote network address. // RemoteAddr returns the remote network address.
RemoteAddr() net.Addr RemoteAddr() net.Addr
} }