mirror of https://github.com/docker/cli.git
Merge pull request #291 from thaJeztah/bump-swarmkit
Bump swarmkit and dependencies to 79381d0840be27f8b3f5c667b348a4467d866eeb
This commit is contained in:
commit
472ce90624
|
@ -18,10 +18,10 @@ github.com/docker/go-events 18b43f1bc85d9cdd42c05a6cd2d444c7a200a894
|
||||||
github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
|
github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
|
||||||
github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
|
github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
|
||||||
github.com/docker/notary v0.4.2
|
github.com/docker/notary v0.4.2
|
||||||
github.com/docker/swarmkit 1a3e510517be82d18ac04380b5f71eddf06c2fc0
|
github.com/docker/swarmkit 79381d0840be27f8b3f5c667b348a4467d866eeb
|
||||||
github.com/flynn-archive/go-shlex 3f9db97f856818214da2e1057f8ad84803971cff
|
github.com/flynn-archive/go-shlex 3f9db97f856818214da2e1057f8ad84803971cff
|
||||||
github.com/gogo/protobuf 7efa791bd276fd4db00867cbd982b552627c24cb
|
github.com/gogo/protobuf 7efa791bd276fd4db00867cbd982b552627c24cb
|
||||||
github.com/golang/protobuf 8ee79997227bf9b34611aee7946ae64735e6fd93
|
github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4
|
||||||
github.com/gorilla/context v1.1
|
github.com/gorilla/context v1.1
|
||||||
github.com/gorilla/mux v1.1
|
github.com/gorilla/mux v1.1
|
||||||
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||||
|
@ -43,11 +43,12 @@ github.com/xeipuuv/gojsonpointer e0fe6f68307607d540ed8eac07a342c33fa1b54a
|
||||||
github.com/xeipuuv/gojsonreference e02fc20de94c78484cd5ffb007f8af96be030a45
|
github.com/xeipuuv/gojsonreference e02fc20de94c78484cd5ffb007f8af96be030a45
|
||||||
github.com/xeipuuv/gojsonschema 93e72a773fade158921402d6a24c819b48aba29d
|
github.com/xeipuuv/gojsonschema 93e72a773fade158921402d6a24c819b48aba29d
|
||||||
golang.org/x/crypto 3fbbcd23f1cb824e69491a5930cfeff09b12f4d2
|
golang.org/x/crypto 3fbbcd23f1cb824e69491a5930cfeff09b12f4d2
|
||||||
golang.org/x/net c427ad74c6d7a814201695e9ffde0c5d400a7674
|
golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6
|
||||||
golang.org/x/sys 8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9
|
golang.org/x/sys 8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9
|
||||||
golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756
|
golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756
|
||||||
golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb
|
golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb
|
||||||
google.golang.org/grpc v1.0.4
|
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
|
||||||
|
google.golang.org/grpc v1.3.0
|
||||||
gopkg.in/yaml.v2 4c78c975fe7c825c6d1466c42be594d1d6f3aba6
|
gopkg.in/yaml.v2 4c78c975fe7c825c6d1466c42be594d1d6f3aba6
|
||||||
github.com/tonistiigi/fsutil 0ac4c11b053b9c5c7c47558f81f96c7100ce50fb
|
github.com/tonistiigi/fsutil 0ac4c11b053b9c5c7c47558f81f96c7100ce50fb
|
||||||
github.com/stevvooe/continuity cd7a8e21e2b6f84799f5dd4b65faf49c8d3ee02d
|
github.com/stevvooe/continuity cd7a8e21e2b6f84799f5dd4b65faf49c8d3ee02d
|
||||||
|
|
|
@ -198,7 +198,8 @@ type Task struct {
|
||||||
// such a cluster default or policy-based value.
|
// such a cluster default or policy-based value.
|
||||||
//
|
//
|
||||||
// If not present, the daemon's default will be used.
|
// If not present, the daemon's default will be used.
|
||||||
LogDriver *Driver `protobuf:"bytes,13,opt,name=log_driver,json=logDriver" json:"log_driver,omitempty"`
|
LogDriver *Driver `protobuf:"bytes,13,opt,name=log_driver,json=logDriver" json:"log_driver,omitempty"`
|
||||||
|
AssignedGenericResources []*GenericResource `protobuf:"bytes,15,rep,name=assigned_generic_resources,json=assignedGenericResources" json:"assigned_generic_resources,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Task) Reset() { *m = Task{} }
|
func (m *Task) Reset() { *m = Task{} }
|
||||||
|
@ -529,6 +530,14 @@ func (m *Task) CopyFrom(src interface{}) {
|
||||||
m.LogDriver = &Driver{}
|
m.LogDriver = &Driver{}
|
||||||
github_com_docker_swarmkit_api_deepcopy.Copy(m.LogDriver, o.LogDriver)
|
github_com_docker_swarmkit_api_deepcopy.Copy(m.LogDriver, o.LogDriver)
|
||||||
}
|
}
|
||||||
|
if o.AssignedGenericResources != nil {
|
||||||
|
m.AssignedGenericResources = make([]*GenericResource, len(o.AssignedGenericResources))
|
||||||
|
for i := range m.AssignedGenericResources {
|
||||||
|
m.AssignedGenericResources[i] = &GenericResource{}
|
||||||
|
github_com_docker_swarmkit_api_deepcopy.Copy(m.AssignedGenericResources[i], o.AssignedGenericResources[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *NetworkAttachment) Copy() *NetworkAttachment {
|
func (m *NetworkAttachment) Copy() *NetworkAttachment {
|
||||||
|
@ -1140,6 +1149,18 @@ func (m *Task) MarshalTo(dAtA []byte) (int, error) {
|
||||||
}
|
}
|
||||||
i += n26
|
i += n26
|
||||||
}
|
}
|
||||||
|
if len(m.AssignedGenericResources) > 0 {
|
||||||
|
for _, msg := range m.AssignedGenericResources {
|
||||||
|
dAtA[i] = 0x7a
|
||||||
|
i++
|
||||||
|
i = encodeVarintObjects(dAtA, i, uint64(msg.Size()))
|
||||||
|
n, err := msg.MarshalTo(dAtA[i:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
i += n
|
||||||
|
}
|
||||||
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1771,6 +1792,12 @@ func (m *Task) Size() (n int) {
|
||||||
l = m.SpecVersion.Size()
|
l = m.SpecVersion.Size()
|
||||||
n += 1 + l + sovObjects(uint64(l))
|
n += 1 + l + sovObjects(uint64(l))
|
||||||
}
|
}
|
||||||
|
if len(m.AssignedGenericResources) > 0 {
|
||||||
|
for _, e := range m.AssignedGenericResources {
|
||||||
|
l = e.Size()
|
||||||
|
n += 1 + l + sovObjects(uint64(l))
|
||||||
|
}
|
||||||
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4419,6 +4446,7 @@ func (this *Task) String() string {
|
||||||
`Endpoint:` + strings.Replace(fmt.Sprintf("%v", this.Endpoint), "Endpoint", "Endpoint", 1) + `,`,
|
`Endpoint:` + strings.Replace(fmt.Sprintf("%v", this.Endpoint), "Endpoint", "Endpoint", 1) + `,`,
|
||||||
`LogDriver:` + strings.Replace(fmt.Sprintf("%v", this.LogDriver), "Driver", "Driver", 1) + `,`,
|
`LogDriver:` + strings.Replace(fmt.Sprintf("%v", this.LogDriver), "Driver", "Driver", 1) + `,`,
|
||||||
`SpecVersion:` + strings.Replace(fmt.Sprintf("%v", this.SpecVersion), "Version", "Version", 1) + `,`,
|
`SpecVersion:` + strings.Replace(fmt.Sprintf("%v", this.SpecVersion), "Version", "Version", 1) + `,`,
|
||||||
|
`AssignedGenericResources:` + strings.Replace(fmt.Sprintf("%v", this.AssignedGenericResources), "GenericResource", "GenericResource", 1) + `,`,
|
||||||
`}`,
|
`}`,
|
||||||
}, "")
|
}, "")
|
||||||
return s
|
return s
|
||||||
|
@ -6001,6 +6029,37 @@ func (m *Task) Unmarshal(dAtA []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
iNdEx = postIndex
|
iNdEx = postIndex
|
||||||
|
case 15:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field AssignedGenericResources", wireType)
|
||||||
|
}
|
||||||
|
var msglen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowObjects
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
msglen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msglen < 0 {
|
||||||
|
return ErrInvalidLengthObjects
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.AssignedGenericResources = append(m.AssignedGenericResources, &GenericResource{})
|
||||||
|
if err := m.AssignedGenericResources[len(m.AssignedGenericResources)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
default:
|
default:
|
||||||
iNdEx = preIndex
|
iNdEx = preIndex
|
||||||
skippy, err := skipObjects(dAtA[iNdEx:])
|
skippy, err := skipObjects(dAtA[iNdEx:])
|
||||||
|
@ -7630,96 +7689,99 @@ var (
|
||||||
func init() { proto.RegisterFile("objects.proto", fileDescriptorObjects) }
|
func init() { proto.RegisterFile("objects.proto", fileDescriptorObjects) }
|
||||||
|
|
||||||
var fileDescriptorObjects = []byte{
|
var fileDescriptorObjects = []byte{
|
||||||
// 1451 bytes of a gzipped FileDescriptorProto
|
// 1491 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0xcd, 0x6f, 0x1b, 0x45,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0xcf, 0x6f, 0x1b, 0x4f,
|
||||||
0x14, 0xef, 0xda, 0x1b, 0x7f, 0x3c, 0x27, 0x56, 0x98, 0x86, 0xe0, 0x9a, 0x60, 0x07, 0x57, 0xa0,
|
0x15, 0xef, 0xda, 0x1b, 0xff, 0x78, 0x4e, 0x4c, 0x98, 0x86, 0xb0, 0x35, 0xc1, 0x0e, 0xae, 0x40,
|
||||||
0x0a, 0x55, 0x4e, 0x09, 0x05, 0xa5, 0x81, 0xd2, 0xda, 0x49, 0xd4, 0x5a, 0xa5, 0x34, 0x9a, 0x96,
|
0x15, 0xaa, 0x9c, 0x12, 0x0a, 0x4a, 0x03, 0xa5, 0xb5, 0x93, 0xa8, 0xb5, 0x4a, 0x69, 0x34, 0x2d,
|
||||||
0x96, 0x9b, 0x99, 0xec, 0x4e, 0xdd, 0xc5, 0xeb, 0x9d, 0xd5, 0xce, 0xd8, 0xc5, 0x37, 0xce, 0xf9,
|
0x2d, 0xb7, 0x65, 0xb2, 0x3b, 0x75, 0x17, 0xaf, 0x77, 0x56, 0x3b, 0x63, 0x17, 0xdf, 0x38, 0x87,
|
||||||
0x07, 0x72, 0xe3, 0xd0, 0x13, 0x77, 0xb8, 0x70, 0xe1, 0xc0, 0xa9, 0x47, 0x4e, 0x88, 0x53, 0x44,
|
0x3f, 0x20, 0x37, 0x0e, 0xfd, 0x17, 0xe0, 0xc2, 0x85, 0x03, 0xa7, 0x1e, 0x39, 0x21, 0x4e, 0x11,
|
||||||
0xfd, 0x5f, 0x20, 0x71, 0x40, 0x33, 0x3b, 0x6b, 0x6f, 0xea, 0x75, 0x92, 0xa2, 0x2a, 0xe2, 0xe4,
|
0xf5, 0x7f, 0x81, 0xc4, 0xe1, 0xab, 0x99, 0x9d, 0xb5, 0x37, 0xf1, 0x3a, 0x49, 0xbf, 0xaa, 0xa2,
|
||||||
0xf9, 0xf8, 0xfd, 0xde, 0xd7, 0xbc, 0xf7, 0x66, 0xd6, 0xb0, 0xc0, 0xf6, 0xbe, 0xa5, 0x96, 0xe0,
|
0xef, 0x29, 0x33, 0x3b, 0x9f, 0xcf, 0x9b, 0xf7, 0xde, 0xbc, 0x5f, 0x31, 0xac, 0xb0, 0xa3, 0x3f,
|
||||||
0x75, 0x3f, 0x60, 0x82, 0x21, 0x64, 0x33, 0xab, 0x4b, 0x83, 0x3a, 0x7f, 0x4a, 0x82, 0x5e, 0xd7,
|
0x50, 0x47, 0xf0, 0x56, 0x18, 0x31, 0xc1, 0x10, 0x72, 0x99, 0xd3, 0xa7, 0x51, 0x8b, 0xbf, 0x27,
|
||||||
0x11, 0xf5, 0xc1, 0x87, 0xe5, 0x82, 0x18, 0xfa, 0x54, 0x03, 0xca, 0x05, 0xee, 0x53, 0x2b, 0x9a,
|
0xd1, 0xa0, 0xef, 0x89, 0xd6, 0xe8, 0x27, 0xb5, 0x8a, 0x18, 0x87, 0x54, 0x03, 0x6a, 0x15, 0x1e,
|
||||||
0x54, 0x3b, 0x8c, 0x75, 0x5c, 0xba, 0xa6, 0x66, 0x7b, 0xfd, 0xc7, 0x6b, 0xc2, 0xe9, 0x51, 0x2e,
|
0x52, 0x27, 0xd9, 0x34, 0x7a, 0x8c, 0xf5, 0x7c, 0xba, 0xa5, 0x76, 0x47, 0xc3, 0xb7, 0x5b, 0xc2,
|
||||||
0x48, 0xcf, 0xd7, 0x80, 0xa5, 0x0e, 0xeb, 0x30, 0x35, 0x5c, 0x93, 0x23, 0xbd, 0x7a, 0xe1, 0x65,
|
0x1b, 0x50, 0x2e, 0xc8, 0x20, 0xd4, 0x80, 0xb5, 0x1e, 0xeb, 0x31, 0xb5, 0xdc, 0x92, 0x2b, 0xfd,
|
||||||
0x1a, 0xf1, 0x86, 0x7a, 0xeb, 0xbc, 0xef, 0xf6, 0x3b, 0x8e, 0xb7, 0x16, 0xfe, 0x84, 0x8b, 0xb5,
|
0xf5, 0xd6, 0x79, 0x1a, 0x09, 0xc6, 0xfa, 0xe8, 0x66, 0xe8, 0x0f, 0x7b, 0x5e, 0xb0, 0x15, 0xff,
|
||||||
0x5f, 0x0c, 0x30, 0xef, 0x52, 0x41, 0xd0, 0xa7, 0x90, 0x1d, 0xd0, 0x80, 0x3b, 0xcc, 0x2b, 0x19,
|
0x89, 0x3f, 0x36, 0xff, 0x6e, 0x80, 0xf9, 0x9c, 0x0a, 0x82, 0x7e, 0x01, 0xc5, 0x11, 0x8d, 0xb8,
|
||||||
0xab, 0xc6, 0xa5, 0xc2, 0xfa, 0xdb, 0xf5, 0x69, 0x7b, 0xeb, 0x0f, 0x43, 0x48, 0xd3, 0x7c, 0x7e,
|
0xc7, 0x02, 0xcb, 0xd8, 0x34, 0xee, 0x54, 0xb6, 0xbf, 0xd7, 0x9a, 0xd7, 0xb7, 0xf5, 0x3a, 0x86,
|
||||||
0x58, 0x3d, 0x87, 0x23, 0x06, 0xba, 0x06, 0x60, 0x05, 0x94, 0x08, 0x6a, 0xb7, 0x89, 0x28, 0xa5,
|
0x74, 0xcc, 0x8f, 0xa7, 0x8d, 0x1b, 0x38, 0x61, 0xa0, 0x07, 0x00, 0x4e, 0x44, 0x89, 0xa0, 0xae,
|
||||||
0x14, 0xbf, 0x5c, 0x0f, 0x4d, 0xa9, 0x47, 0xa6, 0xd4, 0x1f, 0x44, 0x1e, 0xe0, 0xbc, 0x46, 0x37,
|
0x4d, 0x84, 0x95, 0x53, 0xfc, 0x5a, 0x2b, 0x56, 0xa5, 0x95, 0xa8, 0xd2, 0x7a, 0x95, 0x58, 0x80,
|
||||||
0x84, 0xa4, 0xf6, 0x7d, 0x3b, 0xa2, 0xa6, 0x4f, 0xa6, 0x6a, 0x74, 0x43, 0xd4, 0x7e, 0x32, 0xc1,
|
0xcb, 0x1a, 0xdd, 0x16, 0x92, 0x3a, 0x0c, 0xdd, 0x84, 0x9a, 0xbf, 0x9c, 0xaa, 0xd1, 0x6d, 0xd1,
|
||||||
0xfc, 0x92, 0xd9, 0x14, 0x2d, 0x43, 0xca, 0xb1, 0x95, 0xd9, 0xf9, 0x66, 0x66, 0x74, 0x58, 0x4d,
|
0xfc, 0xab, 0x09, 0xe6, 0x6f, 0x98, 0x4b, 0xd1, 0x3a, 0xe4, 0x3c, 0x57, 0xa9, 0x5d, 0xee, 0x14,
|
||||||
0xb5, 0xb6, 0x71, 0xca, 0xb1, 0xd1, 0x3a, 0x98, 0x3d, 0x2a, 0x88, 0x36, 0xa8, 0x94, 0xe4, 0x90,
|
0x26, 0xa7, 0x8d, 0x5c, 0x77, 0x1f, 0xe7, 0x3c, 0x17, 0x6d, 0x83, 0x39, 0xa0, 0x82, 0x68, 0x85,
|
||||||
0xf4, 0x5d, 0x7b, 0xa3, 0xb0, 0xe8, 0x13, 0x30, 0xe5, 0x31, 0x68, 0x4b, 0x56, 0x92, 0x38, 0x52,
|
0xac, 0x2c, 0x83, 0xa4, 0xed, 0xda, 0x1a, 0x85, 0x45, 0x3f, 0x07, 0x53, 0x3e, 0x83, 0xd6, 0x64,
|
||||||
0xe7, 0x7d, 0x9f, 0x5a, 0x11, 0x4f, 0xe2, 0xd1, 0x0e, 0x14, 0x6c, 0xca, 0xad, 0xc0, 0xf1, 0x85,
|
0x23, 0x8b, 0x23, 0xef, 0x7c, 0x19, 0x52, 0x27, 0xe1, 0x49, 0x3c, 0x3a, 0x80, 0x8a, 0x4b, 0xb9,
|
||||||
0x8c, 0xa1, 0xa9, 0xe8, 0x17, 0x67, 0xd1, 0xb7, 0x27, 0x50, 0x1c, 0xe7, 0xa1, 0xcf, 0x20, 0xc3,
|
0x13, 0x79, 0xa1, 0x90, 0x3e, 0x34, 0x15, 0xfd, 0xf6, 0x22, 0xfa, 0xfe, 0x0c, 0x8a, 0xd3, 0x3c,
|
||||||
0x05, 0x11, 0x7d, 0x5e, 0x9a, 0x53, 0x12, 0x2a, 0x33, 0x0d, 0x50, 0x28, 0x6d, 0x82, 0xe6, 0xa0,
|
0xf4, 0x4b, 0x28, 0x70, 0x41, 0xc4, 0x90, 0x5b, 0x4b, 0x4a, 0x42, 0x7d, 0xa1, 0x02, 0x0a, 0xa5,
|
||||||
0xdb, 0x50, 0xec, 0x11, 0x8f, 0x74, 0x68, 0xd0, 0xd6, 0x52, 0x32, 0x4a, 0xca, 0xbb, 0x89, 0xae,
|
0x55, 0xd0, 0x1c, 0xf4, 0x14, 0xaa, 0x03, 0x12, 0x90, 0x1e, 0x8d, 0x6c, 0x2d, 0xa5, 0xa0, 0xa4,
|
||||||
0x87, 0xc8, 0x50, 0x10, 0x5e, 0xe8, 0xc5, 0xa7, 0x68, 0x07, 0x80, 0x08, 0x41, 0xac, 0x27, 0x3d,
|
0xfc, 0x20, 0xd3, 0xf4, 0x18, 0x19, 0x0b, 0xc2, 0x2b, 0x83, 0xf4, 0x16, 0x1d, 0x00, 0x10, 0x21,
|
||||||
0xea, 0x89, 0x52, 0x56, 0x49, 0x79, 0x2f, 0xd1, 0x16, 0x2a, 0x9e, 0xb2, 0xa0, 0xdb, 0x18, 0x83,
|
0x88, 0xf3, 0x6e, 0x40, 0x03, 0x61, 0x15, 0x95, 0x94, 0x1f, 0x66, 0xea, 0x42, 0xc5, 0x7b, 0x16,
|
||||||
0x71, 0x8c, 0x88, 0x6e, 0x41, 0xc1, 0xa2, 0x81, 0x70, 0x1e, 0x3b, 0x16, 0x11, 0xb4, 0x94, 0x53,
|
0xf5, 0xdb, 0x53, 0x30, 0x4e, 0x11, 0xd1, 0x13, 0xa8, 0x38, 0x34, 0x12, 0xde, 0x5b, 0xcf, 0x21,
|
||||||
0x72, 0xaa, 0x49, 0x72, 0xb6, 0x26, 0x30, 0xed, 0x54, 0x9c, 0x89, 0xae, 0x80, 0x19, 0x30, 0x97,
|
0x82, 0x5a, 0x25, 0x25, 0xa7, 0x91, 0x25, 0x67, 0x6f, 0x06, 0xd3, 0x46, 0xa5, 0x99, 0xe8, 0x1e,
|
||||||
0x96, 0xf2, 0xab, 0xc6, 0xa5, 0xe2, 0xec, 0x63, 0xc1, 0xcc, 0xa5, 0x58, 0x21, 0x37, 0x97, 0xf7,
|
0x98, 0x11, 0xf3, 0xa9, 0x55, 0xde, 0x34, 0xee, 0x54, 0x17, 0x3f, 0x0b, 0x66, 0x3e, 0xc5, 0x0a,
|
||||||
0x0f, 0x6a, 0x08, 0x16, 0x73, 0xc6, 0xa2, 0xa1, 0x52, 0xc3, 0xb8, 0x62, 0x7c, 0x6d, 0x7c, 0x63,
|
0xb9, 0xbb, 0x7e, 0x7c, 0xd2, 0x44, 0xb0, 0x5a, 0x32, 0x56, 0x0d, 0x15, 0x1a, 0xc6, 0x3d, 0xe3,
|
||||||
0xd4, 0xfe, 0x49, 0x43, 0xf6, 0x3e, 0x0d, 0x06, 0x8e, 0xf5, 0x7a, 0x13, 0xe7, 0xda, 0x91, 0xc4,
|
0x77, 0xc6, 0xef, 0x8d, 0xe6, 0xff, 0xf3, 0x50, 0x7c, 0x49, 0xa3, 0x91, 0xe7, 0x7c, 0xd9, 0xc0,
|
||||||
0x49, 0xf4, 0x51, 0xab, 0x9d, 0xca, 0x9d, 0x0d, 0xc8, 0x51, 0xcf, 0xf6, 0x99, 0xe3, 0x09, 0x9d,
|
0x79, 0x70, 0x26, 0x70, 0x32, 0x6d, 0xd4, 0xd7, 0xce, 0xc5, 0xce, 0x0e, 0x94, 0x68, 0xe0, 0x86,
|
||||||
0x38, 0x89, 0x0e, 0xee, 0x68, 0x0c, 0x1e, 0xa3, 0xd1, 0x0e, 0x2c, 0x84, 0xf5, 0xd0, 0x3e, 0x92,
|
0xcc, 0x0b, 0x84, 0x0e, 0x9c, 0x4c, 0x03, 0x0f, 0x34, 0x06, 0x4f, 0xd1, 0xe8, 0x00, 0x56, 0xe2,
|
||||||
0x35, 0xab, 0x49, 0xf4, 0xaf, 0x14, 0x50, 0x1f, 0xf7, 0x7c, 0x3f, 0x36, 0x43, 0xdb, 0xb0, 0xe0,
|
0x7c, 0xb0, 0xcf, 0x44, 0xcd, 0x66, 0x16, 0xfd, 0xb7, 0x0a, 0xa8, 0x9f, 0x7b, 0x79, 0x98, 0xda,
|
||||||
0x07, 0x74, 0xe0, 0xb0, 0x3e, 0x6f, 0x2b, 0x27, 0x32, 0xa7, 0x72, 0x02, 0xcf, 0x47, 0x2c, 0x39,
|
0xa1, 0x7d, 0x58, 0x09, 0x23, 0x3a, 0xf2, 0xd8, 0x90, 0xdb, 0xca, 0x88, 0xc2, 0x95, 0x8c, 0xc0,
|
||||||
0x43, 0x9f, 0xc3, 0xbc, 0x24, 0xb7, 0xa3, 0x3e, 0x02, 0x27, 0xf6, 0x11, 0xac, 0x5a, 0x9e, 0x9e,
|
0xcb, 0x09, 0x4b, 0xee, 0xd0, 0xaf, 0x60, 0x59, 0x92, 0xed, 0xa4, 0x8e, 0xc0, 0xa5, 0x75, 0x04,
|
||||||
0xa0, 0x7b, 0xf0, 0xe6, 0x11, 0x2b, 0xc6, 0x82, 0x0a, 0x27, 0x0b, 0x3a, 0x1f, 0xb7, 0x44, 0x2f,
|
0xab, 0x92, 0xa7, 0x37, 0xe8, 0x05, 0x7c, 0xe7, 0x8c, 0x16, 0x53, 0x41, 0x95, 0xcb, 0x05, 0xdd,
|
||||||
0x6e, 0xa2, 0xfd, 0x83, 0x5a, 0x11, 0xe6, 0xe3, 0x29, 0x50, 0xfb, 0x21, 0x05, 0xb9, 0x28, 0x90,
|
0x4c, 0x6b, 0xa2, 0x3f, 0xee, 0xa2, 0xe3, 0x93, 0x66, 0x15, 0x96, 0xd3, 0x21, 0xd0, 0xfc, 0x4b,
|
||||||
0xe8, 0xaa, 0x3e, 0x33, 0x63, 0x76, 0xd4, 0x22, 0xac, 0xf2, 0x37, 0x3c, 0xae, 0xab, 0x30, 0xe7,
|
0x0e, 0x4a, 0x89, 0x23, 0xd1, 0x7d, 0xfd, 0x66, 0xc6, 0x62, 0xaf, 0x25, 0x58, 0x65, 0x6f, 0xfc,
|
||||||
0xb3, 0x40, 0xf0, 0x52, 0x6a, 0x35, 0x3d, 0xab, 0x44, 0x77, 0x59, 0x20, 0xb6, 0x98, 0xf7, 0xd8,
|
0x5c, 0xf7, 0x61, 0x29, 0x64, 0x91, 0xe0, 0x56, 0x6e, 0x33, 0xbf, 0x28, 0x45, 0x0f, 0x59, 0x24,
|
||||||
0xe9, 0xe0, 0x10, 0x8c, 0x1e, 0x41, 0x61, 0xe0, 0x04, 0xa2, 0x4f, 0xdc, 0xb6, 0xe3, 0xf3, 0x52,
|
0xf6, 0x58, 0xf0, 0xd6, 0xeb, 0xe1, 0x18, 0x8c, 0xde, 0x40, 0x65, 0xe4, 0x45, 0x62, 0x48, 0x7c,
|
||||||
0x5a, 0x71, 0xdf, 0x3f, 0x4e, 0x65, 0xfd, 0x61, 0x88, 0x6f, 0xed, 0x36, 0x8b, 0xa3, 0xc3, 0x2a,
|
0xdb, 0x0b, 0xb9, 0x95, 0x57, 0xdc, 0x1f, 0x5d, 0x74, 0x65, 0xeb, 0x75, 0x8c, 0xef, 0x1e, 0x76,
|
||||||
0x8c, 0xa7, 0x1c, 0x83, 0x16, 0xd5, 0xf2, 0x79, 0xf9, 0x2e, 0xe4, 0xc7, 0x3b, 0xe8, 0x32, 0x80,
|
0xaa, 0x93, 0xd3, 0x06, 0x4c, 0xb7, 0x1c, 0x83, 0x16, 0xd5, 0x0d, 0x79, 0xed, 0x39, 0x94, 0xa7,
|
||||||
0x17, 0x56, 0x64, 0x7b, 0x9c, 0xd9, 0x0b, 0xa3, 0xc3, 0x6a, 0x5e, 0xd7, 0x69, 0x6b, 0x1b, 0xe7,
|
0x27, 0xe8, 0x2e, 0x40, 0x10, 0x67, 0xa4, 0x3d, 0x8d, 0xec, 0x95, 0xc9, 0x69, 0xa3, 0xac, 0xf3,
|
||||||
0x35, 0xa0, 0x65, 0x23, 0x04, 0x26, 0xb1, 0xed, 0x40, 0xe5, 0x79, 0x1e, 0xab, 0x71, 0xed, 0xc7,
|
0xb4, 0xbb, 0x8f, 0xcb, 0x1a, 0xd0, 0x75, 0x11, 0x02, 0x93, 0xb8, 0x6e, 0xa4, 0xe2, 0xbc, 0x8c,
|
||||||
0x0c, 0x98, 0x0f, 0x08, 0xef, 0x9e, 0x75, 0x57, 0x95, 0x3a, 0xa7, 0x2a, 0xe3, 0x32, 0x00, 0x0f,
|
0xd5, 0xba, 0xf9, 0xe7, 0x22, 0x98, 0xaf, 0x08, 0xef, 0x5f, 0x77, 0x55, 0x95, 0x77, 0xce, 0x65,
|
||||||
0xf3, 0x4d, 0xba, 0x63, 0x4e, 0xdc, 0xd1, 0x59, 0x28, 0xdd, 0xd1, 0x80, 0xd0, 0x1d, 0xee, 0x32,
|
0xc6, 0x5d, 0x00, 0x1e, 0xc7, 0x9b, 0x34, 0xc7, 0x9c, 0x99, 0xa3, 0xa3, 0x50, 0x9a, 0xa3, 0x01,
|
||||||
0xa1, 0x8a, 0xc0, 0xc4, 0x6a, 0x8c, 0x2e, 0x42, 0xd6, 0x63, 0xb6, 0xa2, 0x67, 0x14, 0x1d, 0x46,
|
0xb1, 0x39, 0xdc, 0x67, 0x42, 0x25, 0x81, 0x89, 0xd5, 0x1a, 0xdd, 0x86, 0x62, 0xc0, 0x5c, 0x45,
|
||||||
0x87, 0xd5, 0x8c, 0xec, 0x15, 0xad, 0x6d, 0x9c, 0x91, 0x5b, 0x2d, 0x5b, 0xb6, 0x29, 0xe2, 0x79,
|
0x2f, 0x28, 0x3a, 0x4c, 0x4e, 0x1b, 0x05, 0x59, 0x2b, 0xba, 0xfb, 0xb8, 0x20, 0x8f, 0xba, 0xae,
|
||||||
0x4c, 0x10, 0xd9, 0x83, 0xb9, 0x6e, 0x77, 0x89, 0xd9, 0xdf, 0x98, 0xc0, 0xa2, 0x36, 0x15, 0x63,
|
0x2c, 0x53, 0x24, 0x08, 0x98, 0x20, 0xb2, 0x06, 0x73, 0x5d, 0xee, 0x32, 0xa3, 0xbf, 0x3d, 0x83,
|
||||||
0xa2, 0x87, 0x70, 0x3e, 0xb2, 0x37, 0x2e, 0x30, 0xf7, 0x2a, 0x02, 0x91, 0x96, 0x10, 0xdb, 0x89,
|
0x25, 0x65, 0x2a, 0xc5, 0x44, 0xaf, 0xe1, 0x66, 0xa2, 0x6f, 0x5a, 0x60, 0xe9, 0x73, 0x04, 0x22,
|
||||||
0x5d, 0x0b, 0xf9, 0xd9, 0xd7, 0x82, 0x8a, 0x60, 0xd2, 0xb5, 0xd0, 0x84, 0x05, 0x9b, 0x72, 0x27,
|
0x2d, 0x21, 0x75, 0x92, 0x6a, 0x0b, 0xe5, 0xc5, 0x6d, 0x41, 0x79, 0x30, 0xab, 0x2d, 0x74, 0x60,
|
||||||
0xa0, 0xb6, 0x6a, 0x13, 0x54, 0x55, 0x66, 0x71, 0xfd, 0x9d, 0xe3, 0x84, 0x50, 0x3c, 0xaf, 0x39,
|
0xc5, 0xa5, 0xdc, 0x8b, 0xa8, 0xab, 0xca, 0x04, 0x55, 0x99, 0x59, 0xdd, 0xfe, 0xfe, 0x45, 0x42,
|
||||||
0x6a, 0x86, 0x1a, 0x90, 0xd3, 0x79, 0xc3, 0x4b, 0x05, 0x95, 0xbb, 0xa7, 0xbc, 0x0e, 0xc6, 0xb4,
|
0x28, 0x5e, 0xd6, 0x1c, 0xb5, 0x43, 0x6d, 0x28, 0xe9, 0xb8, 0xe1, 0x56, 0x45, 0xc5, 0xee, 0x15,
|
||||||
0x23, 0x6d, 0x6e, 0xfe, 0x95, 0xda, 0xdc, 0x35, 0x00, 0x97, 0x75, 0xda, 0x76, 0xe0, 0x0c, 0x68,
|
0xdb, 0xc1, 0x94, 0x76, 0xa6, 0xcc, 0x2d, 0x7f, 0x56, 0x99, 0x7b, 0x00, 0xe0, 0xb3, 0x9e, 0xed,
|
||||||
0x50, 0x5a, 0xd0, 0x8f, 0x84, 0x04, 0xee, 0xb6, 0x42, 0xe0, 0xbc, 0xcb, 0x3a, 0xe1, 0x70, 0xaa,
|
0x46, 0xde, 0x88, 0x46, 0xd6, 0x8a, 0x1e, 0x12, 0x32, 0xb8, 0xfb, 0x0a, 0x81, 0xcb, 0x3e, 0xeb,
|
||||||
0x29, 0x15, 0x5f, 0xad, 0x29, 0x6d, 0x96, 0xf7, 0x0f, 0x6a, 0xcb, 0xb0, 0x14, 0xef, 0x21, 0x1b,
|
0xc5, 0xcb, 0xb9, 0xa2, 0x54, 0xfd, 0xcc, 0xa2, 0x44, 0xa0, 0x46, 0x38, 0xf7, 0x7a, 0x01, 0x75,
|
||||||
0xc6, 0x4d, 0xe3, 0xb6, 0xb1, 0x6b, 0xd4, 0x7e, 0x4b, 0xc1, 0x1b, 0x53, 0x0e, 0xa3, 0x8f, 0x21,
|
0xed, 0x1e, 0x0d, 0x68, 0xe4, 0x39, 0x76, 0x44, 0x39, 0x1b, 0x46, 0x0e, 0xe5, 0xd6, 0xb7, 0x94,
|
||||||
0xab, 0x5d, 0x3e, 0xee, 0x25, 0xa5, 0x79, 0x38, 0xc2, 0xa2, 0x15, 0xc8, 0xcb, 0xfa, 0xa3, 0x9c,
|
0x27, 0x32, 0xdb, 0xfc, 0x93, 0x18, 0x8c, 0x35, 0x16, 0x5b, 0x89, 0x98, 0x73, 0x07, 0x7c, 0xb7,
|
||||||
0xd3, 0xb0, 0xb3, 0xe4, 0xf1, 0x64, 0x01, 0x95, 0x20, 0x4b, 0x5c, 0x87, 0xc8, 0xbd, 0xb4, 0xda,
|
0x76, 0x7c, 0xd2, 0x5c, 0x87, 0xb5, 0x74, 0x99, 0xda, 0x31, 0x1e, 0x1b, 0x4f, 0x8d, 0x43, 0xa3,
|
||||||
0x8b, 0xa6, 0xa8, 0x0f, 0xcb, 0x61, 0x5c, 0xda, 0x93, 0x7b, 0xb7, 0xcd, 0x7c, 0xc1, 0x4b, 0xa6,
|
0xf9, 0xcf, 0x1c, 0x7c, 0x7b, 0xce, 0xa7, 0xe8, 0x67, 0x50, 0xd4, 0x5e, 0xbd, 0x68, 0x58, 0xd3,
|
||||||
0x3a, 0xa6, 0x1b, 0xa7, 0x3a, 0x26, 0x1d, 0xb9, 0xc9, 0xc2, 0x3d, 0x5f, 0xf0, 0x1d, 0x4f, 0x04,
|
0x3c, 0x9c, 0x60, 0xd1, 0x06, 0x94, 0x65, 0x8a, 0x53, 0xce, 0x69, 0x5c, 0xbc, 0xca, 0x78, 0xf6,
|
||||||
0x43, 0xbc, 0x64, 0x27, 0x6c, 0x95, 0x6f, 0xc1, 0x85, 0x99, 0x14, 0xb4, 0x08, 0xe9, 0x2e, 0x1d,
|
0x01, 0x59, 0x50, 0x24, 0xbe, 0x47, 0xe4, 0x59, 0x5e, 0x9d, 0x25, 0x5b, 0x34, 0x84, 0xf5, 0xd8,
|
||||||
0x86, 0xbd, 0x03, 0xcb, 0x21, 0x5a, 0x82, 0xb9, 0x01, 0x71, 0xfb, 0x54, 0xb7, 0x9a, 0x70, 0xb2,
|
0xf5, 0xf6, 0xac, 0xb5, 0xdb, 0x2c, 0x14, 0xdc, 0x32, 0x95, 0xfd, 0x8f, 0xae, 0x14, 0x09, 0xfa,
|
||||||
0x99, 0xda, 0x30, 0x6a, 0xcf, 0x52, 0x90, 0xd5, 0xe6, 0x9c, 0xf5, 0x7d, 0xac, 0xd5, 0x4e, 0x75,
|
0x71, 0x66, 0x1f, 0x5e, 0x84, 0x82, 0x1f, 0x04, 0x22, 0x1a, 0xe3, 0x35, 0x37, 0xe3, 0xa8, 0xf6,
|
||||||
0x9d, 0xeb, 0x30, 0xaf, 0x43, 0x1a, 0x96, 0x8b, 0x79, 0x62, 0xc2, 0x15, 0x42, 0x7c, 0x58, 0x2a,
|
0x04, 0x6e, 0x2d, 0xa4, 0xa0, 0x55, 0xc8, 0xf7, 0xe9, 0x38, 0x2e, 0x4f, 0x58, 0x2e, 0xd1, 0x1a,
|
||||||
0xd7, 0xc1, 0x74, 0x7c, 0xd2, 0xd3, 0x77, 0x71, 0xa2, 0xe6, 0xd6, 0x6e, 0xe3, 0xee, 0x3d, 0x3f,
|
0x2c, 0x8d, 0x88, 0x3f, 0xa4, 0xba, 0x9a, 0xc5, 0x9b, 0xdd, 0xdc, 0x8e, 0xd1, 0xfc, 0x90, 0x83,
|
||||||
0xac, 0xfa, 0xdc, 0xe8, 0xb0, 0x6a, 0xca, 0x05, 0xac, 0x68, 0x89, 0xb7, 0xd6, 0xcf, 0x73, 0x90,
|
0xa2, 0x56, 0xe7, 0xba, 0x5b, 0xbe, 0xbe, 0x76, 0xae, 0xb0, 0x3d, 0x84, 0x65, 0xed, 0xd2, 0x38,
|
||||||
0xdd, 0x72, 0xfb, 0x5c, 0xd0, 0xe0, 0xac, 0x83, 0xa4, 0xd5, 0x4e, 0x05, 0x69, 0x0b, 0xb2, 0x01,
|
0x23, 0xcd, 0x4b, 0x63, 0xba, 0x12, 0xe3, 0xe3, 0x6c, 0x7c, 0x08, 0xa6, 0x17, 0x92, 0x81, 0x6e,
|
||||||
0x63, 0xa2, 0x6d, 0x91, 0xe3, 0xe2, 0x83, 0x19, 0x13, 0x5b, 0x8d, 0x66, 0x51, 0x12, 0x65, 0xe3,
|
0xf7, 0x99, 0x37, 0x77, 0x0f, 0xdb, 0xcf, 0x5f, 0x84, 0x71, 0x61, 0x29, 0x4d, 0x4e, 0x1b, 0xa6,
|
||||||
0x0d, 0xe7, 0x38, 0x23, 0xa9, 0x5b, 0x04, 0x3d, 0x82, 0xe5, 0xe8, 0xba, 0xda, 0x63, 0x4c, 0x70,
|
0xfc, 0x80, 0x15, 0x2d, 0xb3, 0x31, 0xfe, 0x6d, 0x09, 0x8a, 0x7b, 0xfe, 0x90, 0x0b, 0x1a, 0x5d,
|
||||||
0x11, 0x10, 0xbf, 0xdd, 0xa5, 0x43, 0xf9, 0x90, 0x49, 0xcf, 0x7a, 0xb8, 0xee, 0x78, 0x56, 0x30,
|
0xb7, 0x93, 0xf4, 0xb5, 0x73, 0x4e, 0xda, 0x83, 0x62, 0xc4, 0x98, 0xb0, 0x1d, 0x72, 0x91, 0x7f,
|
||||||
0x54, 0xc1, 0xbb, 0x43, 0x87, 0x78, 0x49, 0x0b, 0x68, 0x46, 0xfc, 0x3b, 0x74, 0xc8, 0xd1, 0x0d,
|
0x30, 0x63, 0x62, 0xaf, 0xdd, 0xa9, 0x4a, 0xa2, 0xac, 0xed, 0xf1, 0x1e, 0x17, 0x24, 0x75, 0x8f,
|
||||||
0x58, 0xa1, 0x63, 0x98, 0x94, 0xd8, 0x76, 0x49, 0x4f, 0x5e, 0xc4, 0x6d, 0xcb, 0x65, 0x56, 0x57,
|
0xa0, 0x37, 0xb0, 0x9e, 0x74, 0xc4, 0x23, 0xc6, 0x04, 0x17, 0x11, 0x09, 0xed, 0x3e, 0x1d, 0xcb,
|
||||||
0xdd, 0x05, 0x26, 0xbe, 0x40, 0xe3, 0xa2, 0xbe, 0x08, 0x11, 0x5b, 0x12, 0x80, 0x38, 0x94, 0xf6,
|
0x59, 0x29, 0xbf, 0x68, 0x36, 0x3e, 0x08, 0x9c, 0x68, 0xac, 0x9c, 0xf7, 0x8c, 0x8e, 0xf1, 0x9a,
|
||||||
0x5c, 0x62, 0x75, 0x5d, 0x87, 0xcb, 0x6f, 0x93, 0xd8, 0x5b, 0x54, 0xb6, 0x73, 0x69, 0xdb, 0xc6,
|
0x16, 0xd0, 0x49, 0xf8, 0xcf, 0xe8, 0x98, 0xa3, 0x47, 0xb0, 0x41, 0xa7, 0x30, 0x29, 0xd1, 0xf6,
|
||||||
0x31, 0xd1, 0xaa, 0x37, 0x27, 0xdc, 0xd8, 0xcb, 0x56, 0x57, 0xd4, 0x5b, 0x7b, 0xc9, 0xbb, 0xa8,
|
0xc9, 0x40, 0xf6, 0x7a, 0xdb, 0xf1, 0x99, 0xd3, 0x57, 0xed, 0xc6, 0xc4, 0xb7, 0x68, 0x5a, 0xd4,
|
||||||
0x09, 0x85, 0xbe, 0x27, 0xd5, 0x87, 0x31, 0xc8, 0x9f, 0x36, 0x06, 0x10, 0xb2, 0xa4, 0xe7, 0xe5,
|
0xaf, 0x63, 0xc4, 0x9e, 0x04, 0x20, 0x0e, 0xd6, 0x91, 0x4f, 0x9c, 0xbe, 0xef, 0x71, 0xf9, 0xef,
|
||||||
0x01, 0xac, 0x1c, 0xa7, 0x3c, 0xa1, 0x36, 0x6f, 0xc6, 0x6b, 0xb3, 0xb0, 0xfe, 0x41, 0x92, 0xbe,
|
0x4f, 0x6a, 0xdc, 0x95, 0x1d, 0x43, 0xea, 0xb6, 0x73, 0x81, 0xb7, 0x5a, 0x9d, 0x19, 0x37, 0x35,
|
||||||
0x64, 0x91, 0xb1, 0x3a, 0x4e, 0x4c, 0xdb, 0x5f, 0x0d, 0xc8, 0xdc, 0xa7, 0x56, 0x40, 0xc5, 0x6b,
|
0x3c, 0xeb, 0x8c, 0xfa, 0xee, 0x51, 0xf6, 0x29, 0xea, 0x40, 0x65, 0x18, 0xc8, 0xeb, 0x63, 0x1f,
|
||||||
0xcd, 0xda, 0x8d, 0x23, 0x59, 0x5b, 0x49, 0x7e, 0xa5, 0x4a, 0xad, 0x53, 0x49, 0x5b, 0x86, 0x9c,
|
0x94, 0xaf, 0xea, 0x03, 0x88, 0x59, 0xd2, 0xf2, 0xda, 0x08, 0x36, 0x2e, 0xba, 0x3c, 0x23, 0x37,
|
||||||
0xe3, 0x09, 0x1a, 0x78, 0xc4, 0x55, 0x59, 0x9b, 0xc3, 0xe3, 0x79, 0xa2, 0x03, 0xcf, 0x0c, 0xc8,
|
0x1f, 0xa7, 0x73, 0xb3, 0xb2, 0xfd, 0xe3, 0xac, 0xfb, 0xb2, 0x45, 0xa6, 0xf2, 0x38, 0x33, 0x6c,
|
||||||
0x84, 0xcf, 0xb8, 0xb3, 0x76, 0x20, 0xd4, 0xfa, 0xb2, 0x03, 0x89, 0x46, 0xfe, 0x6d, 0x40, 0x0e,
|
0xff, 0x61, 0x40, 0xe1, 0x25, 0x75, 0x22, 0x2a, 0xbe, 0x68, 0xd4, 0xee, 0x9c, 0x89, 0xda, 0x7a,
|
||||||
0x53, 0xce, 0xfa, 0xc1, 0x6b, 0xfe, 0xa4, 0x79, 0xe9, 0x59, 0x94, 0xfe, 0xcf, 0xcf, 0x22, 0x04,
|
0xf6, 0x20, 0x2c, 0x6f, 0x9d, 0x0b, 0xda, 0x1a, 0x94, 0xbc, 0x40, 0xd0, 0x28, 0x20, 0xbe, 0x8a,
|
||||||
0x66, 0xd7, 0xf1, 0xf4, 0x03, 0x0e, 0xab, 0x31, 0xaa, 0x43, 0xd6, 0x27, 0x43, 0x97, 0x11, 0x5b,
|
0xda, 0x12, 0x9e, 0xee, 0x33, 0x0d, 0xf8, 0x60, 0x40, 0x21, 0x9e, 0x14, 0xaf, 0xdb, 0x80, 0xf8,
|
||||||
0x37, 0xca, 0xa5, 0xa9, 0xaf, 0xfe, 0x86, 0x37, 0xc4, 0x11, 0x68, 0x73, 0x69, 0xff, 0xa0, 0xb6,
|
0xd6, 0xf3, 0x06, 0x64, 0x2a, 0xf9, 0x3f, 0x03, 0x4a, 0x49, 0xc3, 0xfa, 0xa2, 0x6a, 0x9e, 0x9b,
|
||||||
0x08, 0xc5, 0xb8, 0xe7, 0x4f, 0x8c, 0xda, 0x1f, 0x06, 0xe4, 0x77, 0xbe, 0x13, 0xd4, 0x53, 0x5f,
|
0xbc, 0xf2, 0x5f, 0x7b, 0xf2, 0x42, 0x60, 0xf6, 0xbd, 0x40, 0xcf, 0x88, 0x58, 0xad, 0x51, 0x0b,
|
||||||
0x10, 0xff, 0x4b, 0xe7, 0x57, 0xa7, 0xff, 0x19, 0xc8, 0x1f, 0xf9, 0xe8, 0x4f, 0x3a, 0xd4, 0x66,
|
0x8a, 0x21, 0x19, 0xfb, 0x8c, 0xb8, 0xba, 0x50, 0xae, 0xcd, 0xfd, 0xb0, 0xd0, 0x0e, 0xc6, 0x38,
|
||||||
0xe9, 0xf9, 0x8b, 0xca, 0xb9, 0x3f, 0x5f, 0x54, 0xce, 0x7d, 0x3f, 0xaa, 0x18, 0xcf, 0x47, 0x15,
|
0x01, 0xed, 0xae, 0x1d, 0x9f, 0x34, 0x57, 0xa1, 0x9a, 0xb6, 0xfc, 0x9d, 0xd1, 0xfc, 0xb7, 0x01,
|
||||||
0xe3, 0xf7, 0x51, 0xc5, 0xf8, 0x6b, 0x54, 0x31, 0xf6, 0x32, 0x2a, 0x3e, 0x1f, 0xfd, 0x1b, 0x00,
|
0xe5, 0x83, 0x3f, 0x0a, 0x1a, 0xa8, 0x79, 0xe0, 0x1b, 0x69, 0xfc, 0xe6, 0xfc, 0x8f, 0x0f, 0xe5,
|
||||||
0x00, 0xff, 0xff, 0x1b, 0x47, 0x17, 0xba, 0x5f, 0x12, 0x00, 0x00,
|
0x33, 0xbf, 0x2b, 0x64, 0x3d, 0x6a, 0xc7, 0xfa, 0xf8, 0xa9, 0x7e, 0xe3, 0x3f, 0x9f, 0xea, 0x37,
|
||||||
|
0xfe, 0x34, 0xa9, 0x1b, 0x1f, 0x27, 0x75, 0xe3, 0x5f, 0x93, 0xba, 0xf1, 0xdf, 0x49, 0xdd, 0x38,
|
||||||
|
0x2a, 0x28, 0xff, 0xfc, 0xf4, 0xab, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe4, 0x48, 0xcb, 0x39, 0xc2,
|
||||||
|
0x12, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
|
@ -239,6 +239,8 @@ message Task {
|
||||||
//
|
//
|
||||||
// If not present, the daemon's default will be used.
|
// If not present, the daemon's default will be used.
|
||||||
Driver log_driver = 13;
|
Driver log_driver = 13;
|
||||||
|
|
||||||
|
repeated GenericResource assigned_generic_resources = 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkAttachment specifies the network parameters of attachment to
|
// NetworkAttachment specifies the network parameters of attachment to
|
||||||
|
|
|
@ -642,16 +642,94 @@ type NetworkSpec struct {
|
||||||
// swarm internally created only and it was identified by the name
|
// swarm internally created only and it was identified by the name
|
||||||
// "ingress" and the label "com.docker.swarm.internal": "true".
|
// "ingress" and the label "com.docker.swarm.internal": "true".
|
||||||
Ingress bool `protobuf:"varint,7,opt,name=ingress,proto3" json:"ingress,omitempty"`
|
Ingress bool `protobuf:"varint,7,opt,name=ingress,proto3" json:"ingress,omitempty"`
|
||||||
// ConfigFrom indicates that the network specific configuration
|
// ConfigFrom is the source of the configuration for this network.
|
||||||
// for this network will be provided via another network, locally
|
//
|
||||||
// on the node where this network is being plumbed.
|
// Types that are valid to be assigned to ConfigFrom:
|
||||||
ConfigFrom string `protobuf:"bytes,8,opt,name=config_from,json=configFrom,proto3" json:"config_from,omitempty"`
|
// *NetworkSpec_Network
|
||||||
|
ConfigFrom isNetworkSpec_ConfigFrom `protobuf_oneof:"config_from"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *NetworkSpec) Reset() { *m = NetworkSpec{} }
|
func (m *NetworkSpec) Reset() { *m = NetworkSpec{} }
|
||||||
func (*NetworkSpec) ProtoMessage() {}
|
func (*NetworkSpec) ProtoMessage() {}
|
||||||
func (*NetworkSpec) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{9} }
|
func (*NetworkSpec) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{9} }
|
||||||
|
|
||||||
|
type isNetworkSpec_ConfigFrom interface {
|
||||||
|
isNetworkSpec_ConfigFrom()
|
||||||
|
MarshalTo([]byte) (int, error)
|
||||||
|
Size() int
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetworkSpec_Network struct {
|
||||||
|
Network string `protobuf:"bytes,8,opt,name=network,proto3,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*NetworkSpec_Network) isNetworkSpec_ConfigFrom() {}
|
||||||
|
|
||||||
|
func (m *NetworkSpec) GetConfigFrom() isNetworkSpec_ConfigFrom {
|
||||||
|
if m != nil {
|
||||||
|
return m.ConfigFrom
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *NetworkSpec) GetNetwork() string {
|
||||||
|
if x, ok := m.GetConfigFrom().(*NetworkSpec_Network); ok {
|
||||||
|
return x.Network
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX_OneofFuncs is for the internal use of the proto package.
|
||||||
|
func (*NetworkSpec) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
|
||||||
|
return _NetworkSpec_OneofMarshaler, _NetworkSpec_OneofUnmarshaler, _NetworkSpec_OneofSizer, []interface{}{
|
||||||
|
(*NetworkSpec_Network)(nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _NetworkSpec_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
|
||||||
|
m := msg.(*NetworkSpec)
|
||||||
|
// config_from
|
||||||
|
switch x := m.ConfigFrom.(type) {
|
||||||
|
case *NetworkSpec_Network:
|
||||||
|
_ = b.EncodeVarint(8<<3 | proto.WireBytes)
|
||||||
|
_ = b.EncodeStringBytes(x.Network)
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("NetworkSpec.ConfigFrom has unexpected type %T", x)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _NetworkSpec_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
|
||||||
|
m := msg.(*NetworkSpec)
|
||||||
|
switch tag {
|
||||||
|
case 8: // config_from.network
|
||||||
|
if wire != proto.WireBytes {
|
||||||
|
return true, proto.ErrInternalBadWireType
|
||||||
|
}
|
||||||
|
x, err := b.DecodeStringBytes()
|
||||||
|
m.ConfigFrom = &NetworkSpec_Network{x}
|
||||||
|
return true, err
|
||||||
|
default:
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _NetworkSpec_OneofSizer(msg proto.Message) (n int) {
|
||||||
|
m := msg.(*NetworkSpec)
|
||||||
|
// config_from
|
||||||
|
switch x := m.ConfigFrom.(type) {
|
||||||
|
case *NetworkSpec_Network:
|
||||||
|
n += proto.SizeVarint(8<<3 | proto.WireBytes)
|
||||||
|
n += proto.SizeVarint(uint64(len(x.Network)))
|
||||||
|
n += len(x.Network)
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
// ClusterSpec specifies global cluster settings.
|
// ClusterSpec specifies global cluster settings.
|
||||||
type ClusterSpec struct {
|
type ClusterSpec struct {
|
||||||
Annotations Annotations `protobuf:"bytes,1,opt,name=annotations" json:"annotations"`
|
Annotations Annotations `protobuf:"bytes,1,opt,name=annotations" json:"annotations"`
|
||||||
|
@ -682,6 +760,12 @@ type SecretSpec struct {
|
||||||
Annotations Annotations `protobuf:"bytes,1,opt,name=annotations" json:"annotations"`
|
Annotations Annotations `protobuf:"bytes,1,opt,name=annotations" json:"annotations"`
|
||||||
// Data is the secret payload - the maximum size is 500KB (that is, 500*1024 bytes)
|
// Data is the secret payload - the maximum size is 500KB (that is, 500*1024 bytes)
|
||||||
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
|
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
|
||||||
|
// Templating controls whether and how to evaluate the secret payload as
|
||||||
|
// a template. If it is not set, no templating is used.
|
||||||
|
//
|
||||||
|
// The currently recognized values are:
|
||||||
|
// - golang: Go templating
|
||||||
|
Templating *Driver `protobuf:"bytes,3,opt,name=templating" json:"templating,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SecretSpec) Reset() { *m = SecretSpec{} }
|
func (m *SecretSpec) Reset() { *m = SecretSpec{} }
|
||||||
|
@ -695,6 +779,12 @@ type ConfigSpec struct {
|
||||||
// TODO(aaronl): Do we want to revise this to include multiple payloads in a single
|
// TODO(aaronl): Do we want to revise this to include multiple payloads in a single
|
||||||
// ConfigSpec? Define this to be a tar? etc...
|
// ConfigSpec? Define this to be a tar? etc...
|
||||||
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
|
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
|
||||||
|
// Templating controls whether and how to evaluate the secret payload as
|
||||||
|
// a template. If it is not set, no templating is used.
|
||||||
|
//
|
||||||
|
// The currently recognized values are:
|
||||||
|
// - golang: Go templating
|
||||||
|
Templating *Driver `protobuf:"bytes,3,opt,name=templating" json:"templating,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ConfigSpec) Reset() { *m = ConfigSpec{} }
|
func (m *ConfigSpec) Reset() { *m = ConfigSpec{} }
|
||||||
|
@ -1093,6 +1183,16 @@ func (m *NetworkSpec) CopyFrom(src interface{}) {
|
||||||
m.IPAM = &IPAMOptions{}
|
m.IPAM = &IPAMOptions{}
|
||||||
github_com_docker_swarmkit_api_deepcopy.Copy(m.IPAM, o.IPAM)
|
github_com_docker_swarmkit_api_deepcopy.Copy(m.IPAM, o.IPAM)
|
||||||
}
|
}
|
||||||
|
if o.ConfigFrom != nil {
|
||||||
|
switch o.ConfigFrom.(type) {
|
||||||
|
case *NetworkSpec_Network:
|
||||||
|
v := NetworkSpec_Network{
|
||||||
|
Network: o.GetNetwork(),
|
||||||
|
}
|
||||||
|
m.ConfigFrom = &v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ClusterSpec) Copy() *ClusterSpec {
|
func (m *ClusterSpec) Copy() *ClusterSpec {
|
||||||
|
@ -1136,6 +1236,10 @@ func (m *SecretSpec) CopyFrom(src interface{}) {
|
||||||
m.Data = make([]byte, len(o.Data))
|
m.Data = make([]byte, len(o.Data))
|
||||||
copy(m.Data, o.Data)
|
copy(m.Data, o.Data)
|
||||||
}
|
}
|
||||||
|
if o.Templating != nil {
|
||||||
|
m.Templating = &Driver{}
|
||||||
|
github_com_docker_swarmkit_api_deepcopy.Copy(m.Templating, o.Templating)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ConfigSpec) Copy() *ConfigSpec {
|
func (m *ConfigSpec) Copy() *ConfigSpec {
|
||||||
|
@ -1156,6 +1260,10 @@ func (m *ConfigSpec) CopyFrom(src interface{}) {
|
||||||
m.Data = make([]byte, len(o.Data))
|
m.Data = make([]byte, len(o.Data))
|
||||||
copy(m.Data, o.Data)
|
copy(m.Data, o.Data)
|
||||||
}
|
}
|
||||||
|
if o.Templating != nil {
|
||||||
|
m.Templating = &Driver{}
|
||||||
|
github_com_docker_swarmkit_api_deepcopy.Copy(m.Templating, o.Templating)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *NodeSpec) Marshal() (dAtA []byte, err error) {
|
func (m *NodeSpec) Marshal() (dAtA []byte, err error) {
|
||||||
|
@ -2010,15 +2118,24 @@ func (m *NetworkSpec) MarshalTo(dAtA []byte) (int, error) {
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
if len(m.ConfigFrom) > 0 {
|
if m.ConfigFrom != nil {
|
||||||
dAtA[i] = 0x42
|
nn27, err := m.ConfigFrom.MarshalTo(dAtA[i:])
|
||||||
i++
|
if err != nil {
|
||||||
i = encodeVarintSpecs(dAtA, i, uint64(len(m.ConfigFrom)))
|
return 0, err
|
||||||
i += copy(dAtA[i:], m.ConfigFrom)
|
}
|
||||||
|
i += nn27
|
||||||
}
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *NetworkSpec_Network) MarshalTo(dAtA []byte) (int, error) {
|
||||||
|
i := 0
|
||||||
|
dAtA[i] = 0x42
|
||||||
|
i++
|
||||||
|
i = encodeVarintSpecs(dAtA, i, uint64(len(m.Network)))
|
||||||
|
i += copy(dAtA[i:], m.Network)
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
func (m *ClusterSpec) Marshal() (dAtA []byte, err error) {
|
func (m *ClusterSpec) Marshal() (dAtA []byte, err error) {
|
||||||
size := m.Size()
|
size := m.Size()
|
||||||
dAtA = make([]byte, size)
|
dAtA = make([]byte, size)
|
||||||
|
@ -2037,67 +2154,67 @@ func (m *ClusterSpec) MarshalTo(dAtA []byte) (int, error) {
|
||||||
dAtA[i] = 0xa
|
dAtA[i] = 0xa
|
||||||
i++
|
i++
|
||||||
i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size()))
|
i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size()))
|
||||||
n27, err := m.Annotations.MarshalTo(dAtA[i:])
|
n28, err := m.Annotations.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
i += n27
|
|
||||||
dAtA[i] = 0x12
|
|
||||||
i++
|
|
||||||
i = encodeVarintSpecs(dAtA, i, uint64(m.AcceptancePolicy.Size()))
|
|
||||||
n28, err := m.AcceptancePolicy.MarshalTo(dAtA[i:])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n28
|
i += n28
|
||||||
dAtA[i] = 0x1a
|
dAtA[i] = 0x12
|
||||||
i++
|
i++
|
||||||
i = encodeVarintSpecs(dAtA, i, uint64(m.Orchestration.Size()))
|
i = encodeVarintSpecs(dAtA, i, uint64(m.AcceptancePolicy.Size()))
|
||||||
n29, err := m.Orchestration.MarshalTo(dAtA[i:])
|
n29, err := m.AcceptancePolicy.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n29
|
i += n29
|
||||||
dAtA[i] = 0x22
|
dAtA[i] = 0x1a
|
||||||
i++
|
i++
|
||||||
i = encodeVarintSpecs(dAtA, i, uint64(m.Raft.Size()))
|
i = encodeVarintSpecs(dAtA, i, uint64(m.Orchestration.Size()))
|
||||||
n30, err := m.Raft.MarshalTo(dAtA[i:])
|
n30, err := m.Orchestration.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n30
|
i += n30
|
||||||
dAtA[i] = 0x2a
|
dAtA[i] = 0x22
|
||||||
i++
|
i++
|
||||||
i = encodeVarintSpecs(dAtA, i, uint64(m.Dispatcher.Size()))
|
i = encodeVarintSpecs(dAtA, i, uint64(m.Raft.Size()))
|
||||||
n31, err := m.Dispatcher.MarshalTo(dAtA[i:])
|
n31, err := m.Raft.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n31
|
i += n31
|
||||||
dAtA[i] = 0x32
|
dAtA[i] = 0x2a
|
||||||
i++
|
i++
|
||||||
i = encodeVarintSpecs(dAtA, i, uint64(m.CAConfig.Size()))
|
i = encodeVarintSpecs(dAtA, i, uint64(m.Dispatcher.Size()))
|
||||||
n32, err := m.CAConfig.MarshalTo(dAtA[i:])
|
n32, err := m.Dispatcher.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n32
|
i += n32
|
||||||
dAtA[i] = 0x3a
|
dAtA[i] = 0x32
|
||||||
i++
|
i++
|
||||||
i = encodeVarintSpecs(dAtA, i, uint64(m.TaskDefaults.Size()))
|
i = encodeVarintSpecs(dAtA, i, uint64(m.CAConfig.Size()))
|
||||||
n33, err := m.TaskDefaults.MarshalTo(dAtA[i:])
|
n33, err := m.CAConfig.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n33
|
i += n33
|
||||||
dAtA[i] = 0x42
|
dAtA[i] = 0x3a
|
||||||
i++
|
i++
|
||||||
i = encodeVarintSpecs(dAtA, i, uint64(m.EncryptionConfig.Size()))
|
i = encodeVarintSpecs(dAtA, i, uint64(m.TaskDefaults.Size()))
|
||||||
n34, err := m.EncryptionConfig.MarshalTo(dAtA[i:])
|
n34, err := m.TaskDefaults.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n34
|
i += n34
|
||||||
|
dAtA[i] = 0x42
|
||||||
|
i++
|
||||||
|
i = encodeVarintSpecs(dAtA, i, uint64(m.EncryptionConfig.Size()))
|
||||||
|
n35, err := m.EncryptionConfig.MarshalTo(dAtA[i:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
i += n35
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2119,17 +2236,27 @@ func (m *SecretSpec) MarshalTo(dAtA []byte) (int, error) {
|
||||||
dAtA[i] = 0xa
|
dAtA[i] = 0xa
|
||||||
i++
|
i++
|
||||||
i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size()))
|
i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size()))
|
||||||
n35, err := m.Annotations.MarshalTo(dAtA[i:])
|
n36, err := m.Annotations.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n35
|
i += n36
|
||||||
if len(m.Data) > 0 {
|
if len(m.Data) > 0 {
|
||||||
dAtA[i] = 0x12
|
dAtA[i] = 0x12
|
||||||
i++
|
i++
|
||||||
i = encodeVarintSpecs(dAtA, i, uint64(len(m.Data)))
|
i = encodeVarintSpecs(dAtA, i, uint64(len(m.Data)))
|
||||||
i += copy(dAtA[i:], m.Data)
|
i += copy(dAtA[i:], m.Data)
|
||||||
}
|
}
|
||||||
|
if m.Templating != nil {
|
||||||
|
dAtA[i] = 0x1a
|
||||||
|
i++
|
||||||
|
i = encodeVarintSpecs(dAtA, i, uint64(m.Templating.Size()))
|
||||||
|
n37, err := m.Templating.MarshalTo(dAtA[i:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
i += n37
|
||||||
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2151,17 +2278,27 @@ func (m *ConfigSpec) MarshalTo(dAtA []byte) (int, error) {
|
||||||
dAtA[i] = 0xa
|
dAtA[i] = 0xa
|
||||||
i++
|
i++
|
||||||
i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size()))
|
i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size()))
|
||||||
n36, err := m.Annotations.MarshalTo(dAtA[i:])
|
n38, err := m.Annotations.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n36
|
i += n38
|
||||||
if len(m.Data) > 0 {
|
if len(m.Data) > 0 {
|
||||||
dAtA[i] = 0x12
|
dAtA[i] = 0x12
|
||||||
i++
|
i++
|
||||||
i = encodeVarintSpecs(dAtA, i, uint64(len(m.Data)))
|
i = encodeVarintSpecs(dAtA, i, uint64(len(m.Data)))
|
||||||
i += copy(dAtA[i:], m.Data)
|
i += copy(dAtA[i:], m.Data)
|
||||||
}
|
}
|
||||||
|
if m.Templating != nil {
|
||||||
|
dAtA[i] = 0x1a
|
||||||
|
i++
|
||||||
|
i = encodeVarintSpecs(dAtA, i, uint64(m.Templating.Size()))
|
||||||
|
n39, err := m.Templating.MarshalTo(dAtA[i:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
i += n39
|
||||||
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2544,13 +2681,19 @@ func (m *NetworkSpec) Size() (n int) {
|
||||||
if m.Ingress {
|
if m.Ingress {
|
||||||
n += 2
|
n += 2
|
||||||
}
|
}
|
||||||
l = len(m.ConfigFrom)
|
if m.ConfigFrom != nil {
|
||||||
if l > 0 {
|
n += m.ConfigFrom.Size()
|
||||||
n += 1 + l + sovSpecs(uint64(l))
|
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *NetworkSpec_Network) Size() (n int) {
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
l = len(m.Network)
|
||||||
|
n += 1 + l + sovSpecs(uint64(l))
|
||||||
|
return n
|
||||||
|
}
|
||||||
func (m *ClusterSpec) Size() (n int) {
|
func (m *ClusterSpec) Size() (n int) {
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = l
|
||||||
|
@ -2582,6 +2725,10 @@ func (m *SecretSpec) Size() (n int) {
|
||||||
if l > 0 {
|
if l > 0 {
|
||||||
n += 1 + l + sovSpecs(uint64(l))
|
n += 1 + l + sovSpecs(uint64(l))
|
||||||
}
|
}
|
||||||
|
if m.Templating != nil {
|
||||||
|
l = m.Templating.Size()
|
||||||
|
n += 1 + l + sovSpecs(uint64(l))
|
||||||
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2594,6 +2741,10 @@ func (m *ConfigSpec) Size() (n int) {
|
||||||
if l > 0 {
|
if l > 0 {
|
||||||
n += 1 + l + sovSpecs(uint64(l))
|
n += 1 + l + sovSpecs(uint64(l))
|
||||||
}
|
}
|
||||||
|
if m.Templating != nil {
|
||||||
|
l = m.Templating.Size()
|
||||||
|
n += 1 + l + sovSpecs(uint64(l))
|
||||||
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2836,6 +2987,16 @@ func (this *NetworkSpec) String() string {
|
||||||
}, "")
|
}, "")
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
func (this *NetworkSpec_Network) String() string {
|
||||||
|
if this == nil {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
s := strings.Join([]string{`&NetworkSpec_Network{`,
|
||||||
|
`Network:` + fmt.Sprintf("%v", this.Network) + `,`,
|
||||||
|
`}`,
|
||||||
|
}, "")
|
||||||
|
return s
|
||||||
|
}
|
||||||
func (this *ClusterSpec) String() string {
|
func (this *ClusterSpec) String() string {
|
||||||
if this == nil {
|
if this == nil {
|
||||||
return "nil"
|
return "nil"
|
||||||
|
@ -2860,6 +3021,7 @@ func (this *SecretSpec) String() string {
|
||||||
s := strings.Join([]string{`&SecretSpec{`,
|
s := strings.Join([]string{`&SecretSpec{`,
|
||||||
`Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`,
|
`Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`,
|
||||||
`Data:` + fmt.Sprintf("%v", this.Data) + `,`,
|
`Data:` + fmt.Sprintf("%v", this.Data) + `,`,
|
||||||
|
`Templating:` + strings.Replace(fmt.Sprintf("%v", this.Templating), "Driver", "Driver", 1) + `,`,
|
||||||
`}`,
|
`}`,
|
||||||
}, "")
|
}, "")
|
||||||
return s
|
return s
|
||||||
|
@ -2871,6 +3033,7 @@ func (this *ConfigSpec) String() string {
|
||||||
s := strings.Join([]string{`&ConfigSpec{`,
|
s := strings.Join([]string{`&ConfigSpec{`,
|
||||||
`Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`,
|
`Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`,
|
||||||
`Data:` + fmt.Sprintf("%v", this.Data) + `,`,
|
`Data:` + fmt.Sprintf("%v", this.Data) + `,`,
|
||||||
|
`Templating:` + strings.Replace(fmt.Sprintf("%v", this.Templating), "Driver", "Driver", 1) + `,`,
|
||||||
`}`,
|
`}`,
|
||||||
}, "")
|
}, "")
|
||||||
return s
|
return s
|
||||||
|
@ -5259,7 +5422,7 @@ func (m *NetworkSpec) Unmarshal(dAtA []byte) error {
|
||||||
m.Ingress = bool(v != 0)
|
m.Ingress = bool(v != 0)
|
||||||
case 8:
|
case 8:
|
||||||
if wireType != 2 {
|
if wireType != 2 {
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field ConfigFrom", wireType)
|
return fmt.Errorf("proto: wrong wireType = %d for field Network", wireType)
|
||||||
}
|
}
|
||||||
var stringLen uint64
|
var stringLen uint64
|
||||||
for shift := uint(0); ; shift += 7 {
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
@ -5284,7 +5447,7 @@ func (m *NetworkSpec) Unmarshal(dAtA []byte) error {
|
||||||
if postIndex > l {
|
if postIndex > l {
|
||||||
return io.ErrUnexpectedEOF
|
return io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
m.ConfigFrom = string(dAtA[iNdEx:postIndex])
|
m.ConfigFrom = &NetworkSpec_Network{string(dAtA[iNdEx:postIndex])}
|
||||||
iNdEx = postIndex
|
iNdEx = postIndex
|
||||||
default:
|
default:
|
||||||
iNdEx = preIndex
|
iNdEx = preIndex
|
||||||
|
@ -5687,6 +5850,39 @@ func (m *SecretSpec) Unmarshal(dAtA []byte) error {
|
||||||
m.Data = []byte{}
|
m.Data = []byte{}
|
||||||
}
|
}
|
||||||
iNdEx = postIndex
|
iNdEx = postIndex
|
||||||
|
case 3:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Templating", wireType)
|
||||||
|
}
|
||||||
|
var msglen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowSpecs
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
msglen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msglen < 0 {
|
||||||
|
return ErrInvalidLengthSpecs
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
if m.Templating == nil {
|
||||||
|
m.Templating = &Driver{}
|
||||||
|
}
|
||||||
|
if err := m.Templating.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
default:
|
default:
|
||||||
iNdEx = preIndex
|
iNdEx = preIndex
|
||||||
skippy, err := skipSpecs(dAtA[iNdEx:])
|
skippy, err := skipSpecs(dAtA[iNdEx:])
|
||||||
|
@ -5798,6 +5994,39 @@ func (m *ConfigSpec) Unmarshal(dAtA []byte) error {
|
||||||
m.Data = []byte{}
|
m.Data = []byte{}
|
||||||
}
|
}
|
||||||
iNdEx = postIndex
|
iNdEx = postIndex
|
||||||
|
case 3:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Templating", wireType)
|
||||||
|
}
|
||||||
|
var msglen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowSpecs
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
msglen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msglen < 0 {
|
||||||
|
return ErrInvalidLengthSpecs
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
if m.Templating == nil {
|
||||||
|
m.Templating = &Driver{}
|
||||||
|
}
|
||||||
|
if err := m.Templating.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
default:
|
default:
|
||||||
iNdEx = preIndex
|
iNdEx = preIndex
|
||||||
skippy, err := skipSpecs(dAtA[iNdEx:])
|
skippy, err := skipSpecs(dAtA[iNdEx:])
|
||||||
|
@ -5927,121 +6156,122 @@ var (
|
||||||
func init() { proto.RegisterFile("specs.proto", fileDescriptorSpecs) }
|
func init() { proto.RegisterFile("specs.proto", fileDescriptorSpecs) }
|
||||||
|
|
||||||
var fileDescriptorSpecs = []byte{
|
var fileDescriptorSpecs = []byte{
|
||||||
// 1844 bytes of a gzipped FileDescriptorProto
|
// 1867 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x4f, 0x73, 0x1b, 0x49,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x57, 0xcf, 0x73, 0x1b, 0x49,
|
||||||
0x15, 0xb7, 0x6c, 0x59, 0x7f, 0xde, 0xc8, 0x89, 0xdc, 0x24, 0x61, 0xac, 0xb0, 0xb2, 0xa2, 0x0d,
|
0x15, 0xb6, 0x6c, 0x59, 0x3f, 0xde, 0xc8, 0x89, 0xd2, 0x24, 0x61, 0xa2, 0xb0, 0xb2, 0xa2, 0x0d,
|
||||||
0xc1, 0xcb, 0x16, 0x72, 0x61, 0xa8, 0x25, 0x4b, 0x58, 0x40, 0xb2, 0xb4, 0x8e, 0x31, 0x76, 0x54,
|
0xc1, 0xcb, 0x16, 0x72, 0x61, 0xa8, 0x25, 0xbb, 0x61, 0x01, 0xc9, 0x12, 0x8e, 0x31, 0x76, 0x54,
|
||||||
0x6d, 0x6f, 0x20, 0x27, 0x55, 0x7b, 0xa6, 0x2d, 0x4d, 0x79, 0xd4, 0x3d, 0xf4, 0xf4, 0x68, 0x4b,
|
0x6d, 0x6f, 0x20, 0x27, 0x55, 0x7b, 0xa6, 0x3d, 0x9a, 0xf2, 0xa8, 0x7b, 0xe8, 0xe9, 0xd1, 0x96,
|
||||||
0x37, 0x8e, 0x5b, 0xb9, 0x72, 0x76, 0x71, 0xe0, 0xcb, 0xe4, 0x48, 0x71, 0xe2, 0xe4, 0x62, 0xf5,
|
0x6e, 0x1c, 0xb7, 0x72, 0xe5, 0xec, 0xe2, 0x40, 0xf1, 0xbf, 0xe4, 0x48, 0x71, 0xe2, 0xe4, 0x62,
|
||||||
0x09, 0xa8, 0xe2, 0x03, 0x40, 0x75, 0x4f, 0x8f, 0x34, 0x4a, 0xc6, 0x49, 0xaa, 0xc8, 0xde, 0xba,
|
0xfd, 0x2f, 0x70, 0xe3, 0x02, 0xd5, 0x3d, 0x3d, 0xd2, 0x28, 0x19, 0x27, 0xa9, 0x22, 0x07, 0x6e,
|
||||||
0xdf, 0xfc, 0x7e, 0xaf, 0x5f, 0xbf, 0xfe, 0x75, 0xbf, 0x37, 0x60, 0x85, 0x01, 0x75, 0xc2, 0x56,
|
0xdd, 0xaf, 0xbf, 0xef, 0xcd, 0xeb, 0xd7, 0x5f, 0xf7, 0x7b, 0x03, 0x56, 0x14, 0x52, 0x27, 0xea,
|
||||||
0x20, 0xb8, 0xe4, 0x08, 0xb9, 0xdc, 0xb9, 0xa4, 0xa2, 0x15, 0x7e, 0x4d, 0xc4, 0xf8, 0xd2, 0x93,
|
0x84, 0x82, 0x4b, 0x8e, 0x90, 0xcb, 0x9d, 0x73, 0x2a, 0x3a, 0xd1, 0xd7, 0x44, 0x4c, 0xce, 0x7d,
|
||||||
0xad, 0xc9, 0x4f, 0x6b, 0x96, 0x9c, 0x06, 0xd4, 0x00, 0x6a, 0x77, 0x86, 0x7c, 0xc8, 0xf5, 0x70,
|
0xd9, 0x99, 0xfe, 0xb8, 0x61, 0xc9, 0x59, 0x48, 0x0d, 0xa0, 0x71, 0xdb, 0xe3, 0x1e, 0xd7, 0xc3,
|
||||||
0x57, 0x8d, 0x8c, 0xb5, 0x3e, 0xe4, 0x7c, 0xe8, 0xd3, 0x5d, 0x3d, 0x3b, 0x8f, 0x2e, 0x76, 0xdd,
|
0x6d, 0x35, 0x32, 0xd6, 0xa6, 0xc7, 0xb9, 0x17, 0xd0, 0x6d, 0x3d, 0x3b, 0x8d, 0xcf, 0xb6, 0xdd,
|
||||||
0x48, 0x10, 0xe9, 0x71, 0x66, 0xbe, 0x6f, 0xbd, 0xfe, 0x9d, 0xb0, 0x69, 0xfc, 0xa9, 0x79, 0x95,
|
0x58, 0x10, 0xe9, 0x73, 0x66, 0xd6, 0xef, 0xbd, 0xbe, 0x4e, 0xd8, 0x2c, 0x59, 0x6a, 0x5f, 0x14,
|
||||||
0x87, 0xd2, 0x09, 0x77, 0xe9, 0x69, 0x40, 0x1d, 0x74, 0x00, 0x16, 0x61, 0x8c, 0x4b, 0xcd, 0x0d,
|
0xa1, 0x72, 0xc4, 0x5d, 0x7a, 0x1c, 0x52, 0x07, 0xed, 0x81, 0x45, 0x18, 0xe3, 0x52, 0x73, 0x23,
|
||||||
0xed, 0x5c, 0x23, 0xb7, 0x63, 0xed, 0x6d, 0xb7, 0xde, 0x0c, 0xaa, 0xd5, 0x5e, 0xc0, 0x3a, 0xf9,
|
0xbb, 0xd0, 0x2a, 0x6c, 0x59, 0x3b, 0x9b, 0x9d, 0x37, 0x83, 0xea, 0x74, 0x17, 0xb0, 0x5e, 0xf1,
|
||||||
0x57, 0xd7, 0xdb, 0x2b, 0x38, 0xcd, 0x44, 0xbf, 0x81, 0x8a, 0x4b, 0x43, 0x4f, 0x50, 0x77, 0x20,
|
0xd5, 0xe5, 0xe6, 0x0a, 0xce, 0x32, 0xd1, 0x2f, 0xa1, 0xe6, 0xd2, 0xc8, 0x17, 0xd4, 0x1d, 0x09,
|
||||||
0xb8, 0x4f, 0xed, 0xd5, 0x46, 0x6e, 0xe7, 0xd6, 0xde, 0x0f, 0xb2, 0x3c, 0xa9, 0xc5, 0x31, 0xf7,
|
0x1e, 0x50, 0x7b, 0xb5, 0x55, 0xd8, 0xba, 0xb1, 0xf3, 0xbd, 0x3c, 0x4f, 0xea, 0xe3, 0x98, 0x07,
|
||||||
0x29, 0xb6, 0x0c, 0x43, 0x4d, 0xd0, 0x01, 0xc0, 0x98, 0x8e, 0xcf, 0xa9, 0x08, 0x47, 0x5e, 0x60,
|
0x14, 0x5b, 0x86, 0xa1, 0x26, 0x68, 0x0f, 0x60, 0x42, 0x27, 0xa7, 0x54, 0x44, 0x63, 0x3f, 0xb4,
|
||||||
0xaf, 0x69, 0xfa, 0x8f, 0x6e, 0xa2, 0xab, 0xd8, 0x5b, 0xc7, 0x73, 0x38, 0x4e, 0x51, 0xd1, 0x31,
|
0xd7, 0x34, 0xfd, 0x07, 0xd7, 0xd1, 0x55, 0xec, 0x9d, 0xc3, 0x39, 0x1c, 0x67, 0xa8, 0xe8, 0x10,
|
||||||
0x54, 0xc8, 0x84, 0x78, 0x3e, 0x39, 0xf7, 0x7c, 0x4f, 0x4e, 0xed, 0xbc, 0x76, 0xf5, 0xc9, 0x5b,
|
0x6a, 0x64, 0x4a, 0xfc, 0x80, 0x9c, 0xfa, 0x81, 0x2f, 0x67, 0x76, 0x51, 0xbb, 0xfa, 0xe4, 0xad,
|
||||||
0x5d, 0xb5, 0x53, 0x04, 0xbc, 0x44, 0x6f, 0xba, 0x00, 0x8b, 0x85, 0xd0, 0x23, 0x28, 0xf6, 0x7b,
|
0xae, 0xba, 0x19, 0x02, 0x5e, 0xa2, 0xb7, 0x5d, 0x80, 0xc5, 0x87, 0xd0, 0x23, 0x28, 0x0f, 0x07,
|
||||||
0x27, 0xdd, 0xc3, 0x93, 0x83, 0xea, 0x4a, 0x6d, 0xeb, 0xe5, 0x55, 0xe3, 0xae, 0xf2, 0xb1, 0x00,
|
0x47, 0xfd, 0xfd, 0xa3, 0xbd, 0xfa, 0x4a, 0xe3, 0xde, 0xcb, 0x8b, 0xd6, 0x1d, 0xe5, 0x63, 0x01,
|
||||||
0xf4, 0x29, 0x73, 0x3d, 0x36, 0x44, 0x3b, 0x50, 0x6a, 0xef, 0xef, 0xf7, 0xfa, 0x67, 0xbd, 0x6e,
|
0x18, 0x52, 0xe6, 0xfa, 0xcc, 0x43, 0x5b, 0x50, 0xe9, 0xee, 0xee, 0x0e, 0x86, 0x27, 0x83, 0x7e,
|
||||||
0x35, 0x57, 0xab, 0xbd, 0xbc, 0x6a, 0xdc, 0x5b, 0x06, 0xb6, 0x1d, 0x87, 0x06, 0x92, 0xba, 0xb5,
|
0xbd, 0xd0, 0x68, 0xbc, 0xbc, 0x68, 0xdd, 0x5d, 0x06, 0x76, 0x1d, 0x87, 0x86, 0x92, 0xba, 0x8d,
|
||||||
0xfc, 0x37, 0x7f, 0xab, 0xaf, 0x34, 0xbf, 0xc9, 0x41, 0x25, 0x1d, 0x04, 0x7a, 0x04, 0x85, 0xf6,
|
0xe2, 0x37, 0x7f, 0x69, 0xae, 0xb4, 0xbf, 0x29, 0x40, 0x2d, 0x1b, 0x04, 0x7a, 0x04, 0xa5, 0xee,
|
||||||
0xfe, 0xd9, 0xe1, 0xf3, 0x5e, 0x75, 0x65, 0x41, 0x4f, 0x23, 0xda, 0x8e, 0xf4, 0x26, 0x14, 0x3d,
|
0xee, 0xc9, 0xfe, 0xf3, 0x41, 0x7d, 0x65, 0x41, 0xcf, 0x22, 0xba, 0x8e, 0xf4, 0xa7, 0x14, 0x3d,
|
||||||
0x84, 0xf5, 0x7e, 0xfb, 0xab, 0xd3, 0x5e, 0x35, 0xb7, 0x08, 0x27, 0x0d, 0xeb, 0x93, 0x28, 0xd4,
|
0x84, 0xf5, 0x61, 0xf7, 0xab, 0xe3, 0x41, 0xbd, 0xb0, 0x08, 0x27, 0x0b, 0x1b, 0x92, 0x38, 0xd2,
|
||||||
0xa8, 0x2e, 0x6e, 0x1f, 0x9e, 0x54, 0x57, 0xb3, 0x51, 0x5d, 0x41, 0x3c, 0x66, 0x42, 0xf9, 0x6b,
|
0xa8, 0x3e, 0xee, 0xee, 0x1f, 0xd5, 0x57, 0xf3, 0x51, 0x7d, 0x41, 0x7c, 0x66, 0x42, 0xf9, 0x73,
|
||||||
0x1e, 0xac, 0x53, 0x2a, 0x26, 0x9e, 0xf3, 0x81, 0x25, 0xf2, 0x19, 0xe4, 0x25, 0x09, 0x2f, 0xb5,
|
0x11, 0xac, 0x63, 0x2a, 0xa6, 0xbe, 0xf3, 0x81, 0x25, 0xf2, 0x19, 0x14, 0x25, 0x89, 0xce, 0xb5,
|
||||||
0x34, 0xac, 0x6c, 0x69, 0x9c, 0x91, 0xf0, 0x52, 0x2d, 0x6a, 0xe8, 0x1a, 0xaf, 0x94, 0x21, 0x68,
|
0x34, 0xac, 0x7c, 0x69, 0x9c, 0x90, 0xe8, 0x5c, 0x7d, 0xd4, 0xd0, 0x35, 0x5e, 0x29, 0x43, 0xd0,
|
||||||
0xe0, 0x7b, 0x0e, 0x91, 0xd4, 0xd5, 0xca, 0xb0, 0xf6, 0x7e, 0x98, 0xc5, 0xc6, 0x73, 0x94, 0x89,
|
0x30, 0xf0, 0x1d, 0x22, 0xa9, 0xab, 0x95, 0x61, 0xed, 0x7c, 0x3f, 0x8f, 0x8d, 0xe7, 0x28, 0x13,
|
||||||
0xff, 0xe9, 0x0a, 0x4e, 0x51, 0xd1, 0x13, 0x28, 0x0c, 0x7d, 0x7e, 0x4e, 0x7c, 0xad, 0x09, 0x6b,
|
0xff, 0xd3, 0x15, 0x9c, 0xa1, 0xa2, 0x27, 0x50, 0xf2, 0x02, 0x7e, 0x4a, 0x02, 0xad, 0x09, 0x6b,
|
||||||
0xef, 0x41, 0x96, 0x93, 0x03, 0x8d, 0x58, 0x38, 0x30, 0x14, 0xf4, 0x18, 0x0a, 0x51, 0xe0, 0x12,
|
0xe7, 0x41, 0x9e, 0x93, 0x3d, 0x8d, 0x58, 0x38, 0x30, 0x14, 0xf4, 0x18, 0x4a, 0x71, 0xe8, 0x12,
|
||||||
0x49, 0xed, 0x82, 0x26, 0x37, 0xb2, 0xc8, 0x5f, 0x69, 0xc4, 0x3e, 0x67, 0x17, 0xde, 0x10, 0x1b,
|
0x49, 0xed, 0x92, 0x26, 0xb7, 0xf2, 0xc8, 0x5f, 0x69, 0xc4, 0x2e, 0x67, 0x67, 0xbe, 0x87, 0x0d,
|
||||||
0x3c, 0x3a, 0x82, 0x12, 0xa3, 0xf2, 0x6b, 0x2e, 0x2e, 0x43, 0xbb, 0xd8, 0x58, 0xdb, 0xb1, 0xf6,
|
0x1e, 0x1d, 0x40, 0x85, 0x51, 0xf9, 0x35, 0x17, 0xe7, 0x91, 0x5d, 0x6e, 0xad, 0x6d, 0x59, 0x3b,
|
||||||
0x3e, 0xcd, 0x14, 0x63, 0x8c, 0x69, 0x4b, 0x49, 0x9c, 0xd1, 0x98, 0x32, 0x19, 0xbb, 0xe9, 0xac,
|
0x9f, 0xe6, 0x8a, 0x31, 0xc1, 0x74, 0xa5, 0x24, 0xce, 0x78, 0x42, 0x99, 0x4c, 0xdc, 0xf4, 0x56,
|
||||||
0xda, 0x39, 0x3c, 0x77, 0x80, 0x7e, 0x05, 0x25, 0xca, 0xdc, 0x80, 0x7b, 0x4c, 0xda, 0xa5, 0x9b,
|
0xed, 0x02, 0x9e, 0x3b, 0x40, 0x3f, 0x87, 0x0a, 0x65, 0x6e, 0xc8, 0x7d, 0x26, 0xed, 0xca, 0xf5,
|
||||||
0x03, 0xe9, 0x19, 0x8c, 0x4a, 0x26, 0x9e, 0x33, 0x14, 0x5b, 0x70, 0xdf, 0x3f, 0x27, 0xce, 0xa5,
|
0x81, 0x0c, 0x0c, 0x46, 0x25, 0x13, 0xcf, 0x19, 0x8a, 0x2d, 0x78, 0x10, 0x9c, 0x12, 0xe7, 0xdc,
|
||||||
0x5d, 0x7e, 0xcf, 0x6d, 0xcc, 0x19, 0x9d, 0x02, 0xe4, 0xc7, 0xdc, 0xa5, 0xcd, 0x5d, 0xd8, 0x7c,
|
0xae, 0xbe, 0xe7, 0x36, 0xe6, 0x8c, 0x5e, 0x09, 0x8a, 0x13, 0xee, 0xd2, 0xf6, 0x36, 0xdc, 0x7a,
|
||||||
0x23, 0xd5, 0xa8, 0x06, 0x25, 0x93, 0xea, 0x58, 0x23, 0x79, 0x3c, 0x9f, 0x37, 0x6f, 0xc3, 0xc6,
|
0x23, 0xd5, 0xa8, 0x01, 0x15, 0x93, 0xea, 0x44, 0x23, 0x45, 0x3c, 0x9f, 0xb7, 0x6f, 0xc2, 0xc6,
|
||||||
0x52, 0x5a, 0x9b, 0xff, 0xc8, 0x43, 0x29, 0x39, 0x6b, 0xd4, 0x86, 0xb2, 0xc3, 0x99, 0x24, 0x1e,
|
0x52, 0x5a, 0xdb, 0x7f, 0x2f, 0x42, 0x25, 0x3d, 0x6b, 0xd4, 0x85, 0xaa, 0xc3, 0x99, 0x24, 0x3e,
|
||||||
0xa3, 0xc2, 0xc8, 0x2b, 0xf3, 0x64, 0xf6, 0x13, 0x90, 0x62, 0x3d, 0x5d, 0xc1, 0x0b, 0x16, 0xfa,
|
0xa3, 0xc2, 0xc8, 0x2b, 0xf7, 0x64, 0x76, 0x53, 0x90, 0x62, 0x3d, 0x5d, 0xc1, 0x0b, 0x16, 0xfa,
|
||||||
0x12, 0xca, 0x82, 0x86, 0x3c, 0x12, 0x0e, 0x0d, 0x8d, 0xbe, 0x76, 0xb2, 0x15, 0x12, 0x83, 0x30,
|
0x35, 0x54, 0x05, 0x8d, 0x78, 0x2c, 0x1c, 0x1a, 0x19, 0x7d, 0x6d, 0xe5, 0x2b, 0x24, 0x01, 0x61,
|
||||||
0xfd, 0x53, 0xe4, 0x09, 0xaa, 0xb2, 0x1c, 0xe2, 0x05, 0x15, 0x3d, 0x81, 0xa2, 0xa0, 0xa1, 0x24,
|
0xfa, 0x87, 0xd8, 0x17, 0x54, 0x65, 0x39, 0xc2, 0x0b, 0x2a, 0x7a, 0x02, 0x65, 0x41, 0x23, 0x49,
|
||||||
0x42, 0xbe, 0x4d, 0x22, 0x38, 0x86, 0xf4, 0xb9, 0xef, 0x39, 0x53, 0x9c, 0x30, 0xd0, 0x13, 0x28,
|
0x84, 0x7c, 0x9b, 0x44, 0x70, 0x02, 0x19, 0xf2, 0xc0, 0x77, 0x66, 0x38, 0x65, 0xa0, 0x27, 0x50,
|
||||||
0x07, 0x3e, 0x71, 0xb4, 0x57, 0x7b, 0x5d, 0xd3, 0x3f, 0xca, 0xa2, 0xf7, 0x13, 0x10, 0x5e, 0xe0,
|
0x0d, 0x03, 0xe2, 0x68, 0xaf, 0xf6, 0xba, 0xa6, 0x7f, 0x94, 0x47, 0x1f, 0xa6, 0x20, 0xbc, 0xc0,
|
||||||
0xd1, 0xe7, 0x00, 0x3e, 0x1f, 0x0e, 0x5c, 0xe1, 0x4d, 0xa8, 0x30, 0x12, 0xab, 0x65, 0xb1, 0xbb,
|
0xa3, 0xcf, 0x01, 0x02, 0xee, 0x8d, 0x5c, 0xe1, 0x4f, 0xa9, 0x30, 0x12, 0x6b, 0xe4, 0xb1, 0xfb,
|
||||||
0x1a, 0x81, 0xcb, 0x3e, 0x1f, 0xc6, 0x43, 0x74, 0xf0, 0x7f, 0xe9, 0x2b, 0xa5, 0xad, 0x23, 0x00,
|
0x1a, 0x81, 0xab, 0x01, 0xf7, 0x92, 0x21, 0xda, 0xfb, 0x9f, 0xf4, 0x95, 0xd1, 0xd6, 0x01, 0x00,
|
||||||
0x32, 0xff, 0x6a, 0xd4, 0xf5, 0xc9, 0x7b, 0xb9, 0x32, 0x27, 0x92, 0xa2, 0xa3, 0x07, 0x50, 0xb9,
|
0x99, 0xaf, 0x1a, 0x75, 0x7d, 0xf2, 0x5e, 0xae, 0xcc, 0x89, 0x64, 0xe8, 0xe8, 0x01, 0xd4, 0xce,
|
||||||
0xe0, 0xc2, 0xa1, 0x03, 0x73, 0x6b, 0xca, 0x5a, 0x13, 0x96, 0xb6, 0xc5, 0xfa, 0x42, 0x1d, 0x28,
|
0xb8, 0x70, 0xe8, 0xc8, 0xdc, 0x9a, 0xaa, 0xd6, 0x84, 0xa5, 0x6d, 0x89, 0xbe, 0x50, 0x0f, 0xca,
|
||||||
0x0e, 0x29, 0xa3, 0xc2, 0x73, 0x6c, 0xd0, 0x8b, 0x3d, 0xca, 0xbc, 0x90, 0x31, 0x04, 0x47, 0x4c,
|
0x1e, 0x65, 0x54, 0xf8, 0x8e, 0x0d, 0xfa, 0x63, 0x8f, 0x72, 0x2f, 0x64, 0x02, 0xc1, 0x31, 0x93,
|
||||||
0x7a, 0x63, 0x6a, 0x56, 0x4a, 0x88, 0x9d, 0x32, 0x14, 0x45, 0xfc, 0xa5, 0xf9, 0x47, 0x40, 0x6f,
|
0xfe, 0x84, 0x9a, 0x2f, 0xa5, 0xc4, 0x5e, 0x15, 0xca, 0x22, 0x59, 0x69, 0xff, 0x1e, 0xd0, 0x9b,
|
||||||
0x62, 0x11, 0x82, 0xfc, 0xa5, 0xc7, 0x5c, 0x2d, 0xac, 0x32, 0xd6, 0x63, 0xd4, 0x82, 0x62, 0x40,
|
0x58, 0x84, 0xa0, 0x78, 0xee, 0x33, 0x57, 0x0b, 0xab, 0x8a, 0xf5, 0x18, 0x75, 0xa0, 0x1c, 0x92,
|
||||||
0xa6, 0x3e, 0x27, 0xae, 0x11, 0xcb, 0x9d, 0x56, 0x5c, 0x2f, 0x5b, 0x49, 0xbd, 0x6c, 0xb5, 0xd9,
|
0x59, 0xc0, 0x89, 0x6b, 0xc4, 0x72, 0xbb, 0x93, 0xd4, 0xcb, 0x4e, 0x5a, 0x2f, 0x3b, 0x5d, 0x36,
|
||||||
0x14, 0x27, 0xa0, 0xe6, 0x11, 0xdc, 0xcd, 0xdc, 0x32, 0xda, 0x83, 0xca, 0x5c, 0x84, 0x03, 0xcf,
|
0xc3, 0x29, 0xa8, 0x7d, 0x00, 0x77, 0x72, 0xb7, 0x8c, 0x76, 0xa0, 0x36, 0x17, 0xe1, 0xc8, 0x37,
|
||||||
0x2c, 0xd2, 0xb9, 0x3d, 0xbb, 0xde, 0xb6, 0xe6, 0x6a, 0x3d, 0xec, 0x62, 0x6b, 0x0e, 0x3a, 0x74,
|
0x1f, 0xe9, 0xdd, 0xbc, 0xba, 0xdc, 0xb4, 0xe6, 0x6a, 0xdd, 0xef, 0x63, 0x6b, 0x0e, 0xda, 0x77,
|
||||||
0x9b, 0x7f, 0x29, 0xc3, 0xc6, 0x92, 0x94, 0xd1, 0x1d, 0x58, 0xf7, 0xc6, 0x64, 0x48, 0x4d, 0x8c,
|
0xdb, 0x7f, 0xaa, 0xc2, 0xc6, 0x92, 0x94, 0xd1, 0x6d, 0x58, 0xf7, 0x27, 0xc4, 0xa3, 0x26, 0xc6,
|
||||||
0xf1, 0x04, 0xf5, 0xa0, 0xe0, 0x93, 0x73, 0xea, 0x2b, 0x41, 0xab, 0x43, 0xfd, 0xc9, 0x3b, 0xef,
|
0x64, 0x82, 0x06, 0x50, 0x0a, 0xc8, 0x29, 0x0d, 0x94, 0xa0, 0xd5, 0xa1, 0xfe, 0xe8, 0x9d, 0x77,
|
||||||
0x44, 0xeb, 0xf7, 0x1a, 0xdf, 0x63, 0x52, 0x4c, 0xb1, 0x21, 0x23, 0x1b, 0x8a, 0x0e, 0x1f, 0x8f,
|
0xa2, 0xf3, 0x5b, 0x8d, 0x1f, 0x30, 0x29, 0x66, 0xd8, 0x90, 0x91, 0x0d, 0x65, 0x87, 0x4f, 0x26,
|
||||||
0x09, 0x53, 0x4f, 0xe7, 0xda, 0x4e, 0x19, 0x27, 0x53, 0x95, 0x19, 0x22, 0x86, 0xa1, 0x9d, 0xd7,
|
0x84, 0xa9, 0xa7, 0x73, 0x6d, 0xab, 0x8a, 0xd3, 0xa9, 0xca, 0x0c, 0x11, 0x5e, 0x64, 0x17, 0xb5,
|
||||||
0x66, 0x3d, 0x46, 0x55, 0x58, 0xa3, 0x6c, 0x62, 0xaf, 0x6b, 0x93, 0x1a, 0x2a, 0x8b, 0xeb, 0xc5,
|
0x59, 0x8f, 0x51, 0x1d, 0xd6, 0x28, 0x9b, 0xda, 0xeb, 0xda, 0xa4, 0x86, 0xca, 0xe2, 0xfa, 0x89,
|
||||||
0x8a, 0x2c, 0x63, 0x35, 0x54, 0xbc, 0x28, 0xa4, 0xc2, 0x2e, 0xc6, 0x19, 0x55, 0x63, 0xf4, 0x0b,
|
0x22, 0xab, 0x58, 0x0d, 0x15, 0x2f, 0x8e, 0xa8, 0xb0, 0xcb, 0x49, 0x46, 0xd5, 0x18, 0xfd, 0x0c,
|
||||||
0x28, 0x8c, 0x79, 0xc4, 0x64, 0x68, 0x97, 0x74, 0xb0, 0x5b, 0x59, 0xc1, 0x1e, 0x2b, 0x84, 0x79,
|
0x4a, 0x13, 0x1e, 0x33, 0x19, 0xd9, 0x15, 0x1d, 0xec, 0xbd, 0xbc, 0x60, 0x0f, 0x15, 0xc2, 0x3c,
|
||||||
0xda, 0x0d, 0x1c, 0xf5, 0x60, 0x33, 0x94, 0x3c, 0x18, 0x0c, 0x05, 0x71, 0xe8, 0x20, 0xa0, 0xc2,
|
0xed, 0x06, 0x8e, 0x06, 0x70, 0x2b, 0x92, 0x3c, 0x1c, 0x79, 0x82, 0x38, 0x74, 0x14, 0x52, 0xe1,
|
||||||
0xe3, 0xae, 0x79, 0x9a, 0xb6, 0xde, 0x38, 0x94, 0xae, 0x69, 0x72, 0xf0, 0x6d, 0xc5, 0x39, 0x50,
|
0x73, 0xd7, 0x3c, 0x4d, 0xf7, 0xde, 0x38, 0x94, 0xbe, 0x69, 0x72, 0xf0, 0x4d, 0xc5, 0xd9, 0x53,
|
||||||
0x94, 0xbe, 0x66, 0xa0, 0x3e, 0x54, 0x82, 0xc8, 0xf7, 0x07, 0x3c, 0x88, 0xab, 0x54, 0xac, 0xa7,
|
0x94, 0xa1, 0x66, 0xa0, 0x21, 0xd4, 0xc2, 0x38, 0x08, 0x46, 0x3c, 0x4c, 0xaa, 0x54, 0xa2, 0xa7,
|
||||||
0xf7, 0x48, 0x59, 0x3f, 0xf2, 0xfd, 0x67, 0x31, 0x09, 0x5b, 0xc1, 0x62, 0x82, 0xee, 0x41, 0x61,
|
0xf7, 0x48, 0xd9, 0x30, 0x0e, 0x82, 0x67, 0x09, 0x09, 0x5b, 0xe1, 0x62, 0x82, 0xee, 0x42, 0xc9,
|
||||||
0x28, 0x78, 0x14, 0x84, 0xb6, 0xa5, 0x93, 0x61, 0x66, 0xe8, 0x0b, 0x28, 0x86, 0xd4, 0x11, 0x54,
|
0x13, 0x3c, 0x0e, 0x23, 0xdb, 0xd2, 0xc9, 0x30, 0x33, 0xf4, 0x25, 0x94, 0x23, 0xea, 0x08, 0x2a,
|
||||||
0x86, 0x76, 0x45, 0x6f, 0xf5, 0xe3, 0xac, 0x45, 0x4e, 0x35, 0x04, 0xd3, 0x0b, 0x2a, 0x28, 0x73,
|
0x23, 0xbb, 0xa6, 0xb7, 0xfa, 0x71, 0xde, 0x47, 0x8e, 0x35, 0x04, 0xd3, 0x33, 0x2a, 0x28, 0x73,
|
||||||
0x28, 0x4e, 0x38, 0x68, 0x0b, 0xd6, 0xa4, 0x9c, 0xda, 0x1b, 0x8d, 0xdc, 0x4e, 0xa9, 0x53, 0x9c,
|
0x28, 0x4e, 0x39, 0xe8, 0x1e, 0xac, 0x49, 0x39, 0xb3, 0x37, 0x5a, 0x85, 0xad, 0x4a, 0xaf, 0x7c,
|
||||||
0x5d, 0x6f, 0xaf, 0x9d, 0x9d, 0xbd, 0xc0, 0xca, 0xa6, 0x5e, 0xd0, 0x11, 0x0f, 0x25, 0x23, 0x63,
|
0x75, 0xb9, 0xb9, 0x76, 0x72, 0xf2, 0x02, 0x2b, 0x9b, 0x7a, 0x41, 0xc7, 0x3c, 0x92, 0x8c, 0x4c,
|
||||||
0x6a, 0xdf, 0xd2, 0xb9, 0x9d, 0xcf, 0xd1, 0x0b, 0x00, 0x97, 0x85, 0x03, 0x47, 0x5f, 0x59, 0xfb,
|
0xa8, 0x7d, 0x43, 0xe7, 0x76, 0x3e, 0x47, 0x2f, 0x00, 0x5c, 0x16, 0x8d, 0x1c, 0x7d, 0x65, 0xed,
|
||||||
0xb6, 0xde, 0xdd, 0xa7, 0xef, 0xde, 0x5d, 0xf7, 0xe4, 0xd4, 0x54, 0x91, 0x8d, 0xd9, 0xf5, 0x76,
|
0x9b, 0x7a, 0x77, 0x9f, 0xbe, 0x7b, 0x77, 0xfd, 0xa3, 0x63, 0x53, 0x45, 0x36, 0xae, 0x2e, 0x37,
|
||||||
0x79, 0x3e, 0xc5, 0x65, 0x97, 0x85, 0xf1, 0x10, 0x75, 0xc0, 0x1a, 0x51, 0xe2, 0xcb, 0x91, 0x33,
|
0xab, 0xf3, 0x29, 0xae, 0xba, 0x2c, 0x4a, 0x86, 0xa8, 0x07, 0xd6, 0x98, 0x92, 0x40, 0x8e, 0x9d,
|
||||||
0xa2, 0xce, 0xa5, 0x5d, 0xbd, 0xb9, 0x2c, 0x3c, 0xd5, 0x30, 0xe3, 0x21, 0x4d, 0x52, 0x0a, 0x56,
|
0x31, 0x75, 0xce, 0xed, 0xfa, 0xf5, 0x65, 0xe1, 0xa9, 0x86, 0x19, 0x0f, 0x59, 0x92, 0x52, 0xb0,
|
||||||
0xa1, 0x86, 0xf6, 0xa6, 0xce, 0x55, 0x3c, 0x41, 0x1f, 0x01, 0xf0, 0x80, 0xb2, 0x41, 0x28, 0x5d,
|
0x0a, 0x35, 0xb2, 0x6f, 0xe9, 0x5c, 0x25, 0x13, 0xf4, 0x11, 0x00, 0x0f, 0x29, 0x1b, 0x45, 0xd2,
|
||||||
0x8f, 0xd9, 0x48, 0x6d, 0x19, 0x97, 0x95, 0xe5, 0x54, 0x19, 0xd0, 0x7d, 0xf5, 0x68, 0x13, 0x77,
|
0xf5, 0x99, 0x8d, 0xd4, 0x96, 0x71, 0x55, 0x59, 0x8e, 0x95, 0x01, 0xdd, 0x57, 0x8f, 0x36, 0x71,
|
||||||
0xc0, 0x99, 0x3f, 0xb5, 0xbf, 0xa7, 0xbf, 0x96, 0x94, 0xe1, 0x19, 0xf3, 0xa7, 0x68, 0x1b, 0x2c,
|
0x47, 0x9c, 0x05, 0x33, 0xfb, 0x3b, 0x7a, 0xb5, 0xa2, 0x0c, 0xcf, 0x58, 0x30, 0x43, 0x9b, 0x60,
|
||||||
0xad, 0x8b, 0xd0, 0x1b, 0x32, 0xe2, 0xdb, 0x77, 0x74, 0x3e, 0x40, 0x99, 0x4e, 0xb5, 0x45, 0x9d,
|
0x69, 0x5d, 0x44, 0xbe, 0xc7, 0x48, 0x60, 0xdf, 0xd6, 0xf9, 0x00, 0x65, 0x3a, 0xd6, 0x16, 0x75,
|
||||||
0x43, 0x9c, 0x8d, 0xd0, 0xbe, 0x7b, 0xf3, 0x39, 0x98, 0x60, 0x17, 0xe7, 0x60, 0x38, 0xe8, 0xd7,
|
0x0e, 0x49, 0x36, 0x22, 0xfb, 0xce, 0xf5, 0xe7, 0x60, 0x82, 0x5d, 0x9c, 0x83, 0xe1, 0xa0, 0x5f,
|
||||||
0x00, 0x81, 0xf0, 0x26, 0x9e, 0x4f, 0x87, 0x34, 0xb4, 0xef, 0xe9, 0x4d, 0xd7, 0x33, 0x5f, 0xeb,
|
0x00, 0x84, 0xc2, 0x9f, 0xfa, 0x01, 0xf5, 0x68, 0x64, 0xdf, 0xd5, 0x9b, 0x6e, 0xe6, 0xbe, 0xd6,
|
||||||
0x39, 0x0a, 0xa7, 0x18, 0xb5, 0xcf, 0xc1, 0x4a, 0xdd, 0x36, 0x75, 0x4b, 0x2e, 0xe9, 0xd4, 0x5c,
|
0x73, 0x14, 0xce, 0x30, 0x1a, 0x9f, 0x83, 0x95, 0xb9, 0x6d, 0xea, 0x96, 0x9c, 0xd3, 0x99, 0xb9,
|
||||||
0x60, 0x35, 0x54, 0x29, 0x99, 0x10, 0x3f, 0x8a, 0x3b, 0xe1, 0x32, 0x8e, 0x27, 0xbf, 0x5c, 0x7d,
|
0xc0, 0x6a, 0xa8, 0x52, 0x32, 0x25, 0x41, 0x9c, 0x74, 0xc2, 0x55, 0x9c, 0x4c, 0xbe, 0x58, 0x7d,
|
||||||
0x9c, 0xab, 0xed, 0x81, 0x95, 0x52, 0x1d, 0xfa, 0x18, 0x36, 0x04, 0x1d, 0x7a, 0xa1, 0x14, 0xd3,
|
0x5c, 0x68, 0xec, 0x80, 0x95, 0x51, 0x1d, 0xfa, 0x18, 0x36, 0x04, 0xf5, 0xfc, 0x48, 0x8a, 0xd9,
|
||||||
0x01, 0x89, 0xe4, 0xc8, 0xfe, 0xad, 0x26, 0x54, 0x12, 0x63, 0x3b, 0x92, 0xa3, 0xda, 0x00, 0x16,
|
0x88, 0xc4, 0x72, 0x6c, 0xff, 0x4a, 0x13, 0x6a, 0xa9, 0xb1, 0x1b, 0xcb, 0x71, 0x63, 0x04, 0x8b,
|
||||||
0x87, 0x87, 0x1a, 0x60, 0x29, 0x51, 0x84, 0x54, 0x4c, 0xa8, 0x50, 0xd5, 0x56, 0xe5, 0x3c, 0x6d,
|
0xc3, 0x43, 0x2d, 0xb0, 0x94, 0x28, 0x22, 0x2a, 0xa6, 0x54, 0xa8, 0x6a, 0xab, 0x72, 0x9e, 0x35,
|
||||||
0x52, 0xe2, 0x0d, 0x29, 0x11, 0xce, 0x48, 0xbf, 0x1d, 0x65, 0x6c, 0x66, 0xea, 0x31, 0x48, 0x6e,
|
0x29, 0xf1, 0x46, 0x94, 0x08, 0x67, 0xac, 0xdf, 0x8e, 0x2a, 0x36, 0x33, 0xf5, 0x18, 0xa4, 0x37,
|
||||||
0x88, 0x79, 0x0c, 0xcc, 0xb4, 0xf9, 0x9f, 0x1c, 0x54, 0xd2, 0x4d, 0x03, 0xda, 0x8f, 0x8b, 0xbd,
|
0xc4, 0x3c, 0x06, 0x66, 0xda, 0xfe, 0x57, 0x01, 0x6a, 0xd9, 0xa6, 0x01, 0xed, 0x26, 0xc5, 0x5e,
|
||||||
0xde, 0xd2, 0xad, 0xbd, 0xdd, 0x77, 0x35, 0x19, 0xba, 0xb4, 0xfa, 0x91, 0x72, 0x76, 0xac, 0xfa,
|
0x6f, 0xe9, 0xc6, 0xce, 0xf6, 0xbb, 0x9a, 0x0c, 0x5d, 0x5a, 0x83, 0x58, 0x39, 0x3b, 0x54, 0xfd,
|
||||||
0x7b, 0x4d, 0x46, 0x3f, 0x87, 0xf5, 0x80, 0x0b, 0x99, 0x3c, 0x61, 0xd9, 0x09, 0xe6, 0x22, 0x29,
|
0xbd, 0x26, 0xa3, 0x9f, 0xc2, 0x7a, 0xc8, 0x85, 0x4c, 0x9f, 0xb0, 0xfc, 0x04, 0x73, 0x91, 0x96,
|
||||||
0x45, 0x31, 0xb8, 0x39, 0x82, 0x5b, 0xcb, 0xde, 0xd0, 0x43, 0x58, 0x7b, 0x7e, 0xd8, 0xaf, 0xae,
|
0xa2, 0x04, 0xdc, 0x1e, 0xc3, 0x8d, 0x65, 0x6f, 0xe8, 0x21, 0xac, 0x3d, 0xdf, 0x1f, 0xd6, 0x57,
|
||||||
0xd4, 0xee, 0xbf, 0xbc, 0x6a, 0x7c, 0x7f, 0xf9, 0xe3, 0x73, 0x4f, 0xc8, 0x88, 0xf8, 0x87, 0x7d,
|
0x1a, 0xf7, 0x5f, 0x5e, 0xb4, 0xbe, 0xbb, 0xbc, 0xf8, 0xdc, 0x17, 0x32, 0x26, 0xc1, 0xfe, 0x10,
|
||||||
0xf4, 0x63, 0x58, 0xef, 0x9e, 0x9c, 0x62, 0x5c, 0xcd, 0xd5, 0xb6, 0x5f, 0x5e, 0x35, 0xee, 0x2f,
|
0xfd, 0x10, 0xd6, 0xfb, 0x47, 0xc7, 0x18, 0xd7, 0x0b, 0x8d, 0xcd, 0x97, 0x17, 0xad, 0xfb, 0xcb,
|
||||||
0xe3, 0xd4, 0x27, 0x1e, 0x31, 0x17, 0xf3, 0xf3, 0x79, 0xaf, 0xfb, 0xef, 0x55, 0xb0, 0xcc, 0xcb,
|
0x38, 0xb5, 0xc4, 0x63, 0xe6, 0x62, 0x7e, 0x3a, 0xef, 0x75, 0xff, 0xbd, 0x0a, 0x96, 0x79, 0xd9,
|
||||||
0xfe, 0xa1, 0x7f, 0x87, 0x36, 0xe2, 0x52, 0x9e, 0x5c, 0xd9, 0xd5, 0x77, 0x56, 0xf4, 0x4a, 0x4c,
|
0x3f, 0xf4, 0xef, 0xd0, 0x46, 0x52, 0xca, 0xd3, 0x2b, 0xbb, 0xfa, 0xce, 0x8a, 0x5e, 0x4b, 0x08,
|
||||||
0x30, 0x67, 0xfc, 0x00, 0x2a, 0x5e, 0x30, 0xf9, 0x6c, 0x40, 0x19, 0x39, 0xf7, 0x4d, 0xdb, 0x5b,
|
0xe6, 0x8c, 0x1f, 0x40, 0xcd, 0x0f, 0xa7, 0x9f, 0x8d, 0x28, 0x23, 0xa7, 0x81, 0x69, 0x7b, 0x2b,
|
||||||
0xc2, 0x96, 0xb2, 0xf5, 0x62, 0x93, 0x7a, 0x2f, 0x3c, 0x26, 0xa9, 0x60, 0xa6, 0xa1, 0x2d, 0xe1,
|
0xd8, 0x52, 0xb6, 0x41, 0x62, 0x52, 0xef, 0x85, 0xcf, 0x24, 0x15, 0xcc, 0x34, 0xb4, 0x15, 0x3c,
|
||||||
0xf9, 0x1c, 0x7d, 0x01, 0x79, 0x2f, 0x20, 0x63, 0xd3, 0x86, 0x64, 0xee, 0xe0, 0xb0, 0xdf, 0x3e,
|
0x9f, 0xa3, 0x2f, 0xa1, 0xe8, 0x87, 0x64, 0x62, 0xda, 0x90, 0xdc, 0x1d, 0xec, 0x0f, 0xbb, 0x87,
|
||||||
0x36, 0x1a, 0xec, 0x94, 0x66, 0xd7, 0xdb, 0x79, 0x65, 0xc0, 0x9a, 0x86, 0xea, 0x49, 0x27, 0xa0,
|
0x46, 0x83, 0xbd, 0xca, 0xd5, 0xe5, 0x66, 0x51, 0x19, 0xb0, 0xa6, 0xa1, 0x66, 0xda, 0x09, 0xa8,
|
||||||
0x56, 0xd2, 0x6f, 0x7f, 0x09, 0xa7, 0x2c, 0x4a, 0x47, 0x1e, 0x1b, 0x0a, 0x1a, 0x86, 0xba, 0x0a,
|
0x2f, 0xe9, 0xb7, 0xbf, 0x82, 0x33, 0x16, 0xa5, 0x23, 0x9f, 0x79, 0x82, 0x46, 0x91, 0xae, 0x02,
|
||||||
0x94, 0x70, 0x32, 0x55, 0xf7, 0x36, 0xde, 0xf1, 0xe0, 0x42, 0xf0, 0xb1, 0x6e, 0x22, 0xca, 0x18,
|
0x15, 0x9c, 0x4e, 0x51, 0x03, 0xca, 0xa6, 0x9f, 0xd0, 0x0d, 0x44, 0x55, 0xd5, 0x6a, 0x63, 0xe8,
|
||||||
0x62, 0xd3, 0x97, 0x82, 0x8f, 0x9b, 0xff, 0xcd, 0x83, 0xb5, 0xef, 0x47, 0xa1, 0x34, 0xc5, 0xef,
|
0x6d, 0x80, 0x95, 0x64, 0x63, 0x74, 0x26, 0xf8, 0xa4, 0xfd, 0x9f, 0x22, 0x58, 0xbb, 0x41, 0x1c,
|
||||||
0x83, 0xa5, 0xfc, 0x05, 0x6c, 0x12, 0xfd, 0x53, 0x45, 0x98, 0xaa, 0x24, 0xba, 0x39, 0x33, 0x69,
|
0x49, 0x53, 0x06, 0x3f, 0x58, 0xf2, 0x5f, 0xc0, 0x2d, 0xa2, 0x7f, 0xaf, 0x08, 0x53, 0x35, 0x45,
|
||||||
0x7f, 0x98, 0xe9, 0x6e, 0x0e, 0x8e, 0x1b, 0xb9, 0x4e, 0x41, 0xf9, 0xb4, 0x73, 0xb8, 0x4a, 0x5e,
|
0xb7, 0x69, 0xe6, 0x00, 0x1e, 0xe6, 0xba, 0x9b, 0x83, 0x93, 0x96, 0xae, 0x57, 0x52, 0x3e, 0xed,
|
||||||
0xfb, 0x82, 0x4e, 0x61, 0x83, 0x0b, 0x67, 0x44, 0x43, 0x19, 0xd7, 0x1f, 0xf3, 0x13, 0x92, 0xf9,
|
0x02, 0xae, 0x93, 0xd7, 0x56, 0xd0, 0x31, 0x6c, 0x70, 0xe1, 0x8c, 0x69, 0x24, 0x93, 0x4a, 0x64,
|
||||||
0x7b, 0xfa, 0x2c, 0x0d, 0x34, 0x8f, 0x6f, 0x1c, 0xed, 0xb2, 0x0f, 0xf4, 0x18, 0xf2, 0x82, 0x5c,
|
0x7e, 0x47, 0x72, 0x7f, 0x54, 0x9f, 0x65, 0x81, 0xe6, 0x19, 0x4e, 0xa2, 0x5d, 0xf6, 0x81, 0x1e,
|
||||||
0x24, 0x8d, 0x66, 0xe6, 0xd5, 0xc0, 0xe4, 0x42, 0x2e, 0xb9, 0xd0, 0x0c, 0xf4, 0x3b, 0x00, 0xd7,
|
0x43, 0x51, 0x90, 0xb3, 0xb4, 0xe5, 0xcc, 0xbd, 0x24, 0x98, 0x9c, 0xc9, 0x25, 0x17, 0x9a, 0x81,
|
||||||
0x0b, 0x03, 0x22, 0x9d, 0x11, 0x15, 0xe6, 0x88, 0x33, 0xb7, 0xd8, 0x9d, 0xa3, 0x96, 0xbc, 0xa4,
|
0x7e, 0x03, 0xe0, 0xfa, 0x51, 0x48, 0xa4, 0x33, 0xa6, 0xc2, 0x1c, 0x76, 0xee, 0x16, 0xfb, 0x73,
|
||||||
0xd8, 0xe8, 0x08, 0xca, 0x0e, 0x49, 0x44, 0x5a, 0xb8, 0xf9, 0xcf, 0x6c, 0xbf, 0x6d, 0x5c, 0x54,
|
0xd4, 0x92, 0x97, 0x0c, 0x1b, 0x1d, 0x40, 0xd5, 0x21, 0xa9, 0x5c, 0x4b, 0xd7, 0xff, 0xa3, 0xed,
|
||||||
0x95, 0x8b, 0xd9, 0xf5, 0x76, 0x29, 0xb1, 0xe0, 0x92, 0x43, 0x8c, 0x68, 0x8f, 0x60, 0x43, 0xfd,
|
0x76, 0x8d, 0x8b, 0xba, 0x72, 0x71, 0x75, 0xb9, 0x59, 0x49, 0x2d, 0xb8, 0xe2, 0x10, 0x23, 0xdf,
|
||||||
0xb1, 0x0d, 0x5c, 0x7a, 0x41, 0x22, 0x5f, 0xc6, 0xe2, 0xb8, 0xa1, 0x98, 0xa8, 0xf6, 0xbf, 0x6b,
|
0x03, 0xd8, 0x50, 0xff, 0x6e, 0x23, 0x97, 0x9e, 0x91, 0x38, 0x90, 0x89, 0x4c, 0xae, 0x29, 0x2b,
|
||||||
0x70, 0x26, 0xae, 0x8a, 0x4c, 0xd9, 0xd0, 0x1f, 0x60, 0x93, 0x32, 0x47, 0x4c, 0xb5, 0x44, 0x93,
|
0xea, 0x47, 0xa0, 0x6f, 0x70, 0x26, 0xae, 0x9a, 0xcc, 0xd8, 0xd0, 0xef, 0xe0, 0x16, 0x65, 0x8e,
|
||||||
0x08, 0x4b, 0x37, 0x6f, 0xb6, 0x37, 0x07, 0x2f, 0x6d, 0xb6, 0x4a, 0x5f, 0xb3, 0x37, 0x3d, 0x80,
|
0x98, 0x69, 0xb1, 0xa6, 0x11, 0x56, 0xae, 0xdf, 0xec, 0x60, 0x0e, 0x5e, 0xda, 0x6c, 0x9d, 0xbe,
|
||||||
0xb8, 0x3c, 0x7f, 0x58, 0xfd, 0x21, 0xc8, 0xbb, 0x44, 0x12, 0x2d, 0xb9, 0x0a, 0xd6, 0x63, 0xb5,
|
0x66, 0x6f, 0xff, 0xb5, 0x00, 0x90, 0x54, 0xea, 0x0f, 0x2b, 0x40, 0x04, 0x45, 0x97, 0x48, 0xa2,
|
||||||
0x54, 0xbc, 0xe8, 0x77, 0xbe, 0x54, 0xc7, 0x7e, 0xf5, 0x6d, 0x7d, 0xe5, 0x9f, 0xdf, 0xd6, 0x57,
|
0x35, 0x57, 0xc3, 0x7a, 0x8c, 0xbe, 0x00, 0x90, 0x74, 0x12, 0x06, 0x44, 0xfa, 0xcc, 0x33, 0xb2,
|
||||||
0xfe, 0x3c, 0xab, 0xe7, 0x5e, 0xcd, 0xea, 0xb9, 0xbf, 0xcf, 0xea, 0xb9, 0x7f, 0xcd, 0xea, 0xb9,
|
0x79, 0xdb, 0x73, 0x90, 0x41, 0xeb, 0x38, 0x93, 0x90, 0xff, 0xaf, 0xe3, 0xec, 0xd9, 0xaf, 0xbe,
|
||||||
0xf3, 0x82, 0xee, 0x9f, 0x7e, 0xf6, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa3, 0xb7, 0x5c, 0xc9,
|
0x6d, 0xae, 0xfc, 0xe3, 0xdb, 0xe6, 0xca, 0x1f, 0xaf, 0x9a, 0x85, 0x57, 0x57, 0xcd, 0xc2, 0xdf,
|
||||||
0x78, 0x12, 0x00, 0x00,
|
0xae, 0x9a, 0x85, 0x7f, 0x5e, 0x35, 0x0b, 0xa7, 0x25, 0xdd, 0xc3, 0xfd, 0xe4, 0xbf, 0x01, 0x00,
|
||||||
|
0x00, 0xff, 0xff, 0x06, 0x93, 0x6e, 0xba, 0xfc, 0x12, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
|
@ -342,10 +342,14 @@ message NetworkSpec {
|
||||||
// "ingress" and the label "com.docker.swarm.internal": "true".
|
// "ingress" and the label "com.docker.swarm.internal": "true".
|
||||||
bool ingress = 7;
|
bool ingress = 7;
|
||||||
|
|
||||||
// ConfigFrom indicates that the network specific configuration
|
// ConfigFrom is the source of the configuration for this network.
|
||||||
// for this network will be provided via another network, locally
|
oneof config_from {
|
||||||
// on the node where this network is being plumbed.
|
// Network is the name of a network that provides the network
|
||||||
string config_from = 8;
|
// specific configuration for this network, locally on the node
|
||||||
|
// where this network is being plumbed.
|
||||||
|
string network = 8;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClusterSpec specifies global cluster settings.
|
// ClusterSpec specifies global cluster settings.
|
||||||
|
@ -382,6 +386,13 @@ message SecretSpec {
|
||||||
|
|
||||||
// Data is the secret payload - the maximum size is 500KB (that is, 500*1024 bytes)
|
// Data is the secret payload - the maximum size is 500KB (that is, 500*1024 bytes)
|
||||||
bytes data = 2;
|
bytes data = 2;
|
||||||
|
|
||||||
|
// Templating controls whether and how to evaluate the secret payload as
|
||||||
|
// a template. If it is not set, no templating is used.
|
||||||
|
//
|
||||||
|
// The currently recognized values are:
|
||||||
|
// - golang: Go templating
|
||||||
|
Driver templating = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigSpec specifies user-provided configuration files.
|
// ConfigSpec specifies user-provided configuration files.
|
||||||
|
@ -392,4 +403,11 @@ message ConfigSpec {
|
||||||
// TODO(aaronl): Do we want to revise this to include multiple payloads in a single
|
// TODO(aaronl): Do we want to revise this to include multiple payloads in a single
|
||||||
// ConfigSpec? Define this to be a tar? etc...
|
// ConfigSpec? Define this to be a tar? etc...
|
||||||
bytes data = 2;
|
bytes data = 2;
|
||||||
|
|
||||||
|
// Templating controls whether and how to evaluate the secret payload as
|
||||||
|
// a template. If it is not set, no templating is used.
|
||||||
|
//
|
||||||
|
// The currently recognized values are:
|
||||||
|
// - golang: Go templating
|
||||||
|
Driver templating = 3;
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -30,12 +30,32 @@ message Annotations {
|
||||||
repeated IndexEntry indices = 4 [(gogoproto.nullable) = false];
|
repeated IndexEntry indices = 4 [(gogoproto.nullable) = false];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GenericString {
|
||||||
|
string kind = 1;
|
||||||
|
string value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GenericDiscrete {
|
||||||
|
string kind = 1;
|
||||||
|
int64 value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GenericResource {
|
||||||
|
oneof resource {
|
||||||
|
GenericString str = 1;
|
||||||
|
GenericDiscrete discrete = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
message Resources {
|
message Resources {
|
||||||
// Amount of CPUs (e.g. 2000000000 = 2 CPU cores)
|
// Amount of CPUs (e.g. 2000000000 = 2 CPU cores)
|
||||||
int64 nano_cpus = 1 [(gogoproto.customname) = "NanoCPUs"];
|
int64 nano_cpus = 1 [(gogoproto.customname) = "NanoCPUs"];
|
||||||
|
|
||||||
// Amount of memory in bytes.
|
// Amount of memory in bytes.
|
||||||
int64 memory_bytes = 2;
|
int64 memory_bytes = 2;
|
||||||
|
|
||||||
|
// User specified resource (e.g: bananas=2;apple={red,yellow,green})
|
||||||
|
repeated GenericResource generic = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ResourceRequirements {
|
message ResourceRequirements {
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
# grpc and protobuf
|
# grpc and protobuf
|
||||||
google.golang.org/grpc v1.0.4
|
google.golang.org/grpc v1.3.0
|
||||||
github.com/gogo/protobuf v0.4
|
github.com/gogo/protobuf v0.4
|
||||||
github.com/golang/protobuf 8ee79997227bf9b34611aee7946ae64735e6fd93
|
github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.0
|
github.com/matttproud/golang_protobuf_extensions v1.0.0
|
||||||
|
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
|
||||||
|
|
||||||
# metrics
|
# metrics
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0
|
github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0
|
||||||
|
@ -18,15 +19,24 @@ github.com/prometheus/common ebdfc6da46522d58825777cf1f90490a5b1ef1d8
|
||||||
github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5
|
github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5
|
||||||
|
|
||||||
github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621
|
github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621
|
||||||
github.com/docker/docker 9585d0ea19859c5f145bc763d474190447b91c7b
|
github.com/docker/docker 77c9728847358a3ed3581d828fb0753017e1afd3
|
||||||
github.com/docker/go-connections 34b5052da6b11e27f5f2e357b38b571ddddd3928
|
github.com/docker/go-connections 34b5052da6b11e27f5f2e357b38b571ddddd3928
|
||||||
github.com/docker/go-events 37d35add5005832485c0225ec870121b78fcff1c
|
github.com/docker/go-events 37d35add5005832485c0225ec870121b78fcff1c
|
||||||
github.com/docker/go-units 954fed01cc617c55d838fa2230073f2cb17386c8
|
github.com/docker/go-units 954fed01cc617c55d838fa2230073f2cb17386c8
|
||||||
github.com/docker/libkv 9fd56606e928ff1f309808f5d5a0b7a2ef73f9a8
|
github.com/docker/libkv 9fd56606e928ff1f309808f5d5a0b7a2ef73f9a8
|
||||||
github.com/docker/libnetwork 37e20af882e13dd01ade3658b7aabdae3412118b
|
github.com/docker/libnetwork 37e20af882e13dd01ade3658b7aabdae3412118b
|
||||||
github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
|
github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
|
||||||
github.com/opencontainers/runc 8e8d01d38d7b4fb0a35bf89b72bc3e18c98882d7
|
github.com/opencontainers/runc b6b70e53451794e8333e9b602cc096b47a20bd0f
|
||||||
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
|
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
|
||||||
|
github.com/opencontainers/image-spec f03dbe35d449c54915d235f1a3cf8f585a24babe
|
||||||
|
|
||||||
|
# containerd executor
|
||||||
|
github.com/containerd/containerd 7fc91b05917e93d474fab9465547d44eacd10ce3
|
||||||
|
github.com/containerd/continuity f4ad4294c92f596c9241947c416d1297f9faf3ea
|
||||||
|
github.com/containerd/fifo 69b99525e472735860a5269b75af1970142b3062
|
||||||
|
github.com/opencontainers/runtime-spec v1.0.0-rc5
|
||||||
|
github.com/nightlyone/lockfile 1d49c987357a327b5b03aa84cbddd582c328615d
|
||||||
|
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
|
||||||
|
|
||||||
github.com/davecgh/go-spew 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
github.com/davecgh/go-spew 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
||||||
github.com/Microsoft/go-winio f778f05015353be65d242f3fedc18695756153bb
|
github.com/Microsoft/go-winio f778f05015353be65d242f3fedc18695756153bb
|
||||||
|
@ -49,7 +59,7 @@ github.com/spf13/cobra 8e91712f174ced10270cf66615e0a9127e7c4de5
|
||||||
github.com/spf13/pflag 7f60f83a2c81bc3c3c0d5297f61ddfa68da9d3b7
|
github.com/spf13/pflag 7f60f83a2c81bc3c3c0d5297f61ddfa68da9d3b7
|
||||||
github.com/stretchr/testify v1.1.4
|
github.com/stretchr/testify v1.1.4
|
||||||
golang.org/x/crypto 3fbbcd23f1cb824e69491a5930cfeff09b12f4d2
|
golang.org/x/crypto 3fbbcd23f1cb824e69491a5930cfeff09b12f4d2
|
||||||
golang.org/x/net c427ad74c6d7a814201695e9ffde0c5d400a7674
|
golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6
|
||||||
golang.org/x/sys 5eaf0df67e70d6997a9fe0ed24383fa1b01638d3
|
golang.org/x/sys 5eaf0df67e70d6997a9fe0ed24383fa1b01638d3
|
||||||
golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756
|
golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756
|
||||||
golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb
|
golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb
|
||||||
|
|
|
@ -22,7 +22,7 @@ To use this software, you must:
|
||||||
for details or, if you are using gccgo, follow the instructions at
|
for details or, if you are using gccgo, follow the instructions at
|
||||||
https://golang.org/doc/install/gccgo
|
https://golang.org/doc/install/gccgo
|
||||||
- Grab the code from the repository and install the proto package.
|
- Grab the code from the repository and install the proto package.
|
||||||
The simplest way is to run `go get -u github.com/golang/protobuf/{proto,protoc-gen-go}`.
|
The simplest way is to run `go get -u github.com/golang/protobuf/protoc-gen-go`.
|
||||||
The compiler plugin, protoc-gen-go, will be installed in $GOBIN,
|
The compiler plugin, protoc-gen-go, will be installed in $GOBIN,
|
||||||
defaulting to $GOPATH/bin. It must be in your $PATH for the protocol
|
defaulting to $GOPATH/bin. It must be in your $PATH for the protocol
|
||||||
compiler, protoc, to find it.
|
compiler, protoc, to find it.
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// source: github.com/golang/protobuf/ptypes/any/any.proto
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package any is a generated protocol buffer package.
|
||||||
|
|
||||||
|
It is generated from these files:
|
||||||
|
github.com/golang/protobuf/ptypes/any/any.proto
|
||||||
|
|
||||||
|
It has these top-level messages:
|
||||||
|
Any
|
||||||
|
*/
|
||||||
|
package any
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
// `Any` contains an arbitrary serialized protocol buffer message along with a
|
||||||
|
// URL that describes the type of the serialized message.
|
||||||
|
//
|
||||||
|
// Protobuf library provides support to pack/unpack Any values in the form
|
||||||
|
// of utility functions or additional generated methods of the Any type.
|
||||||
|
//
|
||||||
|
// Example 1: Pack and unpack a message in C++.
|
||||||
|
//
|
||||||
|
// Foo foo = ...;
|
||||||
|
// Any any;
|
||||||
|
// any.PackFrom(foo);
|
||||||
|
// ...
|
||||||
|
// if (any.UnpackTo(&foo)) {
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Example 2: Pack and unpack a message in Java.
|
||||||
|
//
|
||||||
|
// Foo foo = ...;
|
||||||
|
// Any any = Any.pack(foo);
|
||||||
|
// ...
|
||||||
|
// if (any.is(Foo.class)) {
|
||||||
|
// foo = any.unpack(Foo.class);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Example 3: Pack and unpack a message in Python.
|
||||||
|
//
|
||||||
|
// foo = Foo(...)
|
||||||
|
// any = Any()
|
||||||
|
// any.Pack(foo)
|
||||||
|
// ...
|
||||||
|
// if any.Is(Foo.DESCRIPTOR):
|
||||||
|
// any.Unpack(foo)
|
||||||
|
// ...
|
||||||
|
//
|
||||||
|
// The pack methods provided by protobuf library will by default use
|
||||||
|
// 'type.googleapis.com/full.type.name' as the type URL and the unpack
|
||||||
|
// methods only use the fully qualified type name after the last '/'
|
||||||
|
// in the type URL, for example "foo.bar.com/x/y.z" will yield type
|
||||||
|
// name "y.z".
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// JSON
|
||||||
|
// ====
|
||||||
|
// The JSON representation of an `Any` value uses the regular
|
||||||
|
// representation of the deserialized, embedded message, with an
|
||||||
|
// additional field `@type` which contains the type URL. Example:
|
||||||
|
//
|
||||||
|
// package google.profile;
|
||||||
|
// message Person {
|
||||||
|
// string first_name = 1;
|
||||||
|
// string last_name = 2;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// "@type": "type.googleapis.com/google.profile.Person",
|
||||||
|
// "firstName": <string>,
|
||||||
|
// "lastName": <string>
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// If the embedded message type is well-known and has a custom JSON
|
||||||
|
// representation, that representation will be embedded adding a field
|
||||||
|
// `value` which holds the custom JSON in addition to the `@type`
|
||||||
|
// field. Example (for message [google.protobuf.Duration][]):
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// "@type": "type.googleapis.com/google.protobuf.Duration",
|
||||||
|
// "value": "1.212s"
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
type Any struct {
|
||||||
|
// A URL/resource name whose content describes the type of the
|
||||||
|
// serialized protocol buffer message.
|
||||||
|
//
|
||||||
|
// For URLs which use the scheme `http`, `https`, or no scheme, the
|
||||||
|
// following restrictions and interpretations apply:
|
||||||
|
//
|
||||||
|
// * If no scheme is provided, `https` is assumed.
|
||||||
|
// * The last segment of the URL's path must represent the fully
|
||||||
|
// qualified name of the type (as in `path/google.protobuf.Duration`).
|
||||||
|
// The name should be in a canonical form (e.g., leading "." is
|
||||||
|
// not accepted).
|
||||||
|
// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
|
||||||
|
// value in binary format, or produce an error.
|
||||||
|
// * Applications are allowed to cache lookup results based on the
|
||||||
|
// URL, or have them precompiled into a binary to avoid any
|
||||||
|
// lookup. Therefore, binary compatibility needs to be preserved
|
||||||
|
// on changes to types. (Use versioned type names to manage
|
||||||
|
// breaking changes.)
|
||||||
|
//
|
||||||
|
// Schemes other than `http`, `https` (or the empty scheme) might be
|
||||||
|
// used with implementation specific semantics.
|
||||||
|
//
|
||||||
|
TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl" json:"type_url,omitempty"`
|
||||||
|
// Must be a valid serialized protocol buffer of the above specified type.
|
||||||
|
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Any) Reset() { *m = Any{} }
|
||||||
|
func (m *Any) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Any) ProtoMessage() {}
|
||||||
|
func (*Any) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||||
|
func (*Any) XXX_WellKnownType() string { return "Any" }
|
||||||
|
|
||||||
|
func (m *Any) GetTypeUrl() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.TypeUrl
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Any) GetValue() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.Value
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*Any)(nil), "google.protobuf.Any")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("github.com/golang/protobuf/ptypes/any/any.proto", fileDescriptor0) }
|
||||||
|
|
||||||
|
var fileDescriptor0 = []byte{
|
||||||
|
// 184 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4f, 0xcf, 0x2c, 0xc9,
|
||||||
|
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0xd7, 0x2f, 0x28,
|
||||||
|
0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x28, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x4f, 0xcc,
|
||||||
|
0xab, 0x04, 0x61, 0x3d, 0xb0, 0xb8, 0x10, 0x7f, 0x7a, 0x7e, 0x7e, 0x7a, 0x4e, 0xaa, 0x1e, 0x4c,
|
||||||
|
0x95, 0x92, 0x19, 0x17, 0xb3, 0x63, 0x5e, 0xa5, 0x90, 0x24, 0x17, 0x07, 0x48, 0x79, 0x7c, 0x69,
|
||||||
|
0x51, 0x8e, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10, 0x3b, 0x88, 0x1f, 0x5a, 0x94, 0x23, 0x24,
|
||||||
|
0xc2, 0xc5, 0x5a, 0x96, 0x98, 0x53, 0x9a, 0x2a, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0x13, 0x04, 0xe1,
|
||||||
|
0x38, 0xe5, 0x73, 0x09, 0x27, 0xe7, 0xe7, 0xea, 0xa1, 0x19, 0xe7, 0xc4, 0xe1, 0x98, 0x57, 0x19,
|
||||||
|
0x00, 0xe2, 0x04, 0x30, 0x46, 0xa9, 0x12, 0xe5, 0xb8, 0x45, 0x4c, 0xcc, 0xee, 0x01, 0x4e, 0xab,
|
||||||
|
0x98, 0xe4, 0xdc, 0x21, 0x46, 0x05, 0x40, 0x95, 0xe8, 0x85, 0xa7, 0xe6, 0xe4, 0x78, 0xe7, 0xe5,
|
||||||
|
0x97, 0xe7, 0x85, 0x80, 0x94, 0x26, 0xb1, 0x81, 0xf5, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff,
|
||||||
|
0x45, 0x1f, 0x1a, 0xf2, 0xf3, 0x00, 0x00, 0x00,
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
// Protocol Buffers - Google's data interchange format
|
||||||
|
// Copyright 2008 Google Inc. All rights reserved.
|
||||||
|
// https://developers.google.com/protocol-buffers/
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package google.protobuf;
|
||||||
|
|
||||||
|
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
|
||||||
|
option go_package = "github.com/golang/protobuf/ptypes/any";
|
||||||
|
option java_package = "com.google.protobuf";
|
||||||
|
option java_outer_classname = "AnyProto";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
option objc_class_prefix = "GPB";
|
||||||
|
|
||||||
|
// `Any` contains an arbitrary serialized protocol buffer message along with a
|
||||||
|
// URL that describes the type of the serialized message.
|
||||||
|
//
|
||||||
|
// Protobuf library provides support to pack/unpack Any values in the form
|
||||||
|
// of utility functions or additional generated methods of the Any type.
|
||||||
|
//
|
||||||
|
// Example 1: Pack and unpack a message in C++.
|
||||||
|
//
|
||||||
|
// Foo foo = ...;
|
||||||
|
// Any any;
|
||||||
|
// any.PackFrom(foo);
|
||||||
|
// ...
|
||||||
|
// if (any.UnpackTo(&foo)) {
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Example 2: Pack and unpack a message in Java.
|
||||||
|
//
|
||||||
|
// Foo foo = ...;
|
||||||
|
// Any any = Any.pack(foo);
|
||||||
|
// ...
|
||||||
|
// if (any.is(Foo.class)) {
|
||||||
|
// foo = any.unpack(Foo.class);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Example 3: Pack and unpack a message in Python.
|
||||||
|
//
|
||||||
|
// foo = Foo(...)
|
||||||
|
// any = Any()
|
||||||
|
// any.Pack(foo)
|
||||||
|
// ...
|
||||||
|
// if any.Is(Foo.DESCRIPTOR):
|
||||||
|
// any.Unpack(foo)
|
||||||
|
// ...
|
||||||
|
//
|
||||||
|
// The pack methods provided by protobuf library will by default use
|
||||||
|
// 'type.googleapis.com/full.type.name' as the type URL and the unpack
|
||||||
|
// methods only use the fully qualified type name after the last '/'
|
||||||
|
// in the type URL, for example "foo.bar.com/x/y.z" will yield type
|
||||||
|
// name "y.z".
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// JSON
|
||||||
|
// ====
|
||||||
|
// The JSON representation of an `Any` value uses the regular
|
||||||
|
// representation of the deserialized, embedded message, with an
|
||||||
|
// additional field `@type` which contains the type URL. Example:
|
||||||
|
//
|
||||||
|
// package google.profile;
|
||||||
|
// message Person {
|
||||||
|
// string first_name = 1;
|
||||||
|
// string last_name = 2;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// "@type": "type.googleapis.com/google.profile.Person",
|
||||||
|
// "firstName": <string>,
|
||||||
|
// "lastName": <string>
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// If the embedded message type is well-known and has a custom JSON
|
||||||
|
// representation, that representation will be embedded adding a field
|
||||||
|
// `value` which holds the custom JSON in addition to the `@type`
|
||||||
|
// field. Example (for message [google.protobuf.Duration][]):
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// "@type": "type.googleapis.com/google.protobuf.Duration",
|
||||||
|
// "value": "1.212s"
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
message Any {
|
||||||
|
// A URL/resource name whose content describes the type of the
|
||||||
|
// serialized protocol buffer message.
|
||||||
|
//
|
||||||
|
// For URLs which use the scheme `http`, `https`, or no scheme, the
|
||||||
|
// following restrictions and interpretations apply:
|
||||||
|
//
|
||||||
|
// * If no scheme is provided, `https` is assumed.
|
||||||
|
// * The last segment of the URL's path must represent the fully
|
||||||
|
// qualified name of the type (as in `path/google.protobuf.Duration`).
|
||||||
|
// The name should be in a canonical form (e.g., leading "." is
|
||||||
|
// not accepted).
|
||||||
|
// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
|
||||||
|
// value in binary format, or produce an error.
|
||||||
|
// * Applications are allowed to cache lookup results based on the
|
||||||
|
// URL, or have them precompiled into a binary to avoid any
|
||||||
|
// lookup. Therefore, binary compatibility needs to be preserved
|
||||||
|
// on changes to types. (Use versioned type names to manage
|
||||||
|
// breaking changes.)
|
||||||
|
//
|
||||||
|
// Schemes other than `http`, `https` (or the empty scheme) might be
|
||||||
|
// used with implementation specific semantics.
|
||||||
|
//
|
||||||
|
string type_url = 1;
|
||||||
|
|
||||||
|
// Must be a valid serialized protocol buffer of the above specified type.
|
||||||
|
bytes value = 2;
|
||||||
|
}
|
|
@ -7,7 +7,7 @@
|
||||||
// and between processes.
|
// and between processes.
|
||||||
//
|
//
|
||||||
// Incoming requests to a server should create a Context, and outgoing calls to
|
// Incoming requests to a server should create a Context, and outgoing calls to
|
||||||
// servers should accept a Context. The chain of function calls between must
|
// servers should accept a Context. The chain of function calls between must
|
||||||
// propagate the Context, optionally replacing it with a modified copy created
|
// propagate the Context, optionally replacing it with a modified copy created
|
||||||
// using WithDeadline, WithTimeout, WithCancel, or WithValue.
|
// using WithDeadline, WithTimeout, WithCancel, or WithValue.
|
||||||
//
|
//
|
||||||
|
@ -16,14 +16,14 @@
|
||||||
// propagation:
|
// propagation:
|
||||||
//
|
//
|
||||||
// Do not store Contexts inside a struct type; instead, pass a Context
|
// Do not store Contexts inside a struct type; instead, pass a Context
|
||||||
// explicitly to each function that needs it. The Context should be the first
|
// explicitly to each function that needs it. The Context should be the first
|
||||||
// parameter, typically named ctx:
|
// parameter, typically named ctx:
|
||||||
//
|
//
|
||||||
// func DoSomething(ctx context.Context, arg Arg) error {
|
// func DoSomething(ctx context.Context, arg Arg) error {
|
||||||
// // ... use ctx ...
|
// // ... use ctx ...
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Do not pass a nil Context, even if a function permits it. Pass context.TODO
|
// Do not pass a nil Context, even if a function permits it. Pass context.TODO
|
||||||
// if you are unsure about which Context to use.
|
// if you are unsure about which Context to use.
|
||||||
//
|
//
|
||||||
// Use context Values only for request-scoped data that transits processes and
|
// Use context Values only for request-scoped data that transits processes and
|
||||||
|
@ -44,13 +44,13 @@ import "time"
|
||||||
// Context's methods may be called by multiple goroutines simultaneously.
|
// Context's methods may be called by multiple goroutines simultaneously.
|
||||||
type Context interface {
|
type Context interface {
|
||||||
// Deadline returns the time when work done on behalf of this context
|
// Deadline returns the time when work done on behalf of this context
|
||||||
// should be canceled. Deadline returns ok==false when no deadline is
|
// should be canceled. Deadline returns ok==false when no deadline is
|
||||||
// set. Successive calls to Deadline return the same results.
|
// set. Successive calls to Deadline return the same results.
|
||||||
Deadline() (deadline time.Time, ok bool)
|
Deadline() (deadline time.Time, ok bool)
|
||||||
|
|
||||||
// Done returns a channel that's closed when work done on behalf of this
|
// Done returns a channel that's closed when work done on behalf of this
|
||||||
// context should be canceled. Done may return nil if this context can
|
// context should be canceled. Done may return nil if this context can
|
||||||
// never be canceled. Successive calls to Done return the same value.
|
// never be canceled. Successive calls to Done return the same value.
|
||||||
//
|
//
|
||||||
// WithCancel arranges for Done to be closed when cancel is called;
|
// WithCancel arranges for Done to be closed when cancel is called;
|
||||||
// WithDeadline arranges for Done to be closed when the deadline
|
// WithDeadline arranges for Done to be closed when the deadline
|
||||||
|
@ -79,24 +79,24 @@ type Context interface {
|
||||||
// a Done channel for cancelation.
|
// a Done channel for cancelation.
|
||||||
Done() <-chan struct{}
|
Done() <-chan struct{}
|
||||||
|
|
||||||
// Err returns a non-nil error value after Done is closed. Err returns
|
// Err returns a non-nil error value after Done is closed. Err returns
|
||||||
// Canceled if the context was canceled or DeadlineExceeded if the
|
// Canceled if the context was canceled or DeadlineExceeded if the
|
||||||
// context's deadline passed. No other values for Err are defined.
|
// context's deadline passed. No other values for Err are defined.
|
||||||
// After Done is closed, successive calls to Err return the same value.
|
// After Done is closed, successive calls to Err return the same value.
|
||||||
Err() error
|
Err() error
|
||||||
|
|
||||||
// Value returns the value associated with this context for key, or nil
|
// Value returns the value associated with this context for key, or nil
|
||||||
// if no value is associated with key. Successive calls to Value with
|
// if no value is associated with key. Successive calls to Value with
|
||||||
// the same key returns the same result.
|
// the same key returns the same result.
|
||||||
//
|
//
|
||||||
// Use context values only for request-scoped data that transits
|
// Use context values only for request-scoped data that transits
|
||||||
// processes and API boundaries, not for passing optional parameters to
|
// processes and API boundaries, not for passing optional parameters to
|
||||||
// functions.
|
// functions.
|
||||||
//
|
//
|
||||||
// A key identifies a specific value in a Context. Functions that wish
|
// A key identifies a specific value in a Context. Functions that wish
|
||||||
// to store values in Context typically allocate a key in a global
|
// to store values in Context typically allocate a key in a global
|
||||||
// variable then use that key as the argument to context.WithValue and
|
// variable then use that key as the argument to context.WithValue and
|
||||||
// Context.Value. A key can be any type that supports equality;
|
// Context.Value. A key can be any type that supports equality;
|
||||||
// packages should define keys as an unexported type to avoid
|
// packages should define keys as an unexported type to avoid
|
||||||
// collisions.
|
// collisions.
|
||||||
//
|
//
|
||||||
|
@ -115,7 +115,7 @@ type Context interface {
|
||||||
// // This prevents collisions with keys defined in other packages.
|
// // This prevents collisions with keys defined in other packages.
|
||||||
// type key int
|
// type key int
|
||||||
//
|
//
|
||||||
// // userKey is the key for user.User values in Contexts. It is
|
// // userKey is the key for user.User values in Contexts. It is
|
||||||
// // unexported; clients use user.NewContext and user.FromContext
|
// // unexported; clients use user.NewContext and user.FromContext
|
||||||
// // instead of using this key directly.
|
// // instead of using this key directly.
|
||||||
// var userKey key = 0
|
// var userKey key = 0
|
||||||
|
@ -134,14 +134,14 @@ type Context interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Background returns a non-nil, empty Context. It is never canceled, has no
|
// Background returns a non-nil, empty Context. It is never canceled, has no
|
||||||
// values, and has no deadline. It is typically used by the main function,
|
// values, and has no deadline. It is typically used by the main function,
|
||||||
// initialization, and tests, and as the top-level Context for incoming
|
// initialization, and tests, and as the top-level Context for incoming
|
||||||
// requests.
|
// requests.
|
||||||
func Background() Context {
|
func Background() Context {
|
||||||
return background
|
return background
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO returns a non-nil, empty Context. Code should use context.TODO when
|
// TODO returns a non-nil, empty Context. Code should use context.TODO when
|
||||||
// it's unclear which Context to use or it is not yet available (because the
|
// it's unclear which Context to use or it is not yet available (because the
|
||||||
// surrounding function has not yet been extended to accept a Context
|
// surrounding function has not yet been extended to accept a Context
|
||||||
// parameter). TODO is recognized by static analysis tools that determine
|
// parameter). TODO is recognized by static analysis tools that determine
|
||||||
|
|
|
@ -35,8 +35,8 @@ func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithDeadline returns a copy of the parent context with the deadline adjusted
|
// WithDeadline returns a copy of the parent context with the deadline adjusted
|
||||||
// to be no later than d. If the parent's deadline is already earlier than d,
|
// to be no later than d. If the parent's deadline is already earlier than d,
|
||||||
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
|
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
|
||||||
// context's Done channel is closed when the deadline expires, when the returned
|
// context's Done channel is closed when the deadline expires, when the returned
|
||||||
// cancel function is called, or when the parent context's Done channel is
|
// cancel function is called, or when the parent context's Done channel is
|
||||||
// closed, whichever happens first.
|
// closed, whichever happens first.
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
|
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
|
||||||
// struct{}, since vars of this type must have distinct addresses.
|
// struct{}, since vars of this type must have distinct addresses.
|
||||||
type emptyCtx int
|
type emptyCtx int
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ func propagateCancel(parent Context, child canceler) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// parentCancelCtx follows a chain of parent references until it finds a
|
// parentCancelCtx follows a chain of parent references until it finds a
|
||||||
// *cancelCtx. This function understands how each of the concrete types in this
|
// *cancelCtx. This function understands how each of the concrete types in this
|
||||||
// package represents its parent.
|
// package represents its parent.
|
||||||
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
|
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
|
||||||
for {
|
for {
|
||||||
|
@ -134,14 +134,14 @@ func removeChild(parent Context, child canceler) {
|
||||||
p.mu.Unlock()
|
p.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// A canceler is a context type that can be canceled directly. The
|
// A canceler is a context type that can be canceled directly. The
|
||||||
// implementations are *cancelCtx and *timerCtx.
|
// implementations are *cancelCtx and *timerCtx.
|
||||||
type canceler interface {
|
type canceler interface {
|
||||||
cancel(removeFromParent bool, err error)
|
cancel(removeFromParent bool, err error)
|
||||||
Done() <-chan struct{}
|
Done() <-chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A cancelCtx can be canceled. When canceled, it also cancels any children
|
// A cancelCtx can be canceled. When canceled, it also cancels any children
|
||||||
// that implement canceler.
|
// that implement canceler.
|
||||||
type cancelCtx struct {
|
type cancelCtx struct {
|
||||||
Context
|
Context
|
||||||
|
@ -193,8 +193,8 @@ func (c *cancelCtx) cancel(removeFromParent bool, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithDeadline returns a copy of the parent context with the deadline adjusted
|
// WithDeadline returns a copy of the parent context with the deadline adjusted
|
||||||
// to be no later than d. If the parent's deadline is already earlier than d,
|
// to be no later than d. If the parent's deadline is already earlier than d,
|
||||||
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
|
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
|
||||||
// context's Done channel is closed when the deadline expires, when the returned
|
// context's Done channel is closed when the deadline expires, when the returned
|
||||||
// cancel function is called, or when the parent context's Done channel is
|
// cancel function is called, or when the parent context's Done channel is
|
||||||
// closed, whichever happens first.
|
// closed, whichever happens first.
|
||||||
|
@ -226,8 +226,8 @@ func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
|
||||||
return c, func() { c.cancel(true, Canceled) }
|
return c, func() { c.cancel(true, Canceled) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
|
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
|
||||||
// implement Done and Err. It implements cancel by stopping its timer then
|
// implement Done and Err. It implements cancel by stopping its timer then
|
||||||
// delegating to cancelCtx.cancel.
|
// delegating to cancelCtx.cancel.
|
||||||
type timerCtx struct {
|
type timerCtx struct {
|
||||||
*cancelCtx
|
*cancelCtx
|
||||||
|
@ -281,7 +281,7 @@ func WithValue(parent Context, key interface{}, val interface{}) Context {
|
||||||
return &valueCtx{parent, key, val}
|
return &valueCtx{parent, key, val}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A valueCtx carries a key-value pair. It implements Value for that key and
|
// A valueCtx carries a key-value pair. It implements Value for that key and
|
||||||
// delegates all other calls to the embedded Context.
|
// delegates all other calls to the embedded Context.
|
||||||
type valueCtx struct {
|
type valueCtx struct {
|
||||||
Context
|
Context
|
||||||
|
|
|
@ -0,0 +1,641 @@
|
||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
// A list of the possible cipher suite ids. Taken from
|
||||||
|
// http://www.iana.org/assignments/tls-parameters/tls-parameters.txt
|
||||||
|
|
||||||
|
const (
|
||||||
|
cipher_TLS_NULL_WITH_NULL_NULL uint16 = 0x0000
|
||||||
|
cipher_TLS_RSA_WITH_NULL_MD5 uint16 = 0x0001
|
||||||
|
cipher_TLS_RSA_WITH_NULL_SHA uint16 = 0x0002
|
||||||
|
cipher_TLS_RSA_EXPORT_WITH_RC4_40_MD5 uint16 = 0x0003
|
||||||
|
cipher_TLS_RSA_WITH_RC4_128_MD5 uint16 = 0x0004
|
||||||
|
cipher_TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
|
||||||
|
cipher_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 uint16 = 0x0006
|
||||||
|
cipher_TLS_RSA_WITH_IDEA_CBC_SHA uint16 = 0x0007
|
||||||
|
cipher_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0008
|
||||||
|
cipher_TLS_RSA_WITH_DES_CBC_SHA uint16 = 0x0009
|
||||||
|
cipher_TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000A
|
||||||
|
cipher_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x000B
|
||||||
|
cipher_TLS_DH_DSS_WITH_DES_CBC_SHA uint16 = 0x000C
|
||||||
|
cipher_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA uint16 = 0x000D
|
||||||
|
cipher_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x000E
|
||||||
|
cipher_TLS_DH_RSA_WITH_DES_CBC_SHA uint16 = 0x000F
|
||||||
|
cipher_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x0010
|
||||||
|
cipher_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0011
|
||||||
|
cipher_TLS_DHE_DSS_WITH_DES_CBC_SHA uint16 = 0x0012
|
||||||
|
cipher_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA uint16 = 0x0013
|
||||||
|
cipher_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0014
|
||||||
|
cipher_TLS_DHE_RSA_WITH_DES_CBC_SHA uint16 = 0x0015
|
||||||
|
cipher_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x0016
|
||||||
|
cipher_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 uint16 = 0x0017
|
||||||
|
cipher_TLS_DH_anon_WITH_RC4_128_MD5 uint16 = 0x0018
|
||||||
|
cipher_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0019
|
||||||
|
cipher_TLS_DH_anon_WITH_DES_CBC_SHA uint16 = 0x001A
|
||||||
|
cipher_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA uint16 = 0x001B
|
||||||
|
// Reserved uint16 = 0x001C-1D
|
||||||
|
cipher_TLS_KRB5_WITH_DES_CBC_SHA uint16 = 0x001E
|
||||||
|
cipher_TLS_KRB5_WITH_3DES_EDE_CBC_SHA uint16 = 0x001F
|
||||||
|
cipher_TLS_KRB5_WITH_RC4_128_SHA uint16 = 0x0020
|
||||||
|
cipher_TLS_KRB5_WITH_IDEA_CBC_SHA uint16 = 0x0021
|
||||||
|
cipher_TLS_KRB5_WITH_DES_CBC_MD5 uint16 = 0x0022
|
||||||
|
cipher_TLS_KRB5_WITH_3DES_EDE_CBC_MD5 uint16 = 0x0023
|
||||||
|
cipher_TLS_KRB5_WITH_RC4_128_MD5 uint16 = 0x0024
|
||||||
|
cipher_TLS_KRB5_WITH_IDEA_CBC_MD5 uint16 = 0x0025
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA uint16 = 0x0026
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA uint16 = 0x0027
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_RC4_40_SHA uint16 = 0x0028
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 uint16 = 0x0029
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 uint16 = 0x002A
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_RC4_40_MD5 uint16 = 0x002B
|
||||||
|
cipher_TLS_PSK_WITH_NULL_SHA uint16 = 0x002C
|
||||||
|
cipher_TLS_DHE_PSK_WITH_NULL_SHA uint16 = 0x002D
|
||||||
|
cipher_TLS_RSA_PSK_WITH_NULL_SHA uint16 = 0x002E
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002F
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA uint16 = 0x0030
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA uint16 = 0x0031
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA uint16 = 0x0032
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0x0033
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA uint16 = 0x0034
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA uint16 = 0x0036
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0037
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA uint16 = 0x0038
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0039
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA uint16 = 0x003A
|
||||||
|
cipher_TLS_RSA_WITH_NULL_SHA256 uint16 = 0x003B
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003C
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x003D
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA256 uint16 = 0x003E
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003F
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 uint16 = 0x0040
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0041
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0042
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0043
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0044
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0045
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0046
|
||||||
|
// Reserved uint16 = 0x0047-4F
|
||||||
|
// Reserved uint16 = 0x0050-58
|
||||||
|
// Reserved uint16 = 0x0059-5C
|
||||||
|
// Unassigned uint16 = 0x005D-5F
|
||||||
|
// Reserved uint16 = 0x0060-66
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x0067
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA256 uint16 = 0x0068
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x0069
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 uint16 = 0x006A
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x006B
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA256 uint16 = 0x006C
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA256 uint16 = 0x006D
|
||||||
|
// Unassigned uint16 = 0x006E-83
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0084
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0085
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0086
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0087
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0088
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0089
|
||||||
|
cipher_TLS_PSK_WITH_RC4_128_SHA uint16 = 0x008A
|
||||||
|
cipher_TLS_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0x008B
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_CBC_SHA uint16 = 0x008C
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_CBC_SHA uint16 = 0x008D
|
||||||
|
cipher_TLS_DHE_PSK_WITH_RC4_128_SHA uint16 = 0x008E
|
||||||
|
cipher_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0x008F
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA uint16 = 0x0090
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA uint16 = 0x0091
|
||||||
|
cipher_TLS_RSA_PSK_WITH_RC4_128_SHA uint16 = 0x0092
|
||||||
|
cipher_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0x0093
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA uint16 = 0x0094
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA uint16 = 0x0095
|
||||||
|
cipher_TLS_RSA_WITH_SEED_CBC_SHA uint16 = 0x0096
|
||||||
|
cipher_TLS_DH_DSS_WITH_SEED_CBC_SHA uint16 = 0x0097
|
||||||
|
cipher_TLS_DH_RSA_WITH_SEED_CBC_SHA uint16 = 0x0098
|
||||||
|
cipher_TLS_DHE_DSS_WITH_SEED_CBC_SHA uint16 = 0x0099
|
||||||
|
cipher_TLS_DHE_RSA_WITH_SEED_CBC_SHA uint16 = 0x009A
|
||||||
|
cipher_TLS_DH_anon_WITH_SEED_CBC_SHA uint16 = 0x009B
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009C
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009D
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009E
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009F
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x00A0
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x00A1
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 uint16 = 0x00A2
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 uint16 = 0x00A3
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_128_GCM_SHA256 uint16 = 0x00A4
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_256_GCM_SHA384 uint16 = 0x00A5
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_128_GCM_SHA256 uint16 = 0x00A6
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_256_GCM_SHA384 uint16 = 0x00A7
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0x00A8
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_GCM_SHA384 uint16 = 0x00A9
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0x00AA
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 uint16 = 0x00AB
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0x00AC
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 uint16 = 0x00AD
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0x00AE
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0x00AF
|
||||||
|
cipher_TLS_PSK_WITH_NULL_SHA256 uint16 = 0x00B0
|
||||||
|
cipher_TLS_PSK_WITH_NULL_SHA384 uint16 = 0x00B1
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0x00B2
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0x00B3
|
||||||
|
cipher_TLS_DHE_PSK_WITH_NULL_SHA256 uint16 = 0x00B4
|
||||||
|
cipher_TLS_DHE_PSK_WITH_NULL_SHA384 uint16 = 0x00B5
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0x00B6
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0x00B7
|
||||||
|
cipher_TLS_RSA_PSK_WITH_NULL_SHA256 uint16 = 0x00B8
|
||||||
|
cipher_TLS_RSA_PSK_WITH_NULL_SHA384 uint16 = 0x00B9
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BA
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BB
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BC
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BD
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BE
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BF
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C0
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C1
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C2
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C3
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C4
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C5
|
||||||
|
// Unassigned uint16 = 0x00C6-FE
|
||||||
|
cipher_TLS_EMPTY_RENEGOTIATION_INFO_SCSV uint16 = 0x00FF
|
||||||
|
// Unassigned uint16 = 0x01-55,*
|
||||||
|
cipher_TLS_FALLBACK_SCSV uint16 = 0x5600
|
||||||
|
// Unassigned uint16 = 0x5601 - 0xC000
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_NULL_SHA uint16 = 0xC001
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_RC4_128_SHA uint16 = 0xC002
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC003
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xC004
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xC005
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_NULL_SHA uint16 = 0xC006
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xC007
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC008
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xC009
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xC00A
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_NULL_SHA uint16 = 0xC00B
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_RC4_128_SHA uint16 = 0xC00C
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC00D
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA uint16 = 0xC00E
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA uint16 = 0xC00F
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_NULL_SHA uint16 = 0xC010
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xC011
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC012
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xC013
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xC014
|
||||||
|
cipher_TLS_ECDH_anon_WITH_NULL_SHA uint16 = 0xC015
|
||||||
|
cipher_TLS_ECDH_anon_WITH_RC4_128_SHA uint16 = 0xC016
|
||||||
|
cipher_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA uint16 = 0xC017
|
||||||
|
cipher_TLS_ECDH_anon_WITH_AES_128_CBC_SHA uint16 = 0xC018
|
||||||
|
cipher_TLS_ECDH_anon_WITH_AES_256_CBC_SHA uint16 = 0xC019
|
||||||
|
cipher_TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC01A
|
||||||
|
cipher_TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC01B
|
||||||
|
cipher_TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA uint16 = 0xC01C
|
||||||
|
cipher_TLS_SRP_SHA_WITH_AES_128_CBC_SHA uint16 = 0xC01D
|
||||||
|
cipher_TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA uint16 = 0xC01E
|
||||||
|
cipher_TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA uint16 = 0xC01F
|
||||||
|
cipher_TLS_SRP_SHA_WITH_AES_256_CBC_SHA uint16 = 0xC020
|
||||||
|
cipher_TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA uint16 = 0xC021
|
||||||
|
cipher_TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA uint16 = 0xC022
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC023
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC024
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC025
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC026
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC027
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC028
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC029
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC02A
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC02B
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC02C
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC02D
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC02E
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC02F
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC030
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC031
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC032
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_RC4_128_SHA uint16 = 0xC033
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0xC034
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA uint16 = 0xC035
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA uint16 = 0xC036
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0xC037
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0xC038
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA uint16 = 0xC039
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA256 uint16 = 0xC03A
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA384 uint16 = 0xC03B
|
||||||
|
cipher_TLS_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC03C
|
||||||
|
cipher_TLS_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC03D
|
||||||
|
cipher_TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC03E
|
||||||
|
cipher_TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC03F
|
||||||
|
cipher_TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC040
|
||||||
|
cipher_TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC041
|
||||||
|
cipher_TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC042
|
||||||
|
cipher_TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC043
|
||||||
|
cipher_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC044
|
||||||
|
cipher_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC045
|
||||||
|
cipher_TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC046
|
||||||
|
cipher_TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC047
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC048
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC049
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC04A
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC04B
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC04C
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC04D
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC04E
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC04F
|
||||||
|
cipher_TLS_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC050
|
||||||
|
cipher_TLS_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC051
|
||||||
|
cipher_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC052
|
||||||
|
cipher_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC053
|
||||||
|
cipher_TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC054
|
||||||
|
cipher_TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC055
|
||||||
|
cipher_TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC056
|
||||||
|
cipher_TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC057
|
||||||
|
cipher_TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC058
|
||||||
|
cipher_TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC059
|
||||||
|
cipher_TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC05A
|
||||||
|
cipher_TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC05B
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC05C
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC05D
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC05E
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC05F
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC060
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC061
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC062
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC063
|
||||||
|
cipher_TLS_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC064
|
||||||
|
cipher_TLS_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC065
|
||||||
|
cipher_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC066
|
||||||
|
cipher_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC067
|
||||||
|
cipher_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC068
|
||||||
|
cipher_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC069
|
||||||
|
cipher_TLS_PSK_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC06A
|
||||||
|
cipher_TLS_PSK_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC06B
|
||||||
|
cipher_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC06C
|
||||||
|
cipher_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC06D
|
||||||
|
cipher_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC06E
|
||||||
|
cipher_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC06F
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC070
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC071
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC072
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC073
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC074
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC075
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC076
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC077
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC078
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC079
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC07A
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC07B
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC07C
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC07D
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC07E
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC07F
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC080
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC081
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC082
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC083
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC084
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC085
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC086
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC087
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC088
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC089
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC08A
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC08B
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC08C
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC08D
|
||||||
|
cipher_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC08E
|
||||||
|
cipher_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC08F
|
||||||
|
cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC090
|
||||||
|
cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC091
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC092
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC093
|
||||||
|
cipher_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC094
|
||||||
|
cipher_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC095
|
||||||
|
cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC096
|
||||||
|
cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC097
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC098
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC099
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC09A
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC09B
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_CCM uint16 = 0xC09C
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_CCM uint16 = 0xC09D
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_128_CCM uint16 = 0xC09E
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_256_CCM uint16 = 0xC09F
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_CCM_8 uint16 = 0xC0A0
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_CCM_8 uint16 = 0xC0A1
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_128_CCM_8 uint16 = 0xC0A2
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_256_CCM_8 uint16 = 0xC0A3
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_CCM uint16 = 0xC0A4
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_CCM uint16 = 0xC0A5
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_128_CCM uint16 = 0xC0A6
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_256_CCM uint16 = 0xC0A7
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_CCM_8 uint16 = 0xC0A8
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_CCM_8 uint16 = 0xC0A9
|
||||||
|
cipher_TLS_PSK_DHE_WITH_AES_128_CCM_8 uint16 = 0xC0AA
|
||||||
|
cipher_TLS_PSK_DHE_WITH_AES_256_CCM_8 uint16 = 0xC0AB
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CCM uint16 = 0xC0AC
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CCM uint16 = 0xC0AD
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 uint16 = 0xC0AE
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 uint16 = 0xC0AF
|
||||||
|
// Unassigned uint16 = 0xC0B0-FF
|
||||||
|
// Unassigned uint16 = 0xC1-CB,*
|
||||||
|
// Unassigned uint16 = 0xCC00-A7
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCA8
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCA9
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAA
|
||||||
|
cipher_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAB
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAC
|
||||||
|
cipher_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAD
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAE
|
||||||
|
)
|
||||||
|
|
||||||
|
// isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec.
|
||||||
|
// References:
|
||||||
|
// https://tools.ietf.org/html/rfc7540#appendix-A
|
||||||
|
// Reject cipher suites from Appendix A.
|
||||||
|
// "This list includes those cipher suites that do not
|
||||||
|
// offer an ephemeral key exchange and those that are
|
||||||
|
// based on the TLS null, stream or block cipher type"
|
||||||
|
func isBadCipher(cipher uint16) bool {
|
||||||
|
switch cipher {
|
||||||
|
case cipher_TLS_NULL_WITH_NULL_NULL,
|
||||||
|
cipher_TLS_RSA_WITH_NULL_MD5,
|
||||||
|
cipher_TLS_RSA_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_RSA_EXPORT_WITH_RC4_40_MD5,
|
||||||
|
cipher_TLS_RSA_WITH_RC4_128_MD5,
|
||||||
|
cipher_TLS_RSA_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5,
|
||||||
|
cipher_TLS_RSA_WITH_IDEA_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_WITH_DES_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA,
|
||||||
|
cipher_TLS_DH_DSS_WITH_DES_CBC_SHA,
|
||||||
|
cipher_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA,
|
||||||
|
cipher_TLS_DH_RSA_WITH_DES_CBC_SHA,
|
||||||
|
cipher_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_DES_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_DES_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5,
|
||||||
|
cipher_TLS_DH_anon_WITH_RC4_128_MD5,
|
||||||
|
cipher_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA,
|
||||||
|
cipher_TLS_DH_anon_WITH_DES_CBC_SHA,
|
||||||
|
cipher_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_KRB5_WITH_DES_CBC_SHA,
|
||||||
|
cipher_TLS_KRB5_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_KRB5_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_KRB5_WITH_IDEA_CBC_SHA,
|
||||||
|
cipher_TLS_KRB5_WITH_DES_CBC_MD5,
|
||||||
|
cipher_TLS_KRB5_WITH_3DES_EDE_CBC_MD5,
|
||||||
|
cipher_TLS_KRB5_WITH_RC4_128_MD5,
|
||||||
|
cipher_TLS_KRB5_WITH_IDEA_CBC_MD5,
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA,
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA,
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_RC4_40_SHA,
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5,
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5,
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_RC4_40_MD5,
|
||||||
|
cipher_TLS_PSK_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_WITH_NULL_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA,
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA,
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA,
|
||||||
|
cipher_TLS_PSK_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_PSK_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_WITH_SEED_CBC_SHA,
|
||||||
|
cipher_TLS_DH_DSS_WITH_SEED_CBC_SHA,
|
||||||
|
cipher_TLS_DH_RSA_WITH_SEED_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_SEED_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_SEED_CBC_SHA,
|
||||||
|
cipher_TLS_DH_anon_WITH_SEED_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_256_GCM_SHA384,
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_GCM_SHA256,
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_GCM_SHA384,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_CBC_SHA384,
|
||||||
|
cipher_TLS_PSK_WITH_NULL_SHA256,
|
||||||
|
cipher_TLS_PSK_WITH_NULL_SHA384,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_NULL_SHA256,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_NULL_SHA384,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_NULL_SHA256,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_NULL_SHA384,
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256,
|
||||||
|
cipher_TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_ECDH_anon_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_ECDH_anon_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA256,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA384,
|
||||||
|
cipher_TLS_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_DH_anon_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_anon_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_RSA_WITH_ARIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_ARIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_anon_WITH_ARIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_anon_WITH_ARIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_PSK_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_PSK_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_PSK_WITH_ARIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_PSK_WITH_ARIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_CCM,
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_CCM,
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_CCM_8,
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_CCM_8,
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_CCM,
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_CCM,
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_CCM_8,
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_CCM_8:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
|
@ -247,7 +247,7 @@ func filterOutClientConn(in []*ClientConn, exclude *ClientConn) []*ClientConn {
|
||||||
}
|
}
|
||||||
|
|
||||||
// noDialClientConnPool is an implementation of http2.ClientConnPool
|
// noDialClientConnPool is an implementation of http2.ClientConnPool
|
||||||
// which never dials. We let the HTTP/1.1 client dial and use its TLS
|
// which never dials. We let the HTTP/1.1 client dial and use its TLS
|
||||||
// connection instead.
|
// connection instead.
|
||||||
type noDialClientConnPool struct{ *clientConnPool }
|
type noDialClientConnPool struct{ *clientConnPool }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Buffer chunks are allocated from a pool to reduce pressure on GC.
|
||||||
|
// The maximum wasted space per dataBuffer is 2x the largest size class,
|
||||||
|
// which happens when the dataBuffer has multiple chunks and there is
|
||||||
|
// one unread byte in both the first and last chunks. We use a few size
|
||||||
|
// classes to minimize overheads for servers that typically receive very
|
||||||
|
// small request bodies.
|
||||||
|
//
|
||||||
|
// TODO: Benchmark to determine if the pools are necessary. The GC may have
|
||||||
|
// improved enough that we can instead allocate chunks like this:
|
||||||
|
// make([]byte, max(16<<10, expectedBytesRemaining))
|
||||||
|
var (
|
||||||
|
dataChunkSizeClasses = []int{
|
||||||
|
1 << 10,
|
||||||
|
2 << 10,
|
||||||
|
4 << 10,
|
||||||
|
8 << 10,
|
||||||
|
16 << 10,
|
||||||
|
}
|
||||||
|
dataChunkPools = [...]sync.Pool{
|
||||||
|
{New: func() interface{} { return make([]byte, 1<<10) }},
|
||||||
|
{New: func() interface{} { return make([]byte, 2<<10) }},
|
||||||
|
{New: func() interface{} { return make([]byte, 4<<10) }},
|
||||||
|
{New: func() interface{} { return make([]byte, 8<<10) }},
|
||||||
|
{New: func() interface{} { return make([]byte, 16<<10) }},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func getDataBufferChunk(size int64) []byte {
|
||||||
|
i := 0
|
||||||
|
for ; i < len(dataChunkSizeClasses)-1; i++ {
|
||||||
|
if size <= int64(dataChunkSizeClasses[i]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dataChunkPools[i].Get().([]byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
func putDataBufferChunk(p []byte) {
|
||||||
|
for i, n := range dataChunkSizeClasses {
|
||||||
|
if len(p) == n {
|
||||||
|
dataChunkPools[i].Put(p)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("unexpected buffer len=%v", len(p)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// dataBuffer is an io.ReadWriter backed by a list of data chunks.
|
||||||
|
// Each dataBuffer is used to read DATA frames on a single stream.
|
||||||
|
// The buffer is divided into chunks so the server can limit the
|
||||||
|
// total memory used by a single connection without limiting the
|
||||||
|
// request body size on any single stream.
|
||||||
|
type dataBuffer struct {
|
||||||
|
chunks [][]byte
|
||||||
|
r int // next byte to read is chunks[0][r]
|
||||||
|
w int // next byte to write is chunks[len(chunks)-1][w]
|
||||||
|
size int // total buffered bytes
|
||||||
|
expected int64 // we expect at least this many bytes in future Write calls (ignored if <= 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var errReadEmpty = errors.New("read from empty dataBuffer")
|
||||||
|
|
||||||
|
// Read copies bytes from the buffer into p.
|
||||||
|
// It is an error to read when no data is available.
|
||||||
|
func (b *dataBuffer) Read(p []byte) (int, error) {
|
||||||
|
if b.size == 0 {
|
||||||
|
return 0, errReadEmpty
|
||||||
|
}
|
||||||
|
var ntotal int
|
||||||
|
for len(p) > 0 && b.size > 0 {
|
||||||
|
readFrom := b.bytesFromFirstChunk()
|
||||||
|
n := copy(p, readFrom)
|
||||||
|
p = p[n:]
|
||||||
|
ntotal += n
|
||||||
|
b.r += n
|
||||||
|
b.size -= n
|
||||||
|
// If the first chunk has been consumed, advance to the next chunk.
|
||||||
|
if b.r == len(b.chunks[0]) {
|
||||||
|
putDataBufferChunk(b.chunks[0])
|
||||||
|
end := len(b.chunks) - 1
|
||||||
|
copy(b.chunks[:end], b.chunks[1:])
|
||||||
|
b.chunks[end] = nil
|
||||||
|
b.chunks = b.chunks[:end]
|
||||||
|
b.r = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ntotal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *dataBuffer) bytesFromFirstChunk() []byte {
|
||||||
|
if len(b.chunks) == 1 {
|
||||||
|
return b.chunks[0][b.r:b.w]
|
||||||
|
}
|
||||||
|
return b.chunks[0][b.r:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of bytes of the unread portion of the buffer.
|
||||||
|
func (b *dataBuffer) Len() int {
|
||||||
|
return b.size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write appends p to the buffer.
|
||||||
|
func (b *dataBuffer) Write(p []byte) (int, error) {
|
||||||
|
ntotal := len(p)
|
||||||
|
for len(p) > 0 {
|
||||||
|
// If the last chunk is empty, allocate a new chunk. Try to allocate
|
||||||
|
// enough to fully copy p plus any additional bytes we expect to
|
||||||
|
// receive. However, this may allocate less than len(p).
|
||||||
|
want := int64(len(p))
|
||||||
|
if b.expected > want {
|
||||||
|
want = b.expected
|
||||||
|
}
|
||||||
|
chunk := b.lastChunkOrAlloc(want)
|
||||||
|
n := copy(chunk[b.w:], p)
|
||||||
|
p = p[n:]
|
||||||
|
b.w += n
|
||||||
|
b.size += n
|
||||||
|
b.expected -= int64(n)
|
||||||
|
}
|
||||||
|
return ntotal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *dataBuffer) lastChunkOrAlloc(want int64) []byte {
|
||||||
|
if len(b.chunks) != 0 {
|
||||||
|
last := b.chunks[len(b.chunks)-1]
|
||||||
|
if b.w < len(last) {
|
||||||
|
return last
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chunk := getDataBufferChunk(want)
|
||||||
|
b.chunks = append(b.chunks, chunk)
|
||||||
|
b.w = 0
|
||||||
|
return chunk
|
||||||
|
}
|
|
@ -1,60 +0,0 @@
|
||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package http2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// fixedBuffer is an io.ReadWriter backed by a fixed size buffer.
|
|
||||||
// It never allocates, but moves old data as new data is written.
|
|
||||||
type fixedBuffer struct {
|
|
||||||
buf []byte
|
|
||||||
r, w int
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
errReadEmpty = errors.New("read from empty fixedBuffer")
|
|
||||||
errWriteFull = errors.New("write on full fixedBuffer")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Read copies bytes from the buffer into p.
|
|
||||||
// It is an error to read when no data is available.
|
|
||||||
func (b *fixedBuffer) Read(p []byte) (n int, err error) {
|
|
||||||
if b.r == b.w {
|
|
||||||
return 0, errReadEmpty
|
|
||||||
}
|
|
||||||
n = copy(p, b.buf[b.r:b.w])
|
|
||||||
b.r += n
|
|
||||||
if b.r == b.w {
|
|
||||||
b.r = 0
|
|
||||||
b.w = 0
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the number of bytes of the unread portion of the buffer.
|
|
||||||
func (b *fixedBuffer) Len() int {
|
|
||||||
return b.w - b.r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write copies bytes from p into the buffer.
|
|
||||||
// It is an error to write more data than the buffer can hold.
|
|
||||||
func (b *fixedBuffer) Write(p []byte) (n int, err error) {
|
|
||||||
// Slide existing data to beginning.
|
|
||||||
if b.r > 0 && len(p) > len(b.buf)-b.w {
|
|
||||||
copy(b.buf, b.buf[b.r:b.w])
|
|
||||||
b.w -= b.r
|
|
||||||
b.r = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write new data.
|
|
||||||
n = copy(b.buf[b.w:], p)
|
|
||||||
b.w += n
|
|
||||||
if n < len(p) {
|
|
||||||
err = errWriteFull
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
|
@ -122,7 +122,7 @@ var flagName = map[FrameType]map[Flags]string{
|
||||||
// a frameParser parses a frame given its FrameHeader and payload
|
// a frameParser parses a frame given its FrameHeader and payload
|
||||||
// bytes. The length of payload will always equal fh.Length (which
|
// bytes. The length of payload will always equal fh.Length (which
|
||||||
// might be 0).
|
// might be 0).
|
||||||
type frameParser func(fh FrameHeader, payload []byte) (Frame, error)
|
type frameParser func(fc *frameCache, fh FrameHeader, payload []byte) (Frame, error)
|
||||||
|
|
||||||
var frameParsers = map[FrameType]frameParser{
|
var frameParsers = map[FrameType]frameParser{
|
||||||
FrameData: parseDataFrame,
|
FrameData: parseDataFrame,
|
||||||
|
@ -312,7 +312,7 @@ type Framer struct {
|
||||||
MaxHeaderListSize uint32
|
MaxHeaderListSize uint32
|
||||||
|
|
||||||
// TODO: track which type of frame & with which flags was sent
|
// TODO: track which type of frame & with which flags was sent
|
||||||
// last. Then return an error (unless AllowIllegalWrites) if
|
// last. Then return an error (unless AllowIllegalWrites) if
|
||||||
// we're in the middle of a header block and a
|
// we're in the middle of a header block and a
|
||||||
// non-Continuation or Continuation on a different stream is
|
// non-Continuation or Continuation on a different stream is
|
||||||
// attempted to be written.
|
// attempted to be written.
|
||||||
|
@ -323,6 +323,8 @@ type Framer struct {
|
||||||
debugFramerBuf *bytes.Buffer
|
debugFramerBuf *bytes.Buffer
|
||||||
debugReadLoggerf func(string, ...interface{})
|
debugReadLoggerf func(string, ...interface{})
|
||||||
debugWriteLoggerf func(string, ...interface{})
|
debugWriteLoggerf func(string, ...interface{})
|
||||||
|
|
||||||
|
frameCache *frameCache // nil if frames aren't reused (default)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fr *Framer) maxHeaderListSize() uint32 {
|
func (fr *Framer) maxHeaderListSize() uint32 {
|
||||||
|
@ -398,6 +400,27 @@ const (
|
||||||
maxFrameSize = 1<<24 - 1
|
maxFrameSize = 1<<24 - 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SetReuseFrames allows the Framer to reuse Frames.
|
||||||
|
// If called on a Framer, Frames returned by calls to ReadFrame are only
|
||||||
|
// valid until the next call to ReadFrame.
|
||||||
|
func (fr *Framer) SetReuseFrames() {
|
||||||
|
if fr.frameCache != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fr.frameCache = &frameCache{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type frameCache struct {
|
||||||
|
dataFrame DataFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fc *frameCache) getDataFrame() *DataFrame {
|
||||||
|
if fc == nil {
|
||||||
|
return &DataFrame{}
|
||||||
|
}
|
||||||
|
return &fc.dataFrame
|
||||||
|
}
|
||||||
|
|
||||||
// NewFramer returns a Framer that writes frames to w and reads them from r.
|
// NewFramer returns a Framer that writes frames to w and reads them from r.
|
||||||
func NewFramer(w io.Writer, r io.Reader) *Framer {
|
func NewFramer(w io.Writer, r io.Reader) *Framer {
|
||||||
fr := &Framer{
|
fr := &Framer{
|
||||||
|
@ -477,7 +500,7 @@ func (fr *Framer) ReadFrame() (Frame, error) {
|
||||||
if _, err := io.ReadFull(fr.r, payload); err != nil {
|
if _, err := io.ReadFull(fr.r, payload); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
f, err := typeFrameParser(fh.Type)(fh, payload)
|
f, err := typeFrameParser(fh.Type)(fr.frameCache, fh, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ce, ok := err.(connError); ok {
|
if ce, ok := err.(connError); ok {
|
||||||
return nil, fr.connError(ce.Code, ce.Reason)
|
return nil, fr.connError(ce.Code, ce.Reason)
|
||||||
|
@ -565,7 +588,7 @@ func (f *DataFrame) Data() []byte {
|
||||||
return f.data
|
return f.data
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseDataFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
func parseDataFrame(fc *frameCache, fh FrameHeader, payload []byte) (Frame, error) {
|
||||||
if fh.StreamID == 0 {
|
if fh.StreamID == 0 {
|
||||||
// DATA frames MUST be associated with a stream. If a
|
// DATA frames MUST be associated with a stream. If a
|
||||||
// DATA frame is received whose stream identifier
|
// DATA frame is received whose stream identifier
|
||||||
|
@ -574,9 +597,9 @@ func parseDataFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
||||||
// PROTOCOL_ERROR.
|
// PROTOCOL_ERROR.
|
||||||
return nil, connError{ErrCodeProtocol, "DATA frame with stream ID 0"}
|
return nil, connError{ErrCodeProtocol, "DATA frame with stream ID 0"}
|
||||||
}
|
}
|
||||||
f := &DataFrame{
|
f := fc.getDataFrame()
|
||||||
FrameHeader: fh,
|
f.FrameHeader = fh
|
||||||
}
|
|
||||||
var padSize byte
|
var padSize byte
|
||||||
if fh.Flags.Has(FlagDataPadded) {
|
if fh.Flags.Has(FlagDataPadded) {
|
||||||
var err error
|
var err error
|
||||||
|
@ -600,6 +623,7 @@ var (
|
||||||
errStreamID = errors.New("invalid stream ID")
|
errStreamID = errors.New("invalid stream ID")
|
||||||
errDepStreamID = errors.New("invalid dependent stream ID")
|
errDepStreamID = errors.New("invalid dependent stream ID")
|
||||||
errPadLength = errors.New("pad length too large")
|
errPadLength = errors.New("pad length too large")
|
||||||
|
errPadBytes = errors.New("padding bytes must all be zeros unless AllowIllegalWrites is enabled")
|
||||||
)
|
)
|
||||||
|
|
||||||
func validStreamIDOrZero(streamID uint32) bool {
|
func validStreamIDOrZero(streamID uint32) bool {
|
||||||
|
@ -623,6 +647,7 @@ func (f *Framer) WriteData(streamID uint32, endStream bool, data []byte) error {
|
||||||
//
|
//
|
||||||
// If pad is nil, the padding bit is not sent.
|
// If pad is nil, the padding bit is not sent.
|
||||||
// The length of pad must not exceed 255 bytes.
|
// The length of pad must not exceed 255 bytes.
|
||||||
|
// The bytes of pad must all be zero, unless f.AllowIllegalWrites is set.
|
||||||
//
|
//
|
||||||
// It will perform exactly one Write to the underlying Writer.
|
// It will perform exactly one Write to the underlying Writer.
|
||||||
// It is the caller's responsibility not to violate the maximum frame size
|
// It is the caller's responsibility not to violate the maximum frame size
|
||||||
|
@ -631,8 +656,18 @@ func (f *Framer) WriteDataPadded(streamID uint32, endStream bool, data, pad []by
|
||||||
if !validStreamID(streamID) && !f.AllowIllegalWrites {
|
if !validStreamID(streamID) && !f.AllowIllegalWrites {
|
||||||
return errStreamID
|
return errStreamID
|
||||||
}
|
}
|
||||||
if len(pad) > 255 {
|
if len(pad) > 0 {
|
||||||
return errPadLength
|
if len(pad) > 255 {
|
||||||
|
return errPadLength
|
||||||
|
}
|
||||||
|
if !f.AllowIllegalWrites {
|
||||||
|
for _, b := range pad {
|
||||||
|
if b != 0 {
|
||||||
|
// "Padding octets MUST be set to zero when sending."
|
||||||
|
return errPadBytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var flags Flags
|
var flags Flags
|
||||||
if endStream {
|
if endStream {
|
||||||
|
@ -660,10 +695,10 @@ type SettingsFrame struct {
|
||||||
p []byte
|
p []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSettingsFrame(fh FrameHeader, p []byte) (Frame, error) {
|
func parseSettingsFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
||||||
if fh.Flags.Has(FlagSettingsAck) && fh.Length > 0 {
|
if fh.Flags.Has(FlagSettingsAck) && fh.Length > 0 {
|
||||||
// When this (ACK 0x1) bit is set, the payload of the
|
// When this (ACK 0x1) bit is set, the payload of the
|
||||||
// SETTINGS frame MUST be empty. Receipt of a
|
// SETTINGS frame MUST be empty. Receipt of a
|
||||||
// SETTINGS frame with the ACK flag set and a length
|
// SETTINGS frame with the ACK flag set and a length
|
||||||
// field value other than 0 MUST be treated as a
|
// field value other than 0 MUST be treated as a
|
||||||
// connection error (Section 5.4.1) of type
|
// connection error (Section 5.4.1) of type
|
||||||
|
@ -672,7 +707,7 @@ func parseSettingsFrame(fh FrameHeader, p []byte) (Frame, error) {
|
||||||
}
|
}
|
||||||
if fh.StreamID != 0 {
|
if fh.StreamID != 0 {
|
||||||
// SETTINGS frames always apply to a connection,
|
// SETTINGS frames always apply to a connection,
|
||||||
// never a single stream. The stream identifier for a
|
// never a single stream. The stream identifier for a
|
||||||
// SETTINGS frame MUST be zero (0x0). If an endpoint
|
// SETTINGS frame MUST be zero (0x0). If an endpoint
|
||||||
// receives a SETTINGS frame whose stream identifier
|
// receives a SETTINGS frame whose stream identifier
|
||||||
// field is anything other than 0x0, the endpoint MUST
|
// field is anything other than 0x0, the endpoint MUST
|
||||||
|
@ -762,7 +797,7 @@ type PingFrame struct {
|
||||||
|
|
||||||
func (f *PingFrame) IsAck() bool { return f.Flags.Has(FlagPingAck) }
|
func (f *PingFrame) IsAck() bool { return f.Flags.Has(FlagPingAck) }
|
||||||
|
|
||||||
func parsePingFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
func parsePingFrame(_ *frameCache, fh FrameHeader, payload []byte) (Frame, error) {
|
||||||
if len(payload) != 8 {
|
if len(payload) != 8 {
|
||||||
return nil, ConnectionError(ErrCodeFrameSize)
|
return nil, ConnectionError(ErrCodeFrameSize)
|
||||||
}
|
}
|
||||||
|
@ -802,7 +837,7 @@ func (f *GoAwayFrame) DebugData() []byte {
|
||||||
return f.debugData
|
return f.debugData
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseGoAwayFrame(fh FrameHeader, p []byte) (Frame, error) {
|
func parseGoAwayFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
||||||
if fh.StreamID != 0 {
|
if fh.StreamID != 0 {
|
||||||
return nil, ConnectionError(ErrCodeProtocol)
|
return nil, ConnectionError(ErrCodeProtocol)
|
||||||
}
|
}
|
||||||
|
@ -842,7 +877,7 @@ func (f *UnknownFrame) Payload() []byte {
|
||||||
return f.p
|
return f.p
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseUnknownFrame(fh FrameHeader, p []byte) (Frame, error) {
|
func parseUnknownFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
||||||
return &UnknownFrame{fh, p}, nil
|
return &UnknownFrame{fh, p}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -853,7 +888,7 @@ type WindowUpdateFrame struct {
|
||||||
Increment uint32 // never read with high bit set
|
Increment uint32 // never read with high bit set
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseWindowUpdateFrame(fh FrameHeader, p []byte) (Frame, error) {
|
func parseWindowUpdateFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
||||||
if len(p) != 4 {
|
if len(p) != 4 {
|
||||||
return nil, ConnectionError(ErrCodeFrameSize)
|
return nil, ConnectionError(ErrCodeFrameSize)
|
||||||
}
|
}
|
||||||
|
@ -918,12 +953,12 @@ func (f *HeadersFrame) HasPriority() bool {
|
||||||
return f.FrameHeader.Flags.Has(FlagHeadersPriority)
|
return f.FrameHeader.Flags.Has(FlagHeadersPriority)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseHeadersFrame(fh FrameHeader, p []byte) (_ Frame, err error) {
|
func parseHeadersFrame(_ *frameCache, fh FrameHeader, p []byte) (_ Frame, err error) {
|
||||||
hf := &HeadersFrame{
|
hf := &HeadersFrame{
|
||||||
FrameHeader: fh,
|
FrameHeader: fh,
|
||||||
}
|
}
|
||||||
if fh.StreamID == 0 {
|
if fh.StreamID == 0 {
|
||||||
// HEADERS frames MUST be associated with a stream. If a HEADERS frame
|
// HEADERS frames MUST be associated with a stream. If a HEADERS frame
|
||||||
// is received whose stream identifier field is 0x0, the recipient MUST
|
// is received whose stream identifier field is 0x0, the recipient MUST
|
||||||
// respond with a connection error (Section 5.4.1) of type
|
// respond with a connection error (Section 5.4.1) of type
|
||||||
// PROTOCOL_ERROR.
|
// PROTOCOL_ERROR.
|
||||||
|
@ -1045,7 +1080,7 @@ type PriorityParam struct {
|
||||||
Exclusive bool
|
Exclusive bool
|
||||||
|
|
||||||
// Weight is the stream's zero-indexed weight. It should be
|
// Weight is the stream's zero-indexed weight. It should be
|
||||||
// set together with StreamDep, or neither should be set. Per
|
// set together with StreamDep, or neither should be set. Per
|
||||||
// the spec, "Add one to the value to obtain a weight between
|
// the spec, "Add one to the value to obtain a weight between
|
||||||
// 1 and 256."
|
// 1 and 256."
|
||||||
Weight uint8
|
Weight uint8
|
||||||
|
@ -1055,7 +1090,7 @@ func (p PriorityParam) IsZero() bool {
|
||||||
return p == PriorityParam{}
|
return p == PriorityParam{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePriorityFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
func parsePriorityFrame(_ *frameCache, fh FrameHeader, payload []byte) (Frame, error) {
|
||||||
if fh.StreamID == 0 {
|
if fh.StreamID == 0 {
|
||||||
return nil, connError{ErrCodeProtocol, "PRIORITY frame with stream ID 0"}
|
return nil, connError{ErrCodeProtocol, "PRIORITY frame with stream ID 0"}
|
||||||
}
|
}
|
||||||
|
@ -1102,7 +1137,7 @@ type RSTStreamFrame struct {
|
||||||
ErrCode ErrCode
|
ErrCode ErrCode
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseRSTStreamFrame(fh FrameHeader, p []byte) (Frame, error) {
|
func parseRSTStreamFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
||||||
if len(p) != 4 {
|
if len(p) != 4 {
|
||||||
return nil, ConnectionError(ErrCodeFrameSize)
|
return nil, ConnectionError(ErrCodeFrameSize)
|
||||||
}
|
}
|
||||||
|
@ -1132,7 +1167,7 @@ type ContinuationFrame struct {
|
||||||
headerFragBuf []byte
|
headerFragBuf []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseContinuationFrame(fh FrameHeader, p []byte) (Frame, error) {
|
func parseContinuationFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
||||||
if fh.StreamID == 0 {
|
if fh.StreamID == 0 {
|
||||||
return nil, connError{ErrCodeProtocol, "CONTINUATION frame with stream ID 0"}
|
return nil, connError{ErrCodeProtocol, "CONTINUATION frame with stream ID 0"}
|
||||||
}
|
}
|
||||||
|
@ -1182,7 +1217,7 @@ func (f *PushPromiseFrame) HeadersEnded() bool {
|
||||||
return f.FrameHeader.Flags.Has(FlagPushPromiseEndHeaders)
|
return f.FrameHeader.Flags.Has(FlagPushPromiseEndHeaders)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePushPromise(fh FrameHeader, p []byte) (_ Frame, err error) {
|
func parsePushPromise(_ *frameCache, fh FrameHeader, p []byte) (_ Frame, err error) {
|
||||||
pp := &PushPromiseFrame{
|
pp := &PushPromiseFrame{
|
||||||
FrameHeader: fh,
|
FrameHeader: fh,
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
package http2
|
package http2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -15,29 +14,3 @@ import (
|
||||||
func transportExpectContinueTimeout(t1 *http.Transport) time.Duration {
|
func transportExpectContinueTimeout(t1 *http.Transport) time.Duration {
|
||||||
return t1.ExpectContinueTimeout
|
return t1.ExpectContinueTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
// isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec.
|
|
||||||
func isBadCipher(cipher uint16) bool {
|
|
||||||
switch cipher {
|
|
||||||
case tls.TLS_RSA_WITH_RC4_128_SHA,
|
|
||||||
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
||||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
|
||||||
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
|
||||||
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
|
||||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
|
||||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
|
||||||
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
|
||||||
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
||||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
|
|
||||||
// Reject cipher suites from Appendix A.
|
|
||||||
// "This list includes those cipher suites that do not
|
|
||||||
// offer an ephemeral key exchange and those that are
|
|
||||||
// based on the TLS null, stream or block cipher type"
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,7 +12,11 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func cloneTLSConfig(c *tls.Config) *tls.Config { return c.Clone() }
|
func cloneTLSConfig(c *tls.Config) *tls.Config {
|
||||||
|
c2 := c.Clone()
|
||||||
|
c2.GetClientCertificate = c.GetClientCertificate // golang.org/issue/19264
|
||||||
|
return c2
|
||||||
|
}
|
||||||
|
|
||||||
var _ http.Pusher = (*responseWriter)(nil)
|
var _ http.Pusher = (*responseWriter)(nil)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.9
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func configureServer19(s *http.Server, conf *Server) error {
|
||||||
|
s.RegisterOnShutdown(conf.state.startGracefulShutdown)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -39,13 +39,14 @@ func NewEncoder(w io.Writer) *Encoder {
|
||||||
tableSizeUpdate: false,
|
tableSizeUpdate: false,
|
||||||
w: w,
|
w: w,
|
||||||
}
|
}
|
||||||
|
e.dynTab.table.init()
|
||||||
e.dynTab.setMaxSize(initialHeaderTableSize)
|
e.dynTab.setMaxSize(initialHeaderTableSize)
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteField encodes f into a single Write to e's underlying Writer.
|
// WriteField encodes f into a single Write to e's underlying Writer.
|
||||||
// This function may also produce bytes for "Header Table Size Update"
|
// This function may also produce bytes for "Header Table Size Update"
|
||||||
// if necessary. If produced, it is done before encoding f.
|
// if necessary. If produced, it is done before encoding f.
|
||||||
func (e *Encoder) WriteField(f HeaderField) error {
|
func (e *Encoder) WriteField(f HeaderField) error {
|
||||||
e.buf = e.buf[:0]
|
e.buf = e.buf[:0]
|
||||||
|
|
||||||
|
@ -88,29 +89,17 @@ func (e *Encoder) WriteField(f HeaderField) error {
|
||||||
// only name matches, i points to that index and nameValueMatch
|
// only name matches, i points to that index and nameValueMatch
|
||||||
// becomes false.
|
// becomes false.
|
||||||
func (e *Encoder) searchTable(f HeaderField) (i uint64, nameValueMatch bool) {
|
func (e *Encoder) searchTable(f HeaderField) (i uint64, nameValueMatch bool) {
|
||||||
for idx, hf := range staticTable {
|
i, nameValueMatch = staticTable.search(f)
|
||||||
if !constantTimeStringCompare(hf.Name, f.Name) {
|
if nameValueMatch {
|
||||||
continue
|
return i, true
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
i = uint64(idx + 1)
|
|
||||||
}
|
|
||||||
if f.Sensitive {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !constantTimeStringCompare(hf.Value, f.Value) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
i = uint64(idx + 1)
|
|
||||||
nameValueMatch = true
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
j, nameValueMatch := e.dynTab.search(f)
|
j, nameValueMatch := e.dynTab.table.search(f)
|
||||||
if nameValueMatch || (i == 0 && j != 0) {
|
if nameValueMatch || (i == 0 && j != 0) {
|
||||||
i = j + uint64(len(staticTable))
|
return j + uint64(staticTable.len()), nameValueMatch
|
||||||
}
|
}
|
||||||
return
|
|
||||||
|
return i, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMaxDynamicTableSize changes the dynamic header table size to v.
|
// SetMaxDynamicTableSize changes the dynamic header table size to v.
|
||||||
|
|
|
@ -61,7 +61,7 @@ func (hf HeaderField) String() string {
|
||||||
func (hf HeaderField) Size() uint32 {
|
func (hf HeaderField) Size() uint32 {
|
||||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.4.1
|
// http://http2.github.io/http2-spec/compression.html#rfc.section.4.1
|
||||||
// "The size of the dynamic table is the sum of the size of
|
// "The size of the dynamic table is the sum of the size of
|
||||||
// its entries. The size of an entry is the sum of its name's
|
// its entries. The size of an entry is the sum of its name's
|
||||||
// length in octets (as defined in Section 5.2), its value's
|
// length in octets (as defined in Section 5.2), its value's
|
||||||
// length in octets (see Section 5.2), plus 32. The size of
|
// length in octets (see Section 5.2), plus 32. The size of
|
||||||
// an entry is calculated using the length of the name and
|
// an entry is calculated using the length of the name and
|
||||||
|
@ -102,6 +102,7 @@ func NewDecoder(maxDynamicTableSize uint32, emitFunc func(f HeaderField)) *Decod
|
||||||
emit: emitFunc,
|
emit: emitFunc,
|
||||||
emitEnabled: true,
|
emitEnabled: true,
|
||||||
}
|
}
|
||||||
|
d.dynTab.table.init()
|
||||||
d.dynTab.allowedMaxSize = maxDynamicTableSize
|
d.dynTab.allowedMaxSize = maxDynamicTableSize
|
||||||
d.dynTab.setMaxSize(maxDynamicTableSize)
|
d.dynTab.setMaxSize(maxDynamicTableSize)
|
||||||
return d
|
return d
|
||||||
|
@ -154,12 +155,9 @@ func (d *Decoder) SetAllowedMaxDynamicTableSize(v uint32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type dynamicTable struct {
|
type dynamicTable struct {
|
||||||
// ents is the FIFO described at
|
|
||||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.2.3.2
|
// http://http2.github.io/http2-spec/compression.html#rfc.section.2.3.2
|
||||||
// The newest (low index) is append at the end, and items are
|
table headerFieldTable
|
||||||
// evicted from the front.
|
size uint32 // in bytes
|
||||||
ents []HeaderField
|
|
||||||
size uint32
|
|
||||||
maxSize uint32 // current maxSize
|
maxSize uint32 // current maxSize
|
||||||
allowedMaxSize uint32 // maxSize may go up to this, inclusive
|
allowedMaxSize uint32 // maxSize may go up to this, inclusive
|
||||||
}
|
}
|
||||||
|
@ -169,95 +167,45 @@ func (dt *dynamicTable) setMaxSize(v uint32) {
|
||||||
dt.evict()
|
dt.evict()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: change dynamicTable to be a struct with a slice and a size int field,
|
|
||||||
// per http://http2.github.io/http2-spec/compression.html#rfc.section.4.1:
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Then make add increment the size. maybe the max size should move from Decoder to
|
|
||||||
// dynamicTable and add should return an ok bool if there was enough space.
|
|
||||||
//
|
|
||||||
// Later we'll need a remove operation on dynamicTable.
|
|
||||||
|
|
||||||
func (dt *dynamicTable) add(f HeaderField) {
|
func (dt *dynamicTable) add(f HeaderField) {
|
||||||
dt.ents = append(dt.ents, f)
|
dt.table.addEntry(f)
|
||||||
dt.size += f.Size()
|
dt.size += f.Size()
|
||||||
dt.evict()
|
dt.evict()
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're too big, evict old stuff (front of the slice)
|
// If we're too big, evict old stuff.
|
||||||
func (dt *dynamicTable) evict() {
|
func (dt *dynamicTable) evict() {
|
||||||
base := dt.ents // keep base pointer of slice
|
var n int
|
||||||
for dt.size > dt.maxSize {
|
for dt.size > dt.maxSize && n < dt.table.len() {
|
||||||
dt.size -= dt.ents[0].Size()
|
dt.size -= dt.table.ents[n].Size()
|
||||||
dt.ents = dt.ents[1:]
|
n++
|
||||||
}
|
}
|
||||||
|
dt.table.evictOldest(n)
|
||||||
// Shift slice contents down if we evicted things.
|
|
||||||
if len(dt.ents) != len(base) {
|
|
||||||
copy(base, dt.ents)
|
|
||||||
dt.ents = base[:len(dt.ents)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// constantTimeStringCompare compares string a and b in a constant
|
|
||||||
// time manner.
|
|
||||||
func constantTimeStringCompare(a, b string) bool {
|
|
||||||
if len(a) != len(b) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
c := byte(0)
|
|
||||||
|
|
||||||
for i := 0; i < len(a); i++ {
|
|
||||||
c |= a[i] ^ b[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return c == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search searches f in the table. The return value i is 0 if there is
|
|
||||||
// no name match. If there is name match or name/value match, i is the
|
|
||||||
// index of that entry (1-based). If both name and value match,
|
|
||||||
// nameValueMatch becomes true.
|
|
||||||
func (dt *dynamicTable) search(f HeaderField) (i uint64, nameValueMatch bool) {
|
|
||||||
l := len(dt.ents)
|
|
||||||
for j := l - 1; j >= 0; j-- {
|
|
||||||
ent := dt.ents[j]
|
|
||||||
if !constantTimeStringCompare(ent.Name, f.Name) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
i = uint64(l - j)
|
|
||||||
}
|
|
||||||
if f.Sensitive {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !constantTimeStringCompare(ent.Value, f.Value) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
i = uint64(l - j)
|
|
||||||
nameValueMatch = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) maxTableIndex() int {
|
func (d *Decoder) maxTableIndex() int {
|
||||||
return len(d.dynTab.ents) + len(staticTable)
|
// This should never overflow. RFC 7540 Section 6.5.2 limits the size of
|
||||||
|
// the dynamic table to 2^32 bytes, where each entry will occupy more than
|
||||||
|
// one byte. Further, the staticTable has a fixed, small length.
|
||||||
|
return d.dynTab.table.len() + staticTable.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) at(i uint64) (hf HeaderField, ok bool) {
|
func (d *Decoder) at(i uint64) (hf HeaderField, ok bool) {
|
||||||
if i < 1 {
|
// See Section 2.3.3.
|
||||||
|
if i == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if i <= uint64(staticTable.len()) {
|
||||||
|
return staticTable.ents[i-1], true
|
||||||
|
}
|
||||||
if i > uint64(d.maxTableIndex()) {
|
if i > uint64(d.maxTableIndex()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if i <= uint64(len(staticTable)) {
|
// In the dynamic table, newer entries have lower indices.
|
||||||
return staticTable[i-1], true
|
// However, dt.ents[0] is the oldest entry. Hence, dt.ents is
|
||||||
}
|
// the reversed dynamic table.
|
||||||
dents := d.dynTab.ents
|
dt := d.dynTab.table
|
||||||
return dents[len(dents)-(int(i)-len(staticTable))], true
|
return dt.ents[dt.len()-(int(i)-staticTable.len())], true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes an entire block.
|
// Decode decodes an entire block.
|
||||||
|
@ -307,7 +255,7 @@ func (d *Decoder) Write(p []byte) (n int, err error) {
|
||||||
err = d.parseHeaderFieldRepr()
|
err = d.parseHeaderFieldRepr()
|
||||||
if err == errNeedMore {
|
if err == errNeedMore {
|
||||||
// Extra paranoia, making sure saveBuf won't
|
// Extra paranoia, making sure saveBuf won't
|
||||||
// get too large. All the varint and string
|
// get too large. All the varint and string
|
||||||
// reading code earlier should already catch
|
// reading code earlier should already catch
|
||||||
// overlong things and return ErrStringLength,
|
// overlong things and return ErrStringLength,
|
||||||
// but keep this as a last resort.
|
// but keep this as a last resort.
|
||||||
|
|
|
@ -4,73 +4,200 @@
|
||||||
|
|
||||||
package hpack
|
package hpack
|
||||||
|
|
||||||
func pair(name, value string) HeaderField {
|
import (
|
||||||
return HeaderField{Name: name, Value: value}
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// headerFieldTable implements a list of HeaderFields.
|
||||||
|
// This is used to implement the static and dynamic tables.
|
||||||
|
type headerFieldTable struct {
|
||||||
|
// For static tables, entries are never evicted.
|
||||||
|
//
|
||||||
|
// For dynamic tables, entries are evicted from ents[0] and added to the end.
|
||||||
|
// Each entry has a unique id that starts at one and increments for each
|
||||||
|
// entry that is added. This unique id is stable across evictions, meaning
|
||||||
|
// it can be used as a pointer to a specific entry. As in hpack, unique ids
|
||||||
|
// are 1-based. The unique id for ents[k] is k + evictCount + 1.
|
||||||
|
//
|
||||||
|
// Zero is not a valid unique id.
|
||||||
|
//
|
||||||
|
// evictCount should not overflow in any remotely practical situation. In
|
||||||
|
// practice, we will have one dynamic table per HTTP/2 connection. If we
|
||||||
|
// assume a very powerful server that handles 1M QPS per connection and each
|
||||||
|
// request adds (then evicts) 100 entries from the table, it would still take
|
||||||
|
// 2M years for evictCount to overflow.
|
||||||
|
ents []HeaderField
|
||||||
|
evictCount uint64
|
||||||
|
|
||||||
|
// byName maps a HeaderField name to the unique id of the newest entry with
|
||||||
|
// the same name. See above for a definition of "unique id".
|
||||||
|
byName map[string]uint64
|
||||||
|
|
||||||
|
// byNameValue maps a HeaderField name/value pair to the unique id of the newest
|
||||||
|
// entry with the same name and value. See above for a definition of "unique id".
|
||||||
|
byNameValue map[pairNameValue]uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type pairNameValue struct {
|
||||||
|
name, value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *headerFieldTable) init() {
|
||||||
|
t.byName = make(map[string]uint64)
|
||||||
|
t.byNameValue = make(map[pairNameValue]uint64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// len reports the number of entries in the table.
|
||||||
|
func (t *headerFieldTable) len() int {
|
||||||
|
return len(t.ents)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addEntry adds a new entry.
|
||||||
|
func (t *headerFieldTable) addEntry(f HeaderField) {
|
||||||
|
id := uint64(t.len()) + t.evictCount + 1
|
||||||
|
t.byName[f.Name] = id
|
||||||
|
t.byNameValue[pairNameValue{f.Name, f.Value}] = id
|
||||||
|
t.ents = append(t.ents, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// evictOldest evicts the n oldest entries in the table.
|
||||||
|
func (t *headerFieldTable) evictOldest(n int) {
|
||||||
|
if n > t.len() {
|
||||||
|
panic(fmt.Sprintf("evictOldest(%v) on table with %v entries", n, t.len()))
|
||||||
|
}
|
||||||
|
for k := 0; k < n; k++ {
|
||||||
|
f := t.ents[k]
|
||||||
|
id := t.evictCount + uint64(k) + 1
|
||||||
|
if t.byName[f.Name] == id {
|
||||||
|
delete(t.byName, f.Name)
|
||||||
|
}
|
||||||
|
if p := (pairNameValue{f.Name, f.Value}); t.byNameValue[p] == id {
|
||||||
|
delete(t.byNameValue, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
copy(t.ents, t.ents[n:])
|
||||||
|
for k := t.len() - n; k < t.len(); k++ {
|
||||||
|
t.ents[k] = HeaderField{} // so strings can be garbage collected
|
||||||
|
}
|
||||||
|
t.ents = t.ents[:t.len()-n]
|
||||||
|
if t.evictCount+uint64(n) < t.evictCount {
|
||||||
|
panic("evictCount overflow")
|
||||||
|
}
|
||||||
|
t.evictCount += uint64(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// search finds f in the table. If there is no match, i is 0.
|
||||||
|
// If both name and value match, i is the matched index and nameValueMatch
|
||||||
|
// becomes true. If only name matches, i points to that index and
|
||||||
|
// nameValueMatch becomes false.
|
||||||
|
//
|
||||||
|
// The returned index is a 1-based HPACK index. For dynamic tables, HPACK says
|
||||||
|
// that index 1 should be the newest entry, but t.ents[0] is the oldest entry,
|
||||||
|
// meaning t.ents is reversed for dynamic tables. Hence, when t is a dynamic
|
||||||
|
// table, the return value i actually refers to the entry t.ents[t.len()-i].
|
||||||
|
//
|
||||||
|
// All tables are assumed to be a dynamic tables except for the global
|
||||||
|
// staticTable pointer.
|
||||||
|
//
|
||||||
|
// See Section 2.3.3.
|
||||||
|
func (t *headerFieldTable) search(f HeaderField) (i uint64, nameValueMatch bool) {
|
||||||
|
if !f.Sensitive {
|
||||||
|
if id := t.byNameValue[pairNameValue{f.Name, f.Value}]; id != 0 {
|
||||||
|
return t.idToIndex(id), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if id := t.byName[f.Name]; id != 0 {
|
||||||
|
return t.idToIndex(id), false
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// idToIndex converts a unique id to an HPACK index.
|
||||||
|
// See Section 2.3.3.
|
||||||
|
func (t *headerFieldTable) idToIndex(id uint64) uint64 {
|
||||||
|
if id <= t.evictCount {
|
||||||
|
panic(fmt.Sprintf("id (%v) <= evictCount (%v)", id, t.evictCount))
|
||||||
|
}
|
||||||
|
k := id - t.evictCount - 1 // convert id to an index t.ents[k]
|
||||||
|
if t != staticTable {
|
||||||
|
return uint64(t.len()) - k // dynamic table
|
||||||
|
}
|
||||||
|
return k + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B
|
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B
|
||||||
var staticTable = [...]HeaderField{
|
var staticTable = newStaticTable()
|
||||||
pair(":authority", ""), // index 1 (1-based)
|
var staticTableEntries = [...]HeaderField{
|
||||||
pair(":method", "GET"),
|
{Name: ":authority"},
|
||||||
pair(":method", "POST"),
|
{Name: ":method", Value: "GET"},
|
||||||
pair(":path", "/"),
|
{Name: ":method", Value: "POST"},
|
||||||
pair(":path", "/index.html"),
|
{Name: ":path", Value: "/"},
|
||||||
pair(":scheme", "http"),
|
{Name: ":path", Value: "/index.html"},
|
||||||
pair(":scheme", "https"),
|
{Name: ":scheme", Value: "http"},
|
||||||
pair(":status", "200"),
|
{Name: ":scheme", Value: "https"},
|
||||||
pair(":status", "204"),
|
{Name: ":status", Value: "200"},
|
||||||
pair(":status", "206"),
|
{Name: ":status", Value: "204"},
|
||||||
pair(":status", "304"),
|
{Name: ":status", Value: "206"},
|
||||||
pair(":status", "400"),
|
{Name: ":status", Value: "304"},
|
||||||
pair(":status", "404"),
|
{Name: ":status", Value: "400"},
|
||||||
pair(":status", "500"),
|
{Name: ":status", Value: "404"},
|
||||||
pair("accept-charset", ""),
|
{Name: ":status", Value: "500"},
|
||||||
pair("accept-encoding", "gzip, deflate"),
|
{Name: "accept-charset"},
|
||||||
pair("accept-language", ""),
|
{Name: "accept-encoding", Value: "gzip, deflate"},
|
||||||
pair("accept-ranges", ""),
|
{Name: "accept-language"},
|
||||||
pair("accept", ""),
|
{Name: "accept-ranges"},
|
||||||
pair("access-control-allow-origin", ""),
|
{Name: "accept"},
|
||||||
pair("age", ""),
|
{Name: "access-control-allow-origin"},
|
||||||
pair("allow", ""),
|
{Name: "age"},
|
||||||
pair("authorization", ""),
|
{Name: "allow"},
|
||||||
pair("cache-control", ""),
|
{Name: "authorization"},
|
||||||
pair("content-disposition", ""),
|
{Name: "cache-control"},
|
||||||
pair("content-encoding", ""),
|
{Name: "content-disposition"},
|
||||||
pair("content-language", ""),
|
{Name: "content-encoding"},
|
||||||
pair("content-length", ""),
|
{Name: "content-language"},
|
||||||
pair("content-location", ""),
|
{Name: "content-length"},
|
||||||
pair("content-range", ""),
|
{Name: "content-location"},
|
||||||
pair("content-type", ""),
|
{Name: "content-range"},
|
||||||
pair("cookie", ""),
|
{Name: "content-type"},
|
||||||
pair("date", ""),
|
{Name: "cookie"},
|
||||||
pair("etag", ""),
|
{Name: "date"},
|
||||||
pair("expect", ""),
|
{Name: "etag"},
|
||||||
pair("expires", ""),
|
{Name: "expect"},
|
||||||
pair("from", ""),
|
{Name: "expires"},
|
||||||
pair("host", ""),
|
{Name: "from"},
|
||||||
pair("if-match", ""),
|
{Name: "host"},
|
||||||
pair("if-modified-since", ""),
|
{Name: "if-match"},
|
||||||
pair("if-none-match", ""),
|
{Name: "if-modified-since"},
|
||||||
pair("if-range", ""),
|
{Name: "if-none-match"},
|
||||||
pair("if-unmodified-since", ""),
|
{Name: "if-range"},
|
||||||
pair("last-modified", ""),
|
{Name: "if-unmodified-since"},
|
||||||
pair("link", ""),
|
{Name: "last-modified"},
|
||||||
pair("location", ""),
|
{Name: "link"},
|
||||||
pair("max-forwards", ""),
|
{Name: "location"},
|
||||||
pair("proxy-authenticate", ""),
|
{Name: "max-forwards"},
|
||||||
pair("proxy-authorization", ""),
|
{Name: "proxy-authenticate"},
|
||||||
pair("range", ""),
|
{Name: "proxy-authorization"},
|
||||||
pair("referer", ""),
|
{Name: "range"},
|
||||||
pair("refresh", ""),
|
{Name: "referer"},
|
||||||
pair("retry-after", ""),
|
{Name: "refresh"},
|
||||||
pair("server", ""),
|
{Name: "retry-after"},
|
||||||
pair("set-cookie", ""),
|
{Name: "server"},
|
||||||
pair("strict-transport-security", ""),
|
{Name: "set-cookie"},
|
||||||
pair("transfer-encoding", ""),
|
{Name: "strict-transport-security"},
|
||||||
pair("user-agent", ""),
|
{Name: "transfer-encoding"},
|
||||||
pair("vary", ""),
|
{Name: "user-agent"},
|
||||||
pair("via", ""),
|
{Name: "vary"},
|
||||||
pair("www-authenticate", ""),
|
{Name: "via"},
|
||||||
|
{Name: "www-authenticate"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStaticTable() *headerFieldTable {
|
||||||
|
t := &headerFieldTable{}
|
||||||
|
t.init()
|
||||||
|
for _, e := range staticTableEntries[:] {
|
||||||
|
t.addEntry(e)
|
||||||
|
}
|
||||||
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
var huffmanCodes = [256]uint32{
|
var huffmanCodes = [256]uint32{
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
package http2
|
package http2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -20,27 +19,3 @@ func transportExpectContinueTimeout(t1 *http.Transport) time.Duration {
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec.
|
|
||||||
func isBadCipher(cipher uint16) bool {
|
|
||||||
switch cipher {
|
|
||||||
case tls.TLS_RSA_WITH_RC4_128_SHA,
|
|
||||||
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
||||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
|
||||||
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
|
||||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
|
||||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
|
||||||
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
|
||||||
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
||||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
|
|
||||||
// Reject cipher suites from Appendix A.
|
|
||||||
// "This list includes those cipher suites that do not
|
|
||||||
// offer an ephemeral key exchange and those that are
|
|
||||||
// based on the TLS null, stream or block cipher type"
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !go1.9
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func configureServer19(s *http.Server, conf *Server) error {
|
||||||
|
// not supported prior to go1.9
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -10,13 +10,13 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// pipe is a goroutine-safe io.Reader/io.Writer pair. It's like
|
// pipe is a goroutine-safe io.Reader/io.Writer pair. It's like
|
||||||
// io.Pipe except there are no PipeReader/PipeWriter halves, and the
|
// io.Pipe except there are no PipeReader/PipeWriter halves, and the
|
||||||
// underlying buffer is an interface. (io.Pipe is always unbuffered)
|
// underlying buffer is an interface. (io.Pipe is always unbuffered)
|
||||||
type pipe struct {
|
type pipe struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
c sync.Cond // c.L lazily initialized to &p.mu
|
c sync.Cond // c.L lazily initialized to &p.mu
|
||||||
b pipeBuffer
|
b pipeBuffer // nil when done reading
|
||||||
err error // read error once empty. non-nil means closed.
|
err error // read error once empty. non-nil means closed.
|
||||||
breakErr error // immediate read error (caller doesn't see rest of b)
|
breakErr error // immediate read error (caller doesn't see rest of b)
|
||||||
donec chan struct{} // closed on error
|
donec chan struct{} // closed on error
|
||||||
|
@ -32,6 +32,9 @@ type pipeBuffer interface {
|
||||||
func (p *pipe) Len() int {
|
func (p *pipe) Len() int {
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
|
if p.b == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
return p.b.Len()
|
return p.b.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +58,7 @@ func (p *pipe) Read(d []byte) (n int, err error) {
|
||||||
p.readFn() // e.g. copy trailers
|
p.readFn() // e.g. copy trailers
|
||||||
p.readFn = nil // not sticky like p.err
|
p.readFn = nil // not sticky like p.err
|
||||||
}
|
}
|
||||||
|
p.b = nil
|
||||||
return 0, p.err
|
return 0, p.err
|
||||||
}
|
}
|
||||||
p.c.Wait()
|
p.c.Wait()
|
||||||
|
@ -75,6 +79,9 @@ func (p *pipe) Write(d []byte) (n int, err error) {
|
||||||
if p.err != nil {
|
if p.err != nil {
|
||||||
return 0, errClosedPipeWrite
|
return 0, errClosedPipeWrite
|
||||||
}
|
}
|
||||||
|
if p.breakErr != nil {
|
||||||
|
return len(d), nil // discard when there is no reader
|
||||||
|
}
|
||||||
return p.b.Write(d)
|
return p.b.Write(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +116,9 @@ func (p *pipe) closeWithError(dst *error, err error, fn func()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.readFn = fn
|
p.readFn = fn
|
||||||
|
if dst == &p.breakErr {
|
||||||
|
p.b = nil
|
||||||
|
}
|
||||||
*dst = err
|
*dst = err
|
||||||
p.closeDoneLocked()
|
p.closeDoneLocked()
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,9 +110,41 @@ type Server struct {
|
||||||
// activity for the purposes of IdleTimeout.
|
// activity for the purposes of IdleTimeout.
|
||||||
IdleTimeout time.Duration
|
IdleTimeout time.Duration
|
||||||
|
|
||||||
|
// MaxUploadBufferPerConnection is the size of the initial flow
|
||||||
|
// control window for each connections. The HTTP/2 spec does not
|
||||||
|
// allow this to be smaller than 65535 or larger than 2^32-1.
|
||||||
|
// If the value is outside this range, a default value will be
|
||||||
|
// used instead.
|
||||||
|
MaxUploadBufferPerConnection int32
|
||||||
|
|
||||||
|
// MaxUploadBufferPerStream is the size of the initial flow control
|
||||||
|
// window for each stream. The HTTP/2 spec does not allow this to
|
||||||
|
// be larger than 2^32-1. If the value is zero or larger than the
|
||||||
|
// maximum, a default value will be used instead.
|
||||||
|
MaxUploadBufferPerStream int32
|
||||||
|
|
||||||
// NewWriteScheduler constructs a write scheduler for a connection.
|
// NewWriteScheduler constructs a write scheduler for a connection.
|
||||||
// If nil, a default scheduler is chosen.
|
// If nil, a default scheduler is chosen.
|
||||||
NewWriteScheduler func() WriteScheduler
|
NewWriteScheduler func() WriteScheduler
|
||||||
|
|
||||||
|
// Internal state. This is a pointer (rather than embedded directly)
|
||||||
|
// so that we don't embed a Mutex in this struct, which will make the
|
||||||
|
// struct non-copyable, which might break some callers.
|
||||||
|
state *serverInternalState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) initialConnRecvWindowSize() int32 {
|
||||||
|
if s.MaxUploadBufferPerConnection > initialWindowSize {
|
||||||
|
return s.MaxUploadBufferPerConnection
|
||||||
|
}
|
||||||
|
return 1 << 20
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) initialStreamRecvWindowSize() int32 {
|
||||||
|
if s.MaxUploadBufferPerStream > 0 {
|
||||||
|
return s.MaxUploadBufferPerStream
|
||||||
|
}
|
||||||
|
return 1 << 20
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) maxReadFrameSize() uint32 {
|
func (s *Server) maxReadFrameSize() uint32 {
|
||||||
|
@ -129,6 +161,40 @@ func (s *Server) maxConcurrentStreams() uint32 {
|
||||||
return defaultMaxStreams
|
return defaultMaxStreams
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type serverInternalState struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
activeConns map[*serverConn]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serverInternalState) registerConn(sc *serverConn) {
|
||||||
|
if s == nil {
|
||||||
|
return // if the Server was used without calling ConfigureServer
|
||||||
|
}
|
||||||
|
s.mu.Lock()
|
||||||
|
s.activeConns[sc] = struct{}{}
|
||||||
|
s.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serverInternalState) unregisterConn(sc *serverConn) {
|
||||||
|
if s == nil {
|
||||||
|
return // if the Server was used without calling ConfigureServer
|
||||||
|
}
|
||||||
|
s.mu.Lock()
|
||||||
|
delete(s.activeConns, sc)
|
||||||
|
s.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serverInternalState) startGracefulShutdown() {
|
||||||
|
if s == nil {
|
||||||
|
return // if the Server was used without calling ConfigureServer
|
||||||
|
}
|
||||||
|
s.mu.Lock()
|
||||||
|
for sc := range s.activeConns {
|
||||||
|
sc.startGracefulShutdown()
|
||||||
|
}
|
||||||
|
s.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
// ConfigureServer adds HTTP/2 support to a net/http Server.
|
// ConfigureServer adds HTTP/2 support to a net/http Server.
|
||||||
//
|
//
|
||||||
// The configuration conf may be nil.
|
// The configuration conf may be nil.
|
||||||
|
@ -141,9 +207,13 @@ func ConfigureServer(s *http.Server, conf *Server) error {
|
||||||
if conf == nil {
|
if conf == nil {
|
||||||
conf = new(Server)
|
conf = new(Server)
|
||||||
}
|
}
|
||||||
|
conf.state = &serverInternalState{activeConns: make(map[*serverConn]struct{})}
|
||||||
if err := configureServer18(s, conf); err != nil {
|
if err := configureServer18(s, conf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := configureServer19(s, conf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if s.TLSConfig == nil {
|
if s.TLSConfig == nil {
|
||||||
s.TLSConfig = new(tls.Config)
|
s.TLSConfig = new(tls.Config)
|
||||||
|
@ -255,35 +325,37 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
sc := &serverConn{
|
sc := &serverConn{
|
||||||
srv: s,
|
srv: s,
|
||||||
hs: opts.baseConfig(),
|
hs: opts.baseConfig(),
|
||||||
conn: c,
|
conn: c,
|
||||||
baseCtx: baseCtx,
|
baseCtx: baseCtx,
|
||||||
remoteAddrStr: c.RemoteAddr().String(),
|
remoteAddrStr: c.RemoteAddr().String(),
|
||||||
bw: newBufferedWriter(c),
|
bw: newBufferedWriter(c),
|
||||||
handler: opts.handler(),
|
handler: opts.handler(),
|
||||||
streams: make(map[uint32]*stream),
|
streams: make(map[uint32]*stream),
|
||||||
readFrameCh: make(chan readFrameResult),
|
readFrameCh: make(chan readFrameResult),
|
||||||
wantWriteFrameCh: make(chan FrameWriteRequest, 8),
|
wantWriteFrameCh: make(chan FrameWriteRequest, 8),
|
||||||
wantStartPushCh: make(chan startPushRequest, 8),
|
serveMsgCh: make(chan interface{}, 8),
|
||||||
wroteFrameCh: make(chan frameWriteResult, 1), // buffered; one send in writeFrameAsync
|
wroteFrameCh: make(chan frameWriteResult, 1), // buffered; one send in writeFrameAsync
|
||||||
bodyReadCh: make(chan bodyReadMsg), // buffering doesn't matter either way
|
bodyReadCh: make(chan bodyReadMsg), // buffering doesn't matter either way
|
||||||
doneServing: make(chan struct{}),
|
doneServing: make(chan struct{}),
|
||||||
clientMaxStreams: math.MaxUint32, // Section 6.5.2: "Initially, there is no limit to this value"
|
clientMaxStreams: math.MaxUint32, // Section 6.5.2: "Initially, there is no limit to this value"
|
||||||
advMaxStreams: s.maxConcurrentStreams(),
|
advMaxStreams: s.maxConcurrentStreams(),
|
||||||
initialWindowSize: initialWindowSize,
|
initialStreamSendWindowSize: initialWindowSize,
|
||||||
maxFrameSize: initialMaxFrameSize,
|
maxFrameSize: initialMaxFrameSize,
|
||||||
headerTableSize: initialHeaderTableSize,
|
headerTableSize: initialHeaderTableSize,
|
||||||
serveG: newGoroutineLock(),
|
serveG: newGoroutineLock(),
|
||||||
pushEnabled: true,
|
pushEnabled: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.state.registerConn(sc)
|
||||||
|
defer s.state.unregisterConn(sc)
|
||||||
|
|
||||||
// The net/http package sets the write deadline from the
|
// The net/http package sets the write deadline from the
|
||||||
// http.Server.WriteTimeout during the TLS handshake, but then
|
// http.Server.WriteTimeout during the TLS handshake, but then
|
||||||
// passes the connection off to us with the deadline already
|
// passes the connection off to us with the deadline already set.
|
||||||
// set. Disarm it here so that it is not applied to additional
|
// Write deadlines are set per stream in serverConn.newStream.
|
||||||
// streams opened on this connection.
|
// Disarm the net.Conn write deadline here.
|
||||||
// TODO: implement WriteTimeout fully. See Issue 18437.
|
|
||||||
if sc.hs.WriteTimeout != 0 {
|
if sc.hs.WriteTimeout != 0 {
|
||||||
sc.conn.SetWriteDeadline(time.Time{})
|
sc.conn.SetWriteDeadline(time.Time{})
|
||||||
}
|
}
|
||||||
|
@ -294,6 +366,9 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
||||||
sc.writeSched = NewRandomWriteScheduler()
|
sc.writeSched = NewRandomWriteScheduler()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These start at the RFC-specified defaults. If there is a higher
|
||||||
|
// configured value for inflow, that will be updated when we send a
|
||||||
|
// WINDOW_UPDATE shortly after sending SETTINGS.
|
||||||
sc.flow.add(initialWindowSize)
|
sc.flow.add(initialWindowSize)
|
||||||
sc.inflow.add(initialWindowSize)
|
sc.inflow.add(initialWindowSize)
|
||||||
sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf)
|
sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf)
|
||||||
|
@ -376,10 +451,9 @@ type serverConn struct {
|
||||||
doneServing chan struct{} // closed when serverConn.serve ends
|
doneServing chan struct{} // closed when serverConn.serve ends
|
||||||
readFrameCh chan readFrameResult // written by serverConn.readFrames
|
readFrameCh chan readFrameResult // written by serverConn.readFrames
|
||||||
wantWriteFrameCh chan FrameWriteRequest // from handlers -> serve
|
wantWriteFrameCh chan FrameWriteRequest // from handlers -> serve
|
||||||
wantStartPushCh chan startPushRequest // from handlers -> serve
|
|
||||||
wroteFrameCh chan frameWriteResult // from writeFrameAsync -> serve, tickles more frame writes
|
wroteFrameCh chan frameWriteResult // from writeFrameAsync -> serve, tickles more frame writes
|
||||||
bodyReadCh chan bodyReadMsg // from handlers -> serve
|
bodyReadCh chan bodyReadMsg // from handlers -> serve
|
||||||
testHookCh chan func(int) // code to run on the serve loop
|
serveMsgCh chan interface{} // misc messages & code to send to / run on the serve loop
|
||||||
flow flow // conn-wide (not stream-specific) outbound flow control
|
flow flow // conn-wide (not stream-specific) outbound flow control
|
||||||
inflow flow // conn-wide inbound flow control
|
inflow flow // conn-wide inbound flow control
|
||||||
tlsState *tls.ConnectionState // shared by all handlers, like net/http
|
tlsState *tls.ConnectionState // shared by all handlers, like net/http
|
||||||
|
@ -387,38 +461,39 @@ type serverConn struct {
|
||||||
writeSched WriteScheduler
|
writeSched WriteScheduler
|
||||||
|
|
||||||
// Everything following is owned by the serve loop; use serveG.check():
|
// Everything following is owned by the serve loop; use serveG.check():
|
||||||
serveG goroutineLock // used to verify funcs are on serve()
|
serveG goroutineLock // used to verify funcs are on serve()
|
||||||
pushEnabled bool
|
pushEnabled bool
|
||||||
sawFirstSettings bool // got the initial SETTINGS frame after the preface
|
sawFirstSettings bool // got the initial SETTINGS frame after the preface
|
||||||
needToSendSettingsAck bool
|
needToSendSettingsAck bool
|
||||||
unackedSettings int // how many SETTINGS have we sent without ACKs?
|
unackedSettings int // how many SETTINGS have we sent without ACKs?
|
||||||
clientMaxStreams uint32 // SETTINGS_MAX_CONCURRENT_STREAMS from client (our PUSH_PROMISE limit)
|
clientMaxStreams uint32 // SETTINGS_MAX_CONCURRENT_STREAMS from client (our PUSH_PROMISE limit)
|
||||||
advMaxStreams uint32 // our SETTINGS_MAX_CONCURRENT_STREAMS advertised the client
|
advMaxStreams uint32 // our SETTINGS_MAX_CONCURRENT_STREAMS advertised the client
|
||||||
curClientStreams uint32 // number of open streams initiated by the client
|
curClientStreams uint32 // number of open streams initiated by the client
|
||||||
curPushedStreams uint32 // number of open streams initiated by server push
|
curPushedStreams uint32 // number of open streams initiated by server push
|
||||||
maxClientStreamID uint32 // max ever seen from client (odd), or 0 if there have been no client requests
|
maxClientStreamID uint32 // max ever seen from client (odd), or 0 if there have been no client requests
|
||||||
maxPushPromiseID uint32 // ID of the last push promise (even), or 0 if there have been no pushes
|
maxPushPromiseID uint32 // ID of the last push promise (even), or 0 if there have been no pushes
|
||||||
streams map[uint32]*stream
|
streams map[uint32]*stream
|
||||||
initialWindowSize int32
|
initialStreamSendWindowSize int32
|
||||||
maxFrameSize int32
|
maxFrameSize int32
|
||||||
headerTableSize uint32
|
headerTableSize uint32
|
||||||
peerMaxHeaderListSize uint32 // zero means unknown (default)
|
peerMaxHeaderListSize uint32 // zero means unknown (default)
|
||||||
canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case
|
canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case
|
||||||
writingFrame bool // started writing a frame (on serve goroutine or separate)
|
writingFrame bool // started writing a frame (on serve goroutine or separate)
|
||||||
writingFrameAsync bool // started a frame on its own goroutine but haven't heard back on wroteFrameCh
|
writingFrameAsync bool // started a frame on its own goroutine but haven't heard back on wroteFrameCh
|
||||||
needsFrameFlush bool // last frame write wasn't a flush
|
needsFrameFlush bool // last frame write wasn't a flush
|
||||||
inGoAway bool // we've started to or sent GOAWAY
|
inGoAway bool // we've started to or sent GOAWAY
|
||||||
inFrameScheduleLoop bool // whether we're in the scheduleFrameWrite loop
|
inFrameScheduleLoop bool // whether we're in the scheduleFrameWrite loop
|
||||||
needToSendGoAway bool // we need to schedule a GOAWAY frame write
|
needToSendGoAway bool // we need to schedule a GOAWAY frame write
|
||||||
goAwayCode ErrCode
|
goAwayCode ErrCode
|
||||||
shutdownTimerCh <-chan time.Time // nil until used
|
shutdownTimer *time.Timer // nil until used
|
||||||
shutdownTimer *time.Timer // nil until used
|
idleTimer *time.Timer // nil if unused
|
||||||
idleTimer *time.Timer // nil if unused
|
|
||||||
idleTimerCh <-chan time.Time // nil if unused
|
|
||||||
|
|
||||||
// Owned by the writeFrameAsync goroutine:
|
// Owned by the writeFrameAsync goroutine:
|
||||||
headerWriteBuf bytes.Buffer
|
headerWriteBuf bytes.Buffer
|
||||||
hpackEncoder *hpack.Encoder
|
hpackEncoder *hpack.Encoder
|
||||||
|
|
||||||
|
// Used by startGracefulShutdown.
|
||||||
|
shutdownOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *serverConn) maxHeaderListSize() uint32 {
|
func (sc *serverConn) maxHeaderListSize() uint32 {
|
||||||
|
@ -463,10 +538,10 @@ type stream struct {
|
||||||
numTrailerValues int64
|
numTrailerValues int64
|
||||||
weight uint8
|
weight uint8
|
||||||
state streamState
|
state streamState
|
||||||
resetQueued bool // RST_STREAM queued for write; set by sc.resetStream
|
resetQueued bool // RST_STREAM queued for write; set by sc.resetStream
|
||||||
gotTrailerHeader bool // HEADER frame for trailers was seen
|
gotTrailerHeader bool // HEADER frame for trailers was seen
|
||||||
wroteHeaders bool // whether we wrote headers (not status 100)
|
wroteHeaders bool // whether we wrote headers (not status 100)
|
||||||
reqBuf []byte // if non-nil, body pipe buffer to return later at EOF
|
writeDeadline *time.Timer // nil if unused
|
||||||
|
|
||||||
trailer http.Header // accumulated trailers
|
trailer http.Header // accumulated trailers
|
||||||
reqTrailer http.Header // handler's Request.Trailer
|
reqTrailer http.Header // handler's Request.Trailer
|
||||||
|
@ -696,48 +771,48 @@ func (sc *serverConn) serve() {
|
||||||
{SettingMaxFrameSize, sc.srv.maxReadFrameSize()},
|
{SettingMaxFrameSize, sc.srv.maxReadFrameSize()},
|
||||||
{SettingMaxConcurrentStreams, sc.advMaxStreams},
|
{SettingMaxConcurrentStreams, sc.advMaxStreams},
|
||||||
{SettingMaxHeaderListSize, sc.maxHeaderListSize()},
|
{SettingMaxHeaderListSize, sc.maxHeaderListSize()},
|
||||||
|
{SettingInitialWindowSize, uint32(sc.srv.initialStreamRecvWindowSize())},
|
||||||
// TODO: more actual settings, notably
|
|
||||||
// SettingInitialWindowSize, but then we also
|
|
||||||
// want to bump up the conn window size the
|
|
||||||
// same amount here right after the settings
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
sc.unackedSettings++
|
sc.unackedSettings++
|
||||||
|
|
||||||
|
// Each connection starts with intialWindowSize inflow tokens.
|
||||||
|
// If a higher value is configured, we add more tokens.
|
||||||
|
if diff := sc.srv.initialConnRecvWindowSize() - initialWindowSize; diff > 0 {
|
||||||
|
sc.sendWindowUpdate(nil, int(diff))
|
||||||
|
}
|
||||||
|
|
||||||
if err := sc.readPreface(); err != nil {
|
if err := sc.readPreface(); err != nil {
|
||||||
sc.condlogf(err, "http2: server: error reading preface from client %v: %v", sc.conn.RemoteAddr(), err)
|
sc.condlogf(err, "http2: server: error reading preface from client %v: %v", sc.conn.RemoteAddr(), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Now that we've got the preface, get us out of the
|
// Now that we've got the preface, get us out of the
|
||||||
// "StateNew" state. We can't go directly to idle, though.
|
// "StateNew" state. We can't go directly to idle, though.
|
||||||
// Active means we read some data and anticipate a request. We'll
|
// Active means we read some data and anticipate a request. We'll
|
||||||
// do another Active when we get a HEADERS frame.
|
// do another Active when we get a HEADERS frame.
|
||||||
sc.setConnState(http.StateActive)
|
sc.setConnState(http.StateActive)
|
||||||
sc.setConnState(http.StateIdle)
|
sc.setConnState(http.StateIdle)
|
||||||
|
|
||||||
if sc.srv.IdleTimeout != 0 {
|
if sc.srv.IdleTimeout != 0 {
|
||||||
sc.idleTimer = time.NewTimer(sc.srv.IdleTimeout)
|
sc.idleTimer = time.AfterFunc(sc.srv.IdleTimeout, sc.onIdleTimer)
|
||||||
defer sc.idleTimer.Stop()
|
defer sc.idleTimer.Stop()
|
||||||
sc.idleTimerCh = sc.idleTimer.C
|
|
||||||
}
|
|
||||||
|
|
||||||
var gracefulShutdownCh <-chan struct{}
|
|
||||||
if sc.hs != nil {
|
|
||||||
gracefulShutdownCh = h1ServerShutdownChan(sc.hs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
go sc.readFrames() // closed by defer sc.conn.Close above
|
go sc.readFrames() // closed by defer sc.conn.Close above
|
||||||
|
|
||||||
settingsTimer := time.NewTimer(firstSettingsTimeout)
|
settingsTimer := time.AfterFunc(firstSettingsTimeout, sc.onSettingsTimer)
|
||||||
|
defer settingsTimer.Stop()
|
||||||
|
|
||||||
loopNum := 0
|
loopNum := 0
|
||||||
for {
|
for {
|
||||||
loopNum++
|
loopNum++
|
||||||
select {
|
select {
|
||||||
case wr := <-sc.wantWriteFrameCh:
|
case wr := <-sc.wantWriteFrameCh:
|
||||||
|
if se, ok := wr.write.(StreamError); ok {
|
||||||
|
sc.resetStream(se)
|
||||||
|
break
|
||||||
|
}
|
||||||
sc.writeFrame(wr)
|
sc.writeFrame(wr)
|
||||||
case spr := <-sc.wantStartPushCh:
|
|
||||||
sc.startPush(spr)
|
|
||||||
case res := <-sc.wroteFrameCh:
|
case res := <-sc.wroteFrameCh:
|
||||||
sc.wroteFrame(res)
|
sc.wroteFrame(res)
|
||||||
case res := <-sc.readFrameCh:
|
case res := <-sc.readFrameCh:
|
||||||
|
@ -745,26 +820,37 @@ func (sc *serverConn) serve() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res.readMore()
|
res.readMore()
|
||||||
if settingsTimer.C != nil {
|
if settingsTimer != nil {
|
||||||
settingsTimer.Stop()
|
settingsTimer.Stop()
|
||||||
settingsTimer.C = nil
|
settingsTimer = nil
|
||||||
}
|
}
|
||||||
case m := <-sc.bodyReadCh:
|
case m := <-sc.bodyReadCh:
|
||||||
sc.noteBodyRead(m.st, m.n)
|
sc.noteBodyRead(m.st, m.n)
|
||||||
case <-settingsTimer.C:
|
case msg := <-sc.serveMsgCh:
|
||||||
sc.logf("timeout waiting for SETTINGS frames from %v", sc.conn.RemoteAddr())
|
switch v := msg.(type) {
|
||||||
return
|
case func(int):
|
||||||
case <-gracefulShutdownCh:
|
v(loopNum) // for testing
|
||||||
gracefulShutdownCh = nil
|
case *serverMessage:
|
||||||
sc.startGracefulShutdown()
|
switch v {
|
||||||
case <-sc.shutdownTimerCh:
|
case settingsTimerMsg:
|
||||||
sc.vlogf("GOAWAY close timer fired; closing conn from %v", sc.conn.RemoteAddr())
|
sc.logf("timeout waiting for SETTINGS frames from %v", sc.conn.RemoteAddr())
|
||||||
return
|
return
|
||||||
case <-sc.idleTimerCh:
|
case idleTimerMsg:
|
||||||
sc.vlogf("connection is idle")
|
sc.vlogf("connection is idle")
|
||||||
sc.goAway(ErrCodeNo)
|
sc.goAway(ErrCodeNo)
|
||||||
case fn := <-sc.testHookCh:
|
case shutdownTimerMsg:
|
||||||
fn(loopNum)
|
sc.vlogf("GOAWAY close timer fired; closing conn from %v", sc.conn.RemoteAddr())
|
||||||
|
return
|
||||||
|
case gracefulShutdownMsg:
|
||||||
|
sc.startGracefulShutdownInternal()
|
||||||
|
default:
|
||||||
|
panic("unknown timer")
|
||||||
|
}
|
||||||
|
case *startPushRequest:
|
||||||
|
sc.startPush(v)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unexpected type %T", v))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sc.inGoAway && sc.curOpenStreams() == 0 && !sc.needToSendGoAway && !sc.writingFrame {
|
if sc.inGoAway && sc.curOpenStreams() == 0 && !sc.needToSendGoAway && !sc.writingFrame {
|
||||||
|
@ -773,6 +859,36 @@ func (sc *serverConn) serve() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sc *serverConn) awaitGracefulShutdown(sharedCh <-chan struct{}, privateCh chan struct{}) {
|
||||||
|
select {
|
||||||
|
case <-sc.doneServing:
|
||||||
|
case <-sharedCh:
|
||||||
|
close(privateCh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverMessage int
|
||||||
|
|
||||||
|
// Message values sent to serveMsgCh.
|
||||||
|
var (
|
||||||
|
settingsTimerMsg = new(serverMessage)
|
||||||
|
idleTimerMsg = new(serverMessage)
|
||||||
|
shutdownTimerMsg = new(serverMessage)
|
||||||
|
gracefulShutdownMsg = new(serverMessage)
|
||||||
|
)
|
||||||
|
|
||||||
|
func (sc *serverConn) onSettingsTimer() { sc.sendServeMsg(settingsTimerMsg) }
|
||||||
|
func (sc *serverConn) onIdleTimer() { sc.sendServeMsg(idleTimerMsg) }
|
||||||
|
func (sc *serverConn) onShutdownTimer() { sc.sendServeMsg(shutdownTimerMsg) }
|
||||||
|
|
||||||
|
func (sc *serverConn) sendServeMsg(msg interface{}) {
|
||||||
|
sc.serveG.checkNotOn() // NOT
|
||||||
|
select {
|
||||||
|
case sc.serveMsgCh <- msg:
|
||||||
|
case <-sc.doneServing:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// readPreface reads the ClientPreface greeting from the peer
|
// readPreface reads the ClientPreface greeting from the peer
|
||||||
// or returns an error on timeout or an invalid greeting.
|
// or returns an error on timeout or an invalid greeting.
|
||||||
func (sc *serverConn) readPreface() error {
|
func (sc *serverConn) readPreface() error {
|
||||||
|
@ -1014,7 +1130,11 @@ func (sc *serverConn) wroteFrame(res frameWriteResult) {
|
||||||
// stateClosed after the RST_STREAM frame is
|
// stateClosed after the RST_STREAM frame is
|
||||||
// written.
|
// written.
|
||||||
st.state = stateHalfClosedLocal
|
st.state = stateHalfClosedLocal
|
||||||
sc.resetStream(streamError(st.id, ErrCodeCancel))
|
// Section 8.1: a server MAY request that the client abort
|
||||||
|
// transmission of a request without error by sending a
|
||||||
|
// RST_STREAM with an error code of NO_ERROR after sending
|
||||||
|
// a complete response.
|
||||||
|
sc.resetStream(streamError(st.id, ErrCodeNo))
|
||||||
case stateHalfClosedRemote:
|
case stateHalfClosedRemote:
|
||||||
sc.closeStream(st, errHandlerComplete)
|
sc.closeStream(st, errHandlerComplete)
|
||||||
}
|
}
|
||||||
|
@ -1086,10 +1206,19 @@ func (sc *serverConn) scheduleFrameWrite() {
|
||||||
sc.inFrameScheduleLoop = false
|
sc.inFrameScheduleLoop = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// startGracefulShutdown sends a GOAWAY with ErrCodeNo to tell the
|
// startGracefulShutdown gracefully shuts down a connection. This
|
||||||
// client we're gracefully shutting down. The connection isn't closed
|
// sends GOAWAY with ErrCodeNo to tell the client we're gracefully
|
||||||
// until all current streams are done.
|
// shutting down. The connection isn't closed until all current
|
||||||
|
// streams are done.
|
||||||
|
//
|
||||||
|
// startGracefulShutdown returns immediately; it does not wait until
|
||||||
|
// the connection has shut down.
|
||||||
func (sc *serverConn) startGracefulShutdown() {
|
func (sc *serverConn) startGracefulShutdown() {
|
||||||
|
sc.serveG.checkNotOn() // NOT
|
||||||
|
sc.shutdownOnce.Do(func() { sc.sendServeMsg(gracefulShutdownMsg) })
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *serverConn) startGracefulShutdownInternal() {
|
||||||
sc.goAwayIn(ErrCodeNo, 0)
|
sc.goAwayIn(ErrCodeNo, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1121,8 +1250,7 @@ func (sc *serverConn) goAwayIn(code ErrCode, forceCloseIn time.Duration) {
|
||||||
|
|
||||||
func (sc *serverConn) shutDownIn(d time.Duration) {
|
func (sc *serverConn) shutDownIn(d time.Duration) {
|
||||||
sc.serveG.check()
|
sc.serveG.check()
|
||||||
sc.shutdownTimer = time.NewTimer(d)
|
sc.shutdownTimer = time.AfterFunc(d, sc.onShutdownTimer)
|
||||||
sc.shutdownTimerCh = sc.shutdownTimer.C
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *serverConn) resetStream(se StreamError) {
|
func (sc *serverConn) resetStream(se StreamError) {
|
||||||
|
@ -1305,6 +1433,9 @@ func (sc *serverConn) closeStream(st *stream, err error) {
|
||||||
panic(fmt.Sprintf("invariant; can't close stream in state %v", st.state))
|
panic(fmt.Sprintf("invariant; can't close stream in state %v", st.state))
|
||||||
}
|
}
|
||||||
st.state = stateClosed
|
st.state = stateClosed
|
||||||
|
if st.writeDeadline != nil {
|
||||||
|
st.writeDeadline.Stop()
|
||||||
|
}
|
||||||
if st.isPushed() {
|
if st.isPushed() {
|
||||||
sc.curPushedStreams--
|
sc.curPushedStreams--
|
||||||
} else {
|
} else {
|
||||||
|
@ -1317,7 +1448,7 @@ func (sc *serverConn) closeStream(st *stream, err error) {
|
||||||
sc.idleTimer.Reset(sc.srv.IdleTimeout)
|
sc.idleTimer.Reset(sc.srv.IdleTimeout)
|
||||||
}
|
}
|
||||||
if h1ServerKeepAlivesDisabled(sc.hs) {
|
if h1ServerKeepAlivesDisabled(sc.hs) {
|
||||||
sc.startGracefulShutdown()
|
sc.startGracefulShutdownInternal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if p := st.body; p != nil {
|
if p := st.body; p != nil {
|
||||||
|
@ -1395,9 +1526,9 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
|
||||||
// adjust the size of all stream flow control windows that it
|
// adjust the size of all stream flow control windows that it
|
||||||
// maintains by the difference between the new value and the
|
// maintains by the difference between the new value and the
|
||||||
// old value."
|
// old value."
|
||||||
old := sc.initialWindowSize
|
old := sc.initialStreamSendWindowSize
|
||||||
sc.initialWindowSize = int32(val)
|
sc.initialStreamSendWindowSize = int32(val)
|
||||||
growth := sc.initialWindowSize - old // may be negative
|
growth := int32(val) - old // may be negative
|
||||||
for _, st := range sc.streams {
|
for _, st := range sc.streams {
|
||||||
if !st.flow.add(growth) {
|
if !st.flow.add(growth) {
|
||||||
// 6.9.2 Initial Flow Control Window Size
|
// 6.9.2 Initial Flow Control Window Size
|
||||||
|
@ -1504,7 +1635,7 @@ func (sc *serverConn) processGoAway(f *GoAwayFrame) error {
|
||||||
} else {
|
} else {
|
||||||
sc.vlogf("http2: received GOAWAY %+v, starting graceful shutdown", f)
|
sc.vlogf("http2: received GOAWAY %+v, starting graceful shutdown", f)
|
||||||
}
|
}
|
||||||
sc.startGracefulShutdown()
|
sc.startGracefulShutdownInternal()
|
||||||
// http://tools.ietf.org/html/rfc7540#section-6.8
|
// http://tools.ietf.org/html/rfc7540#section-6.8
|
||||||
// We should not create any new streams, which means we should disable push.
|
// We should not create any new streams, which means we should disable push.
|
||||||
sc.pushEnabled = false
|
sc.pushEnabled = false
|
||||||
|
@ -1543,6 +1674,12 @@ func (st *stream) copyTrailersToHandlerRequest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// onWriteTimeout is run on its own goroutine (from time.AfterFunc)
|
||||||
|
// when the stream's WriteTimeout has fired.
|
||||||
|
func (st *stream) onWriteTimeout() {
|
||||||
|
st.sc.writeFrameFromHandler(FrameWriteRequest{write: streamError(st.id, ErrCodeInternal)})
|
||||||
|
}
|
||||||
|
|
||||||
func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
||||||
sc.serveG.check()
|
sc.serveG.check()
|
||||||
id := f.StreamID
|
id := f.StreamID
|
||||||
|
@ -1719,9 +1856,12 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream
|
||||||
}
|
}
|
||||||
st.cw.Init()
|
st.cw.Init()
|
||||||
st.flow.conn = &sc.flow // link to conn-level counter
|
st.flow.conn = &sc.flow // link to conn-level counter
|
||||||
st.flow.add(sc.initialWindowSize)
|
st.flow.add(sc.initialStreamSendWindowSize)
|
||||||
st.inflow.conn = &sc.inflow // link to conn-level counter
|
st.inflow.conn = &sc.inflow // link to conn-level counter
|
||||||
st.inflow.add(initialWindowSize) // TODO: update this when we send a higher initial window size in the initial settings
|
st.inflow.add(sc.srv.initialStreamRecvWindowSize())
|
||||||
|
if sc.hs.WriteTimeout != 0 {
|
||||||
|
st.writeDeadline = time.AfterFunc(sc.hs.WriteTimeout, st.onWriteTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
sc.streams[id] = st
|
sc.streams[id] = st
|
||||||
sc.writeSched.OpenStream(st.id, OpenStreamOptions{PusherID: pusherID})
|
sc.writeSched.OpenStream(st.id, OpenStreamOptions{PusherID: pusherID})
|
||||||
|
@ -1785,16 +1925,14 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if bodyOpen {
|
if bodyOpen {
|
||||||
st.reqBuf = getRequestBodyBuf()
|
|
||||||
req.Body.(*requestBody).pipe = &pipe{
|
|
||||||
b: &fixedBuffer{buf: st.reqBuf},
|
|
||||||
}
|
|
||||||
|
|
||||||
if vv, ok := rp.header["Content-Length"]; ok {
|
if vv, ok := rp.header["Content-Length"]; ok {
|
||||||
req.ContentLength, _ = strconv.ParseInt(vv[0], 10, 64)
|
req.ContentLength, _ = strconv.ParseInt(vv[0], 10, 64)
|
||||||
} else {
|
} else {
|
||||||
req.ContentLength = -1
|
req.ContentLength = -1
|
||||||
}
|
}
|
||||||
|
req.Body.(*requestBody).pipe = &pipe{
|
||||||
|
b: &dataBuffer{expected: req.ContentLength},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return rw, req, nil
|
return rw, req, nil
|
||||||
}
|
}
|
||||||
|
@ -1890,24 +2028,6 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
|
||||||
return rw, req, nil
|
return rw, req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var reqBodyCache = make(chan []byte, 8)
|
|
||||||
|
|
||||||
func getRequestBodyBuf() []byte {
|
|
||||||
select {
|
|
||||||
case b := <-reqBodyCache:
|
|
||||||
return b
|
|
||||||
default:
|
|
||||||
return make([]byte, initialWindowSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func putRequestBodyBuf(b []byte) {
|
|
||||||
select {
|
|
||||||
case reqBodyCache <- b:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run on its own goroutine.
|
// Run on its own goroutine.
|
||||||
func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler func(http.ResponseWriter, *http.Request)) {
|
func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler func(http.ResponseWriter, *http.Request)) {
|
||||||
didPanic := true
|
didPanic := true
|
||||||
|
@ -2003,12 +2123,6 @@ func (sc *serverConn) noteBodyReadFromHandler(st *stream, n int, err error) {
|
||||||
case <-sc.doneServing:
|
case <-sc.doneServing:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == io.EOF {
|
|
||||||
if buf := st.reqBuf; buf != nil {
|
|
||||||
st.reqBuf = nil // shouldn't matter; field unused by other
|
|
||||||
putRequestBodyBuf(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *serverConn) noteBodyRead(st *stream, n int) {
|
func (sc *serverConn) noteBodyRead(st *stream, n int) {
|
||||||
|
@ -2103,8 +2217,8 @@ func (b *requestBody) Read(p []byte) (n int, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// responseWriter is the http.ResponseWriter implementation. It's
|
// responseWriter is the http.ResponseWriter implementation. It's
|
||||||
// intentionally small (1 pointer wide) to minimize garbage. The
|
// intentionally small (1 pointer wide) to minimize garbage. The
|
||||||
// responseWriterState pointer inside is zeroed at the end of a
|
// responseWriterState pointer inside is zeroed at the end of a
|
||||||
// request (in handlerDone) and calls on the responseWriter thereafter
|
// request (in handlerDone) and calls on the responseWriter thereafter
|
||||||
// simply crash (caller's mistake), but the much larger responseWriterState
|
// simply crash (caller's mistake), but the much larger responseWriterState
|
||||||
|
@ -2278,7 +2392,7 @@ const TrailerPrefix = "Trailer:"
|
||||||
// says you SHOULD (but not must) predeclare any trailers in the
|
// says you SHOULD (but not must) predeclare any trailers in the
|
||||||
// header, the official ResponseWriter rules said trailers in Go must
|
// header, the official ResponseWriter rules said trailers in Go must
|
||||||
// be predeclared, and then we reuse the same ResponseWriter.Header()
|
// be predeclared, and then we reuse the same ResponseWriter.Header()
|
||||||
// map to mean both Headers and Trailers. When it's time to write the
|
// map to mean both Headers and Trailers. When it's time to write the
|
||||||
// Trailers, we pick out the fields of Headers that were declared as
|
// Trailers, we pick out the fields of Headers that were declared as
|
||||||
// trailers. That worked for a while, until we found the first major
|
// trailers. That worked for a while, until we found the first major
|
||||||
// user of Trailers in the wild: gRPC (using them only over http2),
|
// user of Trailers in the wild: gRPC (using them only over http2),
|
||||||
|
@ -2514,7 +2628,7 @@ func (w *responseWriter) push(target string, opts pushOptions) error {
|
||||||
return fmt.Errorf("method %q must be GET or HEAD", opts.Method)
|
return fmt.Errorf("method %q must be GET or HEAD", opts.Method)
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := startPushRequest{
|
msg := &startPushRequest{
|
||||||
parent: st,
|
parent: st,
|
||||||
method: opts.Method,
|
method: opts.Method,
|
||||||
url: u,
|
url: u,
|
||||||
|
@ -2527,7 +2641,7 @@ func (w *responseWriter) push(target string, opts pushOptions) error {
|
||||||
return errClientDisconnected
|
return errClientDisconnected
|
||||||
case <-st.cw:
|
case <-st.cw:
|
||||||
return errStreamClosed
|
return errStreamClosed
|
||||||
case sc.wantStartPushCh <- msg:
|
case sc.serveMsgCh <- msg:
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
@ -2549,7 +2663,7 @@ type startPushRequest struct {
|
||||||
done chan error
|
done chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *serverConn) startPush(msg startPushRequest) {
|
func (sc *serverConn) startPush(msg *startPushRequest) {
|
||||||
sc.serveG.check()
|
sc.serveG.check()
|
||||||
|
|
||||||
// http://tools.ietf.org/html/rfc7540#section-6.6.
|
// http://tools.ietf.org/html/rfc7540#section-6.6.
|
||||||
|
@ -2588,7 +2702,7 @@ func (sc *serverConn) startPush(msg startPushRequest) {
|
||||||
// A server that is unable to establish a new stream identifier can send a GOAWAY
|
// A server that is unable to establish a new stream identifier can send a GOAWAY
|
||||||
// frame so that the client is forced to open a new connection for new streams.
|
// frame so that the client is forced to open a new connection for new streams.
|
||||||
if sc.maxPushPromiseID+2 >= 1<<31 {
|
if sc.maxPushPromiseID+2 >= 1<<31 {
|
||||||
sc.startGracefulShutdown()
|
sc.startGracefulShutdownInternal()
|
||||||
return 0, ErrPushLimitReached
|
return 0, ErrPushLimitReached
|
||||||
}
|
}
|
||||||
sc.maxPushPromiseID += 2
|
sc.maxPushPromiseID += 2
|
||||||
|
@ -2713,31 +2827,6 @@ var badTrailer = map[string]bool{
|
||||||
"Www-Authenticate": true,
|
"Www-Authenticate": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// h1ServerShutdownChan returns a channel that will be closed when the
|
|
||||||
// provided *http.Server wants to shut down.
|
|
||||||
//
|
|
||||||
// This is a somewhat hacky way to get at http1 innards. It works
|
|
||||||
// when the http2 code is bundled into the net/http package in the
|
|
||||||
// standard library. The alternatives ended up making the cmd/go tool
|
|
||||||
// depend on http Servers. This is the lightest option for now.
|
|
||||||
// This is tested via the TestServeShutdown* tests in net/http.
|
|
||||||
func h1ServerShutdownChan(hs *http.Server) <-chan struct{} {
|
|
||||||
if fn := testh1ServerShutdownChan; fn != nil {
|
|
||||||
return fn(hs)
|
|
||||||
}
|
|
||||||
var x interface{} = hs
|
|
||||||
type I interface {
|
|
||||||
getDoneChan() <-chan struct{}
|
|
||||||
}
|
|
||||||
if hs, ok := x.(I); ok {
|
|
||||||
return hs.getDoneChan()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// optional test hook for h1ServerShutdownChan.
|
|
||||||
var testh1ServerShutdownChan func(hs *http.Server) <-chan struct{}
|
|
||||||
|
|
||||||
// h1ServerKeepAlivesDisabled reports whether hs has its keep-alives
|
// h1ServerKeepAlivesDisabled reports whether hs has its keep-alives
|
||||||
// disabled. See comments on h1ServerShutdownChan above for why
|
// disabled. See comments on h1ServerShutdownChan above for why
|
||||||
// the code is written this way.
|
// the code is written this way.
|
||||||
|
|
|
@ -575,7 +575,7 @@ func (cc *ClientConn) canTakeNewRequestLocked() bool {
|
||||||
cc.nextStreamID < math.MaxInt32
|
cc.nextStreamID < math.MaxInt32
|
||||||
}
|
}
|
||||||
|
|
||||||
// onIdleTimeout is called from a time.AfterFunc goroutine. It will
|
// onIdleTimeout is called from a time.AfterFunc goroutine. It will
|
||||||
// only be called when we're idle, but because we're coming from a new
|
// only be called when we're idle, but because we're coming from a new
|
||||||
// goroutine, there could be a new request coming in at the same time,
|
// goroutine, there could be a new request coming in at the same time,
|
||||||
// so this simply calls the synchronized closeIfIdle to shut down this
|
// so this simply calls the synchronized closeIfIdle to shut down this
|
||||||
|
@ -809,8 +809,8 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
// 2xx, however, then assume the server DOES potentially
|
// 2xx, however, then assume the server DOES potentially
|
||||||
// want our body (e.g. full-duplex streaming:
|
// want our body (e.g. full-duplex streaming:
|
||||||
// golang.org/issue/13444). If it turns out the server
|
// golang.org/issue/13444). If it turns out the server
|
||||||
// doesn't, they'll RST_STREAM us soon enough. This is a
|
// doesn't, they'll RST_STREAM us soon enough. This is a
|
||||||
// heuristic to avoid adding knobs to Transport. Hopefully
|
// heuristic to avoid adding knobs to Transport. Hopefully
|
||||||
// we can keep it.
|
// we can keep it.
|
||||||
bodyWriter.cancel()
|
bodyWriter.cancel()
|
||||||
cs.abortRequestBodyWrite(errStopReqBodyWrite)
|
cs.abortRequestBodyWrite(errStopReqBodyWrite)
|
||||||
|
@ -1528,8 +1528,7 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer) // TODO(bradfitz): recycle this garbage
|
cs.bufPipe = pipe{b: &dataBuffer{expected: res.ContentLength}}
|
||||||
cs.bufPipe = pipe{b: buf}
|
|
||||||
cs.bytesRemain = res.ContentLength
|
cs.bytesRemain = res.ContentLength
|
||||||
res.Body = transportResponseBody{cs}
|
res.Body = transportResponseBody{cs}
|
||||||
go cs.awaitRequestCancel(cs.req)
|
go cs.awaitRequestCancel(cs.req)
|
||||||
|
@ -1656,6 +1655,7 @@ func (b transportResponseBody) Close() error {
|
||||||
cc.wmu.Lock()
|
cc.wmu.Lock()
|
||||||
if !serverSentStreamEnd {
|
if !serverSentStreamEnd {
|
||||||
cc.fr.WriteRSTStream(cs.ID, ErrCodeCancel)
|
cc.fr.WriteRSTStream(cs.ID, ErrCodeCancel)
|
||||||
|
cs.didReset = true
|
||||||
}
|
}
|
||||||
// Return connection-level flow control.
|
// Return connection-level flow control.
|
||||||
if unread > 0 {
|
if unread > 0 {
|
||||||
|
@ -1703,12 +1703,6 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if f.Length > 0 {
|
if f.Length > 0 {
|
||||||
if len(data) > 0 && cs.bufPipe.b == nil {
|
|
||||||
// Data frame after it's already closed?
|
|
||||||
cc.logf("http2: Transport received DATA frame for closed stream; closing connection")
|
|
||||||
return ConnectionError(ErrCodeProtocol)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check connection-level flow control.
|
// Check connection-level flow control.
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
if cs.inflow.available() >= int32(f.Length) {
|
if cs.inflow.available() >= int32(f.Length) {
|
||||||
|
|
|
@ -53,7 +53,7 @@ type PriorityWriteSchedulerConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPriorityWriteScheduler constructs a WriteScheduler that schedules
|
// NewPriorityWriteScheduler constructs a WriteScheduler that schedules
|
||||||
// frames by following HTTP/2 priorities as described in RFC 7340 Section 5.3.
|
// frames by following HTTP/2 priorities as described in RFC 7540 Section 5.3.
|
||||||
// If cfg is nil, default options are used.
|
// If cfg is nil, default options are used.
|
||||||
func NewPriorityWriteScheduler(cfg *PriorityWriteSchedulerConfig) WriteScheduler {
|
func NewPriorityWriteScheduler(cfg *PriorityWriteSchedulerConfig) WriteScheduler {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copied from the golang.org/x/text repo; DO NOT EDIT
|
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||||
|
|
||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
|
@ -39,27 +39,24 @@ import (
|
||||||
// error in the future.
|
// error in the future.
|
||||||
// I think Option 1 is best, but it is quite opinionated.
|
// I think Option 1 is best, but it is quite opinionated.
|
||||||
|
|
||||||
// ToASCII converts a domain or domain label to its ASCII form. For example,
|
// ToASCII is a wrapper for Punycode.ToASCII.
|
||||||
// ToASCII("bücher.example.com") is "xn--bcher-kva.example.com", and
|
|
||||||
// ToASCII("golang") is "golang". If an error is encountered it will return
|
|
||||||
// an error and a (partially) processed result.
|
|
||||||
func ToASCII(s string) (string, error) {
|
func ToASCII(s string) (string, error) {
|
||||||
return Resolve.process(s, true)
|
return Punycode.process(s, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToUnicode converts a domain or domain label to its Unicode form. For example,
|
// ToUnicode is a wrapper for Punycode.ToUnicode.
|
||||||
// ToUnicode("xn--bcher-kva.example.com") is "bücher.example.com", and
|
|
||||||
// ToUnicode("golang") is "golang". If an error is encountered it will return
|
|
||||||
// an error and a (partially) processed result.
|
|
||||||
func ToUnicode(s string) (string, error) {
|
func ToUnicode(s string) (string, error) {
|
||||||
return NonTransitional.process(s, false)
|
return Punycode.process(s, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An Option configures a Profile at creation time.
|
// An Option configures a Profile at creation time.
|
||||||
type Option func(*options)
|
type Option func(*options)
|
||||||
|
|
||||||
// Transitional sets a Profile to use the Transitional mapping as defined
|
// Transitional sets a Profile to use the Transitional mapping as defined in UTS
|
||||||
// in UTS #46.
|
// #46. This will cause, for example, "ß" to be mapped to "ss". Using the
|
||||||
|
// transitional mapping provides a compromise between IDNA2003 and IDNA2008
|
||||||
|
// compatibility. It is used by most browsers when resolving domain names. This
|
||||||
|
// option is only meaningful if combined with MapForLookup.
|
||||||
func Transitional(transitional bool) Option {
|
func Transitional(transitional bool) Option {
|
||||||
return func(o *options) { o.transitional = true }
|
return func(o *options) { o.transitional = true }
|
||||||
}
|
}
|
||||||
|
@ -70,19 +67,93 @@ func VerifyDNSLength(verify bool) Option {
|
||||||
return func(o *options) { o.verifyDNSLength = verify }
|
return func(o *options) { o.verifyDNSLength = verify }
|
||||||
}
|
}
|
||||||
|
|
||||||
// IgnoreSTD3Rules sets whether ASCII characters outside the A-Z, a-z, 0-9 and
|
// ValidateLabels sets whether to check the mandatory label validation criteria
|
||||||
// the hyphen should be allowed. By default this is not allowed, but IDNA2003,
|
// as defined in Section 5.4 of RFC 5891. This includes testing for correct use
|
||||||
// and as a consequence UTS #46, allows this to be overridden to support
|
// of hyphens ('-'), normalization, validity of runes, and the context rules.
|
||||||
// browsers that allow characters outside this range, for example a '_' (U+005F
|
func ValidateLabels(enable bool) Option {
|
||||||
// LOW LINE). See http://www.rfc- editor.org/std/std3.txt for more details.
|
return func(o *options) {
|
||||||
func IgnoreSTD3Rules(ignore bool) Option {
|
// Don't override existing mappings, but set one that at least checks
|
||||||
return func(o *options) { o.ignoreSTD3Rules = ignore }
|
// normalization if it is not set.
|
||||||
|
if o.mapping == nil && enable {
|
||||||
|
o.mapping = normalize
|
||||||
|
}
|
||||||
|
o.trie = trie
|
||||||
|
o.validateLabels = enable
|
||||||
|
o.fromPuny = validateFromPunycode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrictDomainName limits the set of permissable ASCII characters to those
|
||||||
|
// allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the
|
||||||
|
// hyphen). This is set by default for MapForLookup and ValidateForRegistration.
|
||||||
|
//
|
||||||
|
// This option is useful, for instance, for browsers that allow characters
|
||||||
|
// outside this range, for example a '_' (U+005F LOW LINE). See
|
||||||
|
// http://www.rfc-editor.org/std/std3.txt for more details This option
|
||||||
|
// corresponds to the UseSTD3ASCIIRules option in UTS #46.
|
||||||
|
func StrictDomainName(use bool) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.trie = trie
|
||||||
|
o.useSTD3Rules = use
|
||||||
|
o.fromPuny = validateFromPunycode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: the following options pull in tables. The tables should not be linked
|
||||||
|
// in as long as the options are not used.
|
||||||
|
|
||||||
|
// BidiRule enables the Bidi rule as defined in RFC 5893. Any application
|
||||||
|
// that relies on proper validation of labels should include this rule.
|
||||||
|
func BidiRule() Option {
|
||||||
|
return func(o *options) { o.bidirule = bidirule.ValidString }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateForRegistration sets validation options to verify that a given IDN is
|
||||||
|
// properly formatted for registration as defined by Section 4 of RFC 5891.
|
||||||
|
func ValidateForRegistration() Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.mapping = validateRegistration
|
||||||
|
StrictDomainName(true)(o)
|
||||||
|
ValidateLabels(true)(o)
|
||||||
|
VerifyDNSLength(true)(o)
|
||||||
|
BidiRule()(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapForLookup sets validation and mapping options such that a given IDN is
|
||||||
|
// transformed for domain name lookup according to the requirements set out in
|
||||||
|
// Section 5 of RFC 5891. The mappings follow the recommendations of RFC 5894,
|
||||||
|
// RFC 5895 and UTS 46. It does not add the Bidi Rule. Use the BidiRule option
|
||||||
|
// to add this check.
|
||||||
|
//
|
||||||
|
// The mappings include normalization and mapping case, width and other
|
||||||
|
// compatibility mappings.
|
||||||
|
func MapForLookup() Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.mapping = validateAndMap
|
||||||
|
StrictDomainName(true)(o)
|
||||||
|
ValidateLabels(true)(o)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
transitional bool
|
transitional bool
|
||||||
ignoreSTD3Rules bool
|
useSTD3Rules bool
|
||||||
|
validateLabels bool
|
||||||
verifyDNSLength bool
|
verifyDNSLength bool
|
||||||
|
|
||||||
|
trie *idnaTrie
|
||||||
|
|
||||||
|
// fromPuny calls validation rules when converting A-labels to U-labels.
|
||||||
|
fromPuny func(p *Profile, s string) error
|
||||||
|
|
||||||
|
// mapping implements a validation and mapping step as defined in RFC 5895
|
||||||
|
// or UTS 46, tailored to, for example, domain registration or lookup.
|
||||||
|
mapping func(p *Profile, s string) (string, error)
|
||||||
|
|
||||||
|
// bidirule, if specified, checks whether s conforms to the Bidi Rule
|
||||||
|
// defined in RFC 5893.
|
||||||
|
bidirule func(s string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Profile defines the configuration of a IDNA mapper.
|
// A Profile defines the configuration of a IDNA mapper.
|
||||||
|
@ -97,8 +168,13 @@ func apply(o *options, opts []Option) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Profile.
|
// New creates a new Profile.
|
||||||
// With no options, the returned profile is the non-transitional profile as
|
//
|
||||||
// defined in UTS #46.
|
// With no options, the returned Profile is the most permissive and equals the
|
||||||
|
// Punycode Profile. Options can be passed to further restrict the Profile. The
|
||||||
|
// MapForLookup and ValidateForRegistration options set a collection of options,
|
||||||
|
// for lookup and registration purposes respectively, which can be tailored by
|
||||||
|
// adding more fine-grained options, where later options override earlier
|
||||||
|
// options.
|
||||||
func New(o ...Option) *Profile {
|
func New(o ...Option) *Profile {
|
||||||
p := &Profile{}
|
p := &Profile{}
|
||||||
apply(&p.options, o)
|
apply(&p.options, o)
|
||||||
|
@ -132,33 +208,67 @@ func (p *Profile) String() string {
|
||||||
} else {
|
} else {
|
||||||
s = "NonTransitional"
|
s = "NonTransitional"
|
||||||
}
|
}
|
||||||
if p.ignoreSTD3Rules {
|
if p.useSTD3Rules {
|
||||||
s += ":NoSTD3Rules"
|
s += ":UseSTD3Rules"
|
||||||
|
}
|
||||||
|
if p.validateLabels {
|
||||||
|
s += ":ValidateLabels"
|
||||||
|
}
|
||||||
|
if p.verifyDNSLength {
|
||||||
|
s += ":VerifyDNSLength"
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Resolve is the recommended profile for resolving domain names.
|
// Punycode is a Profile that does raw punycode processing with a minimum
|
||||||
// The configuration of this profile may change over time.
|
// of validation.
|
||||||
Resolve = resolve
|
Punycode *Profile = punycode
|
||||||
|
|
||||||
|
// Lookup is the recommended profile for looking up domain names, according
|
||||||
|
// to Section 5 of RFC 5891. The exact configuration of this profile may
|
||||||
|
// change over time.
|
||||||
|
Lookup *Profile = lookup
|
||||||
|
|
||||||
// Display is the recommended profile for displaying domain names.
|
// Display is the recommended profile for displaying domain names.
|
||||||
// The configuration of this profile may change over time.
|
// The configuration of this profile may change over time.
|
||||||
Display = display
|
Display *Profile = display
|
||||||
|
|
||||||
// NonTransitional defines a profile that implements the Transitional
|
// Registration is the recommended profile for checking whether a given
|
||||||
// mapping as defined in UTS #46 with no additional constraints.
|
// IDN is valid for registration, according to Section 4 of RFC 5891.
|
||||||
NonTransitional = nonTransitional
|
Registration *Profile = registration
|
||||||
|
|
||||||
resolve = &Profile{options{transitional: true}}
|
punycode = &Profile{}
|
||||||
display = &Profile{}
|
lookup = &Profile{options{
|
||||||
nonTransitional = &Profile{}
|
transitional: true,
|
||||||
|
useSTD3Rules: true,
|
||||||
|
validateLabels: true,
|
||||||
|
trie: trie,
|
||||||
|
fromPuny: validateFromPunycode,
|
||||||
|
mapping: validateAndMap,
|
||||||
|
bidirule: bidirule.ValidString,
|
||||||
|
}}
|
||||||
|
display = &Profile{options{
|
||||||
|
useSTD3Rules: true,
|
||||||
|
validateLabels: true,
|
||||||
|
trie: trie,
|
||||||
|
fromPuny: validateFromPunycode,
|
||||||
|
mapping: validateAndMap,
|
||||||
|
bidirule: bidirule.ValidString,
|
||||||
|
}}
|
||||||
|
registration = &Profile{options{
|
||||||
|
useSTD3Rules: true,
|
||||||
|
validateLabels: true,
|
||||||
|
verifyDNSLength: true,
|
||||||
|
trie: trie,
|
||||||
|
fromPuny: validateFromPunycode,
|
||||||
|
mapping: validateRegistration,
|
||||||
|
bidirule: bidirule.ValidString,
|
||||||
|
}}
|
||||||
|
|
||||||
// TODO: profiles
|
// TODO: profiles
|
||||||
// V2008: strict IDNA2008
|
// Register: recommended for approving domain names: don't do any mappings
|
||||||
// Register: recommended for approving domain names: nontransitional, but
|
// but rather reject on invalid input. Bundle or block deviation characters.
|
||||||
// bundle or block deviation characters.
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type labelError struct{ label, code_ string }
|
type labelError struct{ label, code_ string }
|
||||||
|
@ -178,12 +288,117 @@ func (e runeError) Error() string {
|
||||||
// process implements the algorithm described in section 4 of UTS #46,
|
// process implements the algorithm described in section 4 of UTS #46,
|
||||||
// see http://www.unicode.org/reports/tr46.
|
// see http://www.unicode.org/reports/tr46.
|
||||||
func (p *Profile) process(s string, toASCII bool) (string, error) {
|
func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||||
|
var err error
|
||||||
|
if p.mapping != nil {
|
||||||
|
s, err = p.mapping(p, s)
|
||||||
|
}
|
||||||
|
// Remove leading empty labels.
|
||||||
|
for ; len(s) > 0 && s[0] == '.'; s = s[1:] {
|
||||||
|
}
|
||||||
|
// It seems like we should only create this error on ToASCII, but the
|
||||||
|
// UTS 46 conformance tests suggests we should always check this.
|
||||||
|
if err == nil && p.verifyDNSLength && s == "" {
|
||||||
|
err = &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
labels := labelIter{orig: s}
|
||||||
|
for ; !labels.done(); labels.next() {
|
||||||
|
label := labels.label()
|
||||||
|
if label == "" {
|
||||||
|
// Empty labels are not okay. The label iterator skips the last
|
||||||
|
// label if it is empty.
|
||||||
|
if err == nil && p.verifyDNSLength {
|
||||||
|
err = &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(label, acePrefix) {
|
||||||
|
u, err2 := decode(label[len(acePrefix):])
|
||||||
|
if err2 != nil {
|
||||||
|
if err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
// Spec says keep the old label.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
labels.set(u)
|
||||||
|
if err == nil && p.validateLabels {
|
||||||
|
err = p.fromPuny(p, u)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
// This should be called on NonTransitional, according to the
|
||||||
|
// spec, but that currently does not have any effect. Use the
|
||||||
|
// original profile to preserve options.
|
||||||
|
err = p.validateLabel(u)
|
||||||
|
}
|
||||||
|
} else if err == nil {
|
||||||
|
err = p.validateLabel(label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if toASCII {
|
||||||
|
for labels.reset(); !labels.done(); labels.next() {
|
||||||
|
label := labels.label()
|
||||||
|
if !ascii(label) {
|
||||||
|
a, err2 := encode(acePrefix, label)
|
||||||
|
if err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
label = a
|
||||||
|
labels.set(a)
|
||||||
|
}
|
||||||
|
n := len(label)
|
||||||
|
if p.verifyDNSLength && err == nil && (n == 0 || n > 63) {
|
||||||
|
err = &labelError{label, "A4"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = labels.result()
|
||||||
|
if toASCII && p.verifyDNSLength && err == nil {
|
||||||
|
// Compute the length of the domain name minus the root label and its dot.
|
||||||
|
n := len(s)
|
||||||
|
if n > 0 && s[n-1] == '.' {
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
if len(s) < 1 || n > 253 {
|
||||||
|
err = &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalize(p *Profile, s string) (string, error) {
|
||||||
|
return norm.NFC.String(s), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateRegistration(p *Profile, s string) (string, error) {
|
||||||
|
if !norm.NFC.IsNormalString(s) {
|
||||||
|
return s, &labelError{s, "V1"}
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
for i := 0; i < len(s); {
|
||||||
|
v, sz := trie.lookupString(s[i:])
|
||||||
|
i += sz
|
||||||
|
// Copy bytes not copied so far.
|
||||||
|
switch p.simplify(info(v).category()) {
|
||||||
|
// TODO: handle the NV8 defined in the Unicode idna data set to allow
|
||||||
|
// for strict conformance to IDNA2008.
|
||||||
|
case valid, deviation:
|
||||||
|
case disallowed, mapped, unknown, ignored:
|
||||||
|
if err == nil {
|
||||||
|
r, _ := utf8.DecodeRuneInString(s[i:])
|
||||||
|
err = runeError(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateAndMap(p *Profile, s string) (string, error) {
|
||||||
var (
|
var (
|
||||||
b []byte
|
err error
|
||||||
err error
|
b []byte
|
||||||
k, i int
|
k int
|
||||||
)
|
)
|
||||||
for i < len(s) {
|
for i := 0; i < len(s); {
|
||||||
v, sz := trie.lookupString(s[i:])
|
v, sz := trie.lookupString(s[i:])
|
||||||
start := i
|
start := i
|
||||||
i += sz
|
i += sz
|
||||||
|
@ -220,71 +435,6 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||||
// TODO: the punycode converters require strings as input.
|
// TODO: the punycode converters require strings as input.
|
||||||
s = string(b)
|
s = string(b)
|
||||||
}
|
}
|
||||||
// Remove leading empty labels
|
|
||||||
for ; len(s) > 0 && s[0] == '.'; s = s[1:] {
|
|
||||||
}
|
|
||||||
if s == "" {
|
|
||||||
return "", &labelError{s, "A4"}
|
|
||||||
}
|
|
||||||
labels := labelIter{orig: s}
|
|
||||||
for ; !labels.done(); labels.next() {
|
|
||||||
label := labels.label()
|
|
||||||
if label == "" {
|
|
||||||
// Empty labels are not okay. The label iterator skips the last
|
|
||||||
// label if it is empty.
|
|
||||||
if err == nil {
|
|
||||||
err = &labelError{s, "A4"}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(label, acePrefix) {
|
|
||||||
u, err2 := decode(label[len(acePrefix):])
|
|
||||||
if err2 != nil {
|
|
||||||
if err == nil {
|
|
||||||
err = err2
|
|
||||||
}
|
|
||||||
// Spec says keep the old label.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
labels.set(u)
|
|
||||||
if err == nil {
|
|
||||||
err = p.validateFromPunycode(u)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
err = NonTransitional.validate(u)
|
|
||||||
}
|
|
||||||
} else if err == nil {
|
|
||||||
err = p.validate(label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if toASCII {
|
|
||||||
for labels.reset(); !labels.done(); labels.next() {
|
|
||||||
label := labels.label()
|
|
||||||
if !ascii(label) {
|
|
||||||
a, err2 := encode(acePrefix, label)
|
|
||||||
if err == nil {
|
|
||||||
err = err2
|
|
||||||
}
|
|
||||||
label = a
|
|
||||||
labels.set(a)
|
|
||||||
}
|
|
||||||
n := len(label)
|
|
||||||
if p.verifyDNSLength && err == nil && (n == 0 || n > 63) {
|
|
||||||
err = &labelError{label, "A4"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s = labels.result()
|
|
||||||
if toASCII && p.verifyDNSLength && err == nil {
|
|
||||||
// Compute the length of the domain name minus the root label and its dot.
|
|
||||||
n := len(s)
|
|
||||||
if n > 0 && s[n-1] == '.' {
|
|
||||||
n--
|
|
||||||
}
|
|
||||||
if len(s) < 1 || n > 253 {
|
|
||||||
err = &labelError{s, "A4"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s, err
|
return s, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,13 +504,13 @@ const acePrefix = "xn--"
|
||||||
func (p *Profile) simplify(cat category) category {
|
func (p *Profile) simplify(cat category) category {
|
||||||
switch cat {
|
switch cat {
|
||||||
case disallowedSTD3Mapped:
|
case disallowedSTD3Mapped:
|
||||||
if !p.ignoreSTD3Rules {
|
if p.useSTD3Rules {
|
||||||
cat = disallowed
|
cat = disallowed
|
||||||
} else {
|
} else {
|
||||||
cat = mapped
|
cat = mapped
|
||||||
}
|
}
|
||||||
case disallowedSTD3Valid:
|
case disallowedSTD3Valid:
|
||||||
if !p.ignoreSTD3Rules {
|
if p.useSTD3Rules {
|
||||||
cat = disallowed
|
cat = disallowed
|
||||||
} else {
|
} else {
|
||||||
cat = valid
|
cat = valid
|
||||||
|
@ -376,7 +526,7 @@ func (p *Profile) simplify(cat category) category {
|
||||||
return cat
|
return cat
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Profile) validateFromPunycode(s string) error {
|
func validateFromPunycode(p *Profile, s string) error {
|
||||||
if !norm.NFC.IsNormalString(s) {
|
if !norm.NFC.IsNormalString(s) {
|
||||||
return &labelError{s, "V1"}
|
return &labelError{s, "V1"}
|
||||||
}
|
}
|
||||||
|
@ -452,9 +602,22 @@ var joinStates = [][numJoinTypes]joinState{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate validates the criteria from Section 4.1. Item 1, 4, and 6 are
|
// validateLabel validates the criteria from Section 4.1. Item 1, 4, and 6 are
|
||||||
// already implicitly satisfied by the overall implementation.
|
// already implicitly satisfied by the overall implementation.
|
||||||
func (p *Profile) validate(s string) error {
|
func (p *Profile) validateLabel(s string) error {
|
||||||
|
if s == "" {
|
||||||
|
if p.verifyDNSLength {
|
||||||
|
return &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if p.bidirule != nil && !p.bidirule(s) {
|
||||||
|
return &labelError{s, "B"}
|
||||||
|
}
|
||||||
|
if !p.validateLabels {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
trie := p.trie // p.validateLabels is only set if trie is set.
|
||||||
if len(s) > 4 && s[2] == '-' && s[3] == '-' {
|
if len(s) > 4 && s[2] == '-' && s[3] == '-' {
|
||||||
return &labelError{s, "V2"}
|
return &labelError{s, "V2"}
|
||||||
}
|
}
|
||||||
|
@ -467,9 +630,6 @@ func (p *Profile) validate(s string) error {
|
||||||
if x.isModifier() {
|
if x.isModifier() {
|
||||||
return &labelError{s, "V5"}
|
return &labelError{s, "V5"}
|
||||||
}
|
}
|
||||||
if !bidirule.ValidString(s) {
|
|
||||||
return &labelError{s, "B"}
|
|
||||||
}
|
|
||||||
// Quickly return in the absence of zero-width (non) joiners.
|
// Quickly return in the absence of zero-width (non) joiners.
|
||||||
if strings.Index(s, zwj) == -1 && strings.Index(s, zwnj) == -1 {
|
if strings.Index(s, zwj) == -1 && strings.Index(s, zwnj) == -1 {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copied from the golang.org/x/text repo; DO NOT EDIT
|
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||||
|
|
||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
// Copied from the golang.org/x/text repo; DO NOT EDIT
|
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||||
|
|
||||||
// This file was generated by go generate; DO NOT EDIT
|
|
||||||
|
|
||||||
package idna
|
package idna
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copied from the golang.org/x/text repo; DO NOT EDIT
|
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||||
|
|
||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
|
@ -44,7 +44,8 @@ var idnaSparse = sparseBlocks{
|
||||||
offset: idnaSparseOffset[:],
|
offset: idnaSparseOffset[:],
|
||||||
}
|
}
|
||||||
|
|
||||||
var trie = newIdnaTrie(0)
|
// Don't use newIdnaTrie to avoid unconditional linking in of the table.
|
||||||
|
var trie = &idnaTrie{}
|
||||||
|
|
||||||
// lookup determines the type of block n and looks up the value for b.
|
// lookup determines the type of block n and looks up the value for b.
|
||||||
// For n < t.cutoff, the block is a simple lookup table. Otherwise, the block
|
// For n < t.cutoff, the block is a simple lookup table. Otherwise, the block
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
// Copied from the golang.org/x/text repo; DO NOT EDIT
|
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||||
|
|
||||||
// This file was generated by go generate; DO NOT EDIT
|
|
||||||
|
|
||||||
package idna
|
package idna
|
||||||
|
|
||||||
|
|
|
@ -371,7 +371,7 @@ func (ts *timeSeries) ComputeRange(start, finish time.Time, num int) []Observabl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Failed to find a level that covers the desired range. So just
|
// Failed to find a level that covers the desired range. So just
|
||||||
// extract from the last level, even if it doesn't cover the entire
|
// extract from the last level, even if it doesn't cover the entire
|
||||||
// desired range.
|
// desired range.
|
||||||
ts.extract(ts.levels[len(ts.levels)-1], start, finish, num, results)
|
ts.extract(ts.levels[len(ts.levels)-1], start, finish, num, results)
|
||||||
|
|
|
@ -72,24 +72,28 @@ func (s *socks5) Dial(network, addr string) (net.Conn, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
closeConn := &conn
|
if err := s.connect(conn, addr); err != nil {
|
||||||
defer func() {
|
conn.Close()
|
||||||
if closeConn != nil {
|
|
||||||
(*closeConn).Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
host, portStr, err := net.SplitHostPort(addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect takes an existing connection to a socks5 proxy server,
|
||||||
|
// and commands the server to extend that connection to target,
|
||||||
|
// which must be a canonical address with a host and port.
|
||||||
|
func (s *socks5) connect(conn net.Conn, target string) error {
|
||||||
|
host, portStr, err := net.SplitHostPort(target)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
port, err := strconv.Atoi(portStr)
|
port, err := strconv.Atoi(portStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("proxy: failed to parse port number: " + portStr)
|
return errors.New("proxy: failed to parse port number: " + portStr)
|
||||||
}
|
}
|
||||||
if port < 1 || port > 0xffff {
|
if port < 1 || port > 0xffff {
|
||||||
return nil, errors.New("proxy: port number out of range: " + portStr)
|
return errors.New("proxy: port number out of range: " + portStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// the size here is just an estimate
|
// the size here is just an estimate
|
||||||
|
@ -103,17 +107,17 @@ func (s *socks5) Dial(network, addr string) (net.Conn, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := conn.Write(buf); err != nil {
|
if _, err := conn.Write(buf); err != nil {
|
||||||
return nil, errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||||
return nil, errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
}
|
}
|
||||||
if buf[0] != 5 {
|
if buf[0] != 5 {
|
||||||
return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
|
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
|
||||||
}
|
}
|
||||||
if buf[1] == 0xff {
|
if buf[1] == 0xff {
|
||||||
return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
|
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
|
||||||
}
|
}
|
||||||
|
|
||||||
if buf[1] == socks5AuthPassword {
|
if buf[1] == socks5AuthPassword {
|
||||||
|
@ -125,15 +129,15 @@ func (s *socks5) Dial(network, addr string) (net.Conn, error) {
|
||||||
buf = append(buf, s.password...)
|
buf = append(buf, s.password...)
|
||||||
|
|
||||||
if _, err := conn.Write(buf); err != nil {
|
if _, err := conn.Write(buf); err != nil {
|
||||||
return nil, errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||||
return nil, errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if buf[1] != 0 {
|
if buf[1] != 0 {
|
||||||
return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
|
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +154,7 @@ func (s *socks5) Dial(network, addr string) (net.Conn, error) {
|
||||||
buf = append(buf, ip...)
|
buf = append(buf, ip...)
|
||||||
} else {
|
} else {
|
||||||
if len(host) > 255 {
|
if len(host) > 255 {
|
||||||
return nil, errors.New("proxy: destination hostname too long: " + host)
|
return errors.New("proxy: destination hostname too long: " + host)
|
||||||
}
|
}
|
||||||
buf = append(buf, socks5Domain)
|
buf = append(buf, socks5Domain)
|
||||||
buf = append(buf, byte(len(host)))
|
buf = append(buf, byte(len(host)))
|
||||||
|
@ -159,11 +163,11 @@ func (s *socks5) Dial(network, addr string) (net.Conn, error) {
|
||||||
buf = append(buf, byte(port>>8), byte(port))
|
buf = append(buf, byte(port>>8), byte(port))
|
||||||
|
|
||||||
if _, err := conn.Write(buf); err != nil {
|
if _, err := conn.Write(buf); err != nil {
|
||||||
return nil, errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := io.ReadFull(conn, buf[:4]); err != nil {
|
if _, err := io.ReadFull(conn, buf[:4]); err != nil {
|
||||||
return nil, errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
failure := "unknown error"
|
failure := "unknown error"
|
||||||
|
@ -172,7 +176,7 @@ func (s *socks5) Dial(network, addr string) (net.Conn, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(failure) > 0 {
|
if len(failure) > 0 {
|
||||||
return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
|
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
|
||||||
}
|
}
|
||||||
|
|
||||||
bytesToDiscard := 0
|
bytesToDiscard := 0
|
||||||
|
@ -184,11 +188,11 @@ func (s *socks5) Dial(network, addr string) (net.Conn, error) {
|
||||||
case socks5Domain:
|
case socks5Domain:
|
||||||
_, err := io.ReadFull(conn, buf[:1])
|
_, err := io.ReadFull(conn, buf[:1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
}
|
}
|
||||||
bytesToDiscard = int(buf[0])
|
bytesToDiscard = int(buf[0])
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
|
return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cap(buf) < bytesToDiscard {
|
if cap(buf) < bytesToDiscard {
|
||||||
|
@ -197,14 +201,13 @@ func (s *socks5) Dial(network, addr string) (net.Conn, error) {
|
||||||
buf = buf[:bytesToDiscard]
|
buf = buf[:bytesToDiscard]
|
||||||
}
|
}
|
||||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
||||||
return nil, errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also need to discard the port number
|
// Also need to discard the port number
|
||||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||||
return nil, errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
closeConn = nil
|
return nil
|
||||||
return conn, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,6 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"golang.org/x/net/internal/timeseries"
|
"golang.org/x/net/internal/timeseries"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -271,18 +270,6 @@ type contextKeyT string
|
||||||
|
|
||||||
var contextKey = contextKeyT("golang.org/x/net/trace.Trace")
|
var contextKey = contextKeyT("golang.org/x/net/trace.Trace")
|
||||||
|
|
||||||
// NewContext returns a copy of the parent context
|
|
||||||
// and associates it with a Trace.
|
|
||||||
func NewContext(ctx context.Context, tr Trace) context.Context {
|
|
||||||
return context.WithValue(ctx, contextKey, tr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromContext returns the Trace bound to the context, if any.
|
|
||||||
func FromContext(ctx context.Context) (tr Trace, ok bool) {
|
|
||||||
tr, ok = ctx.Value(contextKey).(Trace)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trace represents an active request.
|
// Trace represents an active request.
|
||||||
type Trace interface {
|
type Trace interface {
|
||||||
// LazyLog adds x to the event log. It will be evaluated each time the
|
// LazyLog adds x to the event log. It will be evaluated each time the
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !go1.7
|
||||||
|
|
||||||
|
package trace
|
||||||
|
|
||||||
|
import "golang.org/x/net/context"
|
||||||
|
|
||||||
|
// NewContext returns a copy of the parent context
|
||||||
|
// and associates it with a Trace.
|
||||||
|
func NewContext(ctx context.Context, tr Trace) context.Context {
|
||||||
|
return context.WithValue(ctx, contextKey, tr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromContext returns the Trace bound to the context, if any.
|
||||||
|
func FromContext(ctx context.Context) (tr Trace, ok bool) {
|
||||||
|
tr, ok = ctx.Value(contextKey).(Trace)
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.7
|
||||||
|
|
||||||
|
package trace
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// NewContext returns a copy of the parent context
|
||||||
|
// and associates it with a Trace.
|
||||||
|
func NewContext(ctx context.Context, tr Trace) context.Context {
|
||||||
|
return context.WithValue(ctx, contextKey, tr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromContext returns the Trace bound to the context, if any.
|
||||||
|
func FromContext(ctx context.Context) (tr Trace, ok bool) {
|
||||||
|
tr, ok = ctx.Value(contextKey).(Trace)
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
|
@ -0,0 +1,28 @@
|
||||||
|
Go generated proto packages
|
||||||
|
===========================
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/google/go-genproto.svg?branch=master)](https://travis-ci.org/google/go-genproto)
|
||||||
|
[![GoDoc](https://godoc.org/google.golang.org/genproto?status.svg)](https://godoc.org/google.golang.org/genproto)
|
||||||
|
|
||||||
|
> **IMPORTANT** This repository is currently experimental. The structure
|
||||||
|
> of the contained packages is subject to change. Please see the original
|
||||||
|
> source repositories (listed below) to find out the status of the each
|
||||||
|
> protocol buffer's associated service.
|
||||||
|
|
||||||
|
This repository contains the generated Go packages for common protocol buffer
|
||||||
|
types, and the generated [gRPC][1] code necessary for interacting with Google's gRPC
|
||||||
|
APIs.
|
||||||
|
|
||||||
|
There are two sources for the proto files used in this repository:
|
||||||
|
|
||||||
|
1. [google/protobuf][2]: the code in the `protobuf` and `ptypes` subdirectories
|
||||||
|
is derived from this repo. The messages in `protobuf` are used to describe
|
||||||
|
protocol buffer messages themselves. The messages under `ptypes` define the
|
||||||
|
common well-known types.
|
||||||
|
2. [googleapis/googleapis][3]: the code in the `googleapis` is derived from this
|
||||||
|
repo. The packages here contain types specifically for interacting with Google
|
||||||
|
APIs.
|
||||||
|
|
||||||
|
[1]: http://grpc.io
|
||||||
|
[2]: https://github.com/google/protobuf/
|
||||||
|
[3]: https://github.com/googleapis/googleapis/
|
144
vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go
generated
vendored
Normal file
144
vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go
generated
vendored
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
// Code generated by protoc-gen-go.
|
||||||
|
// source: google/rpc/status.proto
|
||||||
|
// DO NOT EDIT!
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package status is a generated protocol buffer package.
|
||||||
|
|
||||||
|
It is generated from these files:
|
||||||
|
google/rpc/status.proto
|
||||||
|
|
||||||
|
It has these top-level messages:
|
||||||
|
Status
|
||||||
|
*/
|
||||||
|
package status
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
import google_protobuf "github.com/golang/protobuf/ptypes/any"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
// The `Status` type defines a logical error model that is suitable for different
|
||||||
|
// programming environments, including REST APIs and RPC APIs. It is used by
|
||||||
|
// [gRPC](https://github.com/grpc). The error model is designed to be:
|
||||||
|
//
|
||||||
|
// - Simple to use and understand for most users
|
||||||
|
// - Flexible enough to meet unexpected needs
|
||||||
|
//
|
||||||
|
// # Overview
|
||||||
|
//
|
||||||
|
// The `Status` message contains three pieces of data: error code, error message,
|
||||||
|
// and error details. The error code should be an enum value of
|
||||||
|
// [google.rpc.Code][google.rpc.Code], but it may accept additional error codes if needed. The
|
||||||
|
// error message should be a developer-facing English message that helps
|
||||||
|
// developers *understand* and *resolve* the error. If a localized user-facing
|
||||||
|
// error message is needed, put the localized message in the error details or
|
||||||
|
// localize it in the client. The optional error details may contain arbitrary
|
||||||
|
// information about the error. There is a predefined set of error detail types
|
||||||
|
// in the package `google.rpc` which can be used for common error conditions.
|
||||||
|
//
|
||||||
|
// # Language mapping
|
||||||
|
//
|
||||||
|
// The `Status` message is the logical representation of the error model, but it
|
||||||
|
// is not necessarily the actual wire format. When the `Status` message is
|
||||||
|
// exposed in different client libraries and different wire protocols, it can be
|
||||||
|
// mapped differently. For example, it will likely be mapped to some exceptions
|
||||||
|
// in Java, but more likely mapped to some error codes in C.
|
||||||
|
//
|
||||||
|
// # Other uses
|
||||||
|
//
|
||||||
|
// The error model and the `Status` message can be used in a variety of
|
||||||
|
// environments, either with or without APIs, to provide a
|
||||||
|
// consistent developer experience across different environments.
|
||||||
|
//
|
||||||
|
// Example uses of this error model include:
|
||||||
|
//
|
||||||
|
// - Partial errors. If a service needs to return partial errors to the client,
|
||||||
|
// it may embed the `Status` in the normal response to indicate the partial
|
||||||
|
// errors.
|
||||||
|
//
|
||||||
|
// - Workflow errors. A typical workflow has multiple steps. Each step may
|
||||||
|
// have a `Status` message for error reporting purpose.
|
||||||
|
//
|
||||||
|
// - Batch operations. If a client uses batch request and batch response, the
|
||||||
|
// `Status` message should be used directly inside batch response, one for
|
||||||
|
// each error sub-response.
|
||||||
|
//
|
||||||
|
// - Asynchronous operations. If an API call embeds asynchronous operation
|
||||||
|
// results in its response, the status of those operations should be
|
||||||
|
// represented directly using the `Status` message.
|
||||||
|
//
|
||||||
|
// - Logging. If some API errors are stored in logs, the message `Status` could
|
||||||
|
// be used directly after any stripping needed for security/privacy reasons.
|
||||||
|
type Status struct {
|
||||||
|
// The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code].
|
||||||
|
Code int32 `protobuf:"varint,1,opt,name=code" json:"code,omitempty"`
|
||||||
|
// A developer-facing error message, which should be in English. Any
|
||||||
|
// user-facing error message should be localized and sent in the
|
||||||
|
// [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client.
|
||||||
|
Message string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
|
||||||
|
// A list of messages that carry the error details. There will be a
|
||||||
|
// common set of message types for APIs to use.
|
||||||
|
Details []*google_protobuf.Any `protobuf:"bytes,3,rep,name=details" json:"details,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Status) Reset() { *m = Status{} }
|
||||||
|
func (m *Status) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Status) ProtoMessage() {}
|
||||||
|
func (*Status) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||||
|
|
||||||
|
func (m *Status) GetCode() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Code
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Status) GetMessage() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Message
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Status) GetDetails() []*google_protobuf.Any {
|
||||||
|
if m != nil {
|
||||||
|
return m.Details
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*Status)(nil), "google.rpc.Status")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("google/rpc/status.proto", fileDescriptor0) }
|
||||||
|
|
||||||
|
var fileDescriptor0 = []byte{
|
||||||
|
// 209 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4f, 0xcf, 0xcf, 0x4f,
|
||||||
|
0xcf, 0x49, 0xd5, 0x2f, 0x2a, 0x48, 0xd6, 0x2f, 0x2e, 0x49, 0x2c, 0x29, 0x2d, 0xd6, 0x2b, 0x28,
|
||||||
|
0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x82, 0x48, 0xe8, 0x15, 0x15, 0x24, 0x4b, 0x49, 0x42, 0x15, 0x81,
|
||||||
|
0x65, 0x92, 0x4a, 0xd3, 0xf4, 0x13, 0xf3, 0x2a, 0x21, 0xca, 0x94, 0xd2, 0xb8, 0xd8, 0x82, 0xc1,
|
||||||
|
0xda, 0x84, 0x84, 0xb8, 0x58, 0x92, 0xf3, 0x53, 0x52, 0x25, 0x18, 0x15, 0x18, 0x35, 0x58, 0x83,
|
||||||
|
0xc0, 0x6c, 0x21, 0x09, 0x2e, 0xf6, 0xdc, 0xd4, 0xe2, 0xe2, 0xc4, 0xf4, 0x54, 0x09, 0x26, 0x05,
|
||||||
|
0x46, 0x0d, 0xce, 0x20, 0x18, 0x57, 0x48, 0x8f, 0x8b, 0x3d, 0x25, 0xb5, 0x24, 0x31, 0x33, 0xa7,
|
||||||
|
0x58, 0x82, 0x59, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x44, 0x0f, 0x6a, 0x21, 0xcc, 0x12, 0x3d, 0xc7,
|
||||||
|
0xbc, 0xca, 0x20, 0x98, 0x22, 0xa7, 0x38, 0x2e, 0xbe, 0xe4, 0xfc, 0x5c, 0x3d, 0x84, 0xa3, 0x9c,
|
||||||
|
0xb8, 0x21, 0xf6, 0x06, 0x80, 0x94, 0x07, 0x30, 0x46, 0x99, 0x43, 0xa5, 0xd2, 0xf3, 0x73, 0x12,
|
||||||
|
0xf3, 0xd2, 0xf5, 0xf2, 0x8b, 0xd2, 0xf5, 0xd3, 0x53, 0xf3, 0xc0, 0x86, 0xe9, 0x43, 0xa4, 0x12,
|
||||||
|
0x0b, 0x32, 0x8b, 0x91, 0xfc, 0x69, 0x0d, 0xa1, 0x16, 0x31, 0x31, 0x07, 0x05, 0x38, 0x27, 0xb1,
|
||||||
|
0x81, 0x55, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xa4, 0x53, 0xf0, 0x7c, 0x10, 0x01, 0x00,
|
||||||
|
0x00,
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
#gRPC-Go
|
# gRPC-Go
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/grpc/grpc-go.svg)](https://travis-ci.org/grpc/grpc-go) [![GoDoc](https://godoc.org/google.golang.org/grpc?status.svg)](https://godoc.org/google.golang.org/grpc)
|
[![Build Status](https://travis-ci.org/grpc/grpc-go.svg)](https://travis-ci.org/grpc/grpc-go) [![GoDoc](https://godoc.org/google.golang.org/grpc?status.svg)](https://godoc.org/google.golang.org/grpc)
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ $ go get google.golang.org/grpc
|
||||||
Prerequisites
|
Prerequisites
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
This requires Go 1.5 or later.
|
This requires Go 1.6 or later.
|
||||||
|
|
||||||
Constraints
|
Constraints
|
||||||
-----------
|
-----------
|
||||||
|
@ -30,3 +30,12 @@ Status
|
||||||
------
|
------
|
||||||
GA
|
GA
|
||||||
|
|
||||||
|
FAQ
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Compiling error, undefined: grpc.SupportPackageIsVersion
|
||||||
|
|
||||||
|
Please update proto package, gRPC package and rebuild the proto files:
|
||||||
|
- `go get -u github.com/golang/protobuf/{proto,protoc-gen-go}`
|
||||||
|
- `go get -u google.golang.org/grpc`
|
||||||
|
- `protoc --go_out=plugins=grpc:. *.proto`
|
||||||
|
|
|
@ -36,12 +36,14 @@ package grpc
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
|
||||||
"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/peer"
|
||||||
|
"google.golang.org/grpc/stats"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
"google.golang.org/grpc/transport"
|
"google.golang.org/grpc/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -49,7 +51,8 @@ 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(dopts dialOptions, t transport.ClientTransport, c *callInfo, stream *transport.Stream, reply interface{}) (err error) {
|
// TODO ctx is used for stats collection and processing. It is the context passed from the application.
|
||||||
|
func recvResponse(ctx context.Context, dopts dialOptions, t transport.ClientTransport, c *callInfo, stream *transport.Stream, reply interface{}) (err 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.
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -63,24 +66,34 @@ func recvResponse(dopts dialOptions, t transport.ClientTransport, c *callInfo, s
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p := &parser{r: stream}
|
p := &parser{r: stream}
|
||||||
|
var inPayload *stats.InPayload
|
||||||
|
if dopts.copts.StatsHandler != nil {
|
||||||
|
inPayload = &stats.InPayload{
|
||||||
|
Client: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
if err = recv(p, dopts.codec, stream, dopts.dc, reply, math.MaxInt32); err != nil {
|
if err = recv(p, dopts.codec, stream, dopts.dc, reply, dopts.maxMsgSize, inPayload); err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if inPayload != nil && err == io.EOF && stream.Status().Code() == codes.OK {
|
||||||
|
// TODO in the current implementation, inTrailer may be handled before inPayload in some cases.
|
||||||
|
// Fix the order if necessary.
|
||||||
|
dopts.copts.StatsHandler.HandleRPC(ctx, inPayload)
|
||||||
|
}
|
||||||
c.trailerMD = stream.Trailer()
|
c.trailerMD = stream.Trailer()
|
||||||
|
if peer, ok := peer.FromContext(stream.Context()); ok {
|
||||||
|
c.peer = peer
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, compressor Compressor, callHdr *transport.CallHdr, t transport.ClientTransport, args interface{}, opts *transport.Options) (_ *transport.Stream, err error) {
|
func sendRequest(ctx context.Context, dopts dialOptions, compressor Compressor, callHdr *transport.CallHdr, stream *transport.Stream, t transport.ClientTransport, args interface{}, opts *transport.Options) (err error) {
|
||||||
stream, err := t.NewStream(ctx, callHdr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If err is connection error, t will be closed, no need to close stream here.
|
// If err is connection error, t will be closed, no need to close stream here.
|
||||||
|
@ -89,23 +102,35 @@ func sendRequest(ctx context.Context, codec Codec, compressor Compressor, callHd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
var cbuf *bytes.Buffer
|
var (
|
||||||
|
cbuf *bytes.Buffer
|
||||||
|
outPayload *stats.OutPayload
|
||||||
|
)
|
||||||
if compressor != nil {
|
if compressor != nil {
|
||||||
cbuf = new(bytes.Buffer)
|
cbuf = new(bytes.Buffer)
|
||||||
}
|
}
|
||||||
outBuf, err := encode(codec, args, compressor, cbuf)
|
if dopts.copts.StatsHandler != nil {
|
||||||
|
outPayload = &stats.OutPayload{
|
||||||
|
Client: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outBuf, err := encode(dopts.codec, args, compressor, cbuf, outPayload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, Errorf(codes.Internal, "grpc: %v", err)
|
return Errorf(codes.Internal, "grpc: %v", err)
|
||||||
}
|
}
|
||||||
err = t.Write(stream, outBuf, opts)
|
err = t.Write(stream, outBuf, opts)
|
||||||
|
if err == nil && outPayload != nil {
|
||||||
|
outPayload.SentTime = time.Now()
|
||||||
|
dopts.copts.StatsHandler.HandleRPC(ctx, outPayload)
|
||||||
|
}
|
||||||
// t.NewStream(...) could lead to an early rejection of the RPC (e.g., the service/method
|
// t.NewStream(...) could lead to an early rejection of the RPC (e.g., the service/method
|
||||||
// does not exist.) so that t.Write could get io.EOF from wait(...). Leave the following
|
// does not exist.) so that t.Write could get io.EOF from wait(...). Leave the following
|
||||||
// recvResponse to get the final status.
|
// recvResponse to get the final status.
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
// Sent successfully.
|
// Sent successfully.
|
||||||
return stream, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke sends the RPC request on the wire and returns after response is received.
|
// Invoke sends the RPC request on the wire and returns after response is received.
|
||||||
|
@ -118,8 +143,16 @@ func Invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
||||||
return invoke(ctx, method, args, reply, cc, opts...)
|
return invoke(ctx, method, args, reply, cc, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
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) (e error) {
|
||||||
c := defaultCallInfo
|
c := defaultCallInfo
|
||||||
|
if mc, ok := cc.getMethodConfig(method); ok {
|
||||||
|
c.failFast = !mc.WaitForReady
|
||||||
|
if mc.Timeout > 0 {
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
ctx, cancel = context.WithTimeout(ctx, mc.Timeout)
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
if err := o.before(&c); err != nil {
|
if err := o.before(&c); err != nil {
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
|
@ -140,12 +173,33 @@ func invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
||||||
c.traceInfo.tr.LazyLog(&c.traceInfo.firstLine, false)
|
c.traceInfo.tr.LazyLog(&c.traceInfo.firstLine, false)
|
||||||
// TODO(dsymonds): Arrange for c.traceInfo.firstLine.remoteAddr to be set.
|
// TODO(dsymonds): Arrange for c.traceInfo.firstLine.remoteAddr to be set.
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if e != nil {
|
||||||
c.traceInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
c.traceInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{e}}, true)
|
||||||
c.traceInfo.tr.SetError()
|
c.traceInfo.tr.SetError()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
ctx = newContextWithRPCInfo(ctx)
|
||||||
|
sh := cc.dopts.copts.StatsHandler
|
||||||
|
if sh != nil {
|
||||||
|
ctx = sh.TagRPC(ctx, &stats.RPCTagInfo{FullMethodName: method})
|
||||||
|
begin := &stats.Begin{
|
||||||
|
Client: true,
|
||||||
|
BeginTime: time.Now(),
|
||||||
|
FailFast: c.failFast,
|
||||||
|
}
|
||||||
|
sh.HandleRPC(ctx, begin)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if sh != nil {
|
||||||
|
end := &stats.End{
|
||||||
|
Client: true,
|
||||||
|
EndTime: time.Now(),
|
||||||
|
Error: e,
|
||||||
|
}
|
||||||
|
sh.HandleRPC(ctx, end)
|
||||||
|
}
|
||||||
|
}()
|
||||||
topts := &transport.Options{
|
topts := &transport.Options{
|
||||||
Last: true,
|
Last: true,
|
||||||
Delay: false,
|
Delay: false,
|
||||||
|
@ -167,13 +221,14 @@ func invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
||||||
if cc.dopts.cp != nil {
|
if cc.dopts.cp != nil {
|
||||||
callHdr.SendCompress = cc.dopts.cp.Type()
|
callHdr.SendCompress = cc.dopts.cp.Type()
|
||||||
}
|
}
|
||||||
|
|
||||||
gopts := BalancerGetOptions{
|
gopts := BalancerGetOptions{
|
||||||
BlockingWait: !c.failFast,
|
BlockingWait: !c.failFast,
|
||||||
}
|
}
|
||||||
t, put, err = cc.getTransport(ctx, gopts)
|
t, put, err = cc.getTransport(ctx, gopts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(zhaoq): Probably revisit the error handling.
|
// TODO(zhaoq): Probably revisit the error handling.
|
||||||
if _, ok := err.(*rpcError); ok {
|
if _, ok := status.FromError(err); ok {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err == errConnClosing || err == errConnUnavailable {
|
if err == errConnClosing || err == errConnUnavailable {
|
||||||
|
@ -188,33 +243,49 @@ 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, cc.dopts.cp, callHdr, t, args, topts)
|
stream, err = t.NewStream(ctx, callHdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if put != nil {
|
if put != nil {
|
||||||
put()
|
if _, ok := err.(transport.ConnectionError); ok {
|
||||||
put = nil
|
// If error is connection error, transport was sending data on wire,
|
||||||
}
|
// and we are not sure if anything has been sent on wire.
|
||||||
// Retry a non-failfast RPC when
|
// If error is not connection error, we are sure nothing has been sent.
|
||||||
// i) there is a connection error; or
|
updateRPCInfoInContext(ctx, rpcInfo{bytesSent: true, bytesReceived: false})
|
||||||
// ii) the server started to drain before this RPC was initiated.
|
|
||||||
if _, ok := err.(transport.ConnectionError); ok || err == transport.ErrStreamDrain {
|
|
||||||
if c.failFast {
|
|
||||||
return toRPCErr(err)
|
|
||||||
}
|
}
|
||||||
|
put()
|
||||||
|
}
|
||||||
|
if _, ok := err.(transport.ConnectionError); (ok || err == transport.ErrStreamDrain) && !c.failFast {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
}
|
}
|
||||||
err = recvResponse(cc.dopts, t, &c, stream, reply)
|
err = sendRequest(ctx, cc.dopts, cc.dopts.cp, callHdr, stream, t, args, topts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if put != nil {
|
if put != nil {
|
||||||
|
updateRPCInfoInContext(ctx, rpcInfo{
|
||||||
|
bytesSent: stream.BytesSent(),
|
||||||
|
bytesReceived: stream.BytesReceived(),
|
||||||
|
})
|
||||||
put()
|
put()
|
||||||
put = nil
|
|
||||||
}
|
}
|
||||||
if _, ok := err.(transport.ConnectionError); ok || err == transport.ErrStreamDrain {
|
// Retry a non-failfast RPC when
|
||||||
if c.failFast {
|
// i) there is a connection error; or
|
||||||
return toRPCErr(err)
|
// ii) the server started to drain before this RPC was initiated.
|
||||||
}
|
if _, ok := err.(transport.ConnectionError); (ok || err == transport.ErrStreamDrain) && !c.failFast {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return toRPCErr(err)
|
||||||
|
}
|
||||||
|
err = recvResponse(ctx, cc.dopts, t, &c, stream, reply)
|
||||||
|
if err != nil {
|
||||||
|
if put != nil {
|
||||||
|
updateRPCInfoInContext(ctx, rpcInfo{
|
||||||
|
bytesSent: stream.BytesSent(),
|
||||||
|
bytesReceived: stream.BytesReceived(),
|
||||||
|
})
|
||||||
|
put()
|
||||||
|
}
|
||||||
|
if _, ok := err.(transport.ConnectionError); (ok || err == transport.ErrStreamDrain) && !c.failFast {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
|
@ -224,9 +295,12 @@ func invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
||||||
}
|
}
|
||||||
t.CloseStream(stream, nil)
|
t.CloseStream(stream, nil)
|
||||||
if put != nil {
|
if put != nil {
|
||||||
|
updateRPCInfoInContext(ctx, rpcInfo{
|
||||||
|
bytesSent: stream.BytesSent(),
|
||||||
|
bytesReceived: stream.BytesReceived(),
|
||||||
|
})
|
||||||
put()
|
put()
|
||||||
put = nil
|
|
||||||
}
|
}
|
||||||
return Errorf(stream.StatusCode(), "%s", stream.StatusDesc())
|
return stream.Status().Err()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,8 @@ package grpc
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -45,6 +45,8 @@ import (
|
||||||
"golang.org/x/net/trace"
|
"golang.org/x/net/trace"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
|
"google.golang.org/grpc/keepalive"
|
||||||
|
"google.golang.org/grpc/stats"
|
||||||
"google.golang.org/grpc/transport"
|
"google.golang.org/grpc/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,6 +56,8 @@ var (
|
||||||
ErrClientConnClosing = errors.New("grpc: the client connection is closing")
|
ErrClientConnClosing = errors.New("grpc: the client connection is closing")
|
||||||
// ErrClientConnTimeout indicates that the ClientConn cannot establish the
|
// ErrClientConnTimeout indicates that the ClientConn cannot establish the
|
||||||
// underlying connections within the specified timeout.
|
// underlying connections within the specified timeout.
|
||||||
|
// DEPRECATED: Please use context.DeadlineExceeded instead. This error will be
|
||||||
|
// removed in Q1 2017.
|
||||||
ErrClientConnTimeout = errors.New("grpc: timed out when dialing")
|
ErrClientConnTimeout = errors.New("grpc: timed out when dialing")
|
||||||
|
|
||||||
// errNoTransportSecurity indicates that there is no transport security
|
// errNoTransportSecurity indicates that there is no transport security
|
||||||
|
@ -75,7 +79,6 @@ var (
|
||||||
errConnClosing = errors.New("grpc: the connection is closing")
|
errConnClosing = errors.New("grpc: the connection is closing")
|
||||||
// errConnUnavailable indicates that the connection is unavailable.
|
// errConnUnavailable indicates that the connection is unavailable.
|
||||||
errConnUnavailable = errors.New("grpc: the connection is unavailable")
|
errConnUnavailable = errors.New("grpc: the connection is unavailable")
|
||||||
errNoAddr = errors.New("grpc: there is no address available to dial")
|
|
||||||
// minimum time to give a connection to complete
|
// minimum time to give a connection to complete
|
||||||
minConnectTimeout = 20 * time.Second
|
minConnectTimeout = 20 * time.Second
|
||||||
)
|
)
|
||||||
|
@ -83,22 +86,33 @@ var (
|
||||||
// dialOptions configure a Dial call. dialOptions are set by the DialOption
|
// dialOptions configure a Dial call. dialOptions are set by the DialOption
|
||||||
// values passed to Dial.
|
// values passed to Dial.
|
||||||
type dialOptions struct {
|
type dialOptions struct {
|
||||||
unaryInt UnaryClientInterceptor
|
unaryInt UnaryClientInterceptor
|
||||||
streamInt StreamClientInterceptor
|
streamInt StreamClientInterceptor
|
||||||
codec Codec
|
codec Codec
|
||||||
cp Compressor
|
cp Compressor
|
||||||
dc Decompressor
|
dc Decompressor
|
||||||
bs backoffStrategy
|
bs backoffStrategy
|
||||||
balancer Balancer
|
balancer Balancer
|
||||||
block bool
|
block bool
|
||||||
insecure bool
|
insecure bool
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
copts transport.ConnectOptions
|
scChan <-chan ServiceConfig
|
||||||
|
copts transport.ConnectOptions
|
||||||
|
maxMsgSize int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultClientMaxMsgSize = math.MaxInt32
|
||||||
|
|
||||||
// DialOption configures how we set up the connection.
|
// DialOption configures how we set up the connection.
|
||||||
type DialOption func(*dialOptions)
|
type DialOption func(*dialOptions)
|
||||||
|
|
||||||
|
// WithMaxMsgSize returns a DialOption which sets the maximum message size the client can receive.
|
||||||
|
func WithMaxMsgSize(s int) DialOption {
|
||||||
|
return func(o *dialOptions) {
|
||||||
|
o.maxMsgSize = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithCodec returns a DialOption which sets a codec for message marshaling and unmarshaling.
|
// WithCodec returns a DialOption which sets a codec for message marshaling and unmarshaling.
|
||||||
func WithCodec(c Codec) DialOption {
|
func WithCodec(c Codec) DialOption {
|
||||||
return func(o *dialOptions) {
|
return func(o *dialOptions) {
|
||||||
|
@ -129,6 +143,13 @@ func WithBalancer(b Balancer) DialOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithServiceConfig returns a DialOption which has a channel to read the service configuration.
|
||||||
|
func WithServiceConfig(c <-chan ServiceConfig) DialOption {
|
||||||
|
return func(o *dialOptions) {
|
||||||
|
o.scChan = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithBackoffMaxDelay configures the dialer to use the provided maximum delay
|
// WithBackoffMaxDelay configures the dialer to use the provided maximum delay
|
||||||
// when backing off after failed connection attempts.
|
// when backing off after failed connection attempts.
|
||||||
func WithBackoffMaxDelay(md time.Duration) DialOption {
|
func WithBackoffMaxDelay(md time.Duration) DialOption {
|
||||||
|
@ -199,6 +220,8 @@ func WithTimeout(d time.Duration) DialOption {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithDialer returns a DialOption that specifies a function to use for dialing network addresses.
|
// WithDialer returns a DialOption that specifies a function to use for dialing network addresses.
|
||||||
|
// If FailOnNonTempDialError() is set to true, and an error is returned by f, gRPC checks the error's
|
||||||
|
// Temporary() method to decide if it should try to reconnect to the network address.
|
||||||
func WithDialer(f func(string, time.Duration) (net.Conn, error)) DialOption {
|
func WithDialer(f func(string, time.Duration) (net.Conn, error)) DialOption {
|
||||||
return func(o *dialOptions) {
|
return func(o *dialOptions) {
|
||||||
o.copts.Dialer = func(ctx context.Context, addr string) (net.Conn, error) {
|
o.copts.Dialer = func(ctx context.Context, addr string) (net.Conn, error) {
|
||||||
|
@ -210,6 +233,25 @@ func WithDialer(f func(string, time.Duration) (net.Conn, error)) DialOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithStatsHandler returns a DialOption that specifies the stats handler
|
||||||
|
// for all the RPCs and underlying network connections in this ClientConn.
|
||||||
|
func WithStatsHandler(h stats.Handler) DialOption {
|
||||||
|
return func(o *dialOptions) {
|
||||||
|
o.copts.StatsHandler = h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FailOnNonTempDialError returns a DialOption that specified if gRPC fails on non-temporary dial errors.
|
||||||
|
// If f is true, and dialer returns a non-temporary error, gRPC will fail the connection to the network
|
||||||
|
// address and won't try to reconnect.
|
||||||
|
// The default value of FailOnNonTempDialError is false.
|
||||||
|
// This is an EXPERIMENTAL API.
|
||||||
|
func FailOnNonTempDialError(f bool) DialOption {
|
||||||
|
return func(o *dialOptions) {
|
||||||
|
o.copts.FailOnNonTempDialError = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithUserAgent returns a DialOption that specifies a user agent string for all the RPCs.
|
// WithUserAgent returns a DialOption that specifies a user agent string for all the RPCs.
|
||||||
func WithUserAgent(s string) DialOption {
|
func WithUserAgent(s string) DialOption {
|
||||||
return func(o *dialOptions) {
|
return func(o *dialOptions) {
|
||||||
|
@ -217,6 +259,13 @@ func WithUserAgent(s string) DialOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithKeepaliveParams returns a DialOption that specifies keepalive paramaters for the client transport.
|
||||||
|
func WithKeepaliveParams(kp keepalive.ClientParameters) DialOption {
|
||||||
|
return func(o *dialOptions) {
|
||||||
|
o.copts.KeepaliveParams = kp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithUnaryInterceptor returns a DialOption that specifies the interceptor for unary RPCs.
|
// WithUnaryInterceptor returns a DialOption that specifies the interceptor for unary RPCs.
|
||||||
func WithUnaryInterceptor(f UnaryClientInterceptor) DialOption {
|
func WithUnaryInterceptor(f UnaryClientInterceptor) DialOption {
|
||||||
return func(o *dialOptions) {
|
return func(o *dialOptions) {
|
||||||
|
@ -231,6 +280,15 @@ func WithStreamInterceptor(f StreamClientInterceptor) DialOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithAuthority returns a DialOption that specifies the value to be used as
|
||||||
|
// the :authority pseudo-header. This value only works with WithInsecure and
|
||||||
|
// has no effect if TransportCredentials are present.
|
||||||
|
func WithAuthority(a string) DialOption {
|
||||||
|
return func(o *dialOptions) {
|
||||||
|
o.copts.Authority = a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Dial creates a client connection to the given target.
|
// Dial creates a client connection to the given target.
|
||||||
func Dial(target string, opts ...DialOption) (*ClientConn, error) {
|
func Dial(target string, opts ...DialOption) (*ClientConn, error) {
|
||||||
return DialContext(context.Background(), target, opts...)
|
return DialContext(context.Background(), target, opts...)
|
||||||
|
@ -247,6 +305,32 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
conns: make(map[Address]*addrConn),
|
conns: make(map[Address]*addrConn),
|
||||||
}
|
}
|
||||||
cc.ctx, cc.cancel = context.WithCancel(context.Background())
|
cc.ctx, cc.cancel = context.WithCancel(context.Background())
|
||||||
|
cc.dopts.maxMsgSize = defaultClientMaxMsgSize
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&cc.dopts)
|
||||||
|
}
|
||||||
|
cc.mkp = cc.dopts.copts.KeepaliveParams
|
||||||
|
|
||||||
|
if cc.dopts.copts.Dialer == nil {
|
||||||
|
cc.dopts.copts.Dialer = newProxyDialer(
|
||||||
|
func(ctx context.Context, addr string) (net.Conn, error) {
|
||||||
|
return dialContext(ctx, "tcp", addr)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cc.dopts.copts.UserAgent != "" {
|
||||||
|
cc.dopts.copts.UserAgent += " " + grpcUA
|
||||||
|
} else {
|
||||||
|
cc.dopts.copts.UserAgent = grpcUA
|
||||||
|
}
|
||||||
|
|
||||||
|
if cc.dopts.timeout > 0 {
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
ctx, cancel = context.WithTimeout(ctx, cc.dopts.timeout)
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
@ -259,10 +343,17 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for _, opt := range opts {
|
if cc.dopts.scChan != nil {
|
||||||
opt(&cc.dopts)
|
// Wait for the initial service config.
|
||||||
|
select {
|
||||||
|
case sc, ok := <-cc.dopts.scChan:
|
||||||
|
if ok {
|
||||||
|
cc.sc = sc
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set defaults.
|
// Set defaults.
|
||||||
if cc.dopts.codec == nil {
|
if cc.dopts.codec == nil {
|
||||||
cc.dopts.codec = protoCodec{}
|
cc.dopts.codec = protoCodec{}
|
||||||
|
@ -273,21 +364,18 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
creds := cc.dopts.copts.TransportCredentials
|
creds := cc.dopts.copts.TransportCredentials
|
||||||
if creds != nil && creds.Info().ServerName != "" {
|
if creds != nil && creds.Info().ServerName != "" {
|
||||||
cc.authority = creds.Info().ServerName
|
cc.authority = creds.Info().ServerName
|
||||||
|
} else if cc.dopts.insecure && cc.dopts.copts.Authority != "" {
|
||||||
|
cc.authority = cc.dopts.copts.Authority
|
||||||
} else {
|
} else {
|
||||||
colonPos := strings.LastIndex(target, ":")
|
cc.authority = target
|
||||||
if colonPos == -1 {
|
|
||||||
colonPos = len(target)
|
|
||||||
}
|
|
||||||
cc.authority = target[:colonPos]
|
|
||||||
}
|
}
|
||||||
var ok bool
|
|
||||||
waitC := make(chan error, 1)
|
waitC := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
var addrs []Address
|
defer close(waitC)
|
||||||
if cc.dopts.balancer == nil {
|
if cc.dopts.balancer == nil && cc.sc.LB != nil {
|
||||||
// Connect to target directly if balancer is nil.
|
cc.dopts.balancer = cc.sc.LB
|
||||||
addrs = append(addrs, Address{Addr: target})
|
}
|
||||||
} else {
|
if cc.dopts.balancer != nil {
|
||||||
var credsClone credentials.TransportCredentials
|
var credsClone credentials.TransportCredentials
|
||||||
if creds != nil {
|
if creds != nil {
|
||||||
credsClone = creds.Clone()
|
credsClone = creds.Clone()
|
||||||
|
@ -300,29 +388,23 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ch := cc.dopts.balancer.Notify()
|
ch := cc.dopts.balancer.Notify()
|
||||||
if ch == nil {
|
if ch != nil {
|
||||||
// There is no name resolver installed.
|
if cc.dopts.block {
|
||||||
addrs = append(addrs, Address{Addr: target})
|
doneChan := make(chan struct{})
|
||||||
} else {
|
go cc.lbWatcher(doneChan)
|
||||||
addrs, ok = <-ch
|
<-doneChan
|
||||||
if !ok || len(addrs) == 0 {
|
} else {
|
||||||
waitC <- errNoAddr
|
go cc.lbWatcher(nil)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, a := range addrs {
|
|
||||||
if err := cc.resetAddrConn(a, false, nil); err != nil {
|
|
||||||
waitC <- err
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(waitC)
|
// No balancer, or no resolver within the balancer. Connect directly.
|
||||||
|
if err := cc.resetAddrConn(Address{Addr: target}, cc.dopts.block, nil); err != nil {
|
||||||
|
waitC <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
var timeoutCh <-chan time.Time
|
|
||||||
if cc.dopts.timeout > 0 {
|
|
||||||
timeoutCh = time.After(cc.dopts.timeout)
|
|
||||||
}
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
|
@ -330,14 +412,12 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case <-timeoutCh:
|
|
||||||
return nil, ErrClientConnTimeout
|
|
||||||
}
|
}
|
||||||
// If balancer is nil or balancer.Notify() is nil, ok will be false here.
|
|
||||||
// The lbWatcher goroutine will not be created.
|
if cc.dopts.scChan != nil {
|
||||||
if ok {
|
go cc.scWatcher()
|
||||||
go cc.lbWatcher()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cc, nil
|
return cc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,10 +464,16 @@ type ClientConn struct {
|
||||||
dopts dialOptions
|
dopts dialOptions
|
||||||
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
sc ServiceConfig
|
||||||
conns map[Address]*addrConn
|
conns map[Address]*addrConn
|
||||||
|
// Keepalive parameter can be udated if a GoAway is received.
|
||||||
|
mkp keepalive.ClientParameters
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc *ClientConn) lbWatcher() {
|
// lbWatcher watches the Notify channel of the balancer in cc and manages
|
||||||
|
// connections accordingly. If doneChan is not nil, it is closed after the
|
||||||
|
// first successfull connection is made.
|
||||||
|
func (cc *ClientConn) lbWatcher(doneChan chan struct{}) {
|
||||||
for addrs := range cc.dopts.balancer.Notify() {
|
for addrs := range cc.dopts.balancer.Notify() {
|
||||||
var (
|
var (
|
||||||
add []Address // Addresses need to setup connections.
|
add []Address // Addresses need to setup connections.
|
||||||
|
@ -414,7 +500,15 @@ func (cc *ClientConn) lbWatcher() {
|
||||||
}
|
}
|
||||||
cc.mu.Unlock()
|
cc.mu.Unlock()
|
||||||
for _, a := range add {
|
for _, a := range add {
|
||||||
cc.resetAddrConn(a, true, nil)
|
if doneChan != nil {
|
||||||
|
err := cc.resetAddrConn(a, true, nil)
|
||||||
|
if err == nil {
|
||||||
|
close(doneChan)
|
||||||
|
doneChan = nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cc.resetAddrConn(a, false, nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for _, c := range del {
|
for _, c := range del {
|
||||||
c.tearDown(errConnDrain)
|
c.tearDown(errConnDrain)
|
||||||
|
@ -422,15 +516,36 @@ func (cc *ClientConn) lbWatcher() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cc *ClientConn) scWatcher() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case sc, ok := <-cc.dopts.scChan:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cc.mu.Lock()
|
||||||
|
// TODO: load balance policy runtime change is ignored.
|
||||||
|
// We may revist this decision in the future.
|
||||||
|
cc.sc = sc
|
||||||
|
cc.mu.Unlock()
|
||||||
|
case <-cc.ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// resetAddrConn creates an addrConn for addr and adds it to cc.conns.
|
// resetAddrConn creates an addrConn for addr and adds it to cc.conns.
|
||||||
// If there is an old addrConn for addr, it will be torn down, using tearDownErr as the reason.
|
// If there is an old addrConn for addr, it will be torn down, using tearDownErr as the reason.
|
||||||
// If tearDownErr is nil, errConnDrain will be used instead.
|
// If tearDownErr is nil, errConnDrain will be used instead.
|
||||||
func (cc *ClientConn) resetAddrConn(addr Address, skipWait bool, tearDownErr error) error {
|
func (cc *ClientConn) resetAddrConn(addr Address, block bool, tearDownErr error) error {
|
||||||
ac := &addrConn{
|
ac := &addrConn{
|
||||||
cc: cc,
|
cc: cc,
|
||||||
addr: addr,
|
addr: addr,
|
||||||
dopts: cc.dopts,
|
dopts: cc.dopts,
|
||||||
}
|
}
|
||||||
|
cc.mu.RLock()
|
||||||
|
ac.dopts.copts.KeepaliveParams = cc.mkp
|
||||||
|
cc.mu.RUnlock()
|
||||||
ac.ctx, ac.cancel = context.WithCancel(cc.ctx)
|
ac.ctx, ac.cancel = context.WithCancel(cc.ctx)
|
||||||
ac.stateCV = sync.NewCond(&ac.mu)
|
ac.stateCV = sync.NewCond(&ac.mu)
|
||||||
if EnableTracing {
|
if EnableTracing {
|
||||||
|
@ -475,8 +590,7 @@ func (cc *ClientConn) resetAddrConn(addr Address, skipWait bool, tearDownErr err
|
||||||
stale.tearDown(tearDownErr)
|
stale.tearDown(tearDownErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// skipWait may overwrite the decision in ac.dopts.block.
|
if block {
|
||||||
if ac.dopts.block && !skipWait {
|
|
||||||
if err := ac.resetTransport(false); err != nil {
|
if err := ac.resetTransport(false); err != nil {
|
||||||
if err != errConnClosing {
|
if err != errConnClosing {
|
||||||
// Tear down ac and delete it from cc.conns.
|
// Tear down ac and delete it from cc.conns.
|
||||||
|
@ -509,6 +623,14 @@ func (cc *ClientConn) resetAddrConn(addr Address, skipWait bool, tearDownErr err
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Avoid the locking here.
|
||||||
|
func (cc *ClientConn) getMethodConfig(method string) (m MethodConfig, ok bool) {
|
||||||
|
cc.mu.RLock()
|
||||||
|
defer cc.mu.RUnlock()
|
||||||
|
m, ok = cc.sc.Methods[method]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (cc *ClientConn) getTransport(ctx context.Context, opts BalancerGetOptions) (transport.ClientTransport, func(), error) {
|
func (cc *ClientConn) getTransport(ctx context.Context, opts BalancerGetOptions) (transport.ClientTransport, func(), error) {
|
||||||
var (
|
var (
|
||||||
ac *addrConn
|
ac *addrConn
|
||||||
|
@ -547,6 +669,7 @@ func (cc *ClientConn) getTransport(ctx context.Context, opts BalancerGetOptions)
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
if put != nil {
|
if put != nil {
|
||||||
|
updateRPCInfoInContext(ctx, rpcInfo{bytesSent: false, bytesReceived: false})
|
||||||
put()
|
put()
|
||||||
}
|
}
|
||||||
return nil, nil, errConnClosing
|
return nil, nil, errConnClosing
|
||||||
|
@ -554,6 +677,7 @@ func (cc *ClientConn) getTransport(ctx context.Context, opts BalancerGetOptions)
|
||||||
t, err := ac.wait(ctx, cc.dopts.balancer != nil, !opts.BlockingWait)
|
t, err := ac.wait(ctx, cc.dopts.balancer != nil, !opts.BlockingWait)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if put != nil {
|
if put != nil {
|
||||||
|
updateRPCInfoInContext(ctx, rpcInfo{bytesSent: false, bytesReceived: false})
|
||||||
put()
|
put()
|
||||||
}
|
}
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -605,6 +729,20 @@ type addrConn struct {
|
||||||
tearDownErr error
|
tearDownErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adjustParams updates parameters used to create transports upon
|
||||||
|
// receiving a GoAway.
|
||||||
|
func (ac *addrConn) adjustParams(r transport.GoAwayReason) {
|
||||||
|
switch r {
|
||||||
|
case transport.TooManyPings:
|
||||||
|
v := 2 * ac.dopts.copts.KeepaliveParams.Time
|
||||||
|
ac.cc.mu.Lock()
|
||||||
|
if v > ac.cc.mkp.Time {
|
||||||
|
ac.cc.mkp.Time = v
|
||||||
|
}
|
||||||
|
ac.cc.mu.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// printf records an event in ac's event log, unless ac has been closed.
|
// printf records an event in ac's event log, unless ac has been closed.
|
||||||
// REQUIRES ac.mu is held.
|
// REQUIRES ac.mu is held.
|
||||||
func (ac *addrConn) printf(format string, a ...interface{}) {
|
func (ac *addrConn) printf(format string, a ...interface{}) {
|
||||||
|
@ -689,6 +827,8 @@ func (ac *addrConn) resetTransport(closeTransport bool) error {
|
||||||
Metadata: ac.addr.Metadata,
|
Metadata: ac.addr.Metadata,
|
||||||
}
|
}
|
||||||
newTransport, err := transport.NewClientTransport(ctx, sinfo, ac.dopts.copts)
|
newTransport, err := transport.NewClientTransport(ctx, sinfo, ac.dopts.copts)
|
||||||
|
// Don't call cancel in success path due to a race in Go 1.6:
|
||||||
|
// https://github.com/golang/go/issues/15078.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancel()
|
cancel()
|
||||||
|
|
||||||
|
@ -759,6 +899,7 @@ func (ac *addrConn) transportMonitor() {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case <-t.GoAway():
|
case <-t.GoAway():
|
||||||
|
ac.adjustParams(t.GetGoAwayReason())
|
||||||
// If GoAway happens without any network I/O error, ac is closed without shutting down the
|
// If GoAway happens without any network I/O error, ac is closed without shutting down the
|
||||||
// underlying transport (the transport will be closed when all the pending RPCs finished or
|
// underlying transport (the transport will be closed when all the pending RPCs finished or
|
||||||
// failed.).
|
// failed.).
|
||||||
|
@ -767,9 +908,9 @@ func (ac *addrConn) transportMonitor() {
|
||||||
// In both cases, a new ac is created.
|
// In both cases, a new ac is created.
|
||||||
select {
|
select {
|
||||||
case <-t.Error():
|
case <-t.Error():
|
||||||
ac.cc.resetAddrConn(ac.addr, true, errNetworkIO)
|
ac.cc.resetAddrConn(ac.addr, false, errNetworkIO)
|
||||||
default:
|
default:
|
||||||
ac.cc.resetAddrConn(ac.addr, true, errConnDrain)
|
ac.cc.resetAddrConn(ac.addr, false, errConnDrain)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case <-t.Error():
|
case <-t.Error():
|
||||||
|
@ -778,7 +919,8 @@ func (ac *addrConn) transportMonitor() {
|
||||||
t.Close()
|
t.Close()
|
||||||
return
|
return
|
||||||
case <-t.GoAway():
|
case <-t.GoAway():
|
||||||
ac.cc.resetAddrConn(ac.addr, true, errNetworkIO)
|
ac.adjustParams(t.GetGoAwayReason())
|
||||||
|
ac.cc.resetAddrConn(ac.addr, false, errNetworkIO)
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Codec defines the interface gRPC uses to encode and decode messages.
|
||||||
|
// Note that implementations of this interface must be thread safe;
|
||||||
|
// a Codec's methods can be called from concurrent goroutines.
|
||||||
|
type Codec interface {
|
||||||
|
// Marshal returns the wire format of v.
|
||||||
|
Marshal(v interface{}) ([]byte, error)
|
||||||
|
// Unmarshal parses the wire format into v.
|
||||||
|
Unmarshal(data []byte, v interface{}) error
|
||||||
|
// String returns the name of the Codec implementation. The returned
|
||||||
|
// string will be used as part of content type in transmission.
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// protoCodec is a Codec implementation with protobuf. It is the default codec for gRPC.
|
||||||
|
type protoCodec struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type cachedProtoBuffer struct {
|
||||||
|
lastMarshaledSize uint32
|
||||||
|
proto.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func capToMaxInt32(val int) uint32 {
|
||||||
|
if val > math.MaxInt32 {
|
||||||
|
return uint32(math.MaxInt32)
|
||||||
|
}
|
||||||
|
return uint32(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p protoCodec) marshal(v interface{}, cb *cachedProtoBuffer) ([]byte, error) {
|
||||||
|
protoMsg := v.(proto.Message)
|
||||||
|
newSlice := make([]byte, 0, cb.lastMarshaledSize)
|
||||||
|
|
||||||
|
cb.SetBuf(newSlice)
|
||||||
|
cb.Reset()
|
||||||
|
if err := cb.Marshal(protoMsg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out := cb.Bytes()
|
||||||
|
cb.lastMarshaledSize = capToMaxInt32(len(out))
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p protoCodec) Marshal(v interface{}) ([]byte, error) {
|
||||||
|
cb := protoBufferPool.Get().(*cachedProtoBuffer)
|
||||||
|
out, err := p.marshal(v, cb)
|
||||||
|
|
||||||
|
// put back buffer and lose the ref to the slice
|
||||||
|
cb.SetBuf(nil)
|
||||||
|
protoBufferPool.Put(cb)
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p protoCodec) Unmarshal(data []byte, v interface{}) error {
|
||||||
|
cb := protoBufferPool.Get().(*cachedProtoBuffer)
|
||||||
|
cb.SetBuf(data)
|
||||||
|
err := cb.Unmarshal(v.(proto.Message))
|
||||||
|
cb.SetBuf(nil)
|
||||||
|
protoBufferPool.Put(cb)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (protoCodec) String() string {
|
||||||
|
return "proto"
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
protoBufferPool = &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return &cachedProtoBuffer{
|
||||||
|
Buffer: proto.Buffer{},
|
||||||
|
lastMarshaledSize: 16,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
|
@ -102,6 +102,10 @@ type TransportCredentials interface {
|
||||||
// authentication protocol on rawConn for clients. It returns the authenticated
|
// authentication protocol on rawConn for clients. It returns the authenticated
|
||||||
// connection and the corresponding auth information about the connection.
|
// connection and the corresponding auth information about the connection.
|
||||||
// Implementations must use the provided context to implement timely cancellation.
|
// Implementations must use the provided context to implement timely cancellation.
|
||||||
|
// gRPC will try to reconnect if the error returned is a temporary error
|
||||||
|
// (io.EOF, context.DeadlineExceeded or err.Temporary() == true).
|
||||||
|
// If the returned error is a wrapper error, implementations should make sure that
|
||||||
|
// the error implements Temporary() to have the correct retry behaviors.
|
||||||
ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error)
|
ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error)
|
||||||
// ServerHandshake does the authentication handshake for servers. It returns
|
// ServerHandshake does the authentication handshake for servers. It returns
|
||||||
// the authenticated connection and the corresponding auth information about
|
// the authenticated connection and the corresponding auth information about
|
||||||
|
@ -165,9 +169,7 @@ func (c *tlsCreds) ClientHandshake(ctx context.Context, addr string, rawConn net
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil, nil, ctx.Err()
|
return nil, nil, ctx.Err()
|
||||||
}
|
}
|
||||||
// TODO(zhaoq): Omit the auth info for client now. It is more for
|
return conn, TLSInfo{conn.ConnectionState()}, nil
|
||||||
// information than anything else.
|
|
||||||
return conn, nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) {
|
func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// +build go1.7
|
// +build go1.7
|
||||||
|
// +build !go1.8
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
|
@ -44,8 +45,6 @@ import (
|
||||||
// contains a mutex and must not be copied.
|
// contains a mutex and must not be copied.
|
||||||
//
|
//
|
||||||
// If cfg is nil, a new zero tls.Config is returned.
|
// If cfg is nil, a new zero tls.Config is returned.
|
||||||
//
|
|
||||||
// TODO replace this function with official clone function.
|
|
||||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return &tls.Config{}
|
return &tls.Config{}
|
||||||
|
|
53
vendor/google.golang.org/grpc/credentials/credentials_util_go18.go
generated
vendored
Normal file
53
vendor/google.golang.org/grpc/credentials/credentials_util_go18.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2017, 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 credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
// cloneTLSConfig returns a shallow clone of the exported
|
||||||
|
// fields of cfg, ignoring the unexported sync.Once, which
|
||||||
|
// contains a mutex and must not be copied.
|
||||||
|
//
|
||||||
|
// If cfg is nil, a new zero tls.Config is returned.
|
||||||
|
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||||
|
if cfg == nil {
|
||||||
|
return &tls.Config{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg.Clone()
|
||||||
|
}
|
|
@ -44,8 +44,6 @@ import (
|
||||||
// contains a mutex and must not be copied.
|
// contains a mutex and must not be copied.
|
||||||
//
|
//
|
||||||
// If cfg is nil, a new zero tls.Config is returned.
|
// If cfg is nil, a new zero tls.Config is returned.
|
||||||
//
|
|
||||||
// TODO replace this function with official clone function.
|
|
||||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return &tls.Config{}
|
return &tls.Config{}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
// +build go1.6,!go1.7
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// dialContext connects to the address on the named network.
|
||||||
|
func dialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
return (&net.Dialer{Cancel: ctx.Done()}).Dial(network, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error {
|
||||||
|
req.Cancel = ctx.Done()
|
||||||
|
if err := req.Write(conn); err != nil {
|
||||||
|
return fmt.Errorf("failed to write the HTTP request: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// +build !go1.6
|
// +build go1.7
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2016, Google Inc.
|
* Copyright 2016, Google Inc.
|
||||||
|
@ -32,20 +32,24 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package transport
|
package grpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"net/http"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// dialContext connects to the address on the named network.
|
// dialContext connects to the address on the named network.
|
||||||
func dialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
func dialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
var dialer net.Dialer
|
return (&net.Dialer{}).DialContext(ctx, network, address)
|
||||||
if deadline, ok := ctx.Deadline(); ok {
|
}
|
||||||
dialer.Timeout = deadline.Sub(time.Now())
|
|
||||||
}
|
func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error {
|
||||||
return dialer.Dial(network, address)
|
req = req.WithContext(ctx)
|
||||||
|
if err := req.Write(conn); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
|
@ -0,0 +1,749 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
lbpb "google.golang.org/grpc/grpclb/grpc_lb_v1"
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/naming"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client API for LoadBalancer service.
|
||||||
|
// Mostly copied from generated pb.go file.
|
||||||
|
// To avoid circular dependency.
|
||||||
|
type loadBalancerClient struct {
|
||||||
|
cc *ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *loadBalancerClient) BalanceLoad(ctx context.Context, opts ...CallOption) (*balanceLoadClientStream, error) {
|
||||||
|
desc := &StreamDesc{
|
||||||
|
StreamName: "BalanceLoad",
|
||||||
|
ServerStreams: true,
|
||||||
|
ClientStreams: true,
|
||||||
|
}
|
||||||
|
stream, err := NewClientStream(ctx, desc, c.cc, "/grpc.lb.v1.LoadBalancer/BalanceLoad", opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &balanceLoadClientStream{stream}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type balanceLoadClientStream struct {
|
||||||
|
ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *balanceLoadClientStream) Send(m *lbpb.LoadBalanceRequest) error {
|
||||||
|
return x.ClientStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *balanceLoadClientStream) Recv() (*lbpb.LoadBalanceResponse, error) {
|
||||||
|
m := new(lbpb.LoadBalanceResponse)
|
||||||
|
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressType indicates the address type returned by name resolution.
|
||||||
|
type AddressType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Backend indicates the server is a backend server.
|
||||||
|
Backend AddressType = iota
|
||||||
|
// GRPCLB indicates the server is a grpclb load balancer.
|
||||||
|
GRPCLB
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddrMetadataGRPCLB contains the information the name resolution for grpclb should provide. The
|
||||||
|
// name resolver used by grpclb balancer is required to provide this type of metadata in
|
||||||
|
// its address updates.
|
||||||
|
type AddrMetadataGRPCLB struct {
|
||||||
|
// AddrType is the type of server (grpc load balancer or backend).
|
||||||
|
AddrType AddressType
|
||||||
|
// ServerName is the name of the grpc load balancer. Used for authentication.
|
||||||
|
ServerName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGRPCLBBalancer creates a grpclb load balancer.
|
||||||
|
func NewGRPCLBBalancer(r naming.Resolver) Balancer {
|
||||||
|
return &balancer{
|
||||||
|
r: r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type remoteBalancerInfo struct {
|
||||||
|
addr string
|
||||||
|
// the server name used for authentication with the remote LB server.
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// grpclbAddrInfo consists of the information of a backend server.
|
||||||
|
type grpclbAddrInfo struct {
|
||||||
|
addr Address
|
||||||
|
connected bool
|
||||||
|
// dropForRateLimiting indicates whether this particular request should be
|
||||||
|
// dropped by the client for rate limiting.
|
||||||
|
dropForRateLimiting bool
|
||||||
|
// dropForLoadBalancing indicates whether this particular request should be
|
||||||
|
// dropped by the client for load balancing.
|
||||||
|
dropForLoadBalancing bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type balancer struct {
|
||||||
|
r naming.Resolver
|
||||||
|
target string
|
||||||
|
mu sync.Mutex
|
||||||
|
seq int // a sequence number to make sure addrCh does not get stale addresses.
|
||||||
|
w naming.Watcher
|
||||||
|
addrCh chan []Address
|
||||||
|
rbs []remoteBalancerInfo
|
||||||
|
addrs []*grpclbAddrInfo
|
||||||
|
next int
|
||||||
|
waitCh chan struct{}
|
||||||
|
done bool
|
||||||
|
expTimer *time.Timer
|
||||||
|
rand *rand.Rand
|
||||||
|
|
||||||
|
clientStats lbpb.ClientStats
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *balancer) watchAddrUpdates(w naming.Watcher, ch chan []remoteBalancerInfo) error {
|
||||||
|
updates, err := w.Next()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
if b.done {
|
||||||
|
return ErrClientConnClosing
|
||||||
|
}
|
||||||
|
for _, update := range updates {
|
||||||
|
switch update.Op {
|
||||||
|
case naming.Add:
|
||||||
|
var exist bool
|
||||||
|
for _, v := range b.rbs {
|
||||||
|
// TODO: Is the same addr with different server name a different balancer?
|
||||||
|
if update.Addr == v.addr {
|
||||||
|
exist = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if exist {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
md, ok := update.Metadata.(*AddrMetadataGRPCLB)
|
||||||
|
if !ok {
|
||||||
|
// TODO: Revisit the handling here and may introduce some fallback mechanism.
|
||||||
|
grpclog.Printf("The name resolution contains unexpected metadata %v", update.Metadata)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch md.AddrType {
|
||||||
|
case Backend:
|
||||||
|
// TODO: Revisit the handling here and may introduce some fallback mechanism.
|
||||||
|
grpclog.Printf("The name resolution does not give grpclb addresses")
|
||||||
|
continue
|
||||||
|
case GRPCLB:
|
||||||
|
b.rbs = append(b.rbs, remoteBalancerInfo{
|
||||||
|
addr: update.Addr,
|
||||||
|
name: md.ServerName,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
grpclog.Printf("Received unknow address type %d", md.AddrType)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case naming.Delete:
|
||||||
|
for i, v := range b.rbs {
|
||||||
|
if update.Addr == v.addr {
|
||||||
|
copy(b.rbs[i:], b.rbs[i+1:])
|
||||||
|
b.rbs = b.rbs[:len(b.rbs)-1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
grpclog.Println("Unknown update.Op ", update.Op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: Fall back to the basic round-robin load balancing if the resulting address is
|
||||||
|
// not a load balancer.
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
ch <- b.rbs
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *balancer) serverListExpire(seq int) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
// TODO: gRPC interanls do not clear the connections when the server list is stale.
|
||||||
|
// This means RPCs will keep using the existing server list until b receives new
|
||||||
|
// server list even though the list is expired. Revisit this behavior later.
|
||||||
|
if b.done || seq < b.seq {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.next = 0
|
||||||
|
b.addrs = nil
|
||||||
|
// Ask grpc internals to close all the corresponding connections.
|
||||||
|
b.addrCh <- nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertDuration(d *lbpb.Duration) time.Duration {
|
||||||
|
if d == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return time.Duration(d.Seconds)*time.Second + time.Duration(d.Nanos)*time.Nanosecond
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *balancer) processServerList(l *lbpb.ServerList, seq int) {
|
||||||
|
if l == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
servers := l.GetServers()
|
||||||
|
expiration := convertDuration(l.GetExpirationInterval())
|
||||||
|
var (
|
||||||
|
sl []*grpclbAddrInfo
|
||||||
|
addrs []Address
|
||||||
|
)
|
||||||
|
for _, s := range servers {
|
||||||
|
md := metadata.Pairs("lb-token", s.LoadBalanceToken)
|
||||||
|
addr := Address{
|
||||||
|
Addr: fmt.Sprintf("%s:%d", net.IP(s.IpAddress), s.Port),
|
||||||
|
Metadata: &md,
|
||||||
|
}
|
||||||
|
sl = append(sl, &grpclbAddrInfo{
|
||||||
|
addr: addr,
|
||||||
|
dropForRateLimiting: s.DropForRateLimiting,
|
||||||
|
dropForLoadBalancing: s.DropForLoadBalancing,
|
||||||
|
})
|
||||||
|
addrs = append(addrs, addr)
|
||||||
|
}
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
if b.done || seq < b.seq {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(sl) > 0 {
|
||||||
|
// reset b.next to 0 when replacing the server list.
|
||||||
|
b.next = 0
|
||||||
|
b.addrs = sl
|
||||||
|
b.addrCh <- addrs
|
||||||
|
if b.expTimer != nil {
|
||||||
|
b.expTimer.Stop()
|
||||||
|
b.expTimer = nil
|
||||||
|
}
|
||||||
|
if expiration > 0 {
|
||||||
|
b.expTimer = time.AfterFunc(expiration, func() {
|
||||||
|
b.serverListExpire(seq)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *balancer) sendLoadReport(s *balanceLoadClientStream, interval time.Duration, done <-chan struct{}) {
|
||||||
|
ticker := time.NewTicker(interval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
case <-done:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.mu.Lock()
|
||||||
|
stats := b.clientStats
|
||||||
|
b.clientStats = lbpb.ClientStats{} // Clear the stats.
|
||||||
|
b.mu.Unlock()
|
||||||
|
t := time.Now()
|
||||||
|
stats.Timestamp = &lbpb.Timestamp{
|
||||||
|
Seconds: t.Unix(),
|
||||||
|
Nanos: int32(t.Nanosecond()),
|
||||||
|
}
|
||||||
|
if err := s.Send(&lbpb.LoadBalanceRequest{
|
||||||
|
LoadBalanceRequestType: &lbpb.LoadBalanceRequest_ClientStats{
|
||||||
|
ClientStats: &stats,
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *balancer) callRemoteBalancer(lbc *loadBalancerClient, seq int) (retry bool) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
stream, err := lbc.BalanceLoad(ctx)
|
||||||
|
if err != nil {
|
||||||
|
grpclog.Printf("Failed to perform RPC to the remote balancer %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.mu.Lock()
|
||||||
|
if b.done {
|
||||||
|
b.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.mu.Unlock()
|
||||||
|
initReq := &lbpb.LoadBalanceRequest{
|
||||||
|
LoadBalanceRequestType: &lbpb.LoadBalanceRequest_InitialRequest{
|
||||||
|
InitialRequest: &lbpb.InitialLoadBalanceRequest{
|
||||||
|
Name: b.target,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := stream.Send(initReq); err != nil {
|
||||||
|
// TODO: backoff on retry?
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
reply, err := stream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
// TODO: backoff on retry?
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
initResp := reply.GetInitialResponse()
|
||||||
|
if initResp == nil {
|
||||||
|
grpclog.Println("Failed to receive the initial response from the remote balancer.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO: Support delegation.
|
||||||
|
if initResp.LoadBalancerDelegate != "" {
|
||||||
|
// delegation
|
||||||
|
grpclog.Println("TODO: Delegation is not supported yet.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
streamDone := make(chan struct{})
|
||||||
|
defer close(streamDone)
|
||||||
|
b.mu.Lock()
|
||||||
|
b.clientStats = lbpb.ClientStats{} // Clear client stats.
|
||||||
|
b.mu.Unlock()
|
||||||
|
if d := convertDuration(initResp.ClientStatsReportInterval); d > 0 {
|
||||||
|
go b.sendLoadReport(stream, d, streamDone)
|
||||||
|
}
|
||||||
|
// Retrieve the server list.
|
||||||
|
for {
|
||||||
|
reply, err := stream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
b.mu.Lock()
|
||||||
|
if b.done || seq < b.seq {
|
||||||
|
b.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.seq++ // tick when receiving a new list of servers.
|
||||||
|
seq = b.seq
|
||||||
|
b.mu.Unlock()
|
||||||
|
if serverList := reply.GetServerList(); serverList != nil {
|
||||||
|
b.processServerList(serverList, seq)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *balancer) Start(target string, config BalancerConfig) error {
|
||||||
|
b.rand = rand.New(rand.NewSource(time.Now().Unix()))
|
||||||
|
// TODO: Fall back to the basic direct connection if there is no name resolver.
|
||||||
|
if b.r == nil {
|
||||||
|
return errors.New("there is no name resolver installed")
|
||||||
|
}
|
||||||
|
b.target = target
|
||||||
|
b.mu.Lock()
|
||||||
|
if b.done {
|
||||||
|
b.mu.Unlock()
|
||||||
|
return ErrClientConnClosing
|
||||||
|
}
|
||||||
|
b.addrCh = make(chan []Address)
|
||||||
|
w, err := b.r.Resolve(target)
|
||||||
|
if err != nil {
|
||||||
|
b.mu.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.w = w
|
||||||
|
b.mu.Unlock()
|
||||||
|
balancerAddrsCh := make(chan []remoteBalancerInfo, 1)
|
||||||
|
// Spawn a goroutine to monitor the name resolution of remote load balancer.
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
if err := b.watchAddrUpdates(w, balancerAddrsCh); err != nil {
|
||||||
|
grpclog.Printf("grpc: the naming watcher stops working due to %v.\n", err)
|
||||||
|
close(balancerAddrsCh)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// Spawn a goroutine to talk to the remote load balancer.
|
||||||
|
go func() {
|
||||||
|
var (
|
||||||
|
cc *ClientConn
|
||||||
|
// ccError is closed when there is an error in the current cc.
|
||||||
|
// A new rb should be picked from rbs and connected.
|
||||||
|
ccError chan struct{}
|
||||||
|
rb *remoteBalancerInfo
|
||||||
|
rbs []remoteBalancerInfo
|
||||||
|
rbIdx int
|
||||||
|
)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if ccError != nil {
|
||||||
|
select {
|
||||||
|
case <-ccError:
|
||||||
|
default:
|
||||||
|
close(ccError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cc != nil {
|
||||||
|
cc.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
var ok bool
|
||||||
|
select {
|
||||||
|
case rbs, ok = <-balancerAddrsCh:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
foundIdx := -1
|
||||||
|
if rb != nil {
|
||||||
|
for i, trb := range rbs {
|
||||||
|
if trb == *rb {
|
||||||
|
foundIdx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if foundIdx >= 0 {
|
||||||
|
if foundIdx >= 1 {
|
||||||
|
// Move the address in use to the beginning of the list.
|
||||||
|
b.rbs[0], b.rbs[foundIdx] = b.rbs[foundIdx], b.rbs[0]
|
||||||
|
rbIdx = 0
|
||||||
|
}
|
||||||
|
continue // If found, don't dial new cc.
|
||||||
|
} else if len(rbs) > 0 {
|
||||||
|
// Pick a random one from the list, instead of always using the first one.
|
||||||
|
if l := len(rbs); l > 1 && rb != nil {
|
||||||
|
tmpIdx := b.rand.Intn(l - 1)
|
||||||
|
b.rbs[0], b.rbs[tmpIdx] = b.rbs[tmpIdx], b.rbs[0]
|
||||||
|
}
|
||||||
|
rbIdx = 0
|
||||||
|
rb = &rbs[0]
|
||||||
|
} else {
|
||||||
|
// foundIdx < 0 && len(rbs) <= 0.
|
||||||
|
rb = nil
|
||||||
|
}
|
||||||
|
case <-ccError:
|
||||||
|
ccError = nil
|
||||||
|
if rbIdx < len(rbs)-1 {
|
||||||
|
rbIdx++
|
||||||
|
rb = &rbs[rbIdx]
|
||||||
|
} else {
|
||||||
|
rb = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rb == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if cc != nil {
|
||||||
|
cc.Close()
|
||||||
|
}
|
||||||
|
// Talk to the remote load balancer to get the server list.
|
||||||
|
var err error
|
||||||
|
creds := config.DialCreds
|
||||||
|
ccError = make(chan struct{})
|
||||||
|
if creds == nil {
|
||||||
|
cc, err = Dial(rb.addr, WithInsecure())
|
||||||
|
} else {
|
||||||
|
if rb.name != "" {
|
||||||
|
if err := creds.OverrideServerName(rb.name); err != nil {
|
||||||
|
grpclog.Printf("Failed to override the server name in the credentials: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cc, err = Dial(rb.addr, WithTransportCredentials(creds))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
grpclog.Printf("Failed to setup a connection to the remote balancer %v: %v", rb.addr, err)
|
||||||
|
close(ccError)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b.mu.Lock()
|
||||||
|
b.seq++ // tick when getting a new balancer address
|
||||||
|
seq := b.seq
|
||||||
|
b.next = 0
|
||||||
|
b.mu.Unlock()
|
||||||
|
go func(cc *ClientConn, ccError chan struct{}) {
|
||||||
|
lbc := &loadBalancerClient{cc}
|
||||||
|
b.callRemoteBalancer(lbc, seq)
|
||||||
|
cc.Close()
|
||||||
|
select {
|
||||||
|
case <-ccError:
|
||||||
|
default:
|
||||||
|
close(ccError)
|
||||||
|
}
|
||||||
|
}(cc, ccError)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *balancer) down(addr Address, err error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
for _, a := range b.addrs {
|
||||||
|
if addr == a.addr {
|
||||||
|
a.connected = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *balancer) Up(addr Address) func(error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
if b.done {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var cnt int
|
||||||
|
for _, a := range b.addrs {
|
||||||
|
if a.addr == addr {
|
||||||
|
if a.connected {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
a.connected = true
|
||||||
|
}
|
||||||
|
if a.connected && !a.dropForRateLimiting && !a.dropForLoadBalancing {
|
||||||
|
cnt++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// addr is the only one which is connected. Notify the Get() callers who are blocking.
|
||||||
|
if cnt == 1 && b.waitCh != nil {
|
||||||
|
close(b.waitCh)
|
||||||
|
b.waitCh = nil
|
||||||
|
}
|
||||||
|
return func(err error) {
|
||||||
|
b.down(addr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *balancer) Get(ctx context.Context, opts BalancerGetOptions) (addr Address, put func(), err error) {
|
||||||
|
var ch chan struct{}
|
||||||
|
b.mu.Lock()
|
||||||
|
if b.done {
|
||||||
|
b.mu.Unlock()
|
||||||
|
err = ErrClientConnClosing
|
||||||
|
return
|
||||||
|
}
|
||||||
|
seq := b.seq
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
put = func() {
|
||||||
|
s, ok := rpcInfoFromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
if b.done || seq < b.seq {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.clientStats.NumCallsFinished++
|
||||||
|
if !s.bytesSent {
|
||||||
|
b.clientStats.NumCallsFinishedWithClientFailedToSend++
|
||||||
|
} else if s.bytesReceived {
|
||||||
|
b.clientStats.NumCallsFinishedKnownReceived++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
b.clientStats.NumCallsStarted++
|
||||||
|
if len(b.addrs) > 0 {
|
||||||
|
if b.next >= len(b.addrs) {
|
||||||
|
b.next = 0
|
||||||
|
}
|
||||||
|
next := b.next
|
||||||
|
for {
|
||||||
|
a := b.addrs[next]
|
||||||
|
next = (next + 1) % len(b.addrs)
|
||||||
|
if a.connected {
|
||||||
|
if !a.dropForRateLimiting && !a.dropForLoadBalancing {
|
||||||
|
addr = a.addr
|
||||||
|
b.next = next
|
||||||
|
b.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !opts.BlockingWait {
|
||||||
|
b.next = next
|
||||||
|
if a.dropForLoadBalancing {
|
||||||
|
b.clientStats.NumCallsFinished++
|
||||||
|
b.clientStats.NumCallsFinishedWithDropForLoadBalancing++
|
||||||
|
} else if a.dropForRateLimiting {
|
||||||
|
b.clientStats.NumCallsFinished++
|
||||||
|
b.clientStats.NumCallsFinishedWithDropForRateLimiting++
|
||||||
|
}
|
||||||
|
b.mu.Unlock()
|
||||||
|
err = Errorf(codes.Unavailable, "%s drops requests", a.addr.Addr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if next == b.next {
|
||||||
|
// Has iterated all the possible address but none is connected.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !opts.BlockingWait {
|
||||||
|
if len(b.addrs) == 0 {
|
||||||
|
b.clientStats.NumCallsFinished++
|
||||||
|
b.clientStats.NumCallsFinishedWithClientFailedToSend++
|
||||||
|
b.mu.Unlock()
|
||||||
|
err = Errorf(codes.Unavailable, "there is no address available")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Returns the next addr on b.addrs for a failfast RPC.
|
||||||
|
addr = b.addrs[b.next].addr
|
||||||
|
b.next++
|
||||||
|
b.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Wait on b.waitCh for non-failfast RPCs.
|
||||||
|
if b.waitCh == nil {
|
||||||
|
ch = make(chan struct{})
|
||||||
|
b.waitCh = ch
|
||||||
|
} else {
|
||||||
|
ch = b.waitCh
|
||||||
|
}
|
||||||
|
b.mu.Unlock()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
b.mu.Lock()
|
||||||
|
b.clientStats.NumCallsFinished++
|
||||||
|
b.clientStats.NumCallsFinishedWithClientFailedToSend++
|
||||||
|
b.mu.Unlock()
|
||||||
|
err = ctx.Err()
|
||||||
|
return
|
||||||
|
case <-ch:
|
||||||
|
b.mu.Lock()
|
||||||
|
if b.done {
|
||||||
|
b.clientStats.NumCallsFinished++
|
||||||
|
b.clientStats.NumCallsFinishedWithClientFailedToSend++
|
||||||
|
b.mu.Unlock()
|
||||||
|
err = ErrClientConnClosing
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(b.addrs) > 0 {
|
||||||
|
if b.next >= len(b.addrs) {
|
||||||
|
b.next = 0
|
||||||
|
}
|
||||||
|
next := b.next
|
||||||
|
for {
|
||||||
|
a := b.addrs[next]
|
||||||
|
next = (next + 1) % len(b.addrs)
|
||||||
|
if a.connected {
|
||||||
|
if !a.dropForRateLimiting && !a.dropForLoadBalancing {
|
||||||
|
addr = a.addr
|
||||||
|
b.next = next
|
||||||
|
b.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !opts.BlockingWait {
|
||||||
|
b.next = next
|
||||||
|
if a.dropForLoadBalancing {
|
||||||
|
b.clientStats.NumCallsFinished++
|
||||||
|
b.clientStats.NumCallsFinishedWithDropForLoadBalancing++
|
||||||
|
} else if a.dropForRateLimiting {
|
||||||
|
b.clientStats.NumCallsFinished++
|
||||||
|
b.clientStats.NumCallsFinishedWithDropForRateLimiting++
|
||||||
|
}
|
||||||
|
b.mu.Unlock()
|
||||||
|
err = Errorf(codes.Unavailable, "drop requests for the addreess %s", a.addr.Addr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if next == b.next {
|
||||||
|
// Has iterated all the possible address but none is connected.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The newly added addr got removed by Down() again.
|
||||||
|
if b.waitCh == nil {
|
||||||
|
ch = make(chan struct{})
|
||||||
|
b.waitCh = ch
|
||||||
|
} else {
|
||||||
|
ch = b.waitCh
|
||||||
|
}
|
||||||
|
b.mu.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *balancer) Notify() <-chan []Address {
|
||||||
|
return b.addrCh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *balancer) Close() error {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
b.done = true
|
||||||
|
if b.expTimer != nil {
|
||||||
|
b.expTimer.Stop()
|
||||||
|
}
|
||||||
|
if b.waitCh != nil {
|
||||||
|
close(b.waitCh)
|
||||||
|
}
|
||||||
|
if b.addrCh != nil {
|
||||||
|
close(b.addrCh)
|
||||||
|
}
|
||||||
|
if b.w != nil {
|
||||||
|
b.w.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,629 @@
|
||||||
|
// Code generated by protoc-gen-go.
|
||||||
|
// source: grpclb.proto
|
||||||
|
// DO NOT EDIT!
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package grpc_lb_v1 is a generated protocol buffer package.
|
||||||
|
|
||||||
|
It is generated from these files:
|
||||||
|
grpclb.proto
|
||||||
|
|
||||||
|
It has these top-level messages:
|
||||||
|
Duration
|
||||||
|
Timestamp
|
||||||
|
LoadBalanceRequest
|
||||||
|
InitialLoadBalanceRequest
|
||||||
|
ClientStats
|
||||||
|
LoadBalanceResponse
|
||||||
|
InitialLoadBalanceResponse
|
||||||
|
ServerList
|
||||||
|
Server
|
||||||
|
*/
|
||||||
|
package grpc_lb_v1
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
type Duration struct {
|
||||||
|
// Signed seconds of the span of time. Must be from -315,576,000,000
|
||||||
|
// to +315,576,000,000 inclusive.
|
||||||
|
Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"`
|
||||||
|
// Signed fractions of a second at nanosecond resolution of the span
|
||||||
|
// of time. Durations less than one second are represented with a 0
|
||||||
|
// `seconds` field and a positive or negative `nanos` field. For durations
|
||||||
|
// of one second or more, a non-zero value for the `nanos` field must be
|
||||||
|
// of the same sign as the `seconds` field. Must be from -999,999,999
|
||||||
|
// to +999,999,999 inclusive.
|
||||||
|
Nanos int32 `protobuf:"varint,2,opt,name=nanos" json:"nanos,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Duration) Reset() { *m = Duration{} }
|
||||||
|
func (m *Duration) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Duration) ProtoMessage() {}
|
||||||
|
func (*Duration) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||||
|
|
||||||
|
func (m *Duration) GetSeconds() int64 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Seconds
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Duration) GetNanos() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Nanos
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Timestamp struct {
|
||||||
|
// Represents seconds of UTC time since Unix epoch
|
||||||
|
// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
|
||||||
|
// 9999-12-31T23:59:59Z inclusive.
|
||||||
|
Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"`
|
||||||
|
// Non-negative fractions of a second at nanosecond resolution. Negative
|
||||||
|
// second values with fractions must still have non-negative nanos values
|
||||||
|
// that count forward in time. Must be from 0 to 999,999,999
|
||||||
|
// inclusive.
|
||||||
|
Nanos int32 `protobuf:"varint,2,opt,name=nanos" json:"nanos,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Timestamp) Reset() { *m = Timestamp{} }
|
||||||
|
func (m *Timestamp) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Timestamp) ProtoMessage() {}
|
||||||
|
func (*Timestamp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||||
|
|
||||||
|
func (m *Timestamp) GetSeconds() int64 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Seconds
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Timestamp) GetNanos() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Nanos
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoadBalanceRequest struct {
|
||||||
|
// Types that are valid to be assigned to LoadBalanceRequestType:
|
||||||
|
// *LoadBalanceRequest_InitialRequest
|
||||||
|
// *LoadBalanceRequest_ClientStats
|
||||||
|
LoadBalanceRequestType isLoadBalanceRequest_LoadBalanceRequestType `protobuf_oneof:"load_balance_request_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LoadBalanceRequest) Reset() { *m = LoadBalanceRequest{} }
|
||||||
|
func (m *LoadBalanceRequest) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*LoadBalanceRequest) ProtoMessage() {}
|
||||||
|
func (*LoadBalanceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||||
|
|
||||||
|
type isLoadBalanceRequest_LoadBalanceRequestType interface {
|
||||||
|
isLoadBalanceRequest_LoadBalanceRequestType()
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoadBalanceRequest_InitialRequest struct {
|
||||||
|
InitialRequest *InitialLoadBalanceRequest `protobuf:"bytes,1,opt,name=initial_request,json=initialRequest,oneof"`
|
||||||
|
}
|
||||||
|
type LoadBalanceRequest_ClientStats struct {
|
||||||
|
ClientStats *ClientStats `protobuf:"bytes,2,opt,name=client_stats,json=clientStats,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*LoadBalanceRequest_InitialRequest) isLoadBalanceRequest_LoadBalanceRequestType() {}
|
||||||
|
func (*LoadBalanceRequest_ClientStats) isLoadBalanceRequest_LoadBalanceRequestType() {}
|
||||||
|
|
||||||
|
func (m *LoadBalanceRequest) GetLoadBalanceRequestType() isLoadBalanceRequest_LoadBalanceRequestType {
|
||||||
|
if m != nil {
|
||||||
|
return m.LoadBalanceRequestType
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LoadBalanceRequest) GetInitialRequest() *InitialLoadBalanceRequest {
|
||||||
|
if x, ok := m.GetLoadBalanceRequestType().(*LoadBalanceRequest_InitialRequest); ok {
|
||||||
|
return x.InitialRequest
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LoadBalanceRequest) GetClientStats() *ClientStats {
|
||||||
|
if x, ok := m.GetLoadBalanceRequestType().(*LoadBalanceRequest_ClientStats); ok {
|
||||||
|
return x.ClientStats
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX_OneofFuncs is for the internal use of the proto package.
|
||||||
|
func (*LoadBalanceRequest) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
|
||||||
|
return _LoadBalanceRequest_OneofMarshaler, _LoadBalanceRequest_OneofUnmarshaler, _LoadBalanceRequest_OneofSizer, []interface{}{
|
||||||
|
(*LoadBalanceRequest_InitialRequest)(nil),
|
||||||
|
(*LoadBalanceRequest_ClientStats)(nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _LoadBalanceRequest_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
|
||||||
|
m := msg.(*LoadBalanceRequest)
|
||||||
|
// load_balance_request_type
|
||||||
|
switch x := m.LoadBalanceRequestType.(type) {
|
||||||
|
case *LoadBalanceRequest_InitialRequest:
|
||||||
|
b.EncodeVarint(1<<3 | proto.WireBytes)
|
||||||
|
if err := b.EncodeMessage(x.InitialRequest); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *LoadBalanceRequest_ClientStats:
|
||||||
|
b.EncodeVarint(2<<3 | proto.WireBytes)
|
||||||
|
if err := b.EncodeMessage(x.ClientStats); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("LoadBalanceRequest.LoadBalanceRequestType has unexpected type %T", x)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _LoadBalanceRequest_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
|
||||||
|
m := msg.(*LoadBalanceRequest)
|
||||||
|
switch tag {
|
||||||
|
case 1: // load_balance_request_type.initial_request
|
||||||
|
if wire != proto.WireBytes {
|
||||||
|
return true, proto.ErrInternalBadWireType
|
||||||
|
}
|
||||||
|
msg := new(InitialLoadBalanceRequest)
|
||||||
|
err := b.DecodeMessage(msg)
|
||||||
|
m.LoadBalanceRequestType = &LoadBalanceRequest_InitialRequest{msg}
|
||||||
|
return true, err
|
||||||
|
case 2: // load_balance_request_type.client_stats
|
||||||
|
if wire != proto.WireBytes {
|
||||||
|
return true, proto.ErrInternalBadWireType
|
||||||
|
}
|
||||||
|
msg := new(ClientStats)
|
||||||
|
err := b.DecodeMessage(msg)
|
||||||
|
m.LoadBalanceRequestType = &LoadBalanceRequest_ClientStats{msg}
|
||||||
|
return true, err
|
||||||
|
default:
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _LoadBalanceRequest_OneofSizer(msg proto.Message) (n int) {
|
||||||
|
m := msg.(*LoadBalanceRequest)
|
||||||
|
// load_balance_request_type
|
||||||
|
switch x := m.LoadBalanceRequestType.(type) {
|
||||||
|
case *LoadBalanceRequest_InitialRequest:
|
||||||
|
s := proto.Size(x.InitialRequest)
|
||||||
|
n += proto.SizeVarint(1<<3 | proto.WireBytes)
|
||||||
|
n += proto.SizeVarint(uint64(s))
|
||||||
|
n += s
|
||||||
|
case *LoadBalanceRequest_ClientStats:
|
||||||
|
s := proto.Size(x.ClientStats)
|
||||||
|
n += proto.SizeVarint(2<<3 | proto.WireBytes)
|
||||||
|
n += proto.SizeVarint(uint64(s))
|
||||||
|
n += s
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
type InitialLoadBalanceRequest struct {
|
||||||
|
// Name of load balanced service (IE, balancer.service.com)
|
||||||
|
// length should be less than 256 bytes.
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *InitialLoadBalanceRequest) Reset() { *m = InitialLoadBalanceRequest{} }
|
||||||
|
func (m *InitialLoadBalanceRequest) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*InitialLoadBalanceRequest) ProtoMessage() {}
|
||||||
|
func (*InitialLoadBalanceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
||||||
|
|
||||||
|
func (m *InitialLoadBalanceRequest) GetName() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains client level statistics that are useful to load balancing. Each
|
||||||
|
// count except the timestamp should be reset to zero after reporting the stats.
|
||||||
|
type ClientStats struct {
|
||||||
|
// The timestamp of generating the report.
|
||||||
|
Timestamp *Timestamp `protobuf:"bytes,1,opt,name=timestamp" json:"timestamp,omitempty"`
|
||||||
|
// The total number of RPCs that started.
|
||||||
|
NumCallsStarted int64 `protobuf:"varint,2,opt,name=num_calls_started,json=numCallsStarted" json:"num_calls_started,omitempty"`
|
||||||
|
// The total number of RPCs that finished.
|
||||||
|
NumCallsFinished int64 `protobuf:"varint,3,opt,name=num_calls_finished,json=numCallsFinished" json:"num_calls_finished,omitempty"`
|
||||||
|
// The total number of RPCs that were dropped by the client because of rate
|
||||||
|
// limiting.
|
||||||
|
NumCallsFinishedWithDropForRateLimiting int64 `protobuf:"varint,4,opt,name=num_calls_finished_with_drop_for_rate_limiting,json=numCallsFinishedWithDropForRateLimiting" json:"num_calls_finished_with_drop_for_rate_limiting,omitempty"`
|
||||||
|
// The total number of RPCs that were dropped by the client because of load
|
||||||
|
// balancing.
|
||||||
|
NumCallsFinishedWithDropForLoadBalancing int64 `protobuf:"varint,5,opt,name=num_calls_finished_with_drop_for_load_balancing,json=numCallsFinishedWithDropForLoadBalancing" json:"num_calls_finished_with_drop_for_load_balancing,omitempty"`
|
||||||
|
// The total number of RPCs that failed to reach a server except dropped RPCs.
|
||||||
|
NumCallsFinishedWithClientFailedToSend int64 `protobuf:"varint,6,opt,name=num_calls_finished_with_client_failed_to_send,json=numCallsFinishedWithClientFailedToSend" json:"num_calls_finished_with_client_failed_to_send,omitempty"`
|
||||||
|
// The total number of RPCs that finished and are known to have been received
|
||||||
|
// by a server.
|
||||||
|
NumCallsFinishedKnownReceived int64 `protobuf:"varint,7,opt,name=num_calls_finished_known_received,json=numCallsFinishedKnownReceived" json:"num_calls_finished_known_received,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ClientStats) Reset() { *m = ClientStats{} }
|
||||||
|
func (m *ClientStats) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*ClientStats) ProtoMessage() {}
|
||||||
|
func (*ClientStats) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
|
||||||
|
|
||||||
|
func (m *ClientStats) GetTimestamp() *Timestamp {
|
||||||
|
if m != nil {
|
||||||
|
return m.Timestamp
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ClientStats) GetNumCallsStarted() int64 {
|
||||||
|
if m != nil {
|
||||||
|
return m.NumCallsStarted
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ClientStats) GetNumCallsFinished() int64 {
|
||||||
|
if m != nil {
|
||||||
|
return m.NumCallsFinished
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ClientStats) GetNumCallsFinishedWithDropForRateLimiting() int64 {
|
||||||
|
if m != nil {
|
||||||
|
return m.NumCallsFinishedWithDropForRateLimiting
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ClientStats) GetNumCallsFinishedWithDropForLoadBalancing() int64 {
|
||||||
|
if m != nil {
|
||||||
|
return m.NumCallsFinishedWithDropForLoadBalancing
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ClientStats) GetNumCallsFinishedWithClientFailedToSend() int64 {
|
||||||
|
if m != nil {
|
||||||
|
return m.NumCallsFinishedWithClientFailedToSend
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ClientStats) GetNumCallsFinishedKnownReceived() int64 {
|
||||||
|
if m != nil {
|
||||||
|
return m.NumCallsFinishedKnownReceived
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoadBalanceResponse struct {
|
||||||
|
// Types that are valid to be assigned to LoadBalanceResponseType:
|
||||||
|
// *LoadBalanceResponse_InitialResponse
|
||||||
|
// *LoadBalanceResponse_ServerList
|
||||||
|
LoadBalanceResponseType isLoadBalanceResponse_LoadBalanceResponseType `protobuf_oneof:"load_balance_response_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LoadBalanceResponse) Reset() { *m = LoadBalanceResponse{} }
|
||||||
|
func (m *LoadBalanceResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*LoadBalanceResponse) ProtoMessage() {}
|
||||||
|
func (*LoadBalanceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
|
||||||
|
|
||||||
|
type isLoadBalanceResponse_LoadBalanceResponseType interface {
|
||||||
|
isLoadBalanceResponse_LoadBalanceResponseType()
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoadBalanceResponse_InitialResponse struct {
|
||||||
|
InitialResponse *InitialLoadBalanceResponse `protobuf:"bytes,1,opt,name=initial_response,json=initialResponse,oneof"`
|
||||||
|
}
|
||||||
|
type LoadBalanceResponse_ServerList struct {
|
||||||
|
ServerList *ServerList `protobuf:"bytes,2,opt,name=server_list,json=serverList,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*LoadBalanceResponse_InitialResponse) isLoadBalanceResponse_LoadBalanceResponseType() {}
|
||||||
|
func (*LoadBalanceResponse_ServerList) isLoadBalanceResponse_LoadBalanceResponseType() {}
|
||||||
|
|
||||||
|
func (m *LoadBalanceResponse) GetLoadBalanceResponseType() isLoadBalanceResponse_LoadBalanceResponseType {
|
||||||
|
if m != nil {
|
||||||
|
return m.LoadBalanceResponseType
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LoadBalanceResponse) GetInitialResponse() *InitialLoadBalanceResponse {
|
||||||
|
if x, ok := m.GetLoadBalanceResponseType().(*LoadBalanceResponse_InitialResponse); ok {
|
||||||
|
return x.InitialResponse
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LoadBalanceResponse) GetServerList() *ServerList {
|
||||||
|
if x, ok := m.GetLoadBalanceResponseType().(*LoadBalanceResponse_ServerList); ok {
|
||||||
|
return x.ServerList
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX_OneofFuncs is for the internal use of the proto package.
|
||||||
|
func (*LoadBalanceResponse) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
|
||||||
|
return _LoadBalanceResponse_OneofMarshaler, _LoadBalanceResponse_OneofUnmarshaler, _LoadBalanceResponse_OneofSizer, []interface{}{
|
||||||
|
(*LoadBalanceResponse_InitialResponse)(nil),
|
||||||
|
(*LoadBalanceResponse_ServerList)(nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _LoadBalanceResponse_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
|
||||||
|
m := msg.(*LoadBalanceResponse)
|
||||||
|
// load_balance_response_type
|
||||||
|
switch x := m.LoadBalanceResponseType.(type) {
|
||||||
|
case *LoadBalanceResponse_InitialResponse:
|
||||||
|
b.EncodeVarint(1<<3 | proto.WireBytes)
|
||||||
|
if err := b.EncodeMessage(x.InitialResponse); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *LoadBalanceResponse_ServerList:
|
||||||
|
b.EncodeVarint(2<<3 | proto.WireBytes)
|
||||||
|
if err := b.EncodeMessage(x.ServerList); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("LoadBalanceResponse.LoadBalanceResponseType has unexpected type %T", x)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _LoadBalanceResponse_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
|
||||||
|
m := msg.(*LoadBalanceResponse)
|
||||||
|
switch tag {
|
||||||
|
case 1: // load_balance_response_type.initial_response
|
||||||
|
if wire != proto.WireBytes {
|
||||||
|
return true, proto.ErrInternalBadWireType
|
||||||
|
}
|
||||||
|
msg := new(InitialLoadBalanceResponse)
|
||||||
|
err := b.DecodeMessage(msg)
|
||||||
|
m.LoadBalanceResponseType = &LoadBalanceResponse_InitialResponse{msg}
|
||||||
|
return true, err
|
||||||
|
case 2: // load_balance_response_type.server_list
|
||||||
|
if wire != proto.WireBytes {
|
||||||
|
return true, proto.ErrInternalBadWireType
|
||||||
|
}
|
||||||
|
msg := new(ServerList)
|
||||||
|
err := b.DecodeMessage(msg)
|
||||||
|
m.LoadBalanceResponseType = &LoadBalanceResponse_ServerList{msg}
|
||||||
|
return true, err
|
||||||
|
default:
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _LoadBalanceResponse_OneofSizer(msg proto.Message) (n int) {
|
||||||
|
m := msg.(*LoadBalanceResponse)
|
||||||
|
// load_balance_response_type
|
||||||
|
switch x := m.LoadBalanceResponseType.(type) {
|
||||||
|
case *LoadBalanceResponse_InitialResponse:
|
||||||
|
s := proto.Size(x.InitialResponse)
|
||||||
|
n += proto.SizeVarint(1<<3 | proto.WireBytes)
|
||||||
|
n += proto.SizeVarint(uint64(s))
|
||||||
|
n += s
|
||||||
|
case *LoadBalanceResponse_ServerList:
|
||||||
|
s := proto.Size(x.ServerList)
|
||||||
|
n += proto.SizeVarint(2<<3 | proto.WireBytes)
|
||||||
|
n += proto.SizeVarint(uint64(s))
|
||||||
|
n += s
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
type InitialLoadBalanceResponse struct {
|
||||||
|
// This is an application layer redirect that indicates the client should use
|
||||||
|
// the specified server for load balancing. When this field is non-empty in
|
||||||
|
// the response, the client should open a separate connection to the
|
||||||
|
// load_balancer_delegate and call the BalanceLoad method. Its length should
|
||||||
|
// be less than 64 bytes.
|
||||||
|
LoadBalancerDelegate string `protobuf:"bytes,1,opt,name=load_balancer_delegate,json=loadBalancerDelegate" json:"load_balancer_delegate,omitempty"`
|
||||||
|
// This interval defines how often the client should send the client stats
|
||||||
|
// to the load balancer. Stats should only be reported when the duration is
|
||||||
|
// positive.
|
||||||
|
ClientStatsReportInterval *Duration `protobuf:"bytes,2,opt,name=client_stats_report_interval,json=clientStatsReportInterval" json:"client_stats_report_interval,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *InitialLoadBalanceResponse) Reset() { *m = InitialLoadBalanceResponse{} }
|
||||||
|
func (m *InitialLoadBalanceResponse) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*InitialLoadBalanceResponse) ProtoMessage() {}
|
||||||
|
func (*InitialLoadBalanceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
|
||||||
|
|
||||||
|
func (m *InitialLoadBalanceResponse) GetLoadBalancerDelegate() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.LoadBalancerDelegate
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *InitialLoadBalanceResponse) GetClientStatsReportInterval() *Duration {
|
||||||
|
if m != nil {
|
||||||
|
return m.ClientStatsReportInterval
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerList struct {
|
||||||
|
// Contains a list of servers selected by the load balancer. The list will
|
||||||
|
// be updated when server resolutions change or as needed to balance load
|
||||||
|
// across more servers. The client should consume the server list in order
|
||||||
|
// unless instructed otherwise via the client_config.
|
||||||
|
Servers []*Server `protobuf:"bytes,1,rep,name=servers" json:"servers,omitempty"`
|
||||||
|
// Indicates the amount of time that the client should consider this server
|
||||||
|
// list as valid. It may be considered stale after waiting this interval of
|
||||||
|
// time after receiving the list. If the interval is not positive, the
|
||||||
|
// client can assume the list is valid until the next list is received.
|
||||||
|
ExpirationInterval *Duration `protobuf:"bytes,3,opt,name=expiration_interval,json=expirationInterval" json:"expiration_interval,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerList) Reset() { *m = ServerList{} }
|
||||||
|
func (m *ServerList) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*ServerList) ProtoMessage() {}
|
||||||
|
func (*ServerList) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
|
||||||
|
|
||||||
|
func (m *ServerList) GetServers() []*Server {
|
||||||
|
if m != nil {
|
||||||
|
return m.Servers
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerList) GetExpirationInterval() *Duration {
|
||||||
|
if m != nil {
|
||||||
|
return m.ExpirationInterval
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains server information. When none of the [drop_for_*] fields are true,
|
||||||
|
// use the other fields. When drop_for_rate_limiting is true, ignore all other
|
||||||
|
// fields. Use drop_for_load_balancing only when it is true and
|
||||||
|
// drop_for_rate_limiting is false.
|
||||||
|
type Server struct {
|
||||||
|
// A resolved address for the server, serialized in network-byte-order. It may
|
||||||
|
// either be an IPv4 or IPv6 address.
|
||||||
|
IpAddress []byte `protobuf:"bytes,1,opt,name=ip_address,json=ipAddress,proto3" json:"ip_address,omitempty"`
|
||||||
|
// A resolved port number for the server.
|
||||||
|
Port int32 `protobuf:"varint,2,opt,name=port" json:"port,omitempty"`
|
||||||
|
// An opaque but printable token given to the frontend for each pick. All
|
||||||
|
// frontend requests for that pick must include the token in its initial
|
||||||
|
// metadata. The token is used by the backend to verify the request and to
|
||||||
|
// allow the backend to report load to the gRPC LB system.
|
||||||
|
//
|
||||||
|
// Its length is variable but less than 50 bytes.
|
||||||
|
LoadBalanceToken string `protobuf:"bytes,3,opt,name=load_balance_token,json=loadBalanceToken" json:"load_balance_token,omitempty"`
|
||||||
|
// Indicates whether this particular request should be dropped by the client
|
||||||
|
// for rate limiting.
|
||||||
|
DropForRateLimiting bool `protobuf:"varint,4,opt,name=drop_for_rate_limiting,json=dropForRateLimiting" json:"drop_for_rate_limiting,omitempty"`
|
||||||
|
// Indicates whether this particular request should be dropped by the client
|
||||||
|
// for load balancing.
|
||||||
|
DropForLoadBalancing bool `protobuf:"varint,5,opt,name=drop_for_load_balancing,json=dropForLoadBalancing" json:"drop_for_load_balancing,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Server) Reset() { *m = Server{} }
|
||||||
|
func (m *Server) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Server) ProtoMessage() {}
|
||||||
|
func (*Server) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
|
||||||
|
|
||||||
|
func (m *Server) GetIpAddress() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.IpAddress
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Server) GetPort() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Port
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Server) GetLoadBalanceToken() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.LoadBalanceToken
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Server) GetDropForRateLimiting() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.DropForRateLimiting
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Server) GetDropForLoadBalancing() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.DropForLoadBalancing
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*Duration)(nil), "grpc.lb.v1.Duration")
|
||||||
|
proto.RegisterType((*Timestamp)(nil), "grpc.lb.v1.Timestamp")
|
||||||
|
proto.RegisterType((*LoadBalanceRequest)(nil), "grpc.lb.v1.LoadBalanceRequest")
|
||||||
|
proto.RegisterType((*InitialLoadBalanceRequest)(nil), "grpc.lb.v1.InitialLoadBalanceRequest")
|
||||||
|
proto.RegisterType((*ClientStats)(nil), "grpc.lb.v1.ClientStats")
|
||||||
|
proto.RegisterType((*LoadBalanceResponse)(nil), "grpc.lb.v1.LoadBalanceResponse")
|
||||||
|
proto.RegisterType((*InitialLoadBalanceResponse)(nil), "grpc.lb.v1.InitialLoadBalanceResponse")
|
||||||
|
proto.RegisterType((*ServerList)(nil), "grpc.lb.v1.ServerList")
|
||||||
|
proto.RegisterType((*Server)(nil), "grpc.lb.v1.Server")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("grpclb.proto", fileDescriptor0) }
|
||||||
|
|
||||||
|
var fileDescriptor0 = []byte{
|
||||||
|
// 733 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0xdd, 0x4e, 0x1b, 0x39,
|
||||||
|
0x14, 0x66, 0x36, 0xfc, 0xe5, 0x24, 0x5a, 0x58, 0x93, 0x85, 0xc0, 0xc2, 0x2e, 0x1b, 0xa9, 0x34,
|
||||||
|
0xaa, 0x68, 0x68, 0x43, 0x7b, 0xd1, 0x9f, 0x9b, 0x02, 0x45, 0x41, 0xe5, 0xa2, 0x72, 0xa8, 0x7a,
|
||||||
|
0x55, 0x59, 0x4e, 0xc6, 0x80, 0xc5, 0xc4, 0x9e, 0xda, 0x4e, 0x68, 0x2f, 0x7b, 0xd9, 0x47, 0xe9,
|
||||||
|
0x63, 0x54, 0x7d, 0x86, 0xbe, 0x4f, 0x65, 0x7b, 0x26, 0x33, 0x90, 0x1f, 0xd4, 0xbb, 0xf1, 0xf1,
|
||||||
|
0x77, 0xbe, 0xf3, 0xf9, 0xd8, 0xdf, 0x19, 0x28, 0x5f, 0xa8, 0xb8, 0x1b, 0x75, 0x1a, 0xb1, 0x92,
|
||||||
|
0x46, 0x22, 0xb0, 0xab, 0x46, 0xd4, 0x69, 0x0c, 0x1e, 0xd7, 0x9e, 0xc3, 0xe2, 0x51, 0x5f, 0x51,
|
||||||
|
0xc3, 0xa5, 0x40, 0x55, 0x58, 0xd0, 0xac, 0x2b, 0x45, 0xa8, 0xab, 0xc1, 0x76, 0x50, 0x2f, 0xe0,
|
||||||
|
0x74, 0x89, 0x2a, 0x30, 0x27, 0xa8, 0x90, 0xba, 0xfa, 0xc7, 0x76, 0x50, 0x9f, 0xc3, 0x7e, 0x51,
|
||||||
|
0x7b, 0x01, 0xc5, 0x33, 0xde, 0x63, 0xda, 0xd0, 0x5e, 0xfc, 0xdb, 0xc9, 0xdf, 0x03, 0x40, 0xa7,
|
||||||
|
0x92, 0x86, 0x07, 0x34, 0xa2, 0xa2, 0xcb, 0x30, 0xfb, 0xd8, 0x67, 0xda, 0xa0, 0xb7, 0xb0, 0xc4,
|
||||||
|
0x05, 0x37, 0x9c, 0x46, 0x44, 0xf9, 0x90, 0xa3, 0x2b, 0x35, 0xef, 0x35, 0x32, 0xd5, 0x8d, 0x13,
|
||||||
|
0x0f, 0x19, 0xcd, 0x6f, 0xcd, 0xe0, 0x3f, 0x93, 0xfc, 0x94, 0xf1, 0x25, 0x94, 0xbb, 0x11, 0x67,
|
||||||
|
0xc2, 0x10, 0x6d, 0xa8, 0xf1, 0x2a, 0x4a, 0xcd, 0xb5, 0x3c, 0xdd, 0xa1, 0xdb, 0x6f, 0xdb, 0xed,
|
||||||
|
0xd6, 0x0c, 0x2e, 0x75, 0xb3, 0xe5, 0xc1, 0x3f, 0xb0, 0x1e, 0x49, 0x1a, 0x92, 0x8e, 0x2f, 0x93,
|
||||||
|
0x8a, 0x22, 0xe6, 0x73, 0xcc, 0x6a, 0x7b, 0xb0, 0x3e, 0x51, 0x09, 0x42, 0x30, 0x2b, 0x68, 0x8f,
|
||||||
|
0x39, 0xf9, 0x45, 0xec, 0xbe, 0x6b, 0x5f, 0x67, 0xa1, 0x94, 0x2b, 0x86, 0xf6, 0xa1, 0x68, 0xd2,
|
||||||
|
0x0e, 0x26, 0xe7, 0xfc, 0x3b, 0x2f, 0x6c, 0xd8, 0x5e, 0x9c, 0xe1, 0xd0, 0x03, 0xf8, 0x4b, 0xf4,
|
||||||
|
0x7b, 0xa4, 0x4b, 0xa3, 0x48, 0xdb, 0x33, 0x29, 0xc3, 0x42, 0x77, 0xaa, 0x02, 0x5e, 0x12, 0xfd,
|
||||||
|
0xde, 0xa1, 0x8d, 0xb7, 0x7d, 0x18, 0xed, 0x02, 0xca, 0xb0, 0xe7, 0x5c, 0x70, 0x7d, 0xc9, 0xc2,
|
||||||
|
0x6a, 0xc1, 0x81, 0x97, 0x53, 0xf0, 0x71, 0x12, 0x47, 0x04, 0x1a, 0xa3, 0x68, 0x72, 0xcd, 0xcd,
|
||||||
|
0x25, 0x09, 0x95, 0x8c, 0xc9, 0xb9, 0x54, 0x44, 0x51, 0xc3, 0x48, 0xc4, 0x7b, 0xdc, 0x70, 0x71,
|
||||||
|
0x51, 0x9d, 0x75, 0x4c, 0xf7, 0x6f, 0x33, 0xbd, 0xe7, 0xe6, 0xf2, 0x48, 0xc9, 0xf8, 0x58, 0x2a,
|
||||||
|
0x4c, 0x0d, 0x3b, 0x4d, 0xe0, 0x88, 0xc2, 0xde, 0x9d, 0x05, 0x72, 0xed, 0xb6, 0x15, 0xe6, 0x5c,
|
||||||
|
0x85, 0xfa, 0x94, 0x0a, 0x59, 0xef, 0x6d, 0x89, 0x0f, 0xf0, 0x70, 0x52, 0x89, 0xe4, 0x19, 0x9c,
|
||||||
|
0x53, 0x1e, 0xb1, 0x90, 0x18, 0x49, 0x34, 0x13, 0x61, 0x75, 0xde, 0x15, 0xd8, 0x19, 0x57, 0xc0,
|
||||||
|
0x5f, 0xd5, 0xb1, 0xc3, 0x9f, 0xc9, 0x36, 0x13, 0x21, 0x6a, 0xc1, 0xff, 0x63, 0xe8, 0xaf, 0x84,
|
||||||
|
0xbc, 0x16, 0x44, 0xb1, 0x2e, 0xe3, 0x03, 0x16, 0x56, 0x17, 0x1c, 0xe5, 0xd6, 0x6d, 0xca, 0x37,
|
||||||
|
0x16, 0x85, 0x13, 0x50, 0xed, 0x47, 0x00, 0x2b, 0x37, 0x9e, 0x8d, 0x8e, 0xa5, 0xd0, 0x0c, 0xb5,
|
||||||
|
0x61, 0x39, 0x73, 0x80, 0x8f, 0x25, 0x4f, 0x63, 0xe7, 0x2e, 0x0b, 0x78, 0x74, 0x6b, 0x06, 0x2f,
|
||||||
|
0x0d, 0x3d, 0x90, 0x90, 0x3e, 0x83, 0x92, 0x66, 0x6a, 0xc0, 0x14, 0x89, 0xb8, 0x36, 0x89, 0x07,
|
||||||
|
0x56, 0xf3, 0x7c, 0x6d, 0xb7, 0x7d, 0xca, 0x9d, 0x87, 0x40, 0x0f, 0x57, 0x07, 0x9b, 0xb0, 0x71,
|
||||||
|
0xcb, 0x01, 0x9e, 0xd3, 0x5b, 0xe0, 0x5b, 0x00, 0x1b, 0x93, 0xa5, 0xa0, 0x27, 0xb0, 0x9a, 0x4f,
|
||||||
|
0x56, 0x24, 0x64, 0x11, 0xbb, 0xa0, 0x26, 0xb5, 0x45, 0x25, 0xca, 0x92, 0xd4, 0x51, 0xb2, 0x87,
|
||||||
|
0xde, 0xc1, 0x66, 0xde, 0xb2, 0x44, 0xb1, 0x58, 0x2a, 0x43, 0xb8, 0x30, 0x4c, 0x0d, 0x68, 0x94,
|
||||||
|
0xc8, 0xaf, 0xe4, 0xe5, 0xa7, 0x43, 0x0c, 0xaf, 0xe7, 0xdc, 0x8b, 0x5d, 0xde, 0x49, 0x92, 0x56,
|
||||||
|
0xfb, 0x12, 0x00, 0x64, 0xc7, 0x44, 0xbb, 0x76, 0x62, 0xd9, 0x95, 0x9d, 0x58, 0x85, 0x7a, 0xa9,
|
||||||
|
0x89, 0x46, 0xfb, 0x81, 0x53, 0x08, 0x7a, 0x0d, 0x2b, 0xec, 0x53, 0xcc, 0x7d, 0x95, 0x4c, 0x4a,
|
||||||
|
0x61, 0x8a, 0x14, 0x94, 0x25, 0x0c, 0x35, 0xfc, 0x0c, 0x60, 0xde, 0x53, 0xa3, 0x2d, 0x00, 0x1e,
|
||||||
|
0x13, 0x1a, 0x86, 0x8a, 0x69, 0x3f, 0x34, 0xcb, 0xb8, 0xc8, 0xe3, 0x57, 0x3e, 0x60, 0xe7, 0x87,
|
||||||
|
0x55, 0x9f, 0x4c, 0x4d, 0xf7, 0x6d, 0xed, 0x7c, 0xe3, 0x2e, 0x8c, 0xbc, 0x62, 0xc2, 0x69, 0x28,
|
||||||
|
0xe2, 0xe5, 0x5c, 0x2b, 0xcf, 0x6c, 0x1c, 0xed, 0xc3, 0xea, 0x14, 0xdb, 0x2e, 0xe2, 0x95, 0x70,
|
||||||
|
0x8c, 0x45, 0x9f, 0xc2, 0xda, 0x34, 0x2b, 0x2e, 0xe2, 0x4a, 0x38, 0xc6, 0x76, 0xcd, 0x0e, 0x94,
|
||||||
|
0x73, 0xf7, 0xaf, 0x10, 0x86, 0x52, 0xf2, 0x6d, 0xc3, 0xe8, 0xdf, 0x7c, 0x83, 0x46, 0x87, 0xe5,
|
||||||
|
0xc6, 0x7f, 0x13, 0xf7, 0xfd, 0x43, 0xaa, 0x07, 0x8f, 0x82, 0xce, 0xbc, 0xfb, 0x7d, 0xed, 0xff,
|
||||||
|
0x0a, 0x00, 0x00, 0xff, 0xff, 0x64, 0xbf, 0xda, 0x5e, 0xce, 0x06, 0x00, 0x00,
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package grpc.lb.v1;
|
||||||
|
|
||||||
|
message Duration {
|
||||||
|
// Signed seconds of the span of time. Must be from -315,576,000,000
|
||||||
|
// to +315,576,000,000 inclusive.
|
||||||
|
int64 seconds = 1;
|
||||||
|
|
||||||
|
// Signed fractions of a second at nanosecond resolution of the span
|
||||||
|
// of time. Durations less than one second are represented with a 0
|
||||||
|
// `seconds` field and a positive or negative `nanos` field. For durations
|
||||||
|
// of one second or more, a non-zero value for the `nanos` field must be
|
||||||
|
// of the same sign as the `seconds` field. Must be from -999,999,999
|
||||||
|
// to +999,999,999 inclusive.
|
||||||
|
int32 nanos = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Timestamp {
|
||||||
|
|
||||||
|
// Represents seconds of UTC time since Unix epoch
|
||||||
|
// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
|
||||||
|
// 9999-12-31T23:59:59Z inclusive.
|
||||||
|
int64 seconds = 1;
|
||||||
|
|
||||||
|
// Non-negative fractions of a second at nanosecond resolution. Negative
|
||||||
|
// second values with fractions must still have non-negative nanos values
|
||||||
|
// that count forward in time. Must be from 0 to 999,999,999
|
||||||
|
// inclusive.
|
||||||
|
int32 nanos = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
service LoadBalancer {
|
||||||
|
// Bidirectional rpc to get a list of servers.
|
||||||
|
rpc BalanceLoad(stream LoadBalanceRequest)
|
||||||
|
returns (stream LoadBalanceResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
message LoadBalanceRequest {
|
||||||
|
oneof load_balance_request_type {
|
||||||
|
// This message should be sent on the first request to the load balancer.
|
||||||
|
InitialLoadBalanceRequest initial_request = 1;
|
||||||
|
|
||||||
|
// The client stats should be periodically reported to the load balancer
|
||||||
|
// based on the duration defined in the InitialLoadBalanceResponse.
|
||||||
|
ClientStats client_stats = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message InitialLoadBalanceRequest {
|
||||||
|
// Name of load balanced service (IE, balancer.service.com)
|
||||||
|
// length should be less than 256 bytes.
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains client level statistics that are useful to load balancing. Each
|
||||||
|
// count except the timestamp should be reset to zero after reporting the stats.
|
||||||
|
message ClientStats {
|
||||||
|
// The timestamp of generating the report.
|
||||||
|
Timestamp timestamp = 1;
|
||||||
|
|
||||||
|
// The total number of RPCs that started.
|
||||||
|
int64 num_calls_started = 2;
|
||||||
|
|
||||||
|
// The total number of RPCs that finished.
|
||||||
|
int64 num_calls_finished = 3;
|
||||||
|
|
||||||
|
// The total number of RPCs that were dropped by the client because of rate
|
||||||
|
// limiting.
|
||||||
|
int64 num_calls_finished_with_drop_for_rate_limiting = 4;
|
||||||
|
|
||||||
|
// The total number of RPCs that were dropped by the client because of load
|
||||||
|
// balancing.
|
||||||
|
int64 num_calls_finished_with_drop_for_load_balancing = 5;
|
||||||
|
|
||||||
|
// The total number of RPCs that failed to reach a server except dropped RPCs.
|
||||||
|
int64 num_calls_finished_with_client_failed_to_send = 6;
|
||||||
|
|
||||||
|
// The total number of RPCs that finished and are known to have been received
|
||||||
|
// by a server.
|
||||||
|
int64 num_calls_finished_known_received = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LoadBalanceResponse {
|
||||||
|
oneof load_balance_response_type {
|
||||||
|
// This message should be sent on the first response to the client.
|
||||||
|
InitialLoadBalanceResponse initial_response = 1;
|
||||||
|
|
||||||
|
// Contains the list of servers selected by the load balancer. The client
|
||||||
|
// should send requests to these servers in the specified order.
|
||||||
|
ServerList server_list = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message InitialLoadBalanceResponse {
|
||||||
|
// This is an application layer redirect that indicates the client should use
|
||||||
|
// the specified server for load balancing. When this field is non-empty in
|
||||||
|
// the response, the client should open a separate connection to the
|
||||||
|
// load_balancer_delegate and call the BalanceLoad method. Its length should
|
||||||
|
// be less than 64 bytes.
|
||||||
|
string load_balancer_delegate = 1;
|
||||||
|
|
||||||
|
// This interval defines how often the client should send the client stats
|
||||||
|
// to the load balancer. Stats should only be reported when the duration is
|
||||||
|
// positive.
|
||||||
|
Duration client_stats_report_interval = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ServerList {
|
||||||
|
// Contains a list of servers selected by the load balancer. The list will
|
||||||
|
// be updated when server resolutions change or as needed to balance load
|
||||||
|
// across more servers. The client should consume the server list in order
|
||||||
|
// unless instructed otherwise via the client_config.
|
||||||
|
repeated Server servers = 1;
|
||||||
|
|
||||||
|
// Indicates the amount of time that the client should consider this server
|
||||||
|
// list as valid. It may be considered stale after waiting this interval of
|
||||||
|
// time after receiving the list. If the interval is not positive, the
|
||||||
|
// client can assume the list is valid until the next list is received.
|
||||||
|
Duration expiration_interval = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains server information. When none of the [drop_for_*] fields are true,
|
||||||
|
// use the other fields. When drop_for_rate_limiting is true, ignore all other
|
||||||
|
// fields. Use drop_for_load_balancing only when it is true and
|
||||||
|
// drop_for_rate_limiting is false.
|
||||||
|
message Server {
|
||||||
|
// A resolved address for the server, serialized in network-byte-order. It may
|
||||||
|
// either be an IPv4 or IPv6 address.
|
||||||
|
bytes ip_address = 1;
|
||||||
|
|
||||||
|
// A resolved port number for the server.
|
||||||
|
int32 port = 2;
|
||||||
|
|
||||||
|
// An opaque but printable token given to the frontend for each pick. All
|
||||||
|
// frontend requests for that pick must include the token in its initial
|
||||||
|
// metadata. The token is used by the backend to verify the request and to
|
||||||
|
// allow the backend to report load to the gRPC LB system.
|
||||||
|
//
|
||||||
|
// Its length is variable but less than 50 bytes.
|
||||||
|
string load_balance_token = 3;
|
||||||
|
|
||||||
|
// Indicates whether this particular request should be dropped by the client
|
||||||
|
// for rate limiting.
|
||||||
|
bool drop_for_rate_limiting = 4;
|
||||||
|
|
||||||
|
// Indicates whether this particular request should be dropped by the client
|
||||||
|
// for load balancing.
|
||||||
|
bool drop_for_load_balancing = 5;
|
||||||
|
}
|
|
@ -40,7 +40,7 @@ import (
|
||||||
// UnaryInvoker is called by UnaryClientInterceptor to complete RPCs.
|
// UnaryInvoker is called by UnaryClientInterceptor to complete RPCs.
|
||||||
type UnaryInvoker func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, opts ...CallOption) error
|
type UnaryInvoker func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, opts ...CallOption) error
|
||||||
|
|
||||||
// UnaryClientInterceptor intercepts the execution of a unary RPC on the client. inovker is the handler to complete the RPC
|
// UnaryClientInterceptor intercepts the execution of a unary RPC on the client. invoker is the handler to complete the RPC
|
||||||
// and it is the responsibility of the interceptor to call it.
|
// and it is the responsibility of the interceptor to call it.
|
||||||
// This is the EXPERIMENTAL API.
|
// This is the EXPERIMENTAL API.
|
||||||
type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error
|
type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2017, 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 keepalive defines configurable parameters for point-to-point healthcheck.
|
||||||
|
package keepalive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClientParameters is used to set keepalive parameters on the client-side.
|
||||||
|
// These configure how the client will actively probe to notice when a connection broken
|
||||||
|
// and to cause activity so intermediaries are aware the connection is still in use.
|
||||||
|
// Make sure these parameters are set in coordination with the keepalive policy on the server,
|
||||||
|
// as incompatible settings can result in closing of connection.
|
||||||
|
type ClientParameters struct {
|
||||||
|
// After a duration of this time if the client doesn't see any activity it pings the server to see if the transport is still alive.
|
||||||
|
Time time.Duration // The current default value is infinity.
|
||||||
|
// After having pinged for keepalive check, the client waits for a duration of Timeout and if no activity is seen even after that
|
||||||
|
// the connection is closed.
|
||||||
|
Timeout time.Duration // The current default value is 20 seconds.
|
||||||
|
// If true, client runs keepalive checks even with no active RPCs.
|
||||||
|
PermitWithoutStream bool // false by default.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerParameters is used to set keepalive and max-age parameters on the server-side.
|
||||||
|
type ServerParameters struct {
|
||||||
|
// MaxConnectionIdle is a duration for the amount of time after which an idle connection would be closed by sending a GoAway.
|
||||||
|
// Idleness duration is defined since the most recent time the number of outstanding RPCs became zero or the connection establishment.
|
||||||
|
MaxConnectionIdle time.Duration // The current default value is infinity.
|
||||||
|
// MaxConnectionAge is a duration for the maximum amount of time a connection may exist before it will be closed by sending a GoAway.
|
||||||
|
// A random jitter of +/-10% will be added to MaxConnectionAge to spread out connection storms.
|
||||||
|
MaxConnectionAge time.Duration // The current default value is infinity.
|
||||||
|
// MaxConnectinoAgeGrace is an additive period after MaxConnectionAge after which the connection will be forcibly closed.
|
||||||
|
MaxConnectionAgeGrace time.Duration // The current default value is infinity.
|
||||||
|
// After a duration of this time if the server doesn't see any activity it pings the client to see if the transport is still alive.
|
||||||
|
Time time.Duration // The current default value is 2 hours.
|
||||||
|
// After having pinged for keepalive check, the server waits for a duration of Timeout and if no activity is seen even after that
|
||||||
|
// the connection is closed.
|
||||||
|
Timeout time.Duration // The current default value is 20 seconds.
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnforcementPolicy is used to set keepalive enforcement policy on the server-side.
|
||||||
|
// Server will close connection with a client that violates this policy.
|
||||||
|
type EnforcementPolicy struct {
|
||||||
|
// MinTime is the minimum amount of time a client should wait before sending a keepalive ping.
|
||||||
|
MinTime time.Duration // The current default value is 5 minutes.
|
||||||
|
// If true, server expects keepalive pings even when there are no active streams(RPCs).
|
||||||
|
PermitWithoutStream bool // false by default.
|
||||||
|
}
|
|
@ -32,49 +32,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Package metadata define the structure of the metadata supported by gRPC library.
|
// Package metadata define the structure of the metadata supported by gRPC library.
|
||||||
|
// Please refer to http://www.grpc.io/docs/guides/wire.html for more information about custom-metadata.
|
||||||
package metadata // import "google.golang.org/grpc/metadata"
|
package metadata // import "google.golang.org/grpc/metadata"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// DecodeKeyValue returns k, v, nil. It is deprecated and should not be used.
|
||||||
binHdrSuffix = "-bin"
|
|
||||||
)
|
|
||||||
|
|
||||||
// encodeKeyValue encodes key and value qualified for transmission via gRPC.
|
|
||||||
// Transmitting binary headers violates HTTP/2 spec.
|
|
||||||
// TODO(zhaoq): Maybe check if k is ASCII also.
|
|
||||||
func encodeKeyValue(k, v string) (string, string) {
|
|
||||||
k = strings.ToLower(k)
|
|
||||||
if strings.HasSuffix(k, binHdrSuffix) {
|
|
||||||
val := base64.StdEncoding.EncodeToString([]byte(v))
|
|
||||||
v = string(val)
|
|
||||||
}
|
|
||||||
return k, v
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeKeyValue returns the original key and value corresponding to the
|
|
||||||
// encoded data in k, v.
|
|
||||||
// If k is a binary header and v contains comma, v is split on comma before decoded,
|
|
||||||
// and the decoded v will be joined with comma before returned.
|
|
||||||
func DecodeKeyValue(k, v string) (string, string, error) {
|
func DecodeKeyValue(k, v string) (string, string, error) {
|
||||||
if !strings.HasSuffix(k, binHdrSuffix) {
|
return k, v, nil
|
||||||
return k, v, nil
|
|
||||||
}
|
|
||||||
vvs := strings.Split(v, ",")
|
|
||||||
for i, vv := range vvs {
|
|
||||||
val, err := base64.StdEncoding.DecodeString(vv)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
vvs[i] = string(val)
|
|
||||||
}
|
|
||||||
return k, strings.Join(vvs, ","), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MD is a mapping from metadata keys to values. Users should use the following
|
// MD is a mapping from metadata keys to values. Users should use the following
|
||||||
|
@ -82,10 +52,11 @@ func DecodeKeyValue(k, v string) (string, string, error) {
|
||||||
type MD map[string][]string
|
type MD map[string][]string
|
||||||
|
|
||||||
// New creates a MD from given key-value map.
|
// New creates a MD from given key-value map.
|
||||||
|
// Keys are automatically converted to lowercase.
|
||||||
func New(m map[string]string) MD {
|
func New(m map[string]string) MD {
|
||||||
md := MD{}
|
md := MD{}
|
||||||
for k, v := range m {
|
for k, val := range m {
|
||||||
key, val := encodeKeyValue(k, v)
|
key := strings.ToLower(k)
|
||||||
md[key] = append(md[key], val)
|
md[key] = append(md[key], val)
|
||||||
}
|
}
|
||||||
return md
|
return md
|
||||||
|
@ -93,19 +64,19 @@ func New(m map[string]string) MD {
|
||||||
|
|
||||||
// Pairs returns an MD formed by the mapping of key, value ...
|
// Pairs returns an MD formed by the mapping of key, value ...
|
||||||
// Pairs panics if len(kv) is odd.
|
// Pairs panics if len(kv) is odd.
|
||||||
|
// Keys are automatically converted to lowercase.
|
||||||
func Pairs(kv ...string) MD {
|
func Pairs(kv ...string) MD {
|
||||||
if len(kv)%2 == 1 {
|
if len(kv)%2 == 1 {
|
||||||
panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv)))
|
panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv)))
|
||||||
}
|
}
|
||||||
md := MD{}
|
md := MD{}
|
||||||
var k string
|
var key string
|
||||||
for i, s := range kv {
|
for i, s := range kv {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
k = s
|
key = strings.ToLower(s)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
key, val := encodeKeyValue(k, s)
|
md[key] = append(md[key], s)
|
||||||
md[key] = append(md[key], val)
|
|
||||||
}
|
}
|
||||||
return md
|
return md
|
||||||
}
|
}
|
||||||
|
@ -133,15 +104,41 @@ func Join(mds ...MD) MD {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
type mdKey struct{}
|
type mdIncomingKey struct{}
|
||||||
|
type mdOutgoingKey struct{}
|
||||||
|
|
||||||
// NewContext creates a new context with md attached.
|
// NewContext is a wrapper for NewOutgoingContext(ctx, md). Deprecated.
|
||||||
func NewContext(ctx context.Context, md MD) context.Context {
|
func NewContext(ctx context.Context, md MD) context.Context {
|
||||||
return context.WithValue(ctx, mdKey{}, md)
|
return NewOutgoingContext(ctx, md)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromContext returns the MD in ctx if it exists.
|
// NewIncomingContext creates a new context with incoming md attached.
|
||||||
|
func NewIncomingContext(ctx context.Context, md MD) context.Context {
|
||||||
|
return context.WithValue(ctx, mdIncomingKey{}, md)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOutgoingContext creates a new context with outgoing md attached.
|
||||||
|
func NewOutgoingContext(ctx context.Context, md MD) context.Context {
|
||||||
|
return context.WithValue(ctx, mdOutgoingKey{}, md)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromContext is a wrapper for FromIncomingContext(ctx). Deprecated.
|
||||||
func FromContext(ctx context.Context) (md MD, ok bool) {
|
func FromContext(ctx context.Context) (md MD, ok bool) {
|
||||||
md, ok = ctx.Value(mdKey{}).(MD)
|
return FromIncomingContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromIncomingContext returns the incoming MD in ctx if it exists. The
|
||||||
|
// returned md should be immutable, writing to it may cause races.
|
||||||
|
// Modification should be made to the copies of the returned md.
|
||||||
|
func FromIncomingContext(ctx context.Context) (md MD, ok bool) {
|
||||||
|
md, ok = ctx.Value(mdIncomingKey{}).(MD)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromOutgoingContext returns the outgoing MD in ctx if it exists. The
|
||||||
|
// returned md should be immutable, writing to it may cause races.
|
||||||
|
// Modification should be made to the copies of the returned md.
|
||||||
|
func FromOutgoingContext(ctx context.Context) (md MD, ok bool) {
|
||||||
|
md, ok = ctx.Value(mdOutgoingKey{}).(MD)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2017, 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 (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// errDisabled indicates that proxy is disabled for the address.
|
||||||
|
errDisabled = errors.New("proxy is disabled for the address")
|
||||||
|
// The following variable will be overwritten in the tests.
|
||||||
|
httpProxyFromEnvironment = http.ProxyFromEnvironment
|
||||||
|
)
|
||||||
|
|
||||||
|
func mapAddress(ctx context.Context, address string) (string, error) {
|
||||||
|
req := &http.Request{
|
||||||
|
URL: &url.URL{
|
||||||
|
Scheme: "https",
|
||||||
|
Host: address,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
url, err := httpProxyFromEnvironment(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if url == nil {
|
||||||
|
return "", errDisabled
|
||||||
|
}
|
||||||
|
return url.Host, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// To read a response from a net.Conn, http.ReadResponse() takes a bufio.Reader.
|
||||||
|
// It's possible that this reader reads more than what's need for the response and stores
|
||||||
|
// those bytes in the buffer.
|
||||||
|
// bufConn wraps the original net.Conn and the bufio.Reader to make sure we don't lose the
|
||||||
|
// bytes in the buffer.
|
||||||
|
type bufConn struct {
|
||||||
|
net.Conn
|
||||||
|
r io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *bufConn) Read(b []byte) (int, error) {
|
||||||
|
return c.r.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, addr string) (_ net.Conn, err error) {
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
req := (&http.Request{
|
||||||
|
Method: http.MethodConnect,
|
||||||
|
URL: &url.URL{Host: addr},
|
||||||
|
Header: map[string][]string{"User-Agent": {grpcUA}},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := sendHTTPRequest(ctx, req, conn); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to write the HTTP request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := bufio.NewReader(conn)
|
||||||
|
resp, err := http.ReadResponse(r, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("reading server HTTP response: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
dump, err := httputil.DumpResponse(resp, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to do connect handshake, status code: %s", resp.Status)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("failed to do connect handshake, response: %q", dump)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &bufConn{Conn: conn, r: r}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newProxyDialer returns a dialer that connects to proxy first if necessary.
|
||||||
|
// The returned dialer checks if a proxy is necessary, dial to the proxy with the
|
||||||
|
// provided dialer, does HTTP CONNECT handshake and returns the connection.
|
||||||
|
func newProxyDialer(dialer func(context.Context, string) (net.Conn, error)) func(context.Context, string) (net.Conn, error) {
|
||||||
|
return func(ctx context.Context, addr string) (conn net.Conn, err error) {
|
||||||
|
var skipHandshake bool
|
||||||
|
newAddr, err := mapAddress(ctx, addr)
|
||||||
|
if err != nil {
|
||||||
|
if err != errDisabled {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
skipHandshake = true
|
||||||
|
newAddr = addr
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err = dialer(ctx, newAddr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !skipHandshake {
|
||||||
|
conn, err = doHTTPConnectHandshake(ctx, conn, addr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,45 +37,21 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/peer"
|
||||||
|
"google.golang.org/grpc/stats"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
"google.golang.org/grpc/transport"
|
"google.golang.org/grpc/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Codec defines the interface gRPC uses to encode and decode messages.
|
|
||||||
type Codec interface {
|
|
||||||
// Marshal returns the wire format of v.
|
|
||||||
Marshal(v interface{}) ([]byte, error)
|
|
||||||
// Unmarshal parses the wire format into v.
|
|
||||||
Unmarshal(data []byte, v interface{}) error
|
|
||||||
// String returns the name of the Codec implementation. The returned
|
|
||||||
// string will be used as part of content type in transmission.
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// protoCodec is a Codec implementation with protobuf. It is the default codec for gRPC.
|
|
||||||
type protoCodec struct{}
|
|
||||||
|
|
||||||
func (protoCodec) Marshal(v interface{}) ([]byte, error) {
|
|
||||||
return proto.Marshal(v.(proto.Message))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (protoCodec) Unmarshal(data []byte, v interface{}) error {
|
|
||||||
return proto.Unmarshal(data, v.(proto.Message))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (protoCodec) String() string {
|
|
||||||
return "proto"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compressor defines the interface gRPC uses to compress a message.
|
// Compressor defines the interface gRPC uses to compress a message.
|
||||||
type Compressor interface {
|
type Compressor interface {
|
||||||
// Do compresses p into w.
|
// Do compresses p into w.
|
||||||
|
@ -138,6 +114,7 @@ type callInfo struct {
|
||||||
failFast bool
|
failFast bool
|
||||||
headerMD metadata.MD
|
headerMD metadata.MD
|
||||||
trailerMD metadata.MD
|
trailerMD metadata.MD
|
||||||
|
peer *peer.Peer
|
||||||
traceInfo traceInfo // in trace.go
|
traceInfo traceInfo // in trace.go
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,12 +158,22 @@ func Trailer(md *metadata.MD) CallOption {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Peer returns a CallOption that retrieves peer information for a
|
||||||
|
// unary RPC.
|
||||||
|
func Peer(peer *peer.Peer) CallOption {
|
||||||
|
return afterCall(func(c *callInfo) {
|
||||||
|
if c.peer != nil {
|
||||||
|
*peer = *c.peer
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// FailFast configures the action to take when an RPC is attempted on broken
|
// FailFast configures the action to take when an RPC is attempted on broken
|
||||||
// connections or unreachable servers. If failfast is true, the RPC will fail
|
// connections or unreachable servers. If failfast is true, the RPC will fail
|
||||||
// immediately. Otherwise, the RPC client will block the call until a
|
// immediately. Otherwise, the RPC client will block the call until a
|
||||||
// connection is available (or the call is canceled or times out) and will retry
|
// connection is available (or the call is canceled or times out) and will retry
|
||||||
// the call if it fails due to a transient error. Please refer to
|
// the call if it fails due to a transient error. Please refer to
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/fail_fast.md
|
// https://github.com/grpc/grpc/blob/master/doc/fail_fast.md. Note: failFast is default to true.
|
||||||
func FailFast(failFast bool) CallOption {
|
func FailFast(failFast bool) CallOption {
|
||||||
return beforeCall(func(c *callInfo) error {
|
return beforeCall(func(c *callInfo) error {
|
||||||
c.failFast = failFast
|
c.failFast = failFast
|
||||||
|
@ -255,9 +242,11 @@ func (p *parser) recvMsg(maxMsgSize int) (pf payloadFormat, msg []byte, err erro
|
||||||
|
|
||||||
// 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{}, cp Compressor, cbuf *bytes.Buffer) ([]byte, error) {
|
func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer, outPayload *stats.OutPayload) ([]byte, error) {
|
||||||
var b []byte
|
var (
|
||||||
var length uint
|
b []byte
|
||||||
|
length uint
|
||||||
|
)
|
||||||
if msg != nil {
|
if msg != nil {
|
||||||
var err error
|
var err error
|
||||||
// TODO(zhaoq): optimize to reduce memory alloc and copying.
|
// TODO(zhaoq): optimize to reduce memory alloc and copying.
|
||||||
|
@ -265,6 +254,12 @@ func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer) ([]byte
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if outPayload != nil {
|
||||||
|
outPayload.Payload = msg
|
||||||
|
// TODO truncate large payload.
|
||||||
|
outPayload.Data = b
|
||||||
|
outPayload.Length = len(b)
|
||||||
|
}
|
||||||
if cp != nil {
|
if cp != nil {
|
||||||
if err := cp.Do(cbuf, b); err != nil {
|
if err := cp.Do(cbuf, b); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -295,6 +290,10 @@ func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer) ([]byte
|
||||||
// Copy encoded msg to buf
|
// Copy encoded msg to buf
|
||||||
copy(buf[5:], b)
|
copy(buf[5:], b)
|
||||||
|
|
||||||
|
if outPayload != nil {
|
||||||
|
outPayload.WireLength = len(buf)
|
||||||
|
}
|
||||||
|
|
||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,11 +310,14 @@ func checkRecvPayload(pf payloadFormat, recvCompress string, dc Decompressor) er
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func recv(p *parser, c Codec, s *transport.Stream, dc Decompressor, m interface{}, maxMsgSize int) error {
|
func recv(p *parser, c Codec, s *transport.Stream, dc Decompressor, m interface{}, maxMsgSize int, inPayload *stats.InPayload) error {
|
||||||
pf, d, err := p.recvMsg(maxMsgSize)
|
pf, d, err := p.recvMsg(maxMsgSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if inPayload != nil {
|
||||||
|
inPayload.WireLength = len(d)
|
||||||
|
}
|
||||||
if err := checkRecvPayload(pf, s.RecvCompress(), dc); err != nil {
|
if err := checkRecvPayload(pf, s.RecvCompress(), dc); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -333,91 +335,90 @@ func recv(p *parser, c Codec, s *transport.Stream, dc Decompressor, m interface{
|
||||||
if err := c.Unmarshal(d, m); err != nil {
|
if err := c.Unmarshal(d, m); err != nil {
|
||||||
return Errorf(codes.Internal, "grpc: failed to unmarshal the received message %v", err)
|
return Errorf(codes.Internal, "grpc: failed to unmarshal the received message %v", err)
|
||||||
}
|
}
|
||||||
|
if inPayload != nil {
|
||||||
|
inPayload.RecvTime = time.Now()
|
||||||
|
inPayload.Payload = m
|
||||||
|
// TODO truncate large payload.
|
||||||
|
inPayload.Data = d
|
||||||
|
inPayload.Length = len(d)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// rpcError defines the status from an RPC.
|
type rpcInfo struct {
|
||||||
type rpcError struct {
|
bytesSent bool
|
||||||
code codes.Code
|
bytesReceived bool
|
||||||
desc string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *rpcError) Error() string {
|
type rpcInfoContextKey struct{}
|
||||||
return fmt.Sprintf("rpc error: code = %d desc = %s", e.code, e.desc)
|
|
||||||
|
func newContextWithRPCInfo(ctx context.Context) context.Context {
|
||||||
|
return context.WithValue(ctx, rpcInfoContextKey{}, &rpcInfo{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func rpcInfoFromContext(ctx context.Context) (s *rpcInfo, ok bool) {
|
||||||
|
s, ok = ctx.Value(rpcInfoContextKey{}).(*rpcInfo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateRPCInfoInContext(ctx context.Context, s rpcInfo) {
|
||||||
|
if ss, ok := rpcInfoFromContext(ctx); ok {
|
||||||
|
*ss = s
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
// Otherwise, it returns codes.Unknown.
|
// Otherwise, it returns codes.Unknown.
|
||||||
|
//
|
||||||
|
// Deprecated; use status.FromError and Code method instead.
|
||||||
func Code(err error) codes.Code {
|
func Code(err error) codes.Code {
|
||||||
if err == nil {
|
if s, ok := status.FromError(err); ok {
|
||||||
return codes.OK
|
return s.Code()
|
||||||
}
|
|
||||||
if e, ok := err.(*rpcError); ok {
|
|
||||||
return e.code
|
|
||||||
}
|
}
|
||||||
return codes.Unknown
|
return codes.Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorDesc returns the error description of err if it was produced by the rpc system.
|
// ErrorDesc returns the error description of err if it was produced by the rpc system.
|
||||||
// Otherwise, it returns err.Error() or empty string when err is nil.
|
// Otherwise, it returns err.Error() or empty string when err is nil.
|
||||||
|
//
|
||||||
|
// Deprecated; use status.FromError and Message method instead.
|
||||||
func ErrorDesc(err error) string {
|
func ErrorDesc(err error) string {
|
||||||
if err == nil {
|
if s, ok := status.FromError(err); ok {
|
||||||
return ""
|
return s.Message()
|
||||||
}
|
|
||||||
if e, ok := err.(*rpcError); ok {
|
|
||||||
return e.desc
|
|
||||||
}
|
}
|
||||||
return err.Error()
|
return err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errorf returns an error containing an error code and a description;
|
// Errorf returns an error containing an error code and a description;
|
||||||
// Errorf returns nil if c is OK.
|
// Errorf returns nil if c is OK.
|
||||||
|
//
|
||||||
|
// Deprecated; use status.Errorf instead.
|
||||||
func Errorf(c codes.Code, format string, a ...interface{}) error {
|
func Errorf(c codes.Code, format string, a ...interface{}) error {
|
||||||
if c == codes.OK {
|
return status.Errorf(c, format, a...)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &rpcError{
|
|
||||||
code: c,
|
|
||||||
desc: fmt.Sprintf(format, a...),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// toRPCErr converts an error into a rpcError.
|
// toRPCErr converts an error into an error from the status package.
|
||||||
func toRPCErr(err error) error {
|
func toRPCErr(err error) error {
|
||||||
switch e := err.(type) {
|
if _, ok := status.FromError(err); ok {
|
||||||
case *rpcError:
|
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
|
switch e := err.(type) {
|
||||||
case transport.StreamError:
|
case transport.StreamError:
|
||||||
return &rpcError{
|
return status.Error(e.Code, e.Desc)
|
||||||
code: e.Code,
|
|
||||||
desc: e.Desc,
|
|
||||||
}
|
|
||||||
case transport.ConnectionError:
|
case transport.ConnectionError:
|
||||||
return &rpcError{
|
return status.Error(codes.Internal, e.Desc)
|
||||||
code: codes.Internal,
|
|
||||||
desc: e.Desc,
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
switch err {
|
switch err {
|
||||||
case context.DeadlineExceeded:
|
case context.DeadlineExceeded:
|
||||||
return &rpcError{
|
return status.Error(codes.DeadlineExceeded, err.Error())
|
||||||
code: codes.DeadlineExceeded,
|
|
||||||
desc: err.Error(),
|
|
||||||
}
|
|
||||||
case context.Canceled:
|
case context.Canceled:
|
||||||
return &rpcError{
|
return status.Error(codes.Canceled, err.Error())
|
||||||
code: codes.Canceled,
|
|
||||||
desc: err.Error(),
|
|
||||||
}
|
|
||||||
case ErrClientConnClosing:
|
case ErrClientConnClosing:
|
||||||
return &rpcError{
|
return status.Error(codes.FailedPrecondition, err.Error())
|
||||||
code: codes.FailedPrecondition,
|
|
||||||
desc: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return Errorf(codes.Unknown, "%v", err)
|
return status.Error(codes.Unknown, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertCode converts a standard Go error into its canonical code. Note that
|
// convertCode converts a standard Go error into its canonical code. Note that
|
||||||
|
@ -448,6 +449,44 @@ func convertCode(err error) codes.Code {
|
||||||
return codes.Unknown
|
return codes.Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MethodConfig defines the configuration recommended by the service providers for a
|
||||||
|
// particular method.
|
||||||
|
// This is EXPERIMENTAL and subject to change.
|
||||||
|
type MethodConfig struct {
|
||||||
|
// WaitForReady indicates whether RPCs sent to this method should wait until
|
||||||
|
// the connection is ready by default (!failfast). The value specified via the
|
||||||
|
// gRPC client API will override the value set here.
|
||||||
|
WaitForReady bool
|
||||||
|
// Timeout is the default timeout for RPCs sent to this method. The actual
|
||||||
|
// deadline used will be the minimum of the value specified here and the value
|
||||||
|
// set by the application via the gRPC client API. If either one is not set,
|
||||||
|
// then the other will be used. If neither is set, then the RPC has no deadline.
|
||||||
|
Timeout time.Duration
|
||||||
|
// MaxReqSize is the maximum allowed payload size for an individual request in a
|
||||||
|
// stream (client->server) in bytes. The size which is measured is the serialized
|
||||||
|
// payload after per-message compression (but before stream compression) in bytes.
|
||||||
|
// The actual value used is the minumum of the value specified here and the value set
|
||||||
|
// by the application via the gRPC client API. If either one is not set, then the other
|
||||||
|
// will be used. If neither is set, then the built-in default is used.
|
||||||
|
// TODO: support this.
|
||||||
|
MaxReqSize uint32
|
||||||
|
// MaxRespSize is the maximum allowed payload size for an individual response in a
|
||||||
|
// stream (server->client) in bytes.
|
||||||
|
// TODO: support this.
|
||||||
|
MaxRespSize uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceConfig is provided by the service provider and contains parameters for how
|
||||||
|
// clients that connect to the service should behave.
|
||||||
|
// This is EXPERIMENTAL and subject to change.
|
||||||
|
type ServiceConfig struct {
|
||||||
|
// LB is the load balancer the service providers recommends. The balancer specified
|
||||||
|
// via grpc.WithBalancer will override this.
|
||||||
|
LB Balancer
|
||||||
|
// Methods contains a map for the methods in this service.
|
||||||
|
Methods map[string]MethodConfig
|
||||||
|
}
|
||||||
|
|
||||||
// SupportPackageIsVersion4 is referenced from generated protocol buffer files
|
// SupportPackageIsVersion4 is referenced from generated protocol buffer files
|
||||||
// to assert that that code is compatible with this version of the grpc package.
|
// to assert that that code is compatible with this version of the grpc package.
|
||||||
//
|
//
|
||||||
|
@ -455,3 +494,8 @@ func convertCode(err error) codes.Code {
|
||||||
// requires a synchronised update of grpc-go and protoc-gen-go. This constant
|
// requires a synchronised update of grpc-go and protoc-gen-go. This constant
|
||||||
// should not be referenced from any other code.
|
// should not be referenced from any other code.
|
||||||
const SupportPackageIsVersion4 = true
|
const SupportPackageIsVersion4 = true
|
||||||
|
|
||||||
|
// Version is the current grpc version.
|
||||||
|
const Version = "1.3.0"
|
||||||
|
|
||||||
|
const grpcUA = "grpc-go/" + Version
|
||||||
|
|
|
@ -53,7 +53,11 @@ 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/internal"
|
"google.golang.org/grpc/internal"
|
||||||
|
"google.golang.org/grpc/keepalive"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/stats"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/grpc/tap"
|
||||||
"google.golang.org/grpc/transport"
|
"google.golang.org/grpc/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -110,8 +114,13 @@ type options struct {
|
||||||
maxMsgSize int
|
maxMsgSize int
|
||||||
unaryInt UnaryServerInterceptor
|
unaryInt UnaryServerInterceptor
|
||||||
streamInt StreamServerInterceptor
|
streamInt StreamServerInterceptor
|
||||||
|
inTapHandle tap.ServerInHandle
|
||||||
|
statsHandler stats.Handler
|
||||||
maxConcurrentStreams uint32
|
maxConcurrentStreams uint32
|
||||||
useHandlerImpl bool // use http.Handler-based server
|
useHandlerImpl bool // use http.Handler-based server
|
||||||
|
unknownStreamDesc *StreamDesc
|
||||||
|
keepaliveParams keepalive.ServerParameters
|
||||||
|
keepalivePolicy keepalive.EnforcementPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultMaxMsgSize = 1024 * 1024 * 4 // use 4MB as the default message size limit
|
var defaultMaxMsgSize = 1024 * 1024 * 4 // use 4MB as the default message size limit
|
||||||
|
@ -119,6 +128,20 @@ var defaultMaxMsgSize = 1024 * 1024 * 4 // use 4MB as the default message size l
|
||||||
// A ServerOption sets options.
|
// A ServerOption sets options.
|
||||||
type ServerOption func(*options)
|
type ServerOption func(*options)
|
||||||
|
|
||||||
|
// KeepaliveParams returns a ServerOption that sets keepalive and max-age parameters for the server.
|
||||||
|
func KeepaliveParams(kp keepalive.ServerParameters) ServerOption {
|
||||||
|
return func(o *options) {
|
||||||
|
o.keepaliveParams = kp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeepaliveEnforcementPolicy returns a ServerOption that sets keepalive enforcement policy for the server.
|
||||||
|
func KeepaliveEnforcementPolicy(kep keepalive.EnforcementPolicy) ServerOption {
|
||||||
|
return func(o *options) {
|
||||||
|
o.keepalivePolicy = kep
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CustomCodec returns a ServerOption that sets a codec for message marshaling and unmarshaling.
|
// CustomCodec returns a ServerOption that sets a codec for message marshaling and unmarshaling.
|
||||||
func CustomCodec(codec Codec) ServerOption {
|
func CustomCodec(codec Codec) ServerOption {
|
||||||
return func(o *options) {
|
return func(o *options) {
|
||||||
|
@ -186,6 +209,42 @@ func StreamInterceptor(i StreamServerInterceptor) ServerOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InTapHandle returns a ServerOption that sets the tap handle for all the server
|
||||||
|
// transport to be created. Only one can be installed.
|
||||||
|
func InTapHandle(h tap.ServerInHandle) ServerOption {
|
||||||
|
return func(o *options) {
|
||||||
|
if o.inTapHandle != nil {
|
||||||
|
panic("The tap handle has been set.")
|
||||||
|
}
|
||||||
|
o.inTapHandle = h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatsHandler returns a ServerOption that sets the stats handler for the server.
|
||||||
|
func StatsHandler(h stats.Handler) ServerOption {
|
||||||
|
return func(o *options) {
|
||||||
|
o.statsHandler = h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnknownServiceHandler returns a ServerOption that allows for adding a custom
|
||||||
|
// unknown service handler. The provided method is a bidi-streaming RPC service
|
||||||
|
// handler that will be invoked instead of returning the the "unimplemented" gRPC
|
||||||
|
// error whenever a request is received for an unregistered service or method.
|
||||||
|
// The handling function has full access to the Context of the request and the
|
||||||
|
// stream, and the invocation passes through interceptors.
|
||||||
|
func UnknownServiceHandler(streamHandler StreamHandler) ServerOption {
|
||||||
|
return func(o *options) {
|
||||||
|
o.unknownStreamDesc = &StreamDesc{
|
||||||
|
StreamName: "unknown_service_handler",
|
||||||
|
Handler: streamHandler,
|
||||||
|
// We need to assume that the users of the streamHandler will want to use both.
|
||||||
|
ClientStreams: true,
|
||||||
|
ServerStreams: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
|
@ -329,6 +388,7 @@ func (s *Server) useTransportAuthenticator(rawConn net.Conn) (net.Conn, credenti
|
||||||
// read gRPC requests and then call the registered handlers to reply to them.
|
// read gRPC requests and then call the registered handlers to reply to them.
|
||||||
// Serve returns when lis.Accept fails with fatal errors. lis will be closed when
|
// Serve returns when lis.Accept fails with fatal errors. lis will be closed when
|
||||||
// this method returns.
|
// this method returns.
|
||||||
|
// Serve always returns non-nil error.
|
||||||
func (s *Server) Serve(lis net.Listener) error {
|
func (s *Server) Serve(lis net.Listener) error {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
s.printf("serving")
|
s.printf("serving")
|
||||||
|
@ -412,17 +472,25 @@ func (s *Server) handleRawConn(rawConn net.Conn) {
|
||||||
if s.opts.useHandlerImpl {
|
if s.opts.useHandlerImpl {
|
||||||
s.serveUsingHandler(conn)
|
s.serveUsingHandler(conn)
|
||||||
} else {
|
} else {
|
||||||
s.serveNewHTTP2Transport(conn, authInfo)
|
s.serveHTTP2Transport(conn, authInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// serveNewHTTP2Transport sets up a new http/2 transport (using the
|
// serveHTTP2Transport sets up a http/2 transport (using the
|
||||||
// gRPC http2 server transport in transport/http2_server.go) and
|
// gRPC http2 server transport in transport/http2_server.go) and
|
||||||
// serves streams on it.
|
// serves streams on it.
|
||||||
// This is run in its own goroutine (it does network I/O in
|
// This is run in its own goroutine (it does network I/O in
|
||||||
// transport.NewServerTransport).
|
// transport.NewServerTransport).
|
||||||
func (s *Server) serveNewHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) {
|
func (s *Server) serveHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) {
|
||||||
st, err := transport.NewServerTransport("http2", c, s.opts.maxConcurrentStreams, authInfo)
|
config := &transport.ServerConfig{
|
||||||
|
MaxStreams: s.opts.maxConcurrentStreams,
|
||||||
|
AuthInfo: authInfo,
|
||||||
|
InTapHandle: s.opts.inTapHandle,
|
||||||
|
StatsHandler: s.opts.statsHandler,
|
||||||
|
KeepaliveParams: s.opts.keepaliveParams,
|
||||||
|
KeepalivePolicy: s.opts.keepalivePolicy,
|
||||||
|
}
|
||||||
|
st, err := transport.NewServerTransport("http2", c, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
|
s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
|
||||||
|
@ -448,6 +516,12 @@ func (s *Server) serveStreams(st transport.ServerTransport) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
s.handleStream(st, stream, s.traceInfo(st, stream))
|
s.handleStream(st, stream, s.traceInfo(st, stream))
|
||||||
}()
|
}()
|
||||||
|
}, func(ctx context.Context, method string) context.Context {
|
||||||
|
if !EnableTracing {
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
tr := trace.New("grpc.Recv."+methodFamily(method), method)
|
||||||
|
return trace.NewContext(ctx, tr)
|
||||||
})
|
})
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
@ -497,15 +571,17 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// traceInfo returns a traceInfo and associates it with stream, if tracing is enabled.
|
// traceInfo returns a traceInfo and associates it with stream, if tracing is enabled.
|
||||||
// If tracing is not enabled, it returns nil.
|
// If tracing is not enabled, it returns nil.
|
||||||
func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Stream) (trInfo *traceInfo) {
|
func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Stream) (trInfo *traceInfo) {
|
||||||
if !EnableTracing {
|
tr, ok := trace.FromContext(stream.Context())
|
||||||
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
trInfo = &traceInfo{
|
trInfo = &traceInfo{
|
||||||
tr: trace.New("grpc.Recv."+methodFamily(stream.Method()), stream.Method()),
|
tr: tr,
|
||||||
}
|
}
|
||||||
trInfo.firstLine.client = false
|
trInfo.firstLine.client = false
|
||||||
trInfo.firstLine.remoteAddr = st.RemoteAddr()
|
trInfo.firstLine.remoteAddr = st.RemoteAddr()
|
||||||
stream.TraceContext(trInfo.tr)
|
|
||||||
if dl, ok := stream.Context().Deadline(); ok {
|
if dl, ok := stream.Context().Deadline(); ok {
|
||||||
trInfo.firstLine.deadline = dl.Sub(time.Now())
|
trInfo.firstLine.deadline = dl.Sub(time.Now())
|
||||||
}
|
}
|
||||||
|
@ -532,11 +608,17 @@ func (s *Server) removeConn(c io.Closer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, cp Compressor, opts *transport.Options) error {
|
func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, cp Compressor, opts *transport.Options) error {
|
||||||
var cbuf *bytes.Buffer
|
var (
|
||||||
|
cbuf *bytes.Buffer
|
||||||
|
outPayload *stats.OutPayload
|
||||||
|
)
|
||||||
if cp != nil {
|
if cp != nil {
|
||||||
cbuf = new(bytes.Buffer)
|
cbuf = new(bytes.Buffer)
|
||||||
}
|
}
|
||||||
p, err := encode(s.opts.codec, msg, cp, cbuf)
|
if s.opts.statsHandler != nil {
|
||||||
|
outPayload = &stats.OutPayload{}
|
||||||
|
}
|
||||||
|
p, err := encode(s.opts.codec, msg, cp, cbuf, outPayload)
|
||||||
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
|
||||||
|
@ -547,10 +629,33 @@ func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Str
|
||||||
// the optimal option.
|
// the optimal option.
|
||||||
grpclog.Fatalf("grpc: Server failed to encode response %v", err)
|
grpclog.Fatalf("grpc: Server failed to encode response %v", err)
|
||||||
}
|
}
|
||||||
return t.Write(stream, p, opts)
|
err = t.Write(stream, p, opts)
|
||||||
|
if err == nil && outPayload != nil {
|
||||||
|
outPayload.SentTime = time.Now()
|
||||||
|
s.opts.statsHandler.HandleRPC(stream.Context(), outPayload)
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc, trInfo *traceInfo) (err error) {
|
func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc, trInfo *traceInfo) (err error) {
|
||||||
|
sh := s.opts.statsHandler
|
||||||
|
if sh != nil {
|
||||||
|
begin := &stats.Begin{
|
||||||
|
BeginTime: time.Now(),
|
||||||
|
}
|
||||||
|
sh.HandleRPC(stream.Context(), begin)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if sh != nil {
|
||||||
|
end := &stats.End{
|
||||||
|
EndTime: time.Now(),
|
||||||
|
}
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
end.Error = toRPCErr(err)
|
||||||
|
}
|
||||||
|
sh.HandleRPC(stream.Context(), end)
|
||||||
|
}
|
||||||
|
}()
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
defer trInfo.tr.Finish()
|
defer trInfo.tr.Finish()
|
||||||
trInfo.firstLine.client = false
|
trInfo.firstLine.client = false
|
||||||
|
@ -567,7 +672,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
stream.SetSendCompress(s.opts.cp.Type())
|
stream.SetSendCompress(s.opts.cp.Type())
|
||||||
}
|
}
|
||||||
p := &parser{r: stream}
|
p := &parser{r: stream}
|
||||||
for {
|
for { // TODO: delete
|
||||||
pf, req, err := p.recvMsg(s.opts.maxMsgSize)
|
pf, req, err := p.recvMsg(s.opts.maxMsgSize)
|
||||||
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).
|
||||||
|
@ -577,58 +682,68 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
err = Errorf(codes.Internal, io.ErrUnexpectedEOF.Error())
|
err = Errorf(codes.Internal, io.ErrUnexpectedEOF.Error())
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err := err.(type) {
|
if st, ok := status.FromError(err); ok {
|
||||||
case *rpcError:
|
if e := t.WriteStatus(stream, st); e != nil {
|
||||||
if err := t.WriteStatus(stream, err.code, err.desc); err != nil {
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
|
||||||
}
|
}
|
||||||
case transport.ConnectionError:
|
} else {
|
||||||
// Nothing to do here.
|
switch st := err.(type) {
|
||||||
case transport.StreamError:
|
case transport.ConnectionError:
|
||||||
if err := t.WriteStatus(stream, err.Code, err.Desc); err != nil {
|
// Nothing to do here.
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
case transport.StreamError:
|
||||||
|
if e := t.WriteStatus(stream, status.New(st.Code, st.Desc)); e != nil {
|
||||||
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", st, st))
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", err, err))
|
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := checkRecvPayload(pf, stream.RecvCompress(), s.opts.dc); err != nil {
|
if err := checkRecvPayload(pf, stream.RecvCompress(), s.opts.dc); err != nil {
|
||||||
switch err := err.(type) {
|
if st, ok := status.FromError(err); ok {
|
||||||
case *rpcError:
|
if e := t.WriteStatus(stream, st); e != nil {
|
||||||
if err := t.WriteStatus(stream, err.code, err.desc); err != nil {
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
|
||||||
}
|
}
|
||||||
default:
|
return err
|
||||||
if err := t.WriteStatus(stream, codes.Internal, err.Error()); err != nil {
|
}
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
if e := t.WriteStatus(stream, status.New(codes.Internal, err.Error())); e != nil {
|
||||||
}
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO checkRecvPayload always return RPC error. Add a return here if necessary.
|
||||||
|
}
|
||||||
|
var inPayload *stats.InPayload
|
||||||
|
if sh != nil {
|
||||||
|
inPayload = &stats.InPayload{
|
||||||
|
RecvTime: time.Now(),
|
||||||
}
|
}
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
statusCode := codes.OK
|
|
||||||
statusDesc := ""
|
|
||||||
df := func(v interface{}) error {
|
df := func(v interface{}) error {
|
||||||
|
if inPayload != nil {
|
||||||
|
inPayload.WireLength = len(req)
|
||||||
|
}
|
||||||
if pf == compressionMade {
|
if pf == compressionMade {
|
||||||
var err error
|
var err error
|
||||||
req, err = s.opts.dc.Do(bytes.NewReader(req))
|
req, err = s.opts.dc.Do(bytes.NewReader(req))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err := t.WriteStatus(stream, codes.Internal, err.Error()); err != nil {
|
return Errorf(codes.Internal, err.Error())
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(req) > s.opts.maxMsgSize {
|
if len(req) > s.opts.maxMsgSize {
|
||||||
// TODO: Revisit the error code. Currently keep it consistent with
|
// TODO: Revisit the error code. Currently keep it consistent with
|
||||||
// java implementation.
|
// java implementation.
|
||||||
statusCode = codes.Internal
|
return status.Errorf(codes.Internal, "grpc: server received a message of %d bytes exceeding %d limit", len(req), s.opts.maxMsgSize)
|
||||||
statusDesc = fmt.Sprintf("grpc: server received a message of %d bytes exceeding %d limit", len(req), s.opts.maxMsgSize)
|
|
||||||
}
|
}
|
||||||
if err := s.opts.codec.Unmarshal(req, v); err != nil {
|
if err := s.opts.codec.Unmarshal(req, v); err != nil {
|
||||||
return err
|
return status.Errorf(codes.Internal, "grpc: error unmarshalling request: %v", err)
|
||||||
|
}
|
||||||
|
if inPayload != nil {
|
||||||
|
inPayload.Payload = v
|
||||||
|
inPayload.Data = req
|
||||||
|
inPayload.Length = len(req)
|
||||||
|
sh.HandleRPC(stream.Context(), inPayload)
|
||||||
}
|
}
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&payload{sent: false, msg: v}, true)
|
trInfo.tr.LazyLog(&payload{sent: false, msg: v}, true)
|
||||||
|
@ -637,22 +752,20 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
}
|
}
|
||||||
reply, appErr := md.Handler(srv.server, stream.Context(), df, s.opts.unaryInt)
|
reply, appErr := md.Handler(srv.server, stream.Context(), df, s.opts.unaryInt)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
if err, ok := appErr.(*rpcError); ok {
|
appStatus, ok := status.FromError(appErr)
|
||||||
statusCode = err.code
|
if !ok {
|
||||||
statusDesc = err.desc
|
// Convert appErr if it is not a grpc status error.
|
||||||
} else {
|
appErr = status.Error(convertCode(appErr), appErr.Error())
|
||||||
statusCode = convertCode(appErr)
|
appStatus, _ = status.FromError(appErr)
|
||||||
statusDesc = appErr.Error()
|
|
||||||
}
|
}
|
||||||
if trInfo != nil && statusCode != codes.OK {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(stringer(statusDesc), true)
|
trInfo.tr.LazyLog(stringer(appStatus.Message()), true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
}
|
}
|
||||||
if err := t.WriteStatus(stream, statusCode, statusDesc); err != nil {
|
if e := t.WriteStatus(stream, appStatus); e != nil {
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", e)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return nil
|
return appErr
|
||||||
}
|
}
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(stringer("OK"), false)
|
trInfo.tr.LazyLog(stringer("OK"), false)
|
||||||
|
@ -662,38 +775,70 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
Delay: false,
|
Delay: false,
|
||||||
}
|
}
|
||||||
if err := s.sendResponse(t, stream, reply, s.opts.cp, opts); err != nil {
|
if err := s.sendResponse(t, stream, reply, s.opts.cp, opts); err != nil {
|
||||||
switch err := err.(type) {
|
if err == io.EOF {
|
||||||
case transport.ConnectionError:
|
// The entire stream is done (for unary RPC only).
|
||||||
// Nothing to do here.
|
return err
|
||||||
case transport.StreamError:
|
}
|
||||||
statusCode = err.Code
|
if s, ok := status.FromError(err); ok {
|
||||||
statusDesc = err.Desc
|
if e := t.WriteStatus(stream, s); e != nil {
|
||||||
default:
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", e)
|
||||||
statusCode = codes.Unknown
|
}
|
||||||
statusDesc = err.Error()
|
} else {
|
||||||
|
switch st := err.(type) {
|
||||||
|
case transport.ConnectionError:
|
||||||
|
// Nothing to do here.
|
||||||
|
case transport.StreamError:
|
||||||
|
if e := t.WriteStatus(stream, status.New(st.Code, st.Desc)); e != nil {
|
||||||
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("grpc: Unexpected error (%T) from sendResponse: %v", st, st))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&payload{sent: true, msg: reply}, true)
|
trInfo.tr.LazyLog(&payload{sent: true, msg: reply}, true)
|
||||||
}
|
}
|
||||||
return t.WriteStatus(stream, statusCode, statusDesc)
|
// TODO: Should we be logging if writing status failed here, like above?
|
||||||
|
// Should the logging be in WriteStatus? Should we ignore the WriteStatus
|
||||||
|
// error or allow the stats handler to see it?
|
||||||
|
return t.WriteStatus(stream, status.New(codes.OK, ""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
sh := s.opts.statsHandler
|
||||||
|
if sh != nil {
|
||||||
|
begin := &stats.Begin{
|
||||||
|
BeginTime: time.Now(),
|
||||||
|
}
|
||||||
|
sh.HandleRPC(stream.Context(), begin)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if sh != nil {
|
||||||
|
end := &stats.End{
|
||||||
|
EndTime: time.Now(),
|
||||||
|
}
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
end.Error = toRPCErr(err)
|
||||||
|
}
|
||||||
|
sh.HandleRPC(stream.Context(), end)
|
||||||
|
}
|
||||||
|
}()
|
||||||
if s.opts.cp != nil {
|
if s.opts.cp != nil {
|
||||||
stream.SetSendCompress(s.opts.cp.Type())
|
stream.SetSendCompress(s.opts.cp.Type())
|
||||||
}
|
}
|
||||||
ss := &serverStream{
|
ss := &serverStream{
|
||||||
t: t,
|
t: t,
|
||||||
s: stream,
|
s: stream,
|
||||||
p: &parser{r: stream},
|
p: &parser{r: stream},
|
||||||
codec: s.opts.codec,
|
codec: s.opts.codec,
|
||||||
cp: s.opts.cp,
|
cp: s.opts.cp,
|
||||||
dc: s.opts.dc,
|
dc: s.opts.dc,
|
||||||
maxMsgSize: s.opts.maxMsgSize,
|
maxMsgSize: s.opts.maxMsgSize,
|
||||||
trInfo: trInfo,
|
trInfo: trInfo,
|
||||||
|
statsHandler: sh,
|
||||||
}
|
}
|
||||||
if ss.cp != nil {
|
if ss.cp != nil {
|
||||||
ss.cbuf = new(bytes.Buffer)
|
ss.cbuf = new(bytes.Buffer)
|
||||||
|
@ -712,39 +857,47 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
var appErr error
|
var appErr error
|
||||||
|
var server interface{}
|
||||||
|
if srv != nil {
|
||||||
|
server = srv.server
|
||||||
|
}
|
||||||
if s.opts.streamInt == nil {
|
if s.opts.streamInt == nil {
|
||||||
appErr = sd.Handler(srv.server, ss)
|
appErr = sd.Handler(server, ss)
|
||||||
} else {
|
} else {
|
||||||
info := &StreamServerInfo{
|
info := &StreamServerInfo{
|
||||||
FullMethod: stream.Method(),
|
FullMethod: stream.Method(),
|
||||||
IsClientStream: sd.ClientStreams,
|
IsClientStream: sd.ClientStreams,
|
||||||
IsServerStream: sd.ServerStreams,
|
IsServerStream: sd.ServerStreams,
|
||||||
}
|
}
|
||||||
appErr = s.opts.streamInt(srv.server, ss, info, sd.Handler)
|
appErr = s.opts.streamInt(server, ss, info, sd.Handler)
|
||||||
}
|
}
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
if err, ok := appErr.(*rpcError); ok {
|
appStatus, ok := status.FromError(appErr)
|
||||||
ss.statusCode = err.code
|
if !ok {
|
||||||
ss.statusDesc = err.desc
|
switch err := appErr.(type) {
|
||||||
} else if err, ok := appErr.(transport.StreamError); ok {
|
case transport.StreamError:
|
||||||
ss.statusCode = err.Code
|
appStatus = status.New(err.Code, err.Desc)
|
||||||
ss.statusDesc = err.Desc
|
default:
|
||||||
} else {
|
appStatus = status.New(convertCode(appErr), appErr.Error())
|
||||||
ss.statusCode = convertCode(appErr)
|
}
|
||||||
ss.statusDesc = appErr.Error()
|
appErr = appStatus.Err()
|
||||||
}
|
}
|
||||||
|
if trInfo != nil {
|
||||||
|
ss.mu.Lock()
|
||||||
|
ss.trInfo.tr.LazyLog(stringer(appStatus.Message()), true)
|
||||||
|
ss.trInfo.tr.SetError()
|
||||||
|
ss.mu.Unlock()
|
||||||
|
}
|
||||||
|
t.WriteStatus(ss.s, appStatus)
|
||||||
|
// TODO: Should we log an error from WriteStatus here and below?
|
||||||
|
return appErr
|
||||||
}
|
}
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
ss.mu.Lock()
|
ss.mu.Lock()
|
||||||
if ss.statusCode != codes.OK {
|
ss.trInfo.tr.LazyLog(stringer("OK"), false)
|
||||||
ss.trInfo.tr.LazyLog(stringer(ss.statusDesc), true)
|
|
||||||
ss.trInfo.tr.SetError()
|
|
||||||
} else {
|
|
||||||
ss.trInfo.tr.LazyLog(stringer("OK"), false)
|
|
||||||
}
|
|
||||||
ss.mu.Unlock()
|
ss.mu.Unlock()
|
||||||
}
|
}
|
||||||
return t.WriteStatus(ss.s, ss.statusCode, ss.statusDesc)
|
return t.WriteStatus(ss.s, status.New(codes.OK, ""))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -759,7 +912,8 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"Malformed method name %q", []interface{}{sm}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"Malformed method name %q", []interface{}{sm}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
}
|
}
|
||||||
if err := t.WriteStatus(stream, codes.InvalidArgument, fmt.Sprintf("malformed method name: %q", stream.Method())); err != nil {
|
errDesc := fmt.Sprintf("malformed method name: %q", stream.Method())
|
||||||
|
if err := t.WriteStatus(stream, status.New(codes.InvalidArgument, errDesc)); err != nil {
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
|
@ -775,11 +929,16 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
|
||||||
method := sm[pos+1:]
|
method := sm[pos+1:]
|
||||||
srv, ok := s.m[service]
|
srv, ok := s.m[service]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
if unknownDesc := s.opts.unknownStreamDesc; unknownDesc != nil {
|
||||||
|
s.processStreamingRPC(t, stream, nil, unknownDesc, trInfo)
|
||||||
|
return
|
||||||
|
}
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"Unknown service %v", []interface{}{service}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"Unknown service %v", []interface{}{service}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
}
|
}
|
||||||
if err := t.WriteStatus(stream, codes.Unimplemented, fmt.Sprintf("unknown service %v", service)); err != nil {
|
errDesc := fmt.Sprintf("unknown service %v", service)
|
||||||
|
if err := t.WriteStatus(stream, status.New(codes.Unimplemented, errDesc)); err != nil {
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
|
@ -804,7 +963,12 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"Unknown method %v", []interface{}{method}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"Unknown method %v", []interface{}{method}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
}
|
}
|
||||||
if err := t.WriteStatus(stream, codes.Unimplemented, fmt.Sprintf("unknown method %v", method)); err != nil {
|
if unknownDesc := s.opts.unknownStreamDesc; unknownDesc != nil {
|
||||||
|
s.processStreamingRPC(t, stream, nil, unknownDesc, trInfo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errDesc := fmt.Sprintf("unknown method %v", method)
|
||||||
|
if err := t.WriteStatus(stream, status.New(codes.Unimplemented, errDesc)); err != nil {
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConnTagInfo defines the relevant information needed by connection context tagger.
|
||||||
|
type ConnTagInfo struct {
|
||||||
|
// RemoteAddr is the remote address of the corresponding connection.
|
||||||
|
RemoteAddr net.Addr
|
||||||
|
// LocalAddr is the local address of the corresponding connection.
|
||||||
|
LocalAddr net.Addr
|
||||||
|
// TODO add QOS related fields.
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPCTagInfo defines the relevant information needed by RPC context tagger.
|
||||||
|
type RPCTagInfo struct {
|
||||||
|
// FullMethodName is the RPC method in the format of /package.service/method.
|
||||||
|
FullMethodName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler defines the interface for the related stats handling (e.g., RPCs, connections).
|
||||||
|
type Handler interface {
|
||||||
|
// TagRPC can attach some information to the given context.
|
||||||
|
// The returned context is used in the rest lifetime of the RPC.
|
||||||
|
TagRPC(context.Context, *RPCTagInfo) context.Context
|
||||||
|
// HandleRPC processes the RPC stats.
|
||||||
|
HandleRPC(context.Context, RPCStats)
|
||||||
|
|
||||||
|
// TagConn can attach some information to the given context.
|
||||||
|
// The returned context will be used for stats handling.
|
||||||
|
// For conn stats handling, the context used in HandleConn for this
|
||||||
|
// connection will be derived from the context returned.
|
||||||
|
// For RPC stats handling,
|
||||||
|
// - On server side, the context used in HandleRPC for all RPCs on this
|
||||||
|
// connection will be derived from the context returned.
|
||||||
|
// - On client side, the context is not derived from the context returned.
|
||||||
|
TagConn(context.Context, *ConnTagInfo) context.Context
|
||||||
|
// HandleConn processes the Conn stats.
|
||||||
|
HandleConn(context.Context, ConnStats)
|
||||||
|
}
|
|
@ -0,0 +1,223 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 stats is for collecting and reporting various network and RPC stats.
|
||||||
|
// This package is for monitoring purpose only. All fields are read-only.
|
||||||
|
// All APIs are experimental.
|
||||||
|
package stats // import "google.golang.org/grpc/stats"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RPCStats contains stats information about RPCs.
|
||||||
|
type RPCStats interface {
|
||||||
|
isRPCStats()
|
||||||
|
// IsClient returns true if this RPCStats is from client side.
|
||||||
|
IsClient() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin contains stats when an RPC begins.
|
||||||
|
// FailFast are only valid if Client is true.
|
||||||
|
type Begin struct {
|
||||||
|
// Client is true if this Begin is from client side.
|
||||||
|
Client bool
|
||||||
|
// BeginTime is the time when the RPC begins.
|
||||||
|
BeginTime time.Time
|
||||||
|
// FailFast indicates if this RPC is failfast.
|
||||||
|
FailFast bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *Begin) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
func (s *Begin) isRPCStats() {}
|
||||||
|
|
||||||
|
// InPayload contains the information for an incoming payload.
|
||||||
|
type InPayload struct {
|
||||||
|
// Client is true if this InPayload is from client side.
|
||||||
|
Client bool
|
||||||
|
// Payload is the payload with original type.
|
||||||
|
Payload interface{}
|
||||||
|
// Data is the serialized message payload.
|
||||||
|
Data []byte
|
||||||
|
// Length is the length of uncompressed data.
|
||||||
|
Length int
|
||||||
|
// WireLength is the length of data on wire (compressed, signed, encrypted).
|
||||||
|
WireLength int
|
||||||
|
// RecvTime is the time when the payload is received.
|
||||||
|
RecvTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *InPayload) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
func (s *InPayload) isRPCStats() {}
|
||||||
|
|
||||||
|
// InHeader contains stats when a header is received.
|
||||||
|
// FullMethod, addresses and Compression are only valid if Client is false.
|
||||||
|
type InHeader struct {
|
||||||
|
// Client is true if this InHeader is from client side.
|
||||||
|
Client bool
|
||||||
|
// WireLength is the wire length of header.
|
||||||
|
WireLength int
|
||||||
|
|
||||||
|
// FullMethod is the full RPC method string, i.e., /package.service/method.
|
||||||
|
FullMethod string
|
||||||
|
// RemoteAddr is the remote address of the corresponding connection.
|
||||||
|
RemoteAddr net.Addr
|
||||||
|
// LocalAddr is the local address of the corresponding connection.
|
||||||
|
LocalAddr net.Addr
|
||||||
|
// Compression is the compression algorithm used for the RPC.
|
||||||
|
Compression string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *InHeader) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
func (s *InHeader) isRPCStats() {}
|
||||||
|
|
||||||
|
// InTrailer contains stats when a trailer is received.
|
||||||
|
type InTrailer struct {
|
||||||
|
// Client is true if this InTrailer is from client side.
|
||||||
|
Client bool
|
||||||
|
// WireLength is the wire length of trailer.
|
||||||
|
WireLength int
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *InTrailer) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
func (s *InTrailer) isRPCStats() {}
|
||||||
|
|
||||||
|
// OutPayload contains the information for an outgoing payload.
|
||||||
|
type OutPayload struct {
|
||||||
|
// Client is true if this OutPayload is from client side.
|
||||||
|
Client bool
|
||||||
|
// Payload is the payload with original type.
|
||||||
|
Payload interface{}
|
||||||
|
// Data is the serialized message payload.
|
||||||
|
Data []byte
|
||||||
|
// Length is the length of uncompressed data.
|
||||||
|
Length int
|
||||||
|
// WireLength is the length of data on wire (compressed, signed, encrypted).
|
||||||
|
WireLength int
|
||||||
|
// SentTime is the time when the payload is sent.
|
||||||
|
SentTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *OutPayload) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
func (s *OutPayload) isRPCStats() {}
|
||||||
|
|
||||||
|
// OutHeader contains stats when a header is sent.
|
||||||
|
// FullMethod, addresses and Compression are only valid if Client is true.
|
||||||
|
type OutHeader struct {
|
||||||
|
// Client is true if this OutHeader is from client side.
|
||||||
|
Client bool
|
||||||
|
// WireLength is the wire length of header.
|
||||||
|
WireLength int
|
||||||
|
|
||||||
|
// FullMethod is the full RPC method string, i.e., /package.service/method.
|
||||||
|
FullMethod string
|
||||||
|
// RemoteAddr is the remote address of the corresponding connection.
|
||||||
|
RemoteAddr net.Addr
|
||||||
|
// LocalAddr is the local address of the corresponding connection.
|
||||||
|
LocalAddr net.Addr
|
||||||
|
// Compression is the compression algorithm used for the RPC.
|
||||||
|
Compression string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *OutHeader) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
func (s *OutHeader) isRPCStats() {}
|
||||||
|
|
||||||
|
// OutTrailer contains stats when a trailer is sent.
|
||||||
|
type OutTrailer struct {
|
||||||
|
// Client is true if this OutTrailer is from client side.
|
||||||
|
Client bool
|
||||||
|
// WireLength is the wire length of trailer.
|
||||||
|
WireLength int
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *OutTrailer) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
func (s *OutTrailer) isRPCStats() {}
|
||||||
|
|
||||||
|
// End contains stats when an RPC ends.
|
||||||
|
type End struct {
|
||||||
|
// Client is true if this End is from client side.
|
||||||
|
Client bool
|
||||||
|
// EndTime is the time when the RPC ends.
|
||||||
|
EndTime time.Time
|
||||||
|
// Error is the error just happened. It implements status.Status if non-nil.
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *End) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
func (s *End) isRPCStats() {}
|
||||||
|
|
||||||
|
// ConnStats contains stats information about connections.
|
||||||
|
type ConnStats interface {
|
||||||
|
isConnStats()
|
||||||
|
// IsClient returns true if this ConnStats is from client side.
|
||||||
|
IsClient() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnBegin contains the stats of a connection when it is established.
|
||||||
|
type ConnBegin struct {
|
||||||
|
// Client is true if this ConnBegin is from client side.
|
||||||
|
Client bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *ConnBegin) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
func (s *ConnBegin) isConnStats() {}
|
||||||
|
|
||||||
|
// ConnEnd contains the stats of a connection when it ends.
|
||||||
|
type ConnEnd struct {
|
||||||
|
// Client is true if this ConnEnd is from client side.
|
||||||
|
Client bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsClient indicates if this is from client side.
|
||||||
|
func (s *ConnEnd) IsClient() bool { return s.Client }
|
||||||
|
|
||||||
|
func (s *ConnEnd) isConnStats() {}
|
|
@ -0,0 +1,145 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2017, 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 status implements errors returned by gRPC. These errors are
|
||||||
|
// serialized and transmitted on the wire between server and client, and allow
|
||||||
|
// for additional data to be transmitted via the Details field in the status
|
||||||
|
// proto. gRPC service handlers should return an error created by this
|
||||||
|
// package, and gRPC clients should expect a corresponding error to be
|
||||||
|
// returned from the RPC call.
|
||||||
|
//
|
||||||
|
// This package upholds the invariants that a non-nil error may not
|
||||||
|
// contain an OK code, and an OK code must result in a nil error.
|
||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
spb "google.golang.org/genproto/googleapis/rpc/status"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
// statusError is an alias of a status proto. It implements error and Status,
|
||||||
|
// and a nil statusError should never be returned by this package.
|
||||||
|
type statusError spb.Status
|
||||||
|
|
||||||
|
func (se *statusError) Error() string {
|
||||||
|
p := (*spb.Status)(se)
|
||||||
|
return fmt.Sprintf("rpc error: code = %s desc = %s", codes.Code(p.GetCode()), p.GetMessage())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se *statusError) status() *Status {
|
||||||
|
return &Status{s: (*spb.Status)(se)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status represents an RPC status code, message, and details. It is immutable
|
||||||
|
// and should be created with New, Newf, or FromProto.
|
||||||
|
type Status struct {
|
||||||
|
s *spb.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code returns the status code contained in s.
|
||||||
|
func (s *Status) Code() codes.Code {
|
||||||
|
if s == nil || s.s == nil {
|
||||||
|
return codes.OK
|
||||||
|
}
|
||||||
|
return codes.Code(s.s.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message returns the message contained in s.
|
||||||
|
func (s *Status) Message() string {
|
||||||
|
if s == nil || s.s == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return s.s.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proto returns s's status as an spb.Status proto message.
|
||||||
|
func (s *Status) Proto() *spb.Status {
|
||||||
|
if s == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return proto.Clone(s.s).(*spb.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns an immutable error representing s; returns nil if s.Code() is
|
||||||
|
// OK.
|
||||||
|
func (s *Status) Err() error {
|
||||||
|
if s.Code() == codes.OK {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return (*statusError)(s.s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a Status representing c and msg.
|
||||||
|
func New(c codes.Code, msg string) *Status {
|
||||||
|
return &Status{s: &spb.Status{Code: int32(c), Message: msg}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Newf returns New(c, fmt.Sprintf(format, a...)).
|
||||||
|
func Newf(c codes.Code, format string, a ...interface{}) *Status {
|
||||||
|
return New(c, fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns an error representing c and msg. If c is OK, returns nil.
|
||||||
|
func Error(c codes.Code, msg string) error {
|
||||||
|
return New(c, msg).Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf returns Error(c, fmt.Sprintf(format, a...)).
|
||||||
|
func Errorf(c codes.Code, format string, a ...interface{}) error {
|
||||||
|
return Error(c, fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorProto returns an error representing s. If s.Code is OK, returns nil.
|
||||||
|
func ErrorProto(s *spb.Status) error {
|
||||||
|
return FromProto(s).Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromProto returns a Status representing s.
|
||||||
|
func FromProto(s *spb.Status) *Status {
|
||||||
|
return &Status{s: proto.Clone(s).(*spb.Status)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromError returns a Status representing err if it was produced from this
|
||||||
|
// package, otherwise it returns nil, false.
|
||||||
|
func FromError(err error) (s *Status, ok bool) {
|
||||||
|
if err == nil {
|
||||||
|
return &Status{s: &spb.Status{Code: int32(codes.OK)}}, true
|
||||||
|
}
|
||||||
|
if s, ok := err.(*statusError); ok {
|
||||||
|
return s.status(), true
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
|
@ -37,7 +37,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -45,6 +44,8 @@ import (
|
||||||
"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/metadata"
|
||||||
|
"google.golang.org/grpc/stats"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
"google.golang.org/grpc/transport"
|
"google.golang.org/grpc/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -97,7 +98,7 @@ type ClientStream interface {
|
||||||
|
|
||||||
// NewClientStream creates a new Stream for the client side. This is called
|
// NewClientStream creates a new Stream for the client side. This is called
|
||||||
// by generated code.
|
// by generated code.
|
||||||
func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (ClientStream, error) {
|
func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (_ ClientStream, err error) {
|
||||||
if cc.dopts.streamInt != nil {
|
if cc.dopts.streamInt != nil {
|
||||||
return cc.dopts.streamInt(ctx, desc, cc, method, newClientStream, opts...)
|
return cc.dopts.streamInt(ctx, desc, cc, method, newClientStream, opts...)
|
||||||
}
|
}
|
||||||
|
@ -106,11 +107,18 @@ func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
|
|
||||||
func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (_ ClientStream, err error) {
|
func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (_ ClientStream, err error) {
|
||||||
var (
|
var (
|
||||||
t transport.ClientTransport
|
t transport.ClientTransport
|
||||||
s *transport.Stream
|
s *transport.Stream
|
||||||
put func()
|
put func()
|
||||||
|
cancel context.CancelFunc
|
||||||
)
|
)
|
||||||
c := defaultCallInfo
|
c := defaultCallInfo
|
||||||
|
if mc, ok := cc.getMethodConfig(method); ok {
|
||||||
|
c.failFast = !mc.WaitForReady
|
||||||
|
if mc.Timeout > 0 {
|
||||||
|
ctx, cancel = context.WithTimeout(ctx, mc.Timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
if err := o.before(&c); err != nil {
|
if err := o.before(&c); err != nil {
|
||||||
return nil, toRPCErr(err)
|
return nil, toRPCErr(err)
|
||||||
|
@ -143,6 +151,27 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
ctx = newContextWithRPCInfo(ctx)
|
||||||
|
sh := cc.dopts.copts.StatsHandler
|
||||||
|
if sh != nil {
|
||||||
|
ctx = sh.TagRPC(ctx, &stats.RPCTagInfo{FullMethodName: method})
|
||||||
|
begin := &stats.Begin{
|
||||||
|
Client: true,
|
||||||
|
BeginTime: time.Now(),
|
||||||
|
FailFast: c.failFast,
|
||||||
|
}
|
||||||
|
sh.HandleRPC(ctx, begin)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil && sh != nil {
|
||||||
|
// Only handle end stats if err != nil.
|
||||||
|
end := &stats.End{
|
||||||
|
Client: true,
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
sh.HandleRPC(ctx, end)
|
||||||
|
}
|
||||||
|
}()
|
||||||
gopts := BalancerGetOptions{
|
gopts := BalancerGetOptions{
|
||||||
BlockingWait: !c.failFast,
|
BlockingWait: !c.failFast,
|
||||||
}
|
}
|
||||||
|
@ -150,7 +179,7 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
t, put, err = cc.getTransport(ctx, gopts)
|
t, put, err = cc.getTransport(ctx, gopts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(zhaoq): Probably revisit the error handling.
|
// TODO(zhaoq): Probably revisit the error handling.
|
||||||
if _, ok := err.(*rpcError); ok {
|
if _, ok := status.FromError(err); ok {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err == errConnClosing || err == errConnUnavailable {
|
if err == errConnClosing || err == errConnUnavailable {
|
||||||
|
@ -165,14 +194,17 @@ 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 {
|
||||||
|
if _, ok := err.(transport.ConnectionError); ok && put != nil {
|
||||||
|
// If error is connection error, transport was sending data on wire,
|
||||||
|
// and we are not sure if anything has been sent on wire.
|
||||||
|
// If error is not connection error, we are sure nothing has been sent.
|
||||||
|
updateRPCInfoInContext(ctx, rpcInfo{bytesSent: true, bytesReceived: false})
|
||||||
|
}
|
||||||
if put != nil {
|
if put != nil {
|
||||||
put()
|
put()
|
||||||
put = nil
|
put = nil
|
||||||
}
|
}
|
||||||
if _, ok := err.(transport.ConnectionError); ok || err == transport.ErrStreamDrain {
|
if _, ok := err.(transport.ConnectionError); (ok || err == transport.ErrStreamDrain) && !c.failFast {
|
||||||
if c.failFast {
|
|
||||||
return nil, toRPCErr(err)
|
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return nil, toRPCErr(err)
|
return nil, toRPCErr(err)
|
||||||
|
@ -180,12 +212,14 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
cs := &clientStream{
|
cs := &clientStream{
|
||||||
opts: opts,
|
opts: opts,
|
||||||
c: c,
|
c: c,
|
||||||
desc: desc,
|
desc: desc,
|
||||||
codec: cc.dopts.codec,
|
codec: cc.dopts.codec,
|
||||||
cp: cc.dopts.cp,
|
cp: cc.dopts.cp,
|
||||||
dc: cc.dopts.dc,
|
dc: cc.dopts.dc,
|
||||||
|
maxMsgSize: cc.dopts.maxMsgSize,
|
||||||
|
cancel: cancel,
|
||||||
|
|
||||||
put: put,
|
put: put,
|
||||||
t: t,
|
t: t,
|
||||||
|
@ -194,6 +228,9 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
|
|
||||||
tracing: EnableTracing,
|
tracing: EnableTracing,
|
||||||
trInfo: trInfo,
|
trInfo: trInfo,
|
||||||
|
|
||||||
|
statsCtx: ctx,
|
||||||
|
statsHandler: cc.dopts.copts.StatsHandler,
|
||||||
}
|
}
|
||||||
if cc.dopts.cp != nil {
|
if cc.dopts.cp != nil {
|
||||||
cs.cbuf = new(bytes.Buffer)
|
cs.cbuf = new(bytes.Buffer)
|
||||||
|
@ -204,14 +241,13 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
select {
|
select {
|
||||||
case <-t.Error():
|
case <-t.Error():
|
||||||
// Incur transport error, simply exit.
|
// Incur transport error, simply exit.
|
||||||
|
case <-cc.ctx.Done():
|
||||||
|
cs.finish(ErrClientConnClosing)
|
||||||
|
cs.closeTransportStream(ErrClientConnClosing)
|
||||||
case <-s.Done():
|
case <-s.Done():
|
||||||
// TODO: The trace of the RPC is terminated here when there is no pending
|
// TODO: The trace of the RPC is terminated here when there is no pending
|
||||||
// I/O, which is probably not the optimal solution.
|
// I/O, which is probably not the optimal solution.
|
||||||
if s.StatusCode() == codes.OK {
|
cs.finish(s.Status().Err())
|
||||||
cs.finish(nil)
|
|
||||||
} else {
|
|
||||||
cs.finish(Errorf(s.StatusCode(), "%s", s.StatusDesc()))
|
|
||||||
}
|
|
||||||
cs.closeTransportStream(nil)
|
cs.closeTransportStream(nil)
|
||||||
case <-s.GoAway():
|
case <-s.GoAway():
|
||||||
cs.finish(errConnDrain)
|
cs.finish(errConnDrain)
|
||||||
|
@ -227,25 +263,34 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
|
|
||||||
// clientStream implements a client side Stream.
|
// clientStream implements a client side Stream.
|
||||||
type clientStream struct {
|
type clientStream struct {
|
||||||
opts []CallOption
|
opts []CallOption
|
||||||
c callInfo
|
c callInfo
|
||||||
t transport.ClientTransport
|
t transport.ClientTransport
|
||||||
s *transport.Stream
|
s *transport.Stream
|
||||||
p *parser
|
p *parser
|
||||||
desc *StreamDesc
|
desc *StreamDesc
|
||||||
codec Codec
|
codec Codec
|
||||||
cp Compressor
|
cp Compressor
|
||||||
cbuf *bytes.Buffer
|
cbuf *bytes.Buffer
|
||||||
dc Decompressor
|
dc Decompressor
|
||||||
|
maxMsgSize int
|
||||||
|
cancel context.CancelFunc
|
||||||
|
|
||||||
tracing bool // set to EnableTracing when the clientStream is created.
|
tracing bool // set to EnableTracing when the clientStream is created.
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
put func()
|
put func()
|
||||||
closed bool
|
closed bool
|
||||||
|
finished bool
|
||||||
// trInfo.tr is set when the clientStream is created (if EnableTracing is true),
|
// trInfo.tr is set when the clientStream is created (if EnableTracing is true),
|
||||||
// and is set to nil when the clientStream's finish method is called.
|
// and is set to nil when the clientStream's finish method is called.
|
||||||
trInfo traceInfo
|
trInfo traceInfo
|
||||||
|
|
||||||
|
// statsCtx keeps the user context for stats handling.
|
||||||
|
// All stats collection should use the statsCtx (instead of the stream context)
|
||||||
|
// so that all the generated stats for a particular RPC can be associated in the processing phase.
|
||||||
|
statsCtx context.Context
|
||||||
|
statsHandler stats.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *clientStream) Context() context.Context {
|
func (cs *clientStream) Context() context.Context {
|
||||||
|
@ -274,6 +319,8 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
|
||||||
}
|
}
|
||||||
cs.mu.Unlock()
|
cs.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
// TODO Investigate how to signal the stats handling party.
|
||||||
|
// generate error stats if err != nil && err != io.EOF?
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cs.finish(err)
|
cs.finish(err)
|
||||||
|
@ -296,7 +343,13 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
|
||||||
}
|
}
|
||||||
err = toRPCErr(err)
|
err = toRPCErr(err)
|
||||||
}()
|
}()
|
||||||
out, err := encode(cs.codec, m, cs.cp, cs.cbuf)
|
var outPayload *stats.OutPayload
|
||||||
|
if cs.statsHandler != nil {
|
||||||
|
outPayload = &stats.OutPayload{
|
||||||
|
Client: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out, err := encode(cs.codec, m, cs.cp, cs.cbuf, outPayload)
|
||||||
defer func() {
|
defer func() {
|
||||||
if cs.cbuf != nil {
|
if cs.cbuf != nil {
|
||||||
cs.cbuf.Reset()
|
cs.cbuf.Reset()
|
||||||
|
@ -305,11 +358,22 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Errorf(codes.Internal, "grpc: %v", err)
|
return Errorf(codes.Internal, "grpc: %v", err)
|
||||||
}
|
}
|
||||||
return cs.t.Write(cs.s, out, &transport.Options{Last: false})
|
err = cs.t.Write(cs.s, out, &transport.Options{Last: false})
|
||||||
|
if err == nil && outPayload != nil {
|
||||||
|
outPayload.SentTime = time.Now()
|
||||||
|
cs.statsHandler.HandleRPC(cs.statsCtx, outPayload)
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *clientStream) RecvMsg(m interface{}) (err error) {
|
func (cs *clientStream) RecvMsg(m interface{}) (err error) {
|
||||||
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, math.MaxInt32)
|
var inPayload *stats.InPayload
|
||||||
|
if cs.statsHandler != nil {
|
||||||
|
inPayload = &stats.InPayload{
|
||||||
|
Client: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, cs.maxMsgSize, inPayload)
|
||||||
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 {
|
||||||
|
@ -324,21 +388,25 @@ func (cs *clientStream) RecvMsg(m interface{}) (err error) {
|
||||||
}
|
}
|
||||||
cs.mu.Unlock()
|
cs.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
if inPayload != nil {
|
||||||
|
cs.statsHandler.HandleRPC(cs.statsCtx, inPayload)
|
||||||
|
}
|
||||||
if !cs.desc.ClientStreams || cs.desc.ServerStreams {
|
if !cs.desc.ClientStreams || cs.desc.ServerStreams {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Special handling for client streaming rpc.
|
// Special handling for client streaming rpc.
|
||||||
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, math.MaxInt32)
|
// This recv expects EOF or errors, so we don't collect inPayload.
|
||||||
|
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, cs.maxMsgSize, nil)
|
||||||
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 se := cs.s.Status().Err(); se != nil {
|
||||||
cs.finish(err)
|
return se
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return Errorf(cs.s.StatusCode(), "%s", cs.s.StatusDesc())
|
cs.finish(err)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
}
|
}
|
||||||
|
@ -346,11 +414,11 @@ func (cs *clientStream) RecvMsg(m interface{}) (err error) {
|
||||||
cs.closeTransportStream(err)
|
cs.closeTransportStream(err)
|
||||||
}
|
}
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
if cs.s.StatusCode() == codes.OK {
|
if statusErr := cs.s.Status().Err(); statusErr != nil {
|
||||||
// Returns io.EOF to indicate the end of the stream.
|
return statusErr
|
||||||
return
|
|
||||||
}
|
}
|
||||||
return Errorf(cs.s.StatusCode(), "%s", cs.s.StatusDesc())
|
// Returns io.EOF to indicate the end of the stream.
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
}
|
}
|
||||||
|
@ -386,13 +454,37 @@ func (cs *clientStream) closeTransportStream(err error) {
|
||||||
func (cs *clientStream) finish(err error) {
|
func (cs *clientStream) finish(err error) {
|
||||||
cs.mu.Lock()
|
cs.mu.Lock()
|
||||||
defer cs.mu.Unlock()
|
defer cs.mu.Unlock()
|
||||||
|
if cs.finished {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cs.finished = true
|
||||||
|
defer func() {
|
||||||
|
if cs.cancel != nil {
|
||||||
|
cs.cancel()
|
||||||
|
}
|
||||||
|
}()
|
||||||
for _, o := range cs.opts {
|
for _, o := range cs.opts {
|
||||||
o.after(&cs.c)
|
o.after(&cs.c)
|
||||||
}
|
}
|
||||||
if cs.put != nil {
|
if cs.put != nil {
|
||||||
|
updateRPCInfoInContext(cs.s.Context(), rpcInfo{
|
||||||
|
bytesSent: cs.s.BytesSent(),
|
||||||
|
bytesReceived: cs.s.BytesReceived(),
|
||||||
|
})
|
||||||
cs.put()
|
cs.put()
|
||||||
cs.put = nil
|
cs.put = nil
|
||||||
}
|
}
|
||||||
|
if cs.statsHandler != nil {
|
||||||
|
end := &stats.End{
|
||||||
|
Client: true,
|
||||||
|
EndTime: time.Now(),
|
||||||
|
}
|
||||||
|
if err != io.EOF {
|
||||||
|
// end.Error is nil if the RPC finished successfully.
|
||||||
|
end.Error = toRPCErr(err)
|
||||||
|
}
|
||||||
|
cs.statsHandler.HandleRPC(cs.statsCtx, end)
|
||||||
|
}
|
||||||
if !cs.tracing {
|
if !cs.tracing {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -437,10 +529,10 @@ type serverStream struct {
|
||||||
dc Decompressor
|
dc Decompressor
|
||||||
cbuf *bytes.Buffer
|
cbuf *bytes.Buffer
|
||||||
maxMsgSize int
|
maxMsgSize int
|
||||||
statusCode codes.Code
|
|
||||||
statusDesc string
|
|
||||||
trInfo *traceInfo
|
trInfo *traceInfo
|
||||||
|
|
||||||
|
statsHandler stats.Handler
|
||||||
|
|
||||||
mu sync.Mutex // protects trInfo.tr after the service handler runs.
|
mu sync.Mutex // protects trInfo.tr after the service handler runs.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,7 +574,11 @@ func (ss *serverStream) SendMsg(m interface{}) (err error) {
|
||||||
ss.mu.Unlock()
|
ss.mu.Unlock()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
out, err := encode(ss.codec, m, ss.cp, ss.cbuf)
|
var outPayload *stats.OutPayload
|
||||||
|
if ss.statsHandler != nil {
|
||||||
|
outPayload = &stats.OutPayload{}
|
||||||
|
}
|
||||||
|
out, err := encode(ss.codec, m, ss.cp, ss.cbuf, outPayload)
|
||||||
defer func() {
|
defer func() {
|
||||||
if ss.cbuf != nil {
|
if ss.cbuf != nil {
|
||||||
ss.cbuf.Reset()
|
ss.cbuf.Reset()
|
||||||
|
@ -495,6 +591,10 @@ func (ss *serverStream) SendMsg(m interface{}) (err error) {
|
||||||
if err := ss.t.Write(ss.s, out, &transport.Options{Last: false}); err != nil {
|
if err := ss.t.Write(ss.s, out, &transport.Options{Last: false}); err != nil {
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
}
|
}
|
||||||
|
if outPayload != nil {
|
||||||
|
outPayload.SentTime = time.Now()
|
||||||
|
ss.statsHandler.HandleRPC(ss.s.Context(), outPayload)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -513,7 +613,11 @@ func (ss *serverStream) RecvMsg(m interface{}) (err error) {
|
||||||
ss.mu.Unlock()
|
ss.mu.Unlock()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if err := recv(ss.p, ss.codec, ss.s, ss.dc, m, ss.maxMsgSize); err != nil {
|
var inPayload *stats.InPayload
|
||||||
|
if ss.statsHandler != nil {
|
||||||
|
inPayload = &stats.InPayload{}
|
||||||
|
}
|
||||||
|
if err := recv(ss.p, ss.codec, ss.s, ss.dc, m, ss.maxMsgSize, inPayload); err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -522,5 +626,8 @@ func (ss *serverStream) RecvMsg(m interface{}) (err error) {
|
||||||
}
|
}
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
}
|
}
|
||||||
|
if inPayload != nil {
|
||||||
|
ss.statsHandler.HandleRPC(ss.s.Context(), inPayload)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 tap defines the function handles which are executed on the transport
|
||||||
|
// layer of gRPC-Go and related information. Everything here is EXPERIMENTAL.
|
||||||
|
package tap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Info defines the relevant information needed by the handles.
|
||||||
|
type Info struct {
|
||||||
|
// FullMethodName is the string of grpc method (in the format of
|
||||||
|
// /package.service/method).
|
||||||
|
FullMethodName string
|
||||||
|
// TODO: More to be added.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerInHandle defines the function which runs when a new stream is created
|
||||||
|
// on the server side. Note that it is executed in the per-connection I/O goroutine(s) instead
|
||||||
|
// of per-RPC goroutine. Therefore, users should NOT have any blocking/time-consuming
|
||||||
|
// work in this handle. Otherwise all the RPCs would slow down.
|
||||||
|
type ServerInHandle func(ctx context.Context, info *Info) (context.Context, error)
|
|
@ -35,7 +35,9 @@ package transport
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
)
|
)
|
||||||
|
@ -44,8 +46,18 @@ const (
|
||||||
// The default value of flow control window size in HTTP2 spec.
|
// The default value of flow control window size in HTTP2 spec.
|
||||||
defaultWindowSize = 65535
|
defaultWindowSize = 65535
|
||||||
// The initial window size for flow control.
|
// The initial window size for flow control.
|
||||||
initialWindowSize = defaultWindowSize // for an RPC
|
initialWindowSize = defaultWindowSize // for an RPC
|
||||||
initialConnWindowSize = defaultWindowSize * 16 // for a connection
|
initialConnWindowSize = defaultWindowSize * 16 // for a connection
|
||||||
|
infinity = time.Duration(math.MaxInt64)
|
||||||
|
defaultClientKeepaliveTime = infinity
|
||||||
|
defaultClientKeepaliveTimeout = time.Duration(20 * time.Second)
|
||||||
|
defaultMaxStreamsClient = 100
|
||||||
|
defaultMaxConnectionIdle = infinity
|
||||||
|
defaultMaxConnectionAge = infinity
|
||||||
|
defaultMaxConnectionAgeGrace = infinity
|
||||||
|
defaultServerKeepaliveTime = time.Duration(2 * time.Hour)
|
||||||
|
defaultServerKeepaliveTimeout = time.Duration(20 * time.Second)
|
||||||
|
defaultKeepalivePolicyMinTime = time.Duration(5 * time.Minute)
|
||||||
)
|
)
|
||||||
|
|
||||||
// The following defines various control items which could flow through
|
// The following defines various control items which could flow through
|
||||||
|
@ -73,6 +85,8 @@ type resetStream struct {
|
||||||
func (*resetStream) item() {}
|
func (*resetStream) item() {}
|
||||||
|
|
||||||
type goAway struct {
|
type goAway struct {
|
||||||
|
code http2.ErrCode
|
||||||
|
debugData []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*goAway) item() {}
|
func (*goAway) item() {}
|
||||||
|
@ -111,35 +125,9 @@ func newQuotaPool(q int) *quotaPool {
|
||||||
return qb
|
return qb
|
||||||
}
|
}
|
||||||
|
|
||||||
// add adds n to the available quota and tries to send it on acquire.
|
// add cancels the pending quota sent on acquired, incremented by v and sends
|
||||||
func (qb *quotaPool) add(n int) {
|
|
||||||
qb.mu.Lock()
|
|
||||||
defer qb.mu.Unlock()
|
|
||||||
qb.quota += n
|
|
||||||
if qb.quota <= 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case qb.c <- qb.quota:
|
|
||||||
qb.quota = 0
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// cancel cancels the pending quota sent on acquire, if any.
|
|
||||||
func (qb *quotaPool) cancel() {
|
|
||||||
qb.mu.Lock()
|
|
||||||
defer qb.mu.Unlock()
|
|
||||||
select {
|
|
||||||
case n := <-qb.c:
|
|
||||||
qb.quota += n
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset cancels the pending quota sent on acquired, incremented by v and sends
|
|
||||||
// it back on acquire.
|
// it back on acquire.
|
||||||
func (qb *quotaPool) reset(v int) {
|
func (qb *quotaPool) add(v int) {
|
||||||
qb.mu.Lock()
|
qb.mu.Lock()
|
||||||
defer qb.mu.Unlock()
|
defer qb.mu.Unlock()
|
||||||
select {
|
select {
|
||||||
|
@ -151,6 +139,10 @@ func (qb *quotaPool) reset(v int) {
|
||||||
if qb.quota <= 0 {
|
if qb.quota <= 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// After the pool has been created, this is the only place that sends on
|
||||||
|
// the channel. Since mu is held at this point and any quota that was sent
|
||||||
|
// on the channel has been retrieved, we know that this code will always
|
||||||
|
// place any positive quota value on the channel.
|
||||||
select {
|
select {
|
||||||
case qb.c <- qb.quota:
|
case qb.c <- qb.quota:
|
||||||
qb.quota = 0
|
qb.quota = 0
|
||||||
|
|
|
@ -53,6 +53,7 @@ import (
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/peer"
|
"google.golang.org/grpc/peer"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewServerHandlerTransport returns a ServerTransport handling gRPC
|
// NewServerHandlerTransport returns a ServerTransport handling gRPC
|
||||||
|
@ -110,6 +111,10 @@ func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request) (ServerTr
|
||||||
v = v[:i]
|
v = v[:i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
v, err := decodeMetadataHeader(k, v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, streamErrorf(codes.InvalidArgument, "malformed binary metadata: %v", err)
|
||||||
|
}
|
||||||
metakv = append(metakv, k, v)
|
metakv = append(metakv, k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,7 +187,7 @@ func (ht *serverHandlerTransport) do(fn func()) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ht *serverHandlerTransport) WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error {
|
func (ht *serverHandlerTransport) WriteStatus(s *Stream, st *status.Status) error {
|
||||||
err := ht.do(func() {
|
err := ht.do(func() {
|
||||||
ht.writeCommonHeaders(s)
|
ht.writeCommonHeaders(s)
|
||||||
|
|
||||||
|
@ -192,10 +197,13 @@ func (ht *serverHandlerTransport) WriteStatus(s *Stream, statusCode codes.Code,
|
||||||
ht.rw.(http.Flusher).Flush()
|
ht.rw.(http.Flusher).Flush()
|
||||||
|
|
||||||
h := ht.rw.Header()
|
h := ht.rw.Header()
|
||||||
h.Set("Grpc-Status", fmt.Sprintf("%d", statusCode))
|
h.Set("Grpc-Status", fmt.Sprintf("%d", st.Code()))
|
||||||
if statusDesc != "" {
|
if m := st.Message(); m != "" {
|
||||||
h.Set("Grpc-Message", encodeGrpcMessage(statusDesc))
|
h.Set("Grpc-Message", encodeGrpcMessage(m))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Support Grpc-Status-Details-Bin
|
||||||
|
|
||||||
if md := s.Trailer(); len(md) > 0 {
|
if md := s.Trailer(); len(md) > 0 {
|
||||||
for k, vv := range md {
|
for k, vv := range md {
|
||||||
// Clients don't tolerate reading restricted headers after some non restricted ones were sent.
|
// Clients don't tolerate reading restricted headers after some non restricted ones were sent.
|
||||||
|
@ -203,10 +211,9 @@ func (ht *serverHandlerTransport) WriteStatus(s *Stream, statusCode codes.Code,
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, v := range vv {
|
for _, v := range vv {
|
||||||
// http2 ResponseWriter mechanism to
|
// http2 ResponseWriter mechanism to send undeclared Trailers after
|
||||||
// send undeclared Trailers after the
|
// the headers have possibly been written.
|
||||||
// headers have possibly been written.
|
h.Add(http2.TrailerPrefix+k, encodeMetadataHeader(k, v))
|
||||||
h.Add(http2.TrailerPrefix+k, v)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,6 +241,7 @@ func (ht *serverHandlerTransport) writeCommonHeaders(s *Stream) {
|
||||||
// and https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
|
// and https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
|
||||||
h.Add("Trailer", "Grpc-Status")
|
h.Add("Trailer", "Grpc-Status")
|
||||||
h.Add("Trailer", "Grpc-Message")
|
h.Add("Trailer", "Grpc-Message")
|
||||||
|
// TODO: Support Grpc-Status-Details-Bin
|
||||||
|
|
||||||
if s.sendCompress != "" {
|
if s.sendCompress != "" {
|
||||||
h.Set("Grpc-Encoding", s.sendCompress)
|
h.Set("Grpc-Encoding", s.sendCompress)
|
||||||
|
@ -260,6 +268,7 @@ func (ht *serverHandlerTransport) WriteHeader(s *Stream, md metadata.MD) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, v := range vv {
|
for _, v := range vv {
|
||||||
|
v = encodeMetadataHeader(k, v)
|
||||||
h.Add(k, v)
|
h.Add(k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -268,7 +277,7 @@ func (ht *serverHandlerTransport) WriteHeader(s *Stream, md metadata.MD) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream)) {
|
func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream), traceCtx func(context.Context, string) context.Context) {
|
||||||
// With this transport type there will be exactly 1 stream: this HTTP request.
|
// With this transport type there will be exactly 1 stream: this HTTP request.
|
||||||
|
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
|
@ -314,7 +323,7 @@ func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream)) {
|
||||||
if req.TLS != nil {
|
if req.TLS != nil {
|
||||||
pr.AuthInfo = credentials.TLSInfo{State: *req.TLS}
|
pr.AuthInfo = credentials.TLSInfo{State: *req.TLS}
|
||||||
}
|
}
|
||||||
ctx = metadata.NewContext(ctx, ht.headerMD)
|
ctx = metadata.NewIncomingContext(ctx, ht.headerMD)
|
||||||
ctx = peer.NewContext(ctx, pr)
|
ctx = peer.NewContext(ctx, pr)
|
||||||
s.ctx = newContextWithStream(ctx, s)
|
s.ctx = newContextWithStream(ctx, s)
|
||||||
s.dec = &recvBufferReader{ctx: s.ctx, recv: s.buf}
|
s.dec = &recvBufferReader{ctx: s.ctx, recv: s.buf}
|
||||||
|
|
|
@ -35,12 +35,12 @@ package transport
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -49,18 +49,24 @@ import (
|
||||||
"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/keepalive"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/peer"
|
"google.golang.org/grpc/peer"
|
||||||
|
"google.golang.org/grpc/stats"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// http2Client implements the ClientTransport interface with HTTP2.
|
// http2Client implements the ClientTransport interface with HTTP2.
|
||||||
type http2Client struct {
|
type http2Client struct {
|
||||||
target string // server name/addr
|
ctx context.Context
|
||||||
userAgent string
|
target string // server name/addr
|
||||||
md interface{}
|
userAgent string
|
||||||
conn net.Conn // underlying communication channel
|
md interface{}
|
||||||
authInfo credentials.AuthInfo // auth info about the connection
|
conn net.Conn // underlying communication channel
|
||||||
nextID uint32 // the next stream ID to be used
|
remoteAddr net.Addr
|
||||||
|
localAddr net.Addr
|
||||||
|
authInfo credentials.AuthInfo // auth info about the connection
|
||||||
|
nextID uint32 // the next stream ID to be used
|
||||||
|
|
||||||
// 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 sending a value on writableChan
|
||||||
|
@ -76,6 +82,8 @@ type http2Client struct {
|
||||||
// goAway is closed to notify the upper layer (i.e., addrConn.transportMonitor)
|
// goAway is closed to notify the upper layer (i.e., addrConn.transportMonitor)
|
||||||
// that the server sent GoAway on this transport.
|
// that the server sent GoAway on this transport.
|
||||||
goAway chan struct{}
|
goAway chan struct{}
|
||||||
|
// awakenKeepalive is used to wake up keepalive when after it has gone dormant.
|
||||||
|
awakenKeepalive chan struct{}
|
||||||
|
|
||||||
framer *framer
|
framer *framer
|
||||||
hBuf *bytes.Buffer // the buffer for HPACK encoding
|
hBuf *bytes.Buffer // the buffer for HPACK encoding
|
||||||
|
@ -95,6 +103,13 @@ type http2Client struct {
|
||||||
|
|
||||||
creds []credentials.PerRPCCredentials
|
creds []credentials.PerRPCCredentials
|
||||||
|
|
||||||
|
// Boolean to keep track of reading activity on transport.
|
||||||
|
// 1 is true and 0 is false.
|
||||||
|
activity uint32 // Accessed atomically.
|
||||||
|
kp keepalive.ClientParameters
|
||||||
|
|
||||||
|
statsHandler stats.Handler
|
||||||
|
|
||||||
mu sync.Mutex // guard the following variables
|
mu sync.Mutex // guard the following variables
|
||||||
state transportState // the state of underlying connection
|
state transportState // the state of underlying connection
|
||||||
activeStreams map[uint32]*Stream
|
activeStreams map[uint32]*Stream
|
||||||
|
@ -106,6 +121,9 @@ type http2Client struct {
|
||||||
goAwayID uint32
|
goAwayID uint32
|
||||||
// prevGoAway ID records the Last-Stream-ID in the previous GOAway frame.
|
// prevGoAway ID records the Last-Stream-ID in the previous GOAway frame.
|
||||||
prevGoAwayID uint32
|
prevGoAwayID uint32
|
||||||
|
// goAwayReason records the http2.ErrCode and debug data received with the
|
||||||
|
// GoAway frame.
|
||||||
|
goAwayReason GoAwayReason
|
||||||
}
|
}
|
||||||
|
|
||||||
func dial(ctx context.Context, fn func(context.Context, string) (net.Conn, error), addr string) (net.Conn, error) {
|
func dial(ctx context.Context, fn func(context.Context, string) (net.Conn, error), addr string) (net.Conn, error) {
|
||||||
|
@ -150,6 +168,9 @@ func newHTTP2Client(ctx context.Context, addr TargetInfo, opts ConnectOptions) (
|
||||||
scheme := "http"
|
scheme := "http"
|
||||||
conn, err := dial(ctx, opts.Dialer, addr.Addr)
|
conn, err := dial(ctx, opts.Dialer, addr.Addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if opts.FailOnNonTempDialError {
|
||||||
|
return nil, connectionErrorf(isTemporary(err), err, "transport: %v", err)
|
||||||
|
}
|
||||||
return nil, connectionErrorf(true, err, "transport: %v", err)
|
return nil, connectionErrorf(true, err, "transport: %v", err)
|
||||||
}
|
}
|
||||||
// Any further errors will close the underlying connection
|
// Any further errors will close the underlying connection
|
||||||
|
@ -169,23 +190,31 @@ func newHTTP2Client(ctx context.Context, addr TargetInfo, opts ConnectOptions) (
|
||||||
return nil, connectionErrorf(temp, err, "transport: %v", err)
|
return nil, connectionErrorf(temp, err, "transport: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ua := primaryUA
|
kp := opts.KeepaliveParams
|
||||||
if opts.UserAgent != "" {
|
// Validate keepalive parameters.
|
||||||
ua = opts.UserAgent + " " + ua
|
if kp.Time == 0 {
|
||||||
|
kp.Time = defaultClientKeepaliveTime
|
||||||
|
}
|
||||||
|
if kp.Timeout == 0 {
|
||||||
|
kp.Timeout = defaultClientKeepaliveTimeout
|
||||||
}
|
}
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
t := &http2Client{
|
t := &http2Client{
|
||||||
target: addr.Addr,
|
ctx: ctx,
|
||||||
userAgent: ua,
|
target: addr.Addr,
|
||||||
md: addr.Metadata,
|
userAgent: opts.UserAgent,
|
||||||
conn: conn,
|
md: addr.Metadata,
|
||||||
authInfo: authInfo,
|
conn: conn,
|
||||||
|
remoteAddr: conn.RemoteAddr(),
|
||||||
|
localAddr: conn.LocalAddr(),
|
||||||
|
authInfo: authInfo,
|
||||||
// The client initiated stream id is odd starting from 1.
|
// The client initiated stream id is odd starting from 1.
|
||||||
nextID: 1,
|
nextID: 1,
|
||||||
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{}),
|
||||||
goAway: make(chan struct{}),
|
goAway: make(chan struct{}),
|
||||||
|
awakenKeepalive: make(chan struct{}, 1),
|
||||||
framer: newFramer(conn),
|
framer: newFramer(conn),
|
||||||
hBuf: &buf,
|
hBuf: &buf,
|
||||||
hEnc: hpack.NewEncoder(&buf),
|
hEnc: hpack.NewEncoder(&buf),
|
||||||
|
@ -196,8 +225,24 @@ func newHTTP2Client(ctx context.Context, addr TargetInfo, opts ConnectOptions) (
|
||||||
state: reachable,
|
state: reachable,
|
||||||
activeStreams: make(map[uint32]*Stream),
|
activeStreams: make(map[uint32]*Stream),
|
||||||
creds: opts.PerRPCCredentials,
|
creds: opts.PerRPCCredentials,
|
||||||
maxStreams: math.MaxInt32,
|
maxStreams: defaultMaxStreamsClient,
|
||||||
|
streamsQuota: newQuotaPool(defaultMaxStreamsClient),
|
||||||
streamSendQuota: defaultWindowSize,
|
streamSendQuota: defaultWindowSize,
|
||||||
|
kp: kp,
|
||||||
|
statsHandler: opts.StatsHandler,
|
||||||
|
}
|
||||||
|
// Make sure awakenKeepalive can't be written upon.
|
||||||
|
// keepalive routine will make it writable, if need be.
|
||||||
|
t.awakenKeepalive <- struct{}{}
|
||||||
|
if t.statsHandler != nil {
|
||||||
|
t.ctx = t.statsHandler.TagConn(t.ctx, &stats.ConnTagInfo{
|
||||||
|
RemoteAddr: t.remoteAddr,
|
||||||
|
LocalAddr: t.localAddr,
|
||||||
|
})
|
||||||
|
connBegin := &stats.ConnBegin{
|
||||||
|
Client: true,
|
||||||
|
}
|
||||||
|
t.statsHandler.HandleConn(t.ctx, connBegin)
|
||||||
}
|
}
|
||||||
// Start the reader goroutine for incoming message. Each transport has
|
// Start the reader goroutine for incoming message. Each transport has
|
||||||
// a dedicated goroutine which reads HTTP2 frame from network. Then it
|
// a dedicated goroutine which reads HTTP2 frame from network. Then it
|
||||||
|
@ -233,6 +278,9 @@ func newHTTP2Client(ctx context.Context, addr TargetInfo, opts ConnectOptions) (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
go t.controller()
|
go t.controller()
|
||||||
|
if t.kp.Time != infinity {
|
||||||
|
go t.keepalive()
|
||||||
|
}
|
||||||
t.writableChan <- 0
|
t.writableChan <- 0
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
@ -266,16 +314,17 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStream creates a stream and register it into the transport as "active"
|
// NewStream creates a stream and registers it into the transport as "active"
|
||||||
// streams.
|
// streams.
|
||||||
func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) {
|
func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) {
|
||||||
pr := &peer.Peer{
|
pr := &peer.Peer{
|
||||||
Addr: t.conn.RemoteAddr(),
|
Addr: t.remoteAddr,
|
||||||
}
|
}
|
||||||
// Attach Auth info if there is any.
|
// Attach Auth info if there is any.
|
||||||
if t.authInfo != nil {
|
if t.authInfo != nil {
|
||||||
pr.AuthInfo = t.authInfo
|
pr.AuthInfo = t.authInfo
|
||||||
}
|
}
|
||||||
|
userCtx := ctx
|
||||||
ctx = peer.NewContext(ctx, pr)
|
ctx = peer.NewContext(ctx, pr)
|
||||||
authData := make(map[string]string)
|
authData := make(map[string]string)
|
||||||
for _, c := range t.creds {
|
for _, c := range t.creds {
|
||||||
|
@ -313,21 +362,18 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
return nil, ErrConnClosing
|
return nil, ErrConnClosing
|
||||||
}
|
}
|
||||||
checkStreamsQuota := t.streamsQuota != nil
|
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
if checkStreamsQuota {
|
sq, err := wait(ctx, nil, nil, t.shutdownChan, t.streamsQuota.acquire())
|
||||||
sq, err := wait(ctx, nil, nil, t.shutdownChan, t.streamsQuota.acquire())
|
if err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, err
|
}
|
||||||
}
|
// Returns the quota balance back.
|
||||||
// Returns the quota balance back.
|
if sq > 1 {
|
||||||
if sq > 1 {
|
t.streamsQuota.add(sq - 1)
|
||||||
t.streamsQuota.add(sq - 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if _, err := wait(ctx, nil, nil, t.shutdownChan, t.writableChan); err != nil {
|
if _, err := wait(ctx, nil, nil, t.shutdownChan, t.writableChan); err != nil {
|
||||||
// Return the quota back now because there is no stream returned to the caller.
|
// Return the quota back now because there is no stream returned to the caller.
|
||||||
if _, ok := err.(StreamError); ok && checkStreamsQuota {
|
if _, ok := err.(StreamError); ok {
|
||||||
t.streamsQuota.add(1)
|
t.streamsQuota.add(1)
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -335,9 +381,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
if t.state == draining {
|
if t.state == draining {
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
if checkStreamsQuota {
|
t.streamsQuota.add(1)
|
||||||
t.streamsQuota.add(1)
|
|
||||||
}
|
|
||||||
// Need to make t writable again so that the rpc in flight can still proceed.
|
// Need to make t writable again so that the rpc in flight can still proceed.
|
||||||
t.writableChan <- 0
|
t.writableChan <- 0
|
||||||
return nil, ErrStreamDrain
|
return nil, ErrStreamDrain
|
||||||
|
@ -347,18 +391,19 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
|
||||||
return nil, ErrConnClosing
|
return nil, ErrConnClosing
|
||||||
}
|
}
|
||||||
s := t.newStream(ctx, callHdr)
|
s := t.newStream(ctx, callHdr)
|
||||||
|
s.clientStatsCtx = userCtx
|
||||||
t.activeStreams[s.id] = s
|
t.activeStreams[s.id] = s
|
||||||
|
// If the number of active streams change from 0 to 1, then check if keepalive
|
||||||
|
// has gone dormant. If so, wake it up.
|
||||||
|
if len(t.activeStreams) == 1 {
|
||||||
|
select {
|
||||||
|
case t.awakenKeepalive <- struct{}{}:
|
||||||
|
t.framer.writePing(false, false, [8]byte{})
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This stream is not counted when applySetings(...) initialize t.streamsQuota.
|
|
||||||
// Reset t.streamsQuota to the right value.
|
|
||||||
var reset bool
|
|
||||||
if !checkStreamsQuota && t.streamsQuota != nil {
|
|
||||||
reset = true
|
|
||||||
}
|
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
if reset {
|
|
||||||
t.streamsQuota.reset(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HPACK encodes various headers. Note that once WriteField(...) is
|
// HPACK encodes various headers. Note that once WriteField(...) is
|
||||||
// called, the corresponding headers/continuation frame has to be sent
|
// called, the corresponding headers/continuation frame has to be sent
|
||||||
|
@ -390,29 +435,30 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
|
||||||
hasMD bool
|
hasMD bool
|
||||||
endHeaders bool
|
endHeaders bool
|
||||||
)
|
)
|
||||||
if md, ok := metadata.FromContext(ctx); ok {
|
if md, ok := metadata.FromOutgoingContext(ctx); ok {
|
||||||
hasMD = true
|
hasMD = true
|
||||||
for k, v := range md {
|
for k, vv := range md {
|
||||||
// HTTP doesn't allow you to set pseudoheaders after non pseudoheaders were set.
|
// HTTP doesn't allow you to set pseudoheaders after non pseudoheaders were set.
|
||||||
if isReservedHeader(k) {
|
if isReservedHeader(k) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, entry := range v {
|
for _, v := range vv {
|
||||||
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
|
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if md, ok := t.md.(*metadata.MD); ok {
|
if md, ok := t.md.(*metadata.MD); ok {
|
||||||
for k, v := range *md {
|
for k, vv := range *md {
|
||||||
if isReservedHeader(k) {
|
if isReservedHeader(k) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, entry := range v {
|
for _, v := range vv {
|
||||||
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
|
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
first := true
|
first := true
|
||||||
|
bufLen := t.hBuf.Len()
|
||||||
// Sends the headers in a single batch even when they span multiple frames.
|
// Sends the headers in a single batch even when they span multiple frames.
|
||||||
for !endHeaders {
|
for !endHeaders {
|
||||||
size := t.hBuf.Len()
|
size := t.hBuf.Len()
|
||||||
|
@ -447,6 +493,19 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
|
||||||
return nil, connectionErrorf(true, err, "transport: %v", err)
|
return nil, connectionErrorf(true, err, "transport: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.bytesSent = true
|
||||||
|
|
||||||
|
if t.statsHandler != nil {
|
||||||
|
outHeader := &stats.OutHeader{
|
||||||
|
Client: true,
|
||||||
|
WireLength: bufLen,
|
||||||
|
FullMethod: callHdr.Method,
|
||||||
|
RemoteAddr: t.remoteAddr,
|
||||||
|
LocalAddr: t.localAddr,
|
||||||
|
Compression: callHdr.SendCompress,
|
||||||
|
}
|
||||||
|
t.statsHandler.HandleRPC(s.clientStatsCtx, outHeader)
|
||||||
|
}
|
||||||
t.writableChan <- 0
|
t.writableChan <- 0
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
@ -454,15 +513,11 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
|
||||||
// CloseStream clears the footprint of a stream when the stream is not needed any more.
|
// CloseStream clears the footprint of a stream when the stream is not needed any more.
|
||||||
// This must not be executed in reader's goroutine.
|
// This must not be executed in reader's goroutine.
|
||||||
func (t *http2Client) CloseStream(s *Stream, err error) {
|
func (t *http2Client) CloseStream(s *Stream, err error) {
|
||||||
var updateStreams bool
|
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
if t.activeStreams == nil {
|
if t.activeStreams == nil {
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if t.streamsQuota != nil {
|
|
||||||
updateStreams = true
|
|
||||||
}
|
|
||||||
delete(t.activeStreams, s.id)
|
delete(t.activeStreams, s.id)
|
||||||
if t.state == draining && len(t.activeStreams) == 0 {
|
if t.state == draining && len(t.activeStreams) == 0 {
|
||||||
// The transport is draining and s is the last live stream on t.
|
// The transport is draining and s is the last live stream on t.
|
||||||
|
@ -471,10 +526,27 @@ func (t *http2Client) CloseStream(s *Stream, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
if updateStreams {
|
// rstStream is true in case the stream is being closed at the client-side
|
||||||
t.streamsQuota.add(1)
|
// and the server needs to be intimated about it by sending a RST_STREAM
|
||||||
}
|
// frame.
|
||||||
|
// To make sure this frame is written to the wire before the headers of the
|
||||||
|
// next stream waiting for streamsQuota, we add to streamsQuota pool only
|
||||||
|
// after having acquired the writableChan to send RST_STREAM out (look at
|
||||||
|
// the controller() routine).
|
||||||
|
var rstStream bool
|
||||||
|
var rstError http2.ErrCode
|
||||||
|
defer func() {
|
||||||
|
// In case, the client doesn't have to send RST_STREAM to server
|
||||||
|
// we can safely add back to streamsQuota pool now.
|
||||||
|
if !rstStream {
|
||||||
|
t.streamsQuota.add(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.controlBuf.put(&resetStream{s.id, rstError})
|
||||||
|
}()
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
|
rstStream = s.rstStream
|
||||||
|
rstError = s.rstError
|
||||||
if q := s.fc.resetPendingData(); q > 0 {
|
if q := s.fc.resetPendingData(); q > 0 {
|
||||||
if n := t.fc.onRead(q); n > 0 {
|
if n := t.fc.onRead(q); n > 0 {
|
||||||
t.controlBuf.put(&windowUpdate{0, n})
|
t.controlBuf.put(&windowUpdate{0, n})
|
||||||
|
@ -490,8 +562,9 @@ func (t *http2Client) CloseStream(s *Stream, err error) {
|
||||||
}
|
}
|
||||||
s.state = streamDone
|
s.state = streamDone
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
if se, ok := err.(StreamError); ok && se.Code != codes.DeadlineExceeded {
|
if _, ok := err.(StreamError); ok {
|
||||||
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeCancel})
|
rstStream = true
|
||||||
|
rstError = http2.ErrCodeCancel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,6 +598,12 @@ func (t *http2Client) Close() (err error) {
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
s.write(recvMsg{err: ErrConnClosing})
|
s.write(recvMsg{err: ErrConnClosing})
|
||||||
}
|
}
|
||||||
|
if t.statsHandler != nil {
|
||||||
|
connEnd := &stats.ConnEnd{
|
||||||
|
Client: true,
|
||||||
|
}
|
||||||
|
t.statsHandler.HandleConn(t.ctx, connEnd)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,19 +661,14 @@ func (t *http2Client) Write(s *Stream, data []byte, opts *Options) error {
|
||||||
var p []byte
|
var p []byte
|
||||||
if r.Len() > 0 {
|
if r.Len() > 0 {
|
||||||
size := http2MaxFrameLen
|
size := http2MaxFrameLen
|
||||||
s.sendQuotaPool.add(0)
|
|
||||||
// Wait until the stream has some quota to send the data.
|
// Wait until the stream has some quota to send the data.
|
||||||
sq, err := wait(s.ctx, s.done, s.goAway, t.shutdownChan, s.sendQuotaPool.acquire())
|
sq, err := wait(s.ctx, s.done, s.goAway, t.shutdownChan, s.sendQuotaPool.acquire())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.sendQuotaPool.add(0)
|
|
||||||
// Wait until the transport has some quota to send the data.
|
// Wait until the transport has some quota to send the data.
|
||||||
tq, err := wait(s.ctx, s.done, s.goAway, t.shutdownChan, t.sendQuotaPool.acquire())
|
tq, err := wait(s.ctx, s.done, s.goAway, t.shutdownChan, t.sendQuotaPool.acquire())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(StreamError); ok || err == io.EOF {
|
|
||||||
t.sendQuotaPool.cancel()
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if sq < size {
|
if sq < size {
|
||||||
|
@ -704,7 +778,7 @@ func (t *http2Client) updateWindow(s *Stream, n uint32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *http2Client) handleData(f *http2.DataFrame) {
|
func (t *http2Client) handleData(f *http2.DataFrame) {
|
||||||
size := len(f.Data())
|
size := f.Header().Length
|
||||||
if err := t.fc.onData(uint32(size)); err != nil {
|
if err := t.fc.onData(uint32(size)); err != nil {
|
||||||
t.notifyError(connectionErrorf(true, err, "%v", err))
|
t.notifyError(connectionErrorf(true, err, "%v", err))
|
||||||
return
|
return
|
||||||
|
@ -718,6 +792,11 @@ func (t *http2Client) handleData(f *http2.DataFrame) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if size > 0 {
|
if size > 0 {
|
||||||
|
if f.Header().Flags.Has(http2.FlagDataPadded) {
|
||||||
|
if w := t.fc.onRead(uint32(size) - uint32(len(f.Data()))); w > 0 {
|
||||||
|
t.controlBuf.put(&windowUpdate{0, w})
|
||||||
|
}
|
||||||
|
}
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
if s.state == streamDone {
|
if s.state == streamDone {
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
@ -728,22 +807,27 @@ func (t *http2Client) handleData(f *http2.DataFrame) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := s.fc.onData(uint32(size)); err != nil {
|
if err := s.fc.onData(uint32(size)); err != nil {
|
||||||
s.state = streamDone
|
s.rstStream = true
|
||||||
s.statusCode = codes.Internal
|
s.rstError = http2.ErrCodeFlowControl
|
||||||
s.statusDesc = err.Error()
|
s.finish(status.New(codes.Internal, err.Error()))
|
||||||
close(s.done)
|
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
s.write(recvMsg{err: io.EOF})
|
s.write(recvMsg{err: io.EOF})
|
||||||
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeFlowControl})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if f.Header().Flags.Has(http2.FlagDataPadded) {
|
||||||
|
if w := s.fc.onRead(uint32(size) - uint32(len(f.Data()))); w > 0 {
|
||||||
|
t.controlBuf.put(&windowUpdate{s.id, w})
|
||||||
|
}
|
||||||
|
}
|
||||||
s.mu.Unlock()
|
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?
|
||||||
data := make([]byte, size)
|
if len(f.Data()) > 0 {
|
||||||
copy(data, f.Data())
|
data := make([]byte, len(f.Data()))
|
||||||
s.write(recvMsg{data: data})
|
copy(data, f.Data())
|
||||||
|
s.write(recvMsg{data: data})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// The server has closed the stream without sending trailers. Record that
|
// The server has closed the stream without sending trailers. Record that
|
||||||
// the read direction is closed, and set the status appropriately.
|
// the read direction is closed, and set the status appropriately.
|
||||||
|
@ -753,10 +837,7 @@ func (t *http2Client) handleData(f *http2.DataFrame) {
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.state = streamDone
|
s.finish(status.New(codes.Internal, "server closed the stream without sending trailers"))
|
||||||
s.statusCode = codes.Internal
|
|
||||||
s.statusDesc = "server closed the stream without sending trailers"
|
|
||||||
close(s.done)
|
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
s.write(recvMsg{err: io.EOF})
|
s.write(recvMsg{err: io.EOF})
|
||||||
}
|
}
|
||||||
|
@ -772,18 +853,16 @@ func (t *http2Client) handleRSTStream(f *http2.RSTStreamFrame) {
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.state = streamDone
|
|
||||||
if !s.headerDone {
|
if !s.headerDone {
|
||||||
close(s.headerChan)
|
close(s.headerChan)
|
||||||
s.headerDone = true
|
s.headerDone = true
|
||||||
}
|
}
|
||||||
s.statusCode, ok = http2ErrConvTab[http2.ErrCode(f.ErrCode)]
|
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
|
statusCode = codes.Unknown
|
||||||
}
|
}
|
||||||
s.statusDesc = fmt.Sprintf("stream terminated by RST_STREAM with error code: %d", f.ErrCode)
|
s.finish(status.Newf(statusCode, "stream terminated by RST_STREAM with error code: %d", f.ErrCode))
|
||||||
close(s.done)
|
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
s.write(recvMsg{err: io.EOF})
|
s.write(recvMsg{err: io.EOF})
|
||||||
}
|
}
|
||||||
|
@ -811,6 +890,9 @@ func (t *http2Client) handlePing(f *http2.PingFrame) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
|
func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
|
||||||
|
if f.ErrCode == http2.ErrCodeEnhanceYourCalm {
|
||||||
|
grpclog.Printf("Client received GoAway with http2.ErrCodeEnhanceYourCalm.")
|
||||||
|
}
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
if t.state == reachable || t.state == draining {
|
if t.state == reachable || t.state == draining {
|
||||||
if f.LastStreamID > 0 && f.LastStreamID%2 != 1 {
|
if f.LastStreamID > 0 && f.LastStreamID%2 != 1 {
|
||||||
|
@ -832,6 +914,7 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
|
t.setGoAwayReason(f)
|
||||||
}
|
}
|
||||||
t.goAwayID = f.LastStreamID
|
t.goAwayID = f.LastStreamID
|
||||||
close(t.goAway)
|
close(t.goAway)
|
||||||
|
@ -839,6 +922,26 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setGoAwayReason sets the value of t.goAwayReason based
|
||||||
|
// on the GoAway frame received.
|
||||||
|
// It expects a lock on transport's mutext to be held by
|
||||||
|
// the caller.
|
||||||
|
func (t *http2Client) setGoAwayReason(f *http2.GoAwayFrame) {
|
||||||
|
t.goAwayReason = NoReason
|
||||||
|
switch f.ErrCode {
|
||||||
|
case http2.ErrCodeEnhanceYourCalm:
|
||||||
|
if string(f.DebugData()) == "too_many_pings" {
|
||||||
|
t.goAwayReason = TooManyPings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *http2Client) GetGoAwayReason() GoAwayReason {
|
||||||
|
t.mu.Lock()
|
||||||
|
defer t.mu.Unlock()
|
||||||
|
return t.goAwayReason
|
||||||
|
}
|
||||||
|
|
||||||
func (t *http2Client) handleWindowUpdate(f *http2.WindowUpdateFrame) {
|
func (t *http2Client) handleWindowUpdate(f *http2.WindowUpdateFrame) {
|
||||||
id := f.Header().StreamID
|
id := f.Header().StreamID
|
||||||
incr := f.Increment
|
incr := f.Increment
|
||||||
|
@ -857,23 +960,41 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
s.bytesReceived = true
|
||||||
var state decodeState
|
var state decodeState
|
||||||
for _, hf := range frame.Fields {
|
for _, hf := range frame.Fields {
|
||||||
state.processHeaderField(hf)
|
if err := state.processHeaderField(hf); err != nil {
|
||||||
}
|
s.mu.Lock()
|
||||||
if state.err != nil {
|
if !s.headerDone {
|
||||||
s.mu.Lock()
|
close(s.headerChan)
|
||||||
if !s.headerDone {
|
s.headerDone = true
|
||||||
close(s.headerChan)
|
}
|
||||||
s.headerDone = true
|
s.mu.Unlock()
|
||||||
|
s.write(recvMsg{err: err})
|
||||||
|
// Something wrong. Stops reading even when there is remaining.
|
||||||
|
return
|
||||||
}
|
}
|
||||||
s.mu.Unlock()
|
|
||||||
s.write(recvMsg{err: state.err})
|
|
||||||
// Something wrong. Stops reading even when there is remaining.
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
endStream := frame.StreamEnded()
|
endStream := frame.StreamEnded()
|
||||||
|
var isHeader bool
|
||||||
|
defer func() {
|
||||||
|
if t.statsHandler != nil {
|
||||||
|
if isHeader {
|
||||||
|
inHeader := &stats.InHeader{
|
||||||
|
Client: true,
|
||||||
|
WireLength: int(frame.Header().Length),
|
||||||
|
}
|
||||||
|
t.statsHandler.HandleRPC(s.clientStatsCtx, inHeader)
|
||||||
|
} else {
|
||||||
|
inTrailer := &stats.InTrailer{
|
||||||
|
Client: true,
|
||||||
|
WireLength: int(frame.Header().Length),
|
||||||
|
}
|
||||||
|
t.statsHandler.HandleRPC(s.clientStatsCtx, inTrailer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
if !endStream {
|
if !endStream {
|
||||||
|
@ -885,6 +1006,7 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
||||||
}
|
}
|
||||||
close(s.headerChan)
|
close(s.headerChan)
|
||||||
s.headerDone = true
|
s.headerDone = true
|
||||||
|
isHeader = true
|
||||||
}
|
}
|
||||||
if !endStream || s.state == streamDone {
|
if !endStream || s.state == streamDone {
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
@ -894,10 +1016,7 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
||||||
if len(state.mdata) > 0 {
|
if len(state.mdata) > 0 {
|
||||||
s.trailer = state.mdata
|
s.trailer = state.mdata
|
||||||
}
|
}
|
||||||
s.statusCode = state.statusCode
|
s.finish(state.status())
|
||||||
s.statusDesc = state.statusDesc
|
|
||||||
close(s.done)
|
|
||||||
s.state = streamDone
|
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
s.write(recvMsg{err: io.EOF})
|
s.write(recvMsg{err: io.EOF})
|
||||||
}
|
}
|
||||||
|
@ -925,6 +1044,7 @@ func (t *http2Client) reader() {
|
||||||
t.notifyError(err)
|
t.notifyError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
atomic.CompareAndSwapUint32(&t.activity, 0, 1)
|
||||||
sf, ok := frame.(*http2.SettingsFrame)
|
sf, ok := frame.(*http2.SettingsFrame)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.notifyError(err)
|
t.notifyError(err)
|
||||||
|
@ -935,6 +1055,7 @@ func (t *http2Client) reader() {
|
||||||
// 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()
|
||||||
|
atomic.CompareAndSwapUint32(&t.activity, 0, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Abort an active stream if the http2.Framer returns a
|
// Abort an active stream if the http2.Framer returns a
|
||||||
// http2.StreamError. This can happen only if the server's response
|
// http2.StreamError. This can happen only if the server's response
|
||||||
|
@ -986,21 +1107,15 @@ func (t *http2Client) applySettings(ss []http2.Setting) {
|
||||||
s.Val = math.MaxInt32
|
s.Val = math.MaxInt32
|
||||||
}
|
}
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
reset := t.streamsQuota != nil
|
|
||||||
if !reset {
|
|
||||||
t.streamsQuota = newQuotaPool(int(s.Val) - len(t.activeStreams))
|
|
||||||
}
|
|
||||||
ms := t.maxStreams
|
ms := t.maxStreams
|
||||||
t.maxStreams = int(s.Val)
|
t.maxStreams = int(s.Val)
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
if reset {
|
t.streamsQuota.add(int(s.Val) - ms)
|
||||||
t.streamsQuota.reset(int(s.Val) - ms)
|
|
||||||
}
|
|
||||||
case http2.SettingInitialWindowSize:
|
case http2.SettingInitialWindowSize:
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
for _, stream := range t.activeStreams {
|
for _, stream := range t.activeStreams {
|
||||||
// Adjust the sending quota for each stream.
|
// Adjust the sending quota for each stream.
|
||||||
stream.sendQuotaPool.reset(int(s.Val - t.streamSendQuota))
|
stream.sendQuotaPool.add(int(s.Val - t.streamSendQuota))
|
||||||
}
|
}
|
||||||
t.streamSendQuota = s.Val
|
t.streamSendQuota = s.Val
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
|
@ -1028,6 +1143,12 @@ func (t *http2Client) controller() {
|
||||||
t.framer.writeSettings(true, i.ss...)
|
t.framer.writeSettings(true, i.ss...)
|
||||||
}
|
}
|
||||||
case *resetStream:
|
case *resetStream:
|
||||||
|
// If the server needs to be to intimated about stream closing,
|
||||||
|
// then we need to make sure the RST_STREAM frame is written to
|
||||||
|
// the wire before the headers of the next stream waiting on
|
||||||
|
// streamQuota. We ensure this by adding to the streamsQuota pool
|
||||||
|
// only after having acquired the writableChan to send RST_STREAM.
|
||||||
|
t.streamsQuota.add(1)
|
||||||
t.framer.writeRSTStream(true, i.streamID, i.code)
|
t.framer.writeRSTStream(true, i.streamID, i.code)
|
||||||
case *flushIO:
|
case *flushIO:
|
||||||
t.framer.flushWrite()
|
t.framer.flushWrite()
|
||||||
|
@ -1047,6 +1168,61 @@ func (t *http2Client) controller() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// keepalive running in a separate goroutune makes sure the connection is alive by sending pings.
|
||||||
|
func (t *http2Client) keepalive() {
|
||||||
|
p := &ping{data: [8]byte{}}
|
||||||
|
timer := time.NewTimer(t.kp.Time)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
if atomic.CompareAndSwapUint32(&t.activity, 1, 0) {
|
||||||
|
timer.Reset(t.kp.Time)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Check if keepalive should go dormant.
|
||||||
|
t.mu.Lock()
|
||||||
|
if len(t.activeStreams) < 1 && !t.kp.PermitWithoutStream {
|
||||||
|
// Make awakenKeepalive writable.
|
||||||
|
<-t.awakenKeepalive
|
||||||
|
t.mu.Unlock()
|
||||||
|
select {
|
||||||
|
case <-t.awakenKeepalive:
|
||||||
|
// If the control gets here a ping has been sent
|
||||||
|
// need to reset the timer with keepalive.Timeout.
|
||||||
|
case <-t.shutdownChan:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.mu.Unlock()
|
||||||
|
// Send ping.
|
||||||
|
t.controlBuf.put(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// By the time control gets here a ping has been sent one way or the other.
|
||||||
|
timer.Reset(t.kp.Timeout)
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
if atomic.CompareAndSwapUint32(&t.activity, 1, 0) {
|
||||||
|
timer.Reset(t.kp.Time)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Close()
|
||||||
|
return
|
||||||
|
case <-t.shutdownChan:
|
||||||
|
if !timer.Stop() {
|
||||||
|
<-timer.C
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-t.shutdownChan:
|
||||||
|
if !timer.Stop() {
|
||||||
|
<-timer.C
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (t *http2Client) Error() <-chan struct{} {
|
func (t *http2Client) Error() <-chan struct{} {
|
||||||
return t.errorChan
|
return t.errorChan
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,18 +38,26 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"golang.org/x/net/http2/hpack"
|
"golang.org/x/net/http2/hpack"
|
||||||
"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/keepalive"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/peer"
|
"google.golang.org/grpc/peer"
|
||||||
|
"google.golang.org/grpc/stats"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/grpc/tap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrIllegalHeaderWrite indicates that setting header is illegal because of
|
// ErrIllegalHeaderWrite indicates that setting header is illegal because of
|
||||||
|
@ -58,9 +66,13 @@ var ErrIllegalHeaderWrite = errors.New("transport: the stream is done or WriteHe
|
||||||
|
|
||||||
// http2Server implements the ServerTransport interface with HTTP2.
|
// http2Server implements the ServerTransport interface with HTTP2.
|
||||||
type http2Server struct {
|
type http2Server struct {
|
||||||
|
ctx context.Context
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
|
remoteAddr net.Addr
|
||||||
|
localAddr net.Addr
|
||||||
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
|
||||||
|
inTapHandle tap.ServerInHandle
|
||||||
// writableChan synchronizes write access to the transport.
|
// writableChan synchronizes write access to the transport.
|
||||||
// A writer acquires the write lock by receiving a value on writableChan
|
// A writer acquires the write lock by receiving a value on writableChan
|
||||||
// and releases it by sending on writableChan.
|
// and releases it by sending on writableChan.
|
||||||
|
@ -82,21 +94,46 @@ type http2Server struct {
|
||||||
// sendQuotaPool provides flow control to outbound message.
|
// sendQuotaPool provides flow control to outbound message.
|
||||||
sendQuotaPool *quotaPool
|
sendQuotaPool *quotaPool
|
||||||
|
|
||||||
|
stats stats.Handler
|
||||||
|
|
||||||
|
// Flag to keep track of reading activity on transport.
|
||||||
|
// 1 is true and 0 is false.
|
||||||
|
activity uint32 // Accessed atomically.
|
||||||
|
// Keepalive and max-age parameters for the server.
|
||||||
|
kp keepalive.ServerParameters
|
||||||
|
|
||||||
|
// Keepalive enforcement policy.
|
||||||
|
kep keepalive.EnforcementPolicy
|
||||||
|
// The time instance last ping was received.
|
||||||
|
lastPingAt time.Time
|
||||||
|
// Number of times the client has violated keepalive ping policy so far.
|
||||||
|
pingStrikes uint8
|
||||||
|
// Flag to signify that number of ping strikes should be reset to 0.
|
||||||
|
// This is set whenever data or header frames are sent.
|
||||||
|
// 1 means yes.
|
||||||
|
resetPingStrikes uint32 // Accessed atomically.
|
||||||
|
|
||||||
mu sync.Mutex // guard the following
|
mu sync.Mutex // guard the following
|
||||||
state transportState
|
state transportState
|
||||||
activeStreams map[uint32]*Stream
|
activeStreams map[uint32]*Stream
|
||||||
// the per-stream outbound flow control window size set by the peer.
|
// the per-stream outbound flow control window size set by the peer.
|
||||||
streamSendQuota uint32
|
streamSendQuota uint32
|
||||||
|
// idle is the time instant when the connection went idle.
|
||||||
|
// This is either the begining of the connection or when the number of
|
||||||
|
// RPCs go down to 0.
|
||||||
|
// When the connection is busy, this value is set to 0.
|
||||||
|
idle time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is
|
// newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is
|
||||||
// returned if something goes wrong.
|
// returned if something goes wrong.
|
||||||
func newHTTP2Server(conn net.Conn, maxStreams uint32, authInfo credentials.AuthInfo) (_ ServerTransport, err error) {
|
func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err error) {
|
||||||
framer := newFramer(conn)
|
framer := newFramer(conn)
|
||||||
// Send initial settings as connection preface to client.
|
// Send initial settings as connection preface to client.
|
||||||
var settings []http2.Setting
|
var settings []http2.Setting
|
||||||
// TODO(zhaoq): Have a better way to signal "no limit" because 0 is
|
// TODO(zhaoq): Have a better way to signal "no limit" because 0 is
|
||||||
// permitted in the HTTP2 spec.
|
// permitted in the HTTP2 spec.
|
||||||
|
maxStreams := config.MaxStreams
|
||||||
if maxStreams == 0 {
|
if maxStreams == 0 {
|
||||||
maxStreams = math.MaxUint32
|
maxStreams = math.MaxUint32
|
||||||
} else {
|
} else {
|
||||||
|
@ -119,14 +156,40 @@ func newHTTP2Server(conn net.Conn, maxStreams uint32, authInfo credentials.AuthI
|
||||||
return nil, connectionErrorf(true, err, "transport: %v", err)
|
return nil, connectionErrorf(true, err, "transport: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
kp := config.KeepaliveParams
|
||||||
|
if kp.MaxConnectionIdle == 0 {
|
||||||
|
kp.MaxConnectionIdle = defaultMaxConnectionIdle
|
||||||
|
}
|
||||||
|
if kp.MaxConnectionAge == 0 {
|
||||||
|
kp.MaxConnectionAge = defaultMaxConnectionAge
|
||||||
|
}
|
||||||
|
// Add a jitter to MaxConnectionAge.
|
||||||
|
kp.MaxConnectionAge += getJitter(kp.MaxConnectionAge)
|
||||||
|
if kp.MaxConnectionAgeGrace == 0 {
|
||||||
|
kp.MaxConnectionAgeGrace = defaultMaxConnectionAgeGrace
|
||||||
|
}
|
||||||
|
if kp.Time == 0 {
|
||||||
|
kp.Time = defaultServerKeepaliveTime
|
||||||
|
}
|
||||||
|
if kp.Timeout == 0 {
|
||||||
|
kp.Timeout = defaultServerKeepaliveTimeout
|
||||||
|
}
|
||||||
|
kep := config.KeepalivePolicy
|
||||||
|
if kep.MinTime == 0 {
|
||||||
|
kep.MinTime = defaultKeepalivePolicyMinTime
|
||||||
|
}
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
t := &http2Server{
|
t := &http2Server{
|
||||||
|
ctx: context.Background(),
|
||||||
conn: conn,
|
conn: conn,
|
||||||
authInfo: authInfo,
|
remoteAddr: conn.RemoteAddr(),
|
||||||
|
localAddr: conn.LocalAddr(),
|
||||||
|
authInfo: config.AuthInfo,
|
||||||
framer: framer,
|
framer: framer,
|
||||||
hBuf: &buf,
|
hBuf: &buf,
|
||||||
hEnc: hpack.NewEncoder(&buf),
|
hEnc: hpack.NewEncoder(&buf),
|
||||||
maxStreams: maxStreams,
|
maxStreams: maxStreams,
|
||||||
|
inTapHandle: config.InTapHandle,
|
||||||
controlBuf: newRecvBuffer(),
|
controlBuf: newRecvBuffer(),
|
||||||
fc: &inFlow{limit: initialConnWindowSize},
|
fc: &inFlow{limit: initialConnWindowSize},
|
||||||
sendQuotaPool: newQuotaPool(defaultWindowSize),
|
sendQuotaPool: newQuotaPool(defaultWindowSize),
|
||||||
|
@ -135,14 +198,27 @@ func newHTTP2Server(conn net.Conn, maxStreams uint32, authInfo credentials.AuthI
|
||||||
shutdownChan: make(chan struct{}),
|
shutdownChan: make(chan struct{}),
|
||||||
activeStreams: make(map[uint32]*Stream),
|
activeStreams: make(map[uint32]*Stream),
|
||||||
streamSendQuota: defaultWindowSize,
|
streamSendQuota: defaultWindowSize,
|
||||||
|
stats: config.StatsHandler,
|
||||||
|
kp: kp,
|
||||||
|
idle: time.Now(),
|
||||||
|
kep: kep,
|
||||||
|
}
|
||||||
|
if t.stats != nil {
|
||||||
|
t.ctx = t.stats.TagConn(t.ctx, &stats.ConnTagInfo{
|
||||||
|
RemoteAddr: t.remoteAddr,
|
||||||
|
LocalAddr: t.localAddr,
|
||||||
|
})
|
||||||
|
connBegin := &stats.ConnBegin{}
|
||||||
|
t.stats.HandleConn(t.ctx, connBegin)
|
||||||
}
|
}
|
||||||
go t.controller()
|
go t.controller()
|
||||||
|
go t.keepalive()
|
||||||
t.writableChan <- 0
|
t.writableChan <- 0
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// operateHeader takes action on the decoded headers.
|
// operateHeader takes action on the decoded headers.
|
||||||
func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream)) (close bool) {
|
func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream), traceCtx func(context.Context, string) context.Context) (close bool) {
|
||||||
buf := newRecvBuffer()
|
buf := newRecvBuffer()
|
||||||
s := &Stream{
|
s := &Stream{
|
||||||
id: frame.Header().StreamID,
|
id: frame.Header().StreamID,
|
||||||
|
@ -153,13 +229,12 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
||||||
|
|
||||||
var state decodeState
|
var state decodeState
|
||||||
for _, hf := range frame.Fields {
|
for _, hf := range frame.Fields {
|
||||||
state.processHeaderField(hf)
|
if err := state.processHeaderField(hf); err != nil {
|
||||||
}
|
if se, ok := err.(StreamError); ok {
|
||||||
if err := state.err; err != nil {
|
t.controlBuf.put(&resetStream{s.id, statusCodeConvTab[se.Code]})
|
||||||
if se, ok := err.(StreamError); ok {
|
}
|
||||||
t.controlBuf.put(&resetStream{s.id, statusCodeConvTab[se.Code]})
|
return
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if frame.StreamEnded() {
|
if frame.StreamEnded() {
|
||||||
|
@ -168,12 +243,12 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
||||||
}
|
}
|
||||||
s.recvCompress = state.encoding
|
s.recvCompress = state.encoding
|
||||||
if state.timeoutSet {
|
if state.timeoutSet {
|
||||||
s.ctx, s.cancel = context.WithTimeout(context.TODO(), state.timeout)
|
s.ctx, s.cancel = context.WithTimeout(t.ctx, state.timeout)
|
||||||
} else {
|
} else {
|
||||||
s.ctx, s.cancel = context.WithCancel(context.TODO())
|
s.ctx, s.cancel = context.WithCancel(t.ctx)
|
||||||
}
|
}
|
||||||
pr := &peer.Peer{
|
pr := &peer.Peer{
|
||||||
Addr: t.conn.RemoteAddr(),
|
Addr: t.remoteAddr,
|
||||||
}
|
}
|
||||||
// Attach Auth info if there is any.
|
// Attach Auth info if there is any.
|
||||||
if t.authInfo != nil {
|
if t.authInfo != nil {
|
||||||
|
@ -186,7 +261,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
||||||
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(state.mdata) > 0 {
|
if len(state.mdata) > 0 {
|
||||||
s.ctx = metadata.NewContext(s.ctx, state.mdata)
|
s.ctx = metadata.NewIncomingContext(s.ctx, state.mdata)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.dec = &recvBufferReader{
|
s.dec = &recvBufferReader{
|
||||||
|
@ -195,6 +270,18 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
||||||
}
|
}
|
||||||
s.recvCompress = state.encoding
|
s.recvCompress = state.encoding
|
||||||
s.method = state.method
|
s.method = state.method
|
||||||
|
if t.inTapHandle != nil {
|
||||||
|
var err error
|
||||||
|
info := &tap.Info{
|
||||||
|
FullMethodName: state.method,
|
||||||
|
}
|
||||||
|
s.ctx, err = t.inTapHandle(s.ctx, info)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: Log the real error.
|
||||||
|
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeRefusedStream})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
if t.state != reachable {
|
if t.state != reachable {
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
|
@ -214,17 +301,33 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
||||||
t.maxStreamID = s.id
|
t.maxStreamID = s.id
|
||||||
s.sendQuotaPool = newQuotaPool(int(t.streamSendQuota))
|
s.sendQuotaPool = newQuotaPool(int(t.streamSendQuota))
|
||||||
t.activeStreams[s.id] = s
|
t.activeStreams[s.id] = s
|
||||||
|
if len(t.activeStreams) == 1 {
|
||||||
|
t.idle = time.Time{}
|
||||||
|
}
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
s.windowHandler = func(n int) {
|
s.windowHandler = func(n int) {
|
||||||
t.updateWindow(s, uint32(n))
|
t.updateWindow(s, uint32(n))
|
||||||
}
|
}
|
||||||
|
s.ctx = traceCtx(s.ctx, s.method)
|
||||||
|
if t.stats != nil {
|
||||||
|
s.ctx = t.stats.TagRPC(s.ctx, &stats.RPCTagInfo{FullMethodName: s.method})
|
||||||
|
inHeader := &stats.InHeader{
|
||||||
|
FullMethod: s.method,
|
||||||
|
RemoteAddr: t.remoteAddr,
|
||||||
|
LocalAddr: t.localAddr,
|
||||||
|
Compression: s.recvCompress,
|
||||||
|
WireLength: int(frame.Header().Length),
|
||||||
|
}
|
||||||
|
t.stats.HandleRPC(s.ctx, inHeader)
|
||||||
|
}
|
||||||
handle(s)
|
handle(s)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleStreams receives incoming streams using the given handler. This is
|
// HandleStreams receives incoming streams using the given handler. This is
|
||||||
// typically run in a separate goroutine.
|
// typically run in a separate goroutine.
|
||||||
func (t *http2Server) HandleStreams(handle func(*Stream)) {
|
// traceCtx attaches trace to ctx and returns the new context.
|
||||||
|
func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.Context, string) context.Context) {
|
||||||
// Check the validity of client preface.
|
// Check the validity of client preface.
|
||||||
preface := make([]byte, len(clientPreface))
|
preface := make([]byte, len(clientPreface))
|
||||||
if _, err := io.ReadFull(t.conn, preface); err != nil {
|
if _, err := io.ReadFull(t.conn, preface); err != nil {
|
||||||
|
@ -248,6 +351,7 @@ func (t *http2Server) HandleStreams(handle func(*Stream)) {
|
||||||
t.Close()
|
t.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
atomic.StoreUint32(&t.activity, 1)
|
||||||
sf, ok := frame.(*http2.SettingsFrame)
|
sf, ok := frame.(*http2.SettingsFrame)
|
||||||
if !ok {
|
if !ok {
|
||||||
grpclog.Printf("transport: http2Server.HandleStreams saw invalid preface type %T from client", frame)
|
grpclog.Printf("transport: http2Server.HandleStreams saw invalid preface type %T from client", frame)
|
||||||
|
@ -258,6 +362,7 @@ func (t *http2Server) HandleStreams(handle func(*Stream)) {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
frame, err := t.framer.readFrame()
|
frame, err := t.framer.readFrame()
|
||||||
|
atomic.StoreUint32(&t.activity, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if se, ok := err.(http2.StreamError); ok {
|
if se, ok := err.(http2.StreamError); ok {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
|
@ -279,7 +384,7 @@ func (t *http2Server) HandleStreams(handle func(*Stream)) {
|
||||||
}
|
}
|
||||||
switch frame := frame.(type) {
|
switch frame := frame.(type) {
|
||||||
case *http2.MetaHeadersFrame:
|
case *http2.MetaHeadersFrame:
|
||||||
if t.operateHeaders(frame, handle) {
|
if t.operateHeaders(frame, handle, traceCtx) {
|
||||||
t.Close()
|
t.Close()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -334,7 +439,7 @@ func (t *http2Server) updateWindow(s *Stream, n uint32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *http2Server) handleData(f *http2.DataFrame) {
|
func (t *http2Server) handleData(f *http2.DataFrame) {
|
||||||
size := len(f.Data())
|
size := f.Header().Length
|
||||||
if err := t.fc.onData(uint32(size)); err != nil {
|
if err := t.fc.onData(uint32(size)); err != nil {
|
||||||
grpclog.Printf("transport: http2Server %v", err)
|
grpclog.Printf("transport: http2Server %v", err)
|
||||||
t.Close()
|
t.Close()
|
||||||
|
@ -349,6 +454,11 @@ func (t *http2Server) handleData(f *http2.DataFrame) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if size > 0 {
|
if size > 0 {
|
||||||
|
if f.Header().Flags.Has(http2.FlagDataPadded) {
|
||||||
|
if w := t.fc.onRead(uint32(size) - uint32(len(f.Data()))); w > 0 {
|
||||||
|
t.controlBuf.put(&windowUpdate{0, w})
|
||||||
|
}
|
||||||
|
}
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
if s.state == streamDone {
|
if s.state == streamDone {
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
@ -364,13 +474,20 @@ func (t *http2Server) handleData(f *http2.DataFrame) {
|
||||||
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeFlowControl})
|
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeFlowControl})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if f.Header().Flags.Has(http2.FlagDataPadded) {
|
||||||
|
if w := s.fc.onRead(uint32(size) - uint32(len(f.Data()))); w > 0 {
|
||||||
|
t.controlBuf.put(&windowUpdate{s.id, w})
|
||||||
|
}
|
||||||
|
}
|
||||||
s.mu.Unlock()
|
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?
|
||||||
data := make([]byte, size)
|
if len(f.Data()) > 0 {
|
||||||
copy(data, f.Data())
|
data := make([]byte, len(f.Data()))
|
||||||
s.write(recvMsg{data: data})
|
copy(data, f.Data())
|
||||||
|
s.write(recvMsg{data: data})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if f.Header().Flags.Has(http2.FlagDataEndStream) {
|
if f.Header().Flags.Has(http2.FlagDataEndStream) {
|
||||||
// Received the end of stream from the client.
|
// Received the end of stream from the client.
|
||||||
|
@ -404,6 +521,11 @@ func (t *http2Server) handleSettings(f *http2.SettingsFrame) {
|
||||||
t.controlBuf.put(&settings{ack: true, ss: ss})
|
t.controlBuf.put(&settings{ack: true, ss: ss})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxPingStrikes = 2
|
||||||
|
defaultPingTimeout = 2 * time.Hour
|
||||||
|
)
|
||||||
|
|
||||||
func (t *http2Server) handlePing(f *http2.PingFrame) {
|
func (t *http2Server) handlePing(f *http2.PingFrame) {
|
||||||
if f.IsAck() { // Do nothing.
|
if f.IsAck() { // Do nothing.
|
||||||
return
|
return
|
||||||
|
@ -411,6 +533,38 @@ func (t *http2Server) handlePing(f *http2.PingFrame) {
|
||||||
pingAck := &ping{ack: true}
|
pingAck := &ping{ack: true}
|
||||||
copy(pingAck.data[:], f.Data[:])
|
copy(pingAck.data[:], f.Data[:])
|
||||||
t.controlBuf.put(pingAck)
|
t.controlBuf.put(pingAck)
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
defer func() {
|
||||||
|
t.lastPingAt = now
|
||||||
|
}()
|
||||||
|
// A reset ping strikes means that we don't need to check for policy
|
||||||
|
// violation for this ping and the pingStrikes counter should be set
|
||||||
|
// to 0.
|
||||||
|
if atomic.CompareAndSwapUint32(&t.resetPingStrikes, 1, 0) {
|
||||||
|
t.pingStrikes = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.mu.Lock()
|
||||||
|
ns := len(t.activeStreams)
|
||||||
|
t.mu.Unlock()
|
||||||
|
if ns < 1 && !t.kep.PermitWithoutStream {
|
||||||
|
// Keepalive shouldn't be active thus, this new ping should
|
||||||
|
// have come after atleast defaultPingTimeout.
|
||||||
|
if t.lastPingAt.Add(defaultPingTimeout).After(now) {
|
||||||
|
t.pingStrikes++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Check if keepalive policy is respected.
|
||||||
|
if t.lastPingAt.Add(t.kep.MinTime).After(now) {
|
||||||
|
t.pingStrikes++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.pingStrikes > maxPingStrikes {
|
||||||
|
// Send goaway and close the connection.
|
||||||
|
t.controlBuf.put(&goAway{code: http2.ErrCodeEnhanceYourCalm, debugData: []byte("too_many_pings")})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *http2Server) handleWindowUpdate(f *http2.WindowUpdateFrame) {
|
func (t *http2Server) handleWindowUpdate(f *http2.WindowUpdateFrame) {
|
||||||
|
@ -429,6 +583,13 @@ func (t *http2Server) writeHeaders(s *Stream, b *bytes.Buffer, endStream bool) e
|
||||||
first := true
|
first := true
|
||||||
endHeaders := false
|
endHeaders := false
|
||||||
var err error
|
var err error
|
||||||
|
defer func() {
|
||||||
|
if err == nil {
|
||||||
|
// Reset ping strikes when seding headers since that might cause the
|
||||||
|
// peer to send ping.
|
||||||
|
atomic.StoreUint32(&t.resetPingStrikes, 1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
// Sends the headers in a single batch.
|
// Sends the headers in a single batch.
|
||||||
for !endHeaders {
|
for !endHeaders {
|
||||||
size := t.hBuf.Len()
|
size := t.hBuf.Len()
|
||||||
|
@ -483,18 +644,25 @@ func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
|
||||||
if s.sendCompress != "" {
|
if s.sendCompress != "" {
|
||||||
t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress})
|
t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress})
|
||||||
}
|
}
|
||||||
for k, v := range md {
|
for k, vv := range md {
|
||||||
if isReservedHeader(k) {
|
if isReservedHeader(k) {
|
||||||
// Clients don't tolerate reading restricted headers after some non restricted ones were sent.
|
// Clients don't tolerate reading restricted headers after some non restricted ones were sent.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, entry := range v {
|
for _, v := range vv {
|
||||||
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
|
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bufLen := t.hBuf.Len()
|
||||||
if err := t.writeHeaders(s, t.hBuf, false); err != nil {
|
if err := t.writeHeaders(s, t.hBuf, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if t.stats != nil {
|
||||||
|
outHeader := &stats.OutHeader{
|
||||||
|
WireLength: bufLen,
|
||||||
|
}
|
||||||
|
t.stats.HandleRPC(s.Context(), outHeader)
|
||||||
|
}
|
||||||
t.writableChan <- 0
|
t.writableChan <- 0
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -503,7 +671,7 @@ func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
|
||||||
// There is no further I/O operations being able to perform on this stream.
|
// There is no further I/O operations being able to perform on this stream.
|
||||||
// TODO(zhaoq): Now it indicates the end of entire stream. Revisit if early
|
// TODO(zhaoq): Now it indicates the end of entire stream. Revisit if early
|
||||||
// OK is adopted.
|
// OK is adopted.
|
||||||
func (t *http2Server) WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error {
|
func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error {
|
||||||
var headersSent, hasHeader bool
|
var headersSent, hasHeader bool
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
if s.state == streamDone {
|
if s.state == streamDone {
|
||||||
|
@ -534,23 +702,41 @@ func (t *http2Server) WriteStatus(s *Stream, statusCode codes.Code, statusDesc s
|
||||||
t.hEnc.WriteField(
|
t.hEnc.WriteField(
|
||||||
hpack.HeaderField{
|
hpack.HeaderField{
|
||||||
Name: "grpc-status",
|
Name: "grpc-status",
|
||||||
Value: strconv.Itoa(int(statusCode)),
|
Value: strconv.Itoa(int(st.Code())),
|
||||||
})
|
})
|
||||||
t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-message", Value: encodeGrpcMessage(statusDesc)})
|
t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-message", Value: encodeGrpcMessage(st.Message())})
|
||||||
|
|
||||||
|
if p := st.Proto(); p != nil && len(p.Details) > 0 {
|
||||||
|
stBytes, err := proto.Marshal(p)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: return error instead, when callers are able to handle it.
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-status-details-bin", Value: encodeBinHeader(stBytes)})
|
||||||
|
}
|
||||||
|
|
||||||
// Attach the trailer metadata.
|
// Attach the trailer metadata.
|
||||||
for k, v := range s.trailer {
|
for k, vv := range s.trailer {
|
||||||
// Clients don't tolerate reading restricted headers after some non restricted ones were sent.
|
// Clients don't tolerate reading restricted headers after some non restricted ones were sent.
|
||||||
if isReservedHeader(k) {
|
if isReservedHeader(k) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, entry := range v {
|
for _, v := range vv {
|
||||||
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
|
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bufLen := t.hBuf.Len()
|
||||||
if err := t.writeHeaders(s, t.hBuf, true); err != nil {
|
if err := t.writeHeaders(s, t.hBuf, true); err != nil {
|
||||||
t.Close()
|
t.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if t.stats != nil {
|
||||||
|
outTrailer := &stats.OutTrailer{
|
||||||
|
WireLength: bufLen,
|
||||||
|
}
|
||||||
|
t.stats.HandleRPC(s.Context(), outTrailer)
|
||||||
|
}
|
||||||
t.closeStream(s)
|
t.closeStream(s)
|
||||||
t.writableChan <- 0
|
t.writableChan <- 0
|
||||||
return nil
|
return nil
|
||||||
|
@ -558,7 +744,7 @@ func (t *http2Server) WriteStatus(s *Stream, statusCode codes.Code, statusDesc s
|
||||||
|
|
||||||
// Write converts the data into HTTP2 data frame and sends it out. Non-nil error
|
// Write converts the data into HTTP2 data frame and sends it out. Non-nil error
|
||||||
// is returns if it fails (e.g., framing error, transport error).
|
// is returns if it fails (e.g., framing error, transport error).
|
||||||
func (t *http2Server) Write(s *Stream, data []byte, opts *Options) error {
|
func (t *http2Server) Write(s *Stream, data []byte, opts *Options) (err 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()
|
||||||
|
@ -573,25 +759,27 @@ func (t *http2Server) Write(s *Stream, data []byte, opts *Options) error {
|
||||||
if writeHeaderFrame {
|
if writeHeaderFrame {
|
||||||
t.WriteHeader(s, nil)
|
t.WriteHeader(s, nil)
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
if err == nil {
|
||||||
|
// Reset ping strikes when sending data since this might cause
|
||||||
|
// the peer to send ping.
|
||||||
|
atomic.StoreUint32(&t.resetPingStrikes, 1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
r := bytes.NewBuffer(data)
|
r := bytes.NewBuffer(data)
|
||||||
for {
|
for {
|
||||||
if r.Len() == 0 {
|
if r.Len() == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
size := http2MaxFrameLen
|
size := http2MaxFrameLen
|
||||||
s.sendQuotaPool.add(0)
|
|
||||||
// Wait until the stream has some quota to send the data.
|
// Wait until the stream has some quota to send the data.
|
||||||
sq, err := wait(s.ctx, nil, nil, t.shutdownChan, s.sendQuotaPool.acquire())
|
sq, err := wait(s.ctx, nil, nil, t.shutdownChan, s.sendQuotaPool.acquire())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.sendQuotaPool.add(0)
|
|
||||||
// Wait until the transport has some quota to send the data.
|
// Wait until the transport has some quota to send the data.
|
||||||
tq, err := wait(s.ctx, nil, nil, t.shutdownChan, t.sendQuotaPool.acquire())
|
tq, err := wait(s.ctx, nil, nil, t.shutdownChan, t.sendQuotaPool.acquire())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(StreamError); ok {
|
|
||||||
t.sendQuotaPool.cancel()
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if sq < size {
|
if sq < size {
|
||||||
|
@ -659,7 +847,7 @@ func (t *http2Server) applySettings(ss []http2.Setting) {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
for _, stream := range t.activeStreams {
|
for _, stream := range t.activeStreams {
|
||||||
stream.sendQuotaPool.reset(int(s.Val - t.streamSendQuota))
|
stream.sendQuotaPool.add(int(s.Val - t.streamSendQuota))
|
||||||
}
|
}
|
||||||
t.streamSendQuota = s.Val
|
t.streamSendQuota = s.Val
|
||||||
}
|
}
|
||||||
|
@ -667,6 +855,91 @@ func (t *http2Server) applySettings(ss []http2.Setting) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// keepalive running in a separate goroutine does the following:
|
||||||
|
// 1. Gracefully closes an idle connection after a duration of keepalive.MaxConnectionIdle.
|
||||||
|
// 2. Gracefully closes any connection after a duration of keepalive.MaxConnectionAge.
|
||||||
|
// 3. Forcibly closes a connection after an additive period of keepalive.MaxConnectionAgeGrace over keepalive.MaxConnectionAge.
|
||||||
|
// 4. Makes sure a connection is alive by sending pings with a frequency of keepalive.Time and closes a non-resposive connection
|
||||||
|
// after an additional duration of keepalive.Timeout.
|
||||||
|
func (t *http2Server) keepalive() {
|
||||||
|
p := &ping{}
|
||||||
|
var pingSent bool
|
||||||
|
maxIdle := time.NewTimer(t.kp.MaxConnectionIdle)
|
||||||
|
maxAge := time.NewTimer(t.kp.MaxConnectionAge)
|
||||||
|
keepalive := time.NewTimer(t.kp.Time)
|
||||||
|
// NOTE: All exit paths of this function should reset their
|
||||||
|
// respecitve timers. A failure to do so will cause the
|
||||||
|
// following clean-up to deadlock and eventually leak.
|
||||||
|
defer func() {
|
||||||
|
if !maxIdle.Stop() {
|
||||||
|
<-maxIdle.C
|
||||||
|
}
|
||||||
|
if !maxAge.Stop() {
|
||||||
|
<-maxAge.C
|
||||||
|
}
|
||||||
|
if !keepalive.Stop() {
|
||||||
|
<-keepalive.C
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-maxIdle.C:
|
||||||
|
t.mu.Lock()
|
||||||
|
idle := t.idle
|
||||||
|
if idle.IsZero() { // The connection is non-idle.
|
||||||
|
t.mu.Unlock()
|
||||||
|
maxIdle.Reset(t.kp.MaxConnectionIdle)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val := t.kp.MaxConnectionIdle - time.Since(idle)
|
||||||
|
if val <= 0 {
|
||||||
|
// The connection has been idle for a duration of keepalive.MaxConnectionIdle or more.
|
||||||
|
// Gracefully close the connection.
|
||||||
|
t.state = draining
|
||||||
|
t.mu.Unlock()
|
||||||
|
t.Drain()
|
||||||
|
// Reseting the timer so that the clean-up doesn't deadlock.
|
||||||
|
maxIdle.Reset(infinity)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.mu.Unlock()
|
||||||
|
maxIdle.Reset(val)
|
||||||
|
case <-maxAge.C:
|
||||||
|
t.mu.Lock()
|
||||||
|
t.state = draining
|
||||||
|
t.mu.Unlock()
|
||||||
|
t.Drain()
|
||||||
|
maxAge.Reset(t.kp.MaxConnectionAgeGrace)
|
||||||
|
select {
|
||||||
|
case <-maxAge.C:
|
||||||
|
// Close the connection after grace period.
|
||||||
|
t.Close()
|
||||||
|
// Reseting the timer so that the clean-up doesn't deadlock.
|
||||||
|
maxAge.Reset(infinity)
|
||||||
|
case <-t.shutdownChan:
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case <-keepalive.C:
|
||||||
|
if atomic.CompareAndSwapUint32(&t.activity, 1, 0) {
|
||||||
|
pingSent = false
|
||||||
|
keepalive.Reset(t.kp.Time)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if pingSent {
|
||||||
|
t.Close()
|
||||||
|
// Reseting the timer so that the clean-up doesn't deadlock.
|
||||||
|
keepalive.Reset(infinity)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pingSent = true
|
||||||
|
t.controlBuf.put(p)
|
||||||
|
keepalive.Reset(t.kp.Timeout)
|
||||||
|
case <-t.shutdownChan:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// controller running in a separate goroutine takes charge of sending control
|
// controller running in a separate goroutine takes charge of sending control
|
||||||
// frames (e.g., window update, reset stream, setting, etc.) to the server.
|
// frames (e.g., window update, reset stream, setting, etc.) to the server.
|
||||||
func (t *http2Server) controller() {
|
func (t *http2Server) controller() {
|
||||||
|
@ -698,7 +971,10 @@ func (t *http2Server) controller() {
|
||||||
sid := t.maxStreamID
|
sid := t.maxStreamID
|
||||||
t.state = draining
|
t.state = draining
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
t.framer.writeGoAway(true, sid, http2.ErrCodeNo, nil)
|
t.framer.writeGoAway(true, sid, i.code, i.debugData)
|
||||||
|
if i.code == http2.ErrCodeEnhanceYourCalm {
|
||||||
|
t.Close()
|
||||||
|
}
|
||||||
case *flushIO:
|
case *flushIO:
|
||||||
t.framer.flushWrite()
|
t.framer.flushWrite()
|
||||||
case *ping:
|
case *ping:
|
||||||
|
@ -736,6 +1012,10 @@ func (t *http2Server) Close() (err error) {
|
||||||
for _, s := range streams {
|
for _, s := range streams {
|
||||||
s.cancel()
|
s.cancel()
|
||||||
}
|
}
|
||||||
|
if t.stats != nil {
|
||||||
|
connEnd := &stats.ConnEnd{}
|
||||||
|
t.stats.HandleConn(t.ctx, connEnd)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -744,6 +1024,9 @@ func (t *http2Server) Close() (err error) {
|
||||||
func (t *http2Server) closeStream(s *Stream) {
|
func (t *http2Server) closeStream(s *Stream) {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
delete(t.activeStreams, s.id)
|
delete(t.activeStreams, s.id)
|
||||||
|
if len(t.activeStreams) == 0 {
|
||||||
|
t.idle = time.Now()
|
||||||
|
}
|
||||||
if t.state == draining && len(t.activeStreams) == 0 {
|
if t.state == draining && len(t.activeStreams) == 0 {
|
||||||
defer t.Close()
|
defer t.Close()
|
||||||
}
|
}
|
||||||
|
@ -767,9 +1050,21 @@ func (t *http2Server) closeStream(s *Stream) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *http2Server) RemoteAddr() net.Addr {
|
func (t *http2Server) RemoteAddr() net.Addr {
|
||||||
return t.conn.RemoteAddr()
|
return t.remoteAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *http2Server) Drain() {
|
func (t *http2Server) Drain() {
|
||||||
t.controlBuf.put(&goAway{})
|
t.controlBuf.put(&goAway{code: http2.ErrCodeNo})
|
||||||
|
}
|
||||||
|
|
||||||
|
var rgen = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
func getJitter(v time.Duration) time.Duration {
|
||||||
|
if v == infinity {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
// Generate a jitter between +/- 10% of the value.
|
||||||
|
r := int64(v / 10)
|
||||||
|
j := rgen.Int63n(2*r) - r
|
||||||
|
return time.Duration(j)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ package transport
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
@ -44,16 +45,16 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"golang.org/x/net/http2/hpack"
|
"golang.org/x/net/http2/hpack"
|
||||||
|
spb "google.golang.org/genproto/googleapis/rpc/status"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// The primary user agent
|
|
||||||
primaryUA = "grpc-go/1.0"
|
|
||||||
// http2MaxFrameLen specifies the max length of a HTTP2 frame.
|
// http2MaxFrameLen specifies the max length of a HTTP2 frame.
|
||||||
http2MaxFrameLen = 16384 // 16KB frame
|
http2MaxFrameLen = 16384 // 16KB frame
|
||||||
// http://http2.github.io/http2-spec/#SettingValues
|
// http://http2.github.io/http2-spec/#SettingValues
|
||||||
|
@ -92,13 +93,15 @@ 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
|
encoding string
|
||||||
// statusCode caches the stream status received from the trailer
|
// statusGen caches the stream status received from the trailer the server
|
||||||
// the server sent. Client side only.
|
// sent. Client side only. Do not access directly. After all trailers are
|
||||||
statusCode codes.Code
|
// parsed, use the status method to retrieve the status.
|
||||||
statusDesc string
|
statusGen *status.Status
|
||||||
|
// rawStatusCode and rawStatusMsg are set from the raw trailer fields and are not
|
||||||
|
// intended for direct access outside of parsing.
|
||||||
|
rawStatusCode int32
|
||||||
|
rawStatusMsg string
|
||||||
// Server side only fields.
|
// Server side only fields.
|
||||||
timeoutSet bool
|
timeoutSet bool
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
|
@ -121,6 +124,7 @@ func isReservedHeader(hdr string) bool {
|
||||||
"grpc-message",
|
"grpc-message",
|
||||||
"grpc-status",
|
"grpc-status",
|
||||||
"grpc-timeout",
|
"grpc-timeout",
|
||||||
|
"grpc-status-details-bin",
|
||||||
"te":
|
"te":
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
|
@ -139,12 +143,6 @@ func isWhitelistedPseudoHeader(hdr string) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decodeState) setErr(err error) {
|
|
||||||
if d.err == nil {
|
|
||||||
d.err = err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validContentType(t string) bool {
|
func validContentType(t string) bool {
|
||||||
e := "application/grpc"
|
e := "application/grpc"
|
||||||
if !strings.HasPrefix(t, e) {
|
if !strings.HasPrefix(t, e) {
|
||||||
|
@ -158,56 +156,91 @@ func validContentType(t string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decodeState) processHeaderField(f hpack.HeaderField) {
|
func (d *decodeState) status() *status.Status {
|
||||||
|
if d.statusGen == nil {
|
||||||
|
// No status-details were provided; generate status using code/msg.
|
||||||
|
d.statusGen = status.New(codes.Code(d.rawStatusCode), d.rawStatusMsg)
|
||||||
|
}
|
||||||
|
return d.statusGen
|
||||||
|
}
|
||||||
|
|
||||||
|
const binHdrSuffix = "-bin"
|
||||||
|
|
||||||
|
func encodeBinHeader(v []byte) string {
|
||||||
|
return base64.RawStdEncoding.EncodeToString(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeBinHeader(v string) ([]byte, error) {
|
||||||
|
if len(v)%4 == 0 {
|
||||||
|
// Input was padded, or padding was not necessary.
|
||||||
|
return base64.StdEncoding.DecodeString(v)
|
||||||
|
}
|
||||||
|
return base64.RawStdEncoding.DecodeString(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeMetadataHeader(k, v string) string {
|
||||||
|
if strings.HasSuffix(k, binHdrSuffix) {
|
||||||
|
return encodeBinHeader(([]byte)(v))
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeMetadataHeader(k, v string) (string, error) {
|
||||||
|
if strings.HasSuffix(k, binHdrSuffix) {
|
||||||
|
b, err := decodeBinHeader(v)
|
||||||
|
return string(b), err
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decodeState) processHeaderField(f hpack.HeaderField) error {
|
||||||
switch f.Name {
|
switch f.Name {
|
||||||
case "content-type":
|
case "content-type":
|
||||||
if !validContentType(f.Value) {
|
if !validContentType(f.Value) {
|
||||||
d.setErr(streamErrorf(codes.FailedPrecondition, "transport: received the unexpected content-type %q", f.Value))
|
return streamErrorf(codes.FailedPrecondition, "transport: received the unexpected content-type %q", f.Value)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
case "grpc-encoding":
|
case "grpc-encoding":
|
||||||
d.encoding = f.Value
|
d.encoding = f.Value
|
||||||
case "grpc-status":
|
case "grpc-status":
|
||||||
code, err := strconv.Atoi(f.Value)
|
code, err := strconv.Atoi(f.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.setErr(streamErrorf(codes.Internal, "transport: malformed grpc-status: %v", err))
|
return streamErrorf(codes.Internal, "transport: malformed grpc-status: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
d.statusCode = codes.Code(code)
|
d.rawStatusCode = int32(code)
|
||||||
case "grpc-message":
|
case "grpc-message":
|
||||||
d.statusDesc = decodeGrpcMessage(f.Value)
|
d.rawStatusMsg = decodeGrpcMessage(f.Value)
|
||||||
|
case "grpc-status-details-bin":
|
||||||
|
v, err := decodeBinHeader(f.Value)
|
||||||
|
if err != nil {
|
||||||
|
return streamErrorf(codes.Internal, "transport: malformed grpc-status-details-bin: %v", err)
|
||||||
|
}
|
||||||
|
s := &spb.Status{}
|
||||||
|
if err := proto.Unmarshal(v, s); err != nil {
|
||||||
|
return streamErrorf(codes.Internal, "transport: malformed grpc-status-details-bin: %v", err)
|
||||||
|
}
|
||||||
|
d.statusGen = status.FromProto(s)
|
||||||
case "grpc-timeout":
|
case "grpc-timeout":
|
||||||
d.timeoutSet = true
|
d.timeoutSet = true
|
||||||
var err error
|
var err error
|
||||||
d.timeout, err = decodeTimeout(f.Value)
|
if d.timeout, err = decodeTimeout(f.Value); err != nil {
|
||||||
if err != nil {
|
return streamErrorf(codes.Internal, "transport: malformed time-out: %v", err)
|
||||||
d.setErr(streamErrorf(codes.Internal, "transport: malformed time-out: %v", err))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
case ":path":
|
case ":path":
|
||||||
d.method = f.Value
|
d.method = f.Value
|
||||||
default:
|
default:
|
||||||
if !isReservedHeader(f.Name) || isWhitelistedPseudoHeader(f.Name) {
|
if !isReservedHeader(f.Name) || isWhitelistedPseudoHeader(f.Name) {
|
||||||
if f.Name == "user-agent" {
|
|
||||||
i := strings.LastIndex(f.Value, " ")
|
|
||||||
if i == -1 {
|
|
||||||
// There is no application user agent string being set.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Extract the application user agent string.
|
|
||||||
f.Value = f.Value[:i]
|
|
||||||
}
|
|
||||||
if d.mdata == nil {
|
if d.mdata == nil {
|
||||||
d.mdata = make(map[string][]string)
|
d.mdata = make(map[string][]string)
|
||||||
}
|
}
|
||||||
k, v, err := metadata.DecodeKeyValue(f.Name, f.Value)
|
v, err := decodeMetadataHeader(f.Name, f.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Printf("Failed to decode (%q, %q): %v", f.Name, f.Value, err)
|
grpclog.Printf("Failed to decode (%q, %q): %v", f.Name, f.Value, err)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
d.mdata[k] = append(d.mdata[k], v)
|
d.mdata[f.Name] = append(d.mdata[f.Name], v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type timeoutUnit uint8
|
type timeoutUnit uint8
|
||||||
|
@ -379,6 +412,9 @@ func newFramer(conn net.Conn) *framer {
|
||||||
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)
|
||||||
|
// Opt-in to Frame reuse API on framer to reduce garbage.
|
||||||
|
// Frames aren't safe to read from after a subsequent call to ReadFrame.
|
||||||
|
f.fr.SetReuseFrames()
|
||||||
f.fr.ReadMetaHeaders = hpack.NewDecoder(http2InitHeaderTableSize, nil)
|
f.fr.ReadMetaHeaders = hpack.NewDecoder(http2InitHeaderTableSize, nil)
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,10 +45,14 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"golang.org/x/net/trace"
|
"golang.org/x/net/http2"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/keepalive"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/stats"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/grpc/tap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// recvMsg represents the received msg from the transport. All transport
|
// recvMsg represents the received msg from the transport. All transport
|
||||||
|
@ -167,6 +171,11 @@ type Stream struct {
|
||||||
id uint32
|
id uint32
|
||||||
// nil for client side Stream.
|
// nil for client side Stream.
|
||||||
st ServerTransport
|
st ServerTransport
|
||||||
|
// clientStatsCtx keeps the user context for stats handling.
|
||||||
|
// It's only valid on client side. Server side stats context is same as s.ctx.
|
||||||
|
// All client side stats collection should use the clientStatsCtx (instead of the stream context)
|
||||||
|
// so that all the generated stats for a particular RPC can be associated in the processing phase.
|
||||||
|
clientStatsCtx context.Context
|
||||||
// ctx is the associated context of the stream.
|
// ctx is the associated context of the stream.
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
// cancel is always nil for client side Stream.
|
// cancel is always nil for client side Stream.
|
||||||
|
@ -204,9 +213,17 @@ type Stream struct {
|
||||||
// true iff headerChan is closed. Used to avoid closing headerChan
|
// true iff headerChan is closed. Used to avoid closing headerChan
|
||||||
// multiple times.
|
// multiple times.
|
||||||
headerDone bool
|
headerDone bool
|
||||||
// the status received from the server.
|
// the status error received from the server.
|
||||||
statusCode codes.Code
|
status *status.Status
|
||||||
statusDesc string
|
// rstStream indicates whether a RST_STREAM frame needs to be sent
|
||||||
|
// to the server to signify that this stream is closing.
|
||||||
|
rstStream bool
|
||||||
|
// rstError is the error that needs to be sent along with the RST_STREAM frame.
|
||||||
|
rstError http2.ErrCode
|
||||||
|
// bytesSent and bytesReceived indicates whether any bytes have been sent or
|
||||||
|
// received on this stream.
|
||||||
|
bytesSent bool
|
||||||
|
bytesReceived bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecvCompress returns the compression algorithm applied to the inbound
|
// RecvCompress returns the compression algorithm applied to the inbound
|
||||||
|
@ -266,24 +283,14 @@ func (s *Stream) Context() context.Context {
|
||||||
return s.ctx
|
return s.ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraceContext recreates the context of s with a trace.Trace.
|
|
||||||
func (s *Stream) TraceContext(tr trace.Trace) {
|
|
||||||
s.ctx = trace.NewContext(s.ctx, tr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method returns the method for the stream.
|
// Method returns the method for the stream.
|
||||||
func (s *Stream) Method() string {
|
func (s *Stream) Method() string {
|
||||||
return s.method
|
return s.method
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatusCode returns statusCode received from the server.
|
// Status returns the status received from the server.
|
||||||
func (s *Stream) StatusCode() codes.Code {
|
func (s *Stream) Status() *status.Status {
|
||||||
return s.statusCode
|
return s.status
|
||||||
}
|
|
||||||
|
|
||||||
// StatusDesc returns statusDesc received from the server.
|
|
||||||
func (s *Stream) StatusDesc() string {
|
|
||||||
return s.statusDesc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetHeader sets the header metadata. This can be called multiple times.
|
// SetHeader sets the header metadata. This can be called multiple times.
|
||||||
|
@ -330,6 +337,34 @@ func (s *Stream) Read(p []byte) (n int, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// finish sets the stream's state and status, and closes the done channel.
|
||||||
|
// s.mu must be held by the caller. st must always be non-nil.
|
||||||
|
func (s *Stream) finish(st *status.Status) {
|
||||||
|
s.status = st
|
||||||
|
s.state = streamDone
|
||||||
|
close(s.done)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesSent indicates whether any bytes have been sent on this stream.
|
||||||
|
func (s *Stream) BytesSent() bool {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.bytesSent
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesReceived indicates whether any bytes have been received on this stream.
|
||||||
|
func (s *Stream) BytesReceived() bool {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.bytesReceived
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoString is implemented by Stream so context.String() won't
|
||||||
|
// race when printing %#v.
|
||||||
|
func (s *Stream) GoString() string {
|
||||||
|
return fmt.Sprintf("<stream: %p, %v>", s, s.method)
|
||||||
|
}
|
||||||
|
|
||||||
// The key to save transport.Stream in the context.
|
// The key to save transport.Stream in the context.
|
||||||
type streamKey struct{}
|
type streamKey struct{}
|
||||||
|
|
||||||
|
@ -355,22 +390,41 @@ const (
|
||||||
draining
|
draining
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ServerConfig consists of all the configurations to establish a server transport.
|
||||||
|
type ServerConfig struct {
|
||||||
|
MaxStreams uint32
|
||||||
|
AuthInfo credentials.AuthInfo
|
||||||
|
InTapHandle tap.ServerInHandle
|
||||||
|
StatsHandler stats.Handler
|
||||||
|
KeepaliveParams keepalive.ServerParameters
|
||||||
|
KeepalivePolicy keepalive.EnforcementPolicy
|
||||||
|
}
|
||||||
|
|
||||||
// NewServerTransport creates a ServerTransport with conn or non-nil error
|
// NewServerTransport creates a ServerTransport with conn or non-nil error
|
||||||
// if it fails.
|
// if it fails.
|
||||||
func NewServerTransport(protocol string, conn net.Conn, maxStreams uint32, authInfo credentials.AuthInfo) (ServerTransport, error) {
|
func NewServerTransport(protocol string, conn net.Conn, config *ServerConfig) (ServerTransport, error) {
|
||||||
return newHTTP2Server(conn, maxStreams, authInfo)
|
return newHTTP2Server(conn, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnectOptions covers all relevant options for communicating with the server.
|
// ConnectOptions covers all relevant options for communicating with the server.
|
||||||
type ConnectOptions struct {
|
type ConnectOptions struct {
|
||||||
// UserAgent is the application user agent.
|
// UserAgent is the application user agent.
|
||||||
UserAgent string
|
UserAgent string
|
||||||
|
// Authority is the :authority pseudo-header to use. This field has no effect if
|
||||||
|
// TransportCredentials is set.
|
||||||
|
Authority string
|
||||||
// Dialer specifies how to dial a network address.
|
// Dialer specifies how to dial a network address.
|
||||||
Dialer func(context.Context, string) (net.Conn, error)
|
Dialer func(context.Context, string) (net.Conn, error)
|
||||||
|
// FailOnNonTempDialError specifies if gRPC fails on non-temporary dial errors.
|
||||||
|
FailOnNonTempDialError bool
|
||||||
// PerRPCCredentials stores the PerRPCCredentials required to issue RPCs.
|
// PerRPCCredentials stores the PerRPCCredentials required to issue RPCs.
|
||||||
PerRPCCredentials []credentials.PerRPCCredentials
|
PerRPCCredentials []credentials.PerRPCCredentials
|
||||||
// TransportCredentials stores the Authenticator required to setup a client connection.
|
// TransportCredentials stores the Authenticator required to setup a client connection.
|
||||||
TransportCredentials credentials.TransportCredentials
|
TransportCredentials credentials.TransportCredentials
|
||||||
|
// KeepaliveParams stores the keepalive parameters.
|
||||||
|
KeepaliveParams keepalive.ClientParameters
|
||||||
|
// StatsHandler stores the handler for stats.
|
||||||
|
StatsHandler stats.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
// TargetInfo contains the information of the target such as network address and metadata.
|
// TargetInfo contains the information of the target such as network address and metadata.
|
||||||
|
@ -457,6 +511,9 @@ type ClientTransport interface {
|
||||||
// receives the draining signal from the server (e.g., GOAWAY frame in
|
// receives the draining signal from the server (e.g., GOAWAY frame in
|
||||||
// HTTP/2).
|
// HTTP/2).
|
||||||
GoAway() <-chan struct{}
|
GoAway() <-chan struct{}
|
||||||
|
|
||||||
|
// GetGoAwayReason returns the reason why GoAway frame was received.
|
||||||
|
GetGoAwayReason() GoAwayReason
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerTransport is the common interface for all gRPC server-side transport
|
// ServerTransport is the common interface for all gRPC server-side transport
|
||||||
|
@ -466,7 +523,7 @@ type ClientTransport interface {
|
||||||
// Write methods for a given Stream will be called serially.
|
// Write methods for a given Stream will be called serially.
|
||||||
type ServerTransport interface {
|
type ServerTransport interface {
|
||||||
// HandleStreams receives incoming streams using the given handler.
|
// HandleStreams receives incoming streams using the given handler.
|
||||||
HandleStreams(func(*Stream))
|
HandleStreams(func(*Stream), func(context.Context, string) context.Context)
|
||||||
|
|
||||||
// WriteHeader sends the header metadata for the given stream.
|
// WriteHeader sends the header metadata for the given stream.
|
||||||
// WriteHeader may not be called on all streams.
|
// WriteHeader may not be called on all streams.
|
||||||
|
@ -476,10 +533,9 @@ type ServerTransport interface {
|
||||||
// Write may not be called on all streams.
|
// Write may not be called on all streams.
|
||||||
Write(s *Stream, data []byte, opts *Options) error
|
Write(s *Stream, data []byte, opts *Options) error
|
||||||
|
|
||||||
// WriteStatus sends the status of a stream to the client.
|
// WriteStatus sends the status of a stream to the client. WriteStatus is
|
||||||
// WriteStatus is the final call made on a stream and always
|
// the final call made on a stream and always occurs.
|
||||||
// occurs.
|
WriteStatus(s *Stream, st *status.Status) error
|
||||||
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
|
||||||
|
@ -545,6 +601,8 @@ var (
|
||||||
ErrStreamDrain = streamErrorf(codes.Unavailable, "the server stops accepting new RPCs")
|
ErrStreamDrain = streamErrorf(codes.Unavailable, "the server stops accepting new RPCs")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: See if we can replace StreamError with status package errors.
|
||||||
|
|
||||||
// StreamError is an error that only affects one stream within a connection.
|
// StreamError is an error that only affects one stream within a connection.
|
||||||
type StreamError struct {
|
type StreamError struct {
|
||||||
Code codes.Code
|
Code codes.Code
|
||||||
|
@ -552,7 +610,7 @@ type StreamError struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e StreamError) Error() string {
|
func (e StreamError) Error() string {
|
||||||
return fmt.Sprintf("stream error: code = %d desc = %q", e.Code, e.Desc)
|
return fmt.Sprintf("stream error: code = %s desc = %q", e.Code, e.Desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContextErr converts the error from context package into a StreamError.
|
// ContextErr converts the error from context package into a StreamError.
|
||||||
|
@ -593,3 +651,16 @@ func wait(ctx context.Context, done, goAway, closing <-chan struct{}, proceed <-
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GoAwayReason contains the reason for the GoAway frame received.
|
||||||
|
type GoAwayReason uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Invalid indicates that no GoAway frame is received.
|
||||||
|
Invalid GoAwayReason = 0
|
||||||
|
// NoReason is the default value when GoAway frame is received.
|
||||||
|
NoReason GoAwayReason = 1
|
||||||
|
// TooManyPings indicates that a GoAway frame with ErrCodeEnhanceYourCalm
|
||||||
|
// was recieved and that the debug data said "too_many_pings".
|
||||||
|
TooManyPings GoAwayReason = 2
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue