Commit 5859fe10 authored by Eric Chiang's avatar Eric Chiang Committed by GitHub

Merge pull request #910 from ericchiang/update-grpc

*: update grpc and correct protobuf generation
parents e609de50 ba1660ae
...@@ -16,10 +16,6 @@ user=$(shell id -u -n) ...@@ -16,10 +16,6 @@ user=$(shell id -u -n)
group=$(shell id -g -n) group=$(shell id -g -n)
export GOBIN=$(PWD)/bin export GOBIN=$(PWD)/bin
# Prefer ./bin instead of system packages for things like protoc, where we want
# to use the version dex uses, not whatever a developer has installed.
export PATH=$(GOBIN):$(shell printenv PATH)
export GO15VENDOREXPERIMENT=1
LD_FLAGS="-w -X $(REPO_PATH)/version.Version=$(VERSION)" LD_FLAGS="-w -X $(REPO_PATH)/version.Version=$(VERSION)"
...@@ -74,10 +70,10 @@ docker-image: clean-release _output/bin/dex ...@@ -74,10 +70,10 @@ docker-image: clean-release _output/bin/dex
proto: api/api.pb.go server/internal/types.pb.go proto: api/api.pb.go server/internal/types.pb.go
api/api.pb.go: api/api.proto bin/protoc bin/protoc-gen-go api/api.pb.go: api/api.proto bin/protoc bin/protoc-gen-go
@protoc --go_out=plugins=grpc:. api/*.proto @./bin/protoc --go_out=plugins=grpc:. --plugin=protoc-gen-go=./bin/protoc-gen-go api/*.proto
server/internal/types.pb.go: server/internal/types.proto bin/protoc bin/protoc-gen-go server/internal/types.pb.go: server/internal/types.proto bin/protoc bin/protoc-gen-go
@protoc --go_out=. server/internal/*.proto @./bin/protoc --go_out=. --plugin=protoc-gen-go=./bin/protoc-gen-go server/internal/*.proto
bin/protoc: scripts/get-protoc bin/protoc: scripts/get-protoc
@./scripts/get-protoc bin/protoc @./scripts/get-protoc bin/protoc
......
...@@ -353,7 +353,7 @@ var _ grpc.ClientConn ...@@ -353,7 +353,7 @@ var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file // This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against. // is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion3 const _ = grpc.SupportPackageIsVersion4
// Client API for Dex service // Client API for Dex service
...@@ -702,14 +702,14 @@ var _Dex_serviceDesc = grpc.ServiceDesc{ ...@@ -702,14 +702,14 @@ var _Dex_serviceDesc = grpc.ServiceDesc{
}, },
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{},
Metadata: fileDescriptor0, Metadata: "api/api.proto",
} }
func init() { proto.RegisterFile("api/api.proto", fileDescriptor0) } func init() { proto.RegisterFile("api/api.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 786 bytes of a gzipped FileDescriptorProto // 786 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x55, 0x6d, 0x4f, 0xdb, 0x48, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0x6d, 0x4f, 0xdb, 0x48,
0x10, 0x4e, 0xe2, 0x90, 0x38, 0x93, 0xf7, 0x3d, 0x5e, 0x4c, 0xd0, 0x49, 0xb0, 0xe8, 0x24, 0xd0, 0x10, 0x4e, 0xe2, 0x90, 0x38, 0x93, 0xf7, 0x3d, 0x5e, 0x4c, 0xd0, 0x49, 0xb0, 0xe8, 0x24, 0xd0,
0x49, 0x70, 0x70, 0xd2, 0x9d, 0x74, 0xe8, 0xb8, 0x3b, 0xc1, 0xb5, 0x20, 0x55, 0x15, 0xb2, 0x9a, 0x49, 0x70, 0x70, 0xd2, 0x9d, 0x74, 0xe8, 0xb8, 0x3b, 0xc1, 0xb5, 0x20, 0x55, 0x15, 0xb2, 0x9a,
0x7e, 0xac, 0x65, 0xe2, 0x01, 0x56, 0x18, 0xdb, 0xdd, 0xdd, 0x10, 0xda, 0x7f, 0x57, 0xf5, 0x8f, 0x7e, 0xac, 0x65, 0xe2, 0x01, 0x56, 0x18, 0xdb, 0xdd, 0xdd, 0x10, 0xda, 0x7f, 0x57, 0xf5, 0x8f,
......
hash: ba77a77f03b1750479aa4a61de59d7a545dc8bd5c71be10e1c2e9afed37e1925 hash: 94ea60e268ee0ed04e8affdee4db2884fd93ee68ed625d88ccc0e24565b98569
updated: 2017-03-24T11:03:21.332291207-07:00 updated: 2017-04-13T11:28:49.008994259-07:00
imports: imports:
- name: github.com/beevik/etree - name: github.com/beevik/etree
version: 4cd0dd976db869f817248477718071a28e978df0 version: 4cd0dd976db869f817248477718071a28e978df0
...@@ -14,10 +14,12 @@ imports: ...@@ -14,10 +14,12 @@ imports:
- name: github.com/go-sql-driver/mysql - name: github.com/go-sql-driver/mysql
version: 0b58b37b664c21f3010e836f1b931e1d0b0b0685 version: 0b58b37b664c21f3010e836f1b931e1d0b0b0685
- name: github.com/golang/protobuf - name: github.com/golang/protobuf
version: 874264fbbb43f4d91e999fecb4b40143ed611400 version: 2bba0603135d7d7f5cb73b2125beeda19c09f4ef
subpackages: subpackages:
- proto - proto
- protoc-gen-go - protoc-gen-go
- protoc-gen-go/grpc
- ptypes/any
- name: github.com/gorilla/context - name: github.com/gorilla/context
version: aed02d124ae4a0e94fea4541c8effd05bf0c8296 version: aed02d124ae4a0e94fea4541c8effd05bf0c8296
- name: github.com/gorilla/handlers - name: github.com/gorilla/handlers
...@@ -62,11 +64,12 @@ imports: ...@@ -62,11 +64,12 @@ imports:
- bcrypt - bcrypt
- blowfish - blowfish
- name: golang.org/x/net - name: golang.org/x/net
version: 6a513affb38dc9788b449d59ffed099b8de18fa0 version: 5602c733f70afc6dcec6766be0d5034d4c4f14de
subpackages: subpackages:
- context - context
- http2 - http2
- http2/hpack - http2/hpack
- idna
- internal/timeseries - internal/timeseries
- lex/httplex - lex/httplex
- trace - trace
...@@ -79,6 +82,13 @@ imports: ...@@ -79,6 +82,13 @@ imports:
version: 833a04a10549a95dc34458c195cbad61bbb6cb4d version: 833a04a10549a95dc34458c195cbad61bbb6cb4d
subpackages: subpackages:
- unix - unix
- name: golang.org/x/text
version: f4b4367115ec2de254587813edaa901bc1c723a8
subpackages:
- secure/bidirule
- transform
- unicode/bidi
- unicode/norm
- name: google.golang.org/appengine - name: google.golang.org/appengine
version: 267c27e7492265b84fc6719503b14a1e17975d79 version: 267c27e7492265b84fc6719503b14a1e17975d79
subpackages: subpackages:
...@@ -89,16 +99,24 @@ imports: ...@@ -89,16 +99,24 @@ imports:
- internal/remote_api - internal/remote_api
- internal/urlfetch - internal/urlfetch
- urlfetch - urlfetch
- name: google.golang.org/genproto
version: 411e09b969b1170a9f0c467558eb4c4c110d9c77
subpackages:
- googleapis/rpc/status
- name: google.golang.org/grpc - name: google.golang.org/grpc
version: b1a2821ca5a4fd6b6e48ddfbb7d6d7584d839d21 version: 0e8b58d22f34640cb17dbbed1c8aef3b8dcc0e97
subpackages: subpackages:
- codes - codes
- credentials - credentials
- grpclog - grpclog
- internal - internal
- keepalive
- metadata - metadata
- naming - naming
- peer - peer
- stats
- status
- tap
- transport - transport
- name: gopkg.in/asn1-ber.v1 - name: gopkg.in/asn1-ber.v1
version: 4e86f4367175e39f69d9358a5f17b4dda270378d version: 4e86f4367175e39f69d9358a5f17b4dda270378d
......
...@@ -32,7 +32,7 @@ import: ...@@ -32,7 +32,7 @@ import:
# Imported directly and by several third party packages. # Imported directly and by several third party packages.
- package: golang.org/x/net - package: golang.org/x/net
version: 6a513affb38dc9788b449d59ffed099b8de18fa0 version: 5602c733f70afc6dcec6766be0d5034d4c4f14de
subpackages: subpackages:
- context - context
- http2 - http2
...@@ -40,6 +40,8 @@ import: ...@@ -40,6 +40,8 @@ import:
- internal/timeseries - internal/timeseries
- lex/httplex - lex/httplex
- trace - trace
- package: golang.org/x/text
version: f4b4367115ec2de254587813edaa901bc1c723a8
# Used for parsing configs. # Used for parsing configs.
- package: github.com/ghodss/yaml - package: github.com/ghodss/yaml
...@@ -109,7 +111,7 @@ import: ...@@ -109,7 +111,7 @@ import:
# gRPC and protobuf are use for the API. Also import x/net/http2 stack. # gRPC and protobuf are use for the API. Also import x/net/http2 stack.
- package: google.golang.org/grpc - package: google.golang.org/grpc
version: b1a2821ca5a4fd6b6e48ddfbb7d6d7584d839d21 version: 0e8b58d22f34640cb17dbbed1c8aef3b8dcc0e97
subpackages: subpackages:
- codes - codes
- credentials - credentials
...@@ -120,10 +122,13 @@ import: ...@@ -120,10 +122,13 @@ import:
- peer - peer
- transport - transport
- package: github.com/golang/protobuf - package: github.com/golang/protobuf
version: 874264fbbb43f4d91e999fecb4b40143ed611400 version: 2bba0603135d7d7f5cb73b2125beeda19c09f4ef
subpackages: subpackages:
- proto - proto
- protoc-gen-go - protoc-gen-go
- protoc-gen-go/grpc
- package: google.golang.org/genproto
version: 411e09b969b1170a9f0c467558eb4c4c110d9c77
# Structured logging # Structured logging
- package: github.com/Sirupsen/logrus - package: github.com/Sirupsen/logrus
......
...@@ -63,7 +63,7 @@ func init() { proto.RegisterFile("server/internal/types.proto", fileDescriptor0) ...@@ -63,7 +63,7 @@ func init() { proto.RegisterFile("server/internal/types.proto", fileDescriptor0)
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 157 bytes of a gzipped FileDescriptorProto // 157 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x92, 0x2e, 0x4e, 0x2d, 0x2a, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2e, 0x4e, 0x2d, 0x2a,
0x4b, 0x2d, 0xd2, 0xcf, 0xcc, 0x2b, 0x49, 0x2d, 0xca, 0x4b, 0xcc, 0xd1, 0x2f, 0xa9, 0x2c, 0x48, 0x4b, 0x2d, 0xd2, 0xcf, 0xcc, 0x2b, 0x49, 0x2d, 0xca, 0x4b, 0xcc, 0xd1, 0x2f, 0xa9, 0x2c, 0x48,
0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x80, 0x89, 0x2a, 0x39, 0x73, 0xf1, 0x04, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x80, 0x89, 0x2a, 0x39, 0x73, 0xf1, 0x04,
0xa5, 0xa6, 0x15, 0xa5, 0x16, 0x67, 0x84, 0xe4, 0x67, 0xa7, 0xe6, 0x09, 0xc9, 0x72, 0x71, 0x15, 0xa5, 0xa6, 0x15, 0xa5, 0x16, 0x67, 0x84, 0xe4, 0x67, 0xa7, 0xe6, 0x09, 0xc9, 0x72, 0x71, 0x15,
......
...@@ -61,7 +61,6 @@ var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for ...@@ -61,7 +61,6 @@ var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for
// int32, int64, uint32, uint64, bool, and enum // int32, int64, uint32, uint64, bool, and enum
// protocol buffer types. // protocol buffer types.
func DecodeVarint(buf []byte) (x uint64, n int) { func DecodeVarint(buf []byte) (x uint64, n int) {
// x, n already 0
for shift := uint(0); shift < 64; shift += 7 { for shift := uint(0); shift < 64; shift += 7 {
if n >= len(buf) { if n >= len(buf) {
return 0, 0 return 0, 0
...@@ -78,13 +77,7 @@ func DecodeVarint(buf []byte) (x uint64, n int) { ...@@ -78,13 +77,7 @@ func DecodeVarint(buf []byte) (x uint64, n int) {
return 0, 0 return 0, 0
} }
// DecodeVarint reads a varint-encoded integer from the Buffer. func (p *Buffer) decodeVarintSlow() (x uint64, err error) {
// This is the format for the
// int32, int64, uint32, uint64, bool, and enum
// protocol buffer types.
func (p *Buffer) DecodeVarint() (x uint64, err error) {
// x, err already 0
i := p.index i := p.index
l := len(p.buf) l := len(p.buf)
...@@ -107,6 +100,107 @@ func (p *Buffer) DecodeVarint() (x uint64, err error) { ...@@ -107,6 +100,107 @@ func (p *Buffer) DecodeVarint() (x uint64, err error) {
return return
} }
// DecodeVarint reads a varint-encoded integer from the Buffer.
// This is the format for the
// int32, int64, uint32, uint64, bool, and enum
// protocol buffer types.
func (p *Buffer) DecodeVarint() (x uint64, err error) {
i := p.index
buf := p.buf
if i >= len(buf) {
return 0, io.ErrUnexpectedEOF
} else if buf[i] < 0x80 {
p.index++
return uint64(buf[i]), nil
} else if len(buf)-i < 10 {
return p.decodeVarintSlow()
}
var b uint64
// we already checked the first byte
x = uint64(buf[i]) - 0x80
i++
b = uint64(buf[i])
i++
x += b << 7
if b&0x80 == 0 {
goto done
}
x -= 0x80 << 7
b = uint64(buf[i])
i++
x += b << 14
if b&0x80 == 0 {
goto done
}
x -= 0x80 << 14
b = uint64(buf[i])
i++
x += b << 21
if b&0x80 == 0 {
goto done
}
x -= 0x80 << 21
b = uint64(buf[i])
i++
x += b << 28
if b&0x80 == 0 {
goto done
}
x -= 0x80 << 28
b = uint64(buf[i])
i++
x += b << 35
if b&0x80 == 0 {
goto done
}
x -= 0x80 << 35
b = uint64(buf[i])
i++
x += b << 42
if b&0x80 == 0 {
goto done
}
x -= 0x80 << 42
b = uint64(buf[i])
i++
x += b << 49
if b&0x80 == 0 {
goto done
}
x -= 0x80 << 49
b = uint64(buf[i])
i++
x += b << 56
if b&0x80 == 0 {
goto done
}
x -= 0x80 << 56
b = uint64(buf[i])
i++
x += b << 63
if b&0x80 == 0 {
goto done
}
// x -= 0x80 << 63 // Always zero.
return 0, errOverflow
done:
p.index = i
return x, nil
}
// DecodeFixed64 reads a 64-bit integer from the Buffer. // DecodeFixed64 reads a 64-bit integer from the Buffer.
// This is the format for the // This is the format for the
// fixed64, sfixed64, and double protocol buffer types. // fixed64, sfixed64, and double protocol buffer types.
...@@ -340,6 +434,8 @@ func (p *Buffer) DecodeGroup(pb Message) error { ...@@ -340,6 +434,8 @@ func (p *Buffer) DecodeGroup(pb Message) error {
// Buffer and places the decoded result in pb. If the struct // Buffer and places the decoded result in pb. If the struct
// underlying pb does not match the data in the buffer, the results can be // underlying pb does not match the data in the buffer, the results can be
// unpredictable. // unpredictable.
//
// Unlike proto.Unmarshal, this does not reset pb before starting to unmarshal.
func (p *Buffer) Unmarshal(pb Message) error { func (p *Buffer) Unmarshal(pb Message) error {
// If the object can unmarshal itself, let it. // If the object can unmarshal itself, let it.
if u, ok := pb.(Unmarshaler); ok { if u, ok := pb.(Unmarshaler); ok {
......
...@@ -234,10 +234,6 @@ func Marshal(pb Message) ([]byte, error) { ...@@ -234,10 +234,6 @@ func Marshal(pb Message) ([]byte, error) {
} }
p := NewBuffer(nil) p := NewBuffer(nil)
err := p.Marshal(pb) err := p.Marshal(pb)
var state errorState
if err != nil && !state.shouldContinue(err, nil) {
return nil, err
}
if p.buf == nil && err == nil { if p.buf == nil && err == nil {
// Return a non-nil slice on success. // Return a non-nil slice on success.
return []byte{}, nil return []byte{}, nil
...@@ -266,11 +262,8 @@ func (p *Buffer) Marshal(pb Message) error { ...@@ -266,11 +262,8 @@ func (p *Buffer) Marshal(pb Message) error {
// Can the object marshal itself? // Can the object marshal itself?
if m, ok := pb.(Marshaler); ok { if m, ok := pb.(Marshaler); ok {
data, err := m.Marshal() data, err := m.Marshal()
if err != nil {
return err
}
p.buf = append(p.buf, data...) p.buf = append(p.buf, data...)
return nil return err
} }
t, base, err := getbase(pb) t, base, err := getbase(pb)
...@@ -282,7 +275,7 @@ func (p *Buffer) Marshal(pb Message) error { ...@@ -282,7 +275,7 @@ func (p *Buffer) Marshal(pb Message) error {
} }
if collectStats { if collectStats {
stats.Encode++ (stats).Encode++ // Parens are to work around a goimports bug.
} }
if len(p.buf) > maxMarshalSize { if len(p.buf) > maxMarshalSize {
...@@ -309,7 +302,7 @@ func Size(pb Message) (n int) { ...@@ -309,7 +302,7 @@ func Size(pb Message) (n int) {
} }
if collectStats { if collectStats {
stats.Size++ (stats).Size++ // Parens are to work around a goimports bug.
} }
return return
...@@ -1014,7 +1007,6 @@ func size_slice_struct_message(p *Properties, base structPointer) (n int) { ...@@ -1014,7 +1007,6 @@ func size_slice_struct_message(p *Properties, base structPointer) (n int) {
if p.isMarshaler { if p.isMarshaler {
m := structPointer_Interface(structp, p.stype).(Marshaler) m := structPointer_Interface(structp, p.stype).(Marshaler)
data, _ := m.Marshal() data, _ := m.Marshal()
n += len(p.tagcode)
n += sizeRawBytes(data) n += sizeRawBytes(data)
continue continue
} }
...@@ -1083,10 +1075,17 @@ func (o *Buffer) enc_map(p *Properties, base structPointer) error { ...@@ -1083,10 +1075,17 @@ func (o *Buffer) enc_map(p *Properties, base structPointer) error {
func (o *Buffer) enc_exts(p *Properties, base structPointer) error { func (o *Buffer) enc_exts(p *Properties, base structPointer) error {
exts := structPointer_Extensions(base, p.field) exts := structPointer_Extensions(base, p.field)
if err := encodeExtensions(exts); err != nil {
v, mu := exts.extensionsRead()
if v == nil {
return nil
}
mu.Lock()
defer mu.Unlock()
if err := encodeExtensionsMap(v); err != nil {
return err return err
} }
v, _ := exts.extensionsRead()
return o.enc_map_body(v) return o.enc_map_body(v)
} }
......
...@@ -54,13 +54,17 @@ Equality is defined in this way: ...@@ -54,13 +54,17 @@ Equality is defined in this way:
in a proto3 .proto file, fields are not "set"; specifically, in a proto3 .proto file, fields are not "set"; specifically,
zero length proto3 "bytes" fields are equal (nil == {}). zero length proto3 "bytes" fields are equal (nil == {}).
- Two repeated fields are equal iff their lengths are the same, - Two repeated fields are equal iff their lengths are the same,
and their corresponding elements are equal (a "bytes" field, and their corresponding elements are equal. Note a "bytes" field,
although represented by []byte, is not a repeated field) although represented by []byte, is not a repeated field and the
rule for the scalar fields described above applies.
- Two unset fields are equal. - Two unset fields are equal.
- Two unknown field sets are equal if their current - Two unknown field sets are equal if their current
encoded state is equal. encoded state is equal.
- Two extension sets are equal iff they have corresponding - Two extension sets are equal iff they have corresponding
elements that are pairwise equal. elements that are pairwise equal.
- Two map fields are equal iff their lengths are the same,
and they contain the same set of elements. Zero-length map
fields are equal.
- Every other combination of things are not equal. - Every other combination of things are not equal.
The return value is undefined if a and b are not protocol buffers. The return value is undefined if a and b are not protocol buffers.
......
...@@ -154,6 +154,7 @@ type ExtensionDesc struct { ...@@ -154,6 +154,7 @@ type ExtensionDesc struct {
Field int32 // field number Field int32 // field number
Name string // fully-qualified name of extension, for text formatting Name string // fully-qualified name of extension, for text formatting
Tag string // protobuf tag style Tag string // protobuf tag style
Filename string // name of the file in which the extension is defined
} }
func (ed *ExtensionDesc) repeated() bool { func (ed *ExtensionDesc) repeated() bool {
...@@ -500,6 +501,9 @@ func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) { ...@@ -500,6 +501,9 @@ func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) {
registeredExtensions := RegisteredExtensions(pb) registeredExtensions := RegisteredExtensions(pb)
emap, mu := epb.extensionsRead() emap, mu := epb.extensionsRead()
if emap == nil {
return nil, nil
}
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
extensions := make([]*ExtensionDesc, 0, len(emap)) extensions := make([]*ExtensionDesc, 0, len(emap))
......
...@@ -308,7 +308,7 @@ func GetStats() Stats { return stats } ...@@ -308,7 +308,7 @@ func GetStats() Stats { return stats }
// temporary Buffer and are fine for most applications. // temporary Buffer and are fine for most applications.
type Buffer struct { type Buffer struct {
buf []byte // encode/decode byte stream buf []byte // encode/decode byte stream
index int // write point index int // read point
// pools of basic types to amortize allocation. // pools of basic types to amortize allocation.
bools []bool bools []bool
......
...@@ -844,7 +844,15 @@ func RegisterType(x Message, name string) { ...@@ -844,7 +844,15 @@ func RegisterType(x Message, name string) {
} }
// MessageName returns the fully-qualified proto name for the given message type. // MessageName returns the fully-qualified proto name for the given message type.
func MessageName(x Message) string { return revProtoTypes[reflect.TypeOf(x)] } func MessageName(x Message) string {
type xname interface {
XXX_MessageName() string
}
if m, ok := x.(xname); ok {
return m.XXX_MessageName()
}
return revProtoTypes[reflect.TypeOf(x)]
}
// MessageType returns the message type (pointer to struct) for a named message. // MessageType returns the message type (pointer to struct) for a named message.
func MessageType(name string) reflect.Type { return protoTypes[name] } func MessageType(name string) reflect.Type { return protoTypes[name] }
......
...@@ -44,6 +44,9 @@ import ( ...@@ -44,6 +44,9 @@ import (
"unicode/utf8" "unicode/utf8"
) )
// Error string emitted when deserializing Any and fields are already set
const anyRepeatedlyUnpacked = "Any message unpacked multiple times, or %q already set"
type ParseError struct { type ParseError struct {
Message string Message string
Line int // 1-based line number Line int // 1-based line number
...@@ -508,8 +511,16 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error { ...@@ -508,8 +511,16 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
if err != nil { if err != nil {
return p.errorf("failed to marshal message of type %q: %v", messageName, err) return p.errorf("failed to marshal message of type %q: %v", messageName, err)
} }
if fieldSet["type_url"] {
return p.errorf(anyRepeatedlyUnpacked, "type_url")
}
if fieldSet["value"] {
return p.errorf(anyRepeatedlyUnpacked, "value")
}
sv.FieldByName("TypeUrl").SetString(extName) sv.FieldByName("TypeUrl").SetString(extName)
sv.FieldByName("Value").SetBytes(b) sv.FieldByName("Value").SetBytes(b)
fieldSet["type_url"] = true
fieldSet["value"] = true
continue continue
} }
...@@ -581,7 +592,11 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error { ...@@ -581,7 +592,11 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
props = oop.Prop props = oop.Prop
nv := reflect.New(oop.Type.Elem()) nv := reflect.New(oop.Type.Elem())
dst = nv.Elem().Field(0) dst = nv.Elem().Field(0)
sv.Field(oop.Field).Set(nv) field := sv.Field(oop.Field)
if !field.IsNil() {
return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, sv.Type().Field(oop.Field).Name)
}
field.Set(nv)
} }
if !dst.IsValid() { if !dst.IsValid() {
return p.errorf("unknown field name %q in %v", name, st) return p.errorf("unknown field name %q in %v", name, st)
...@@ -781,12 +796,12 @@ func (p *textParser) readAny(v reflect.Value, props *Properties) error { ...@@ -781,12 +796,12 @@ func (p *textParser) readAny(v reflect.Value, props *Properties) error {
fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem())) fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem()))
return p.readAny(fv.Index(fv.Len()-1), props) return p.readAny(fv.Index(fv.Len()-1), props)
case reflect.Bool: case reflect.Bool:
// Either "true", "false", 1 or 0. // true/1/t/True or false/f/0/False.
switch tok.value { switch tok.value {
case "true", "1": case "true", "1", "t", "True":
fv.SetBool(true) fv.SetBool(true)
return nil return nil
case "false", "0": case "false", "0", "f", "False":
fv.SetBool(false) fv.SetBool(false)
return nil return nil
} }
......
This diff is collapsed.
// Code generated by protoc-gen-go.
// source: github.com/golang/protobuf/ptypes/any/any.proto
// DO NOT EDIT!
/*
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 init() {
proto.RegisterType((*Any)(nil), "google.protobuf.Any")
}
func init() { proto.RegisterFile("github.com/golang/protobuf/ptypes/any/any.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 187 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 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, 0x15, 0x71, 0x09, 0x27, 0xe7, 0xe7, 0xea, 0xa1, 0x19, 0xe7, 0xc4, 0xe1, 0x98, 0x57, 0x19,
0x00, 0xe2, 0x04, 0x30, 0x46, 0xa9, 0x12, 0xe5, 0xb8, 0x05, 0x8c, 0x8c, 0x8b, 0x98, 0x98, 0xdd,
0x03, 0x9c, 0x56, 0x31, 0xc9, 0xb9, 0x43, 0x4c, 0x0b, 0x80, 0xaa, 0xd2, 0x0b, 0x4f, 0xcd, 0xc9,
0xf1, 0xce, 0xcb, 0x2f, 0xcf, 0x0b, 0x01, 0xa9, 0x4e, 0x62, 0x03, 0x6b, 0x37, 0x06, 0x04, 0x00,
0x00, 0xff, 0xff, 0xc6, 0x4d, 0x03, 0x23, 0xf6, 0x00, 0x00, 0x00,
}
...@@ -7,7 +7,7 @@ ...@@ -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 @@ ...@@ -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" ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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) { ...@@ -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 ( ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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 { ...@@ -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
......
...@@ -247,7 +247,7 @@ func filterOutClientConn(in []*ClientConn, exclude *ClientConn) []*ClientConn { ...@@ -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 }
......
// 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
}
...@@ -64,9 +64,17 @@ func (e ConnectionError) Error() string { return fmt.Sprintf("connection error: ...@@ -64,9 +64,17 @@ func (e ConnectionError) Error() string { return fmt.Sprintf("connection error:
type StreamError struct { type StreamError struct {
StreamID uint32 StreamID uint32
Code ErrCode Code ErrCode
Cause error // optional additional detail
}
func streamError(id uint32, code ErrCode) StreamError {
return StreamError{StreamID: id, Code: code}
} }
func (e StreamError) Error() string { func (e StreamError) Error() string {
if e.Cause != nil {
return fmt.Sprintf("stream error: stream ID %d; %v; %v", e.StreamID, e.Code, e.Cause)
}
return fmt.Sprintf("stream error: stream ID %d; %v", e.StreamID, e.Code) return fmt.Sprintf("stream error: stream ID %d; %v", e.StreamID, e.Code)
} }
......
// 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
}
This diff is collapsed.
...@@ -39,6 +39,13 @@ type clientTrace httptrace.ClientTrace ...@@ -39,6 +39,13 @@ type clientTrace httptrace.ClientTrace
func reqContext(r *http.Request) context.Context { return r.Context() } func reqContext(r *http.Request) context.Context { return r.Context() }
func (t *Transport) idleConnTimeout() time.Duration {
if t.t1 != nil {
return t.t1.IdleConnTimeout
}
return 0
}
func setResponseUncompressed(res *http.Response) { res.Uncompressed = true } func setResponseUncompressed(res *http.Response) { res.Uncompressed = true }
func traceGotConn(req *http.Request, cc *ClientConn) { func traceGotConn(req *http.Request, cc *ClientConn) {
...@@ -92,3 +99,8 @@ func requestTrace(req *http.Request) *clientTrace { ...@@ -92,3 +99,8 @@ func requestTrace(req *http.Request) *clientTrace {
trace := httptrace.ContextClientTrace(req.Context()) trace := httptrace.ContextClientTrace(req.Context())
return (*clientTrace)(trace) return (*clientTrace)(trace)
} }
// Ping sends a PING frame to the server and waits for the ack.
func (cc *ClientConn) Ping(ctx context.Context) error {
return cc.ping(ctx)
}
// 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.7,!go1.8
package http2
import "crypto/tls"
// temporary copy of Go 1.7's private tls.Config.clone:
func cloneTLSConfig(c *tls.Config) *tls.Config {
return &tls.Config{
Rand: c.Rand,
Time: c.Time,
Certificates: c.Certificates,
NameToCertificate: c.NameToCertificate,
GetCertificate: c.GetCertificate,
RootCAs: c.RootCAs,
NextProtos: c.NextProtos,
ServerName: c.ServerName,
ClientAuth: c.ClientAuth,
ClientCAs: c.ClientCAs,
InsecureSkipVerify: c.InsecureSkipVerify,
CipherSuites: c.CipherSuites,
PreferServerCipherSuites: c.PreferServerCipherSuites,
SessionTicketsDisabled: c.SessionTicketsDisabled,
SessionTicketKey: c.SessionTicketKey,
ClientSessionCache: c.ClientSessionCache,
MinVersion: c.MinVersion,
MaxVersion: c.MaxVersion,
CurvePreferences: c.CurvePreferences,
DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
Renegotiation: c.Renegotiation,
}
}
// 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.8
package http2
import (
"crypto/tls"
"io"
"net/http"
)
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)
// Push implements http.Pusher.
func (w *responseWriter) Push(target string, opts *http.PushOptions) error {
internalOpts := pushOptions{}
if opts != nil {
internalOpts.Method = opts.Method
internalOpts.Header = opts.Header
}
return w.push(target, internalOpts)
}
func configureServer18(h1 *http.Server, h2 *Server) error {
if h2.IdleTimeout == 0 {
if h1.IdleTimeout != 0 {
h2.IdleTimeout = h1.IdleTimeout
} else {
h2.IdleTimeout = h1.ReadTimeout
}
}
return nil
}
func shouldLogPanic(panicValue interface{}) bool {
return panicValue != nil && panicValue != http.ErrAbortHandler
}
func reqGetBody(req *http.Request) func() (io.ReadCloser, error) {
return req.GetBody
}
func reqBodyIsNoBody(body io.ReadCloser) bool {
return body == http.NoBody
}
...@@ -39,13 +39,14 @@ func NewEncoder(w io.Writer) *Encoder { ...@@ -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 { ...@@ -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.
......
...@@ -57,11 +57,11 @@ func (hf HeaderField) String() string { ...@@ -57,11 +57,11 @@ func (hf HeaderField) String() string {
return fmt.Sprintf("header field %q = %q%s", hf.Name, hf.Value, suffix) return fmt.Sprintf("header field %q = %q%s", hf.Name, hf.Value, suffix)
} }
// Size returns the size of an entry per RFC 7540 section 5.2. // Size returns the size of an entry per RFC 7541 section 4.1.
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 ...@@ -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) { ...@@ -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) { ...@@ -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++
}
// Shift slice contents down if we evicted things.
if len(dt.ents) != len(base) {
copy(base, dt.ents)
dt.ents = base[:len(dt.ents)]
} }
} dt.table.evictOldest(n)
// 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) { ...@@ -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,199 @@ ...@@ -4,73 +4,199 @@
package hpack package hpack
import (
"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
}
func pair(name, value string) HeaderField { func pair(name, value string) HeaderField {
return HeaderField{Name: name, Value: value} return HeaderField{Name: name, Value: value}
} }
// 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)
pair(":method", "GET"), func newStaticTable() *headerFieldTable {
pair(":method", "POST"), t := &headerFieldTable{}
pair(":path", "/"), t.init()
pair(":path", "/index.html"), t.addEntry(pair(":authority", ""))
pair(":scheme", "http"), t.addEntry(pair(":method", "GET"))
pair(":scheme", "https"), t.addEntry(pair(":method", "POST"))
pair(":status", "200"), t.addEntry(pair(":path", "/"))
pair(":status", "204"), t.addEntry(pair(":path", "/index.html"))
pair(":status", "206"), t.addEntry(pair(":scheme", "http"))
pair(":status", "304"), t.addEntry(pair(":scheme", "https"))
pair(":status", "400"), t.addEntry(pair(":status", "200"))
pair(":status", "404"), t.addEntry(pair(":status", "204"))
pair(":status", "500"), t.addEntry(pair(":status", "206"))
pair("accept-charset", ""), t.addEntry(pair(":status", "304"))
pair("accept-encoding", "gzip, deflate"), t.addEntry(pair(":status", "400"))
pair("accept-language", ""), t.addEntry(pair(":status", "404"))
pair("accept-ranges", ""), t.addEntry(pair(":status", "500"))
pair("accept", ""), t.addEntry(pair("accept-charset", ""))
pair("access-control-allow-origin", ""), t.addEntry(pair("accept-encoding", "gzip, deflate"))
pair("age", ""), t.addEntry(pair("accept-language", ""))
pair("allow", ""), t.addEntry(pair("accept-ranges", ""))
pair("authorization", ""), t.addEntry(pair("accept", ""))
pair("cache-control", ""), t.addEntry(pair("access-control-allow-origin", ""))
pair("content-disposition", ""), t.addEntry(pair("age", ""))
pair("content-encoding", ""), t.addEntry(pair("allow", ""))
pair("content-language", ""), t.addEntry(pair("authorization", ""))
pair("content-length", ""), t.addEntry(pair("cache-control", ""))
pair("content-location", ""), t.addEntry(pair("content-disposition", ""))
pair("content-range", ""), t.addEntry(pair("content-encoding", ""))
pair("content-type", ""), t.addEntry(pair("content-language", ""))
pair("cookie", ""), t.addEntry(pair("content-length", ""))
pair("date", ""), t.addEntry(pair("content-location", ""))
pair("etag", ""), t.addEntry(pair("content-range", ""))
pair("expect", ""), t.addEntry(pair("content-type", ""))
pair("expires", ""), t.addEntry(pair("cookie", ""))
pair("from", ""), t.addEntry(pair("date", ""))
pair("host", ""), t.addEntry(pair("etag", ""))
pair("if-match", ""), t.addEntry(pair("expect", ""))
pair("if-modified-since", ""), t.addEntry(pair("expires", ""))
pair("if-none-match", ""), t.addEntry(pair("from", ""))
pair("if-range", ""), t.addEntry(pair("host", ""))
pair("if-unmodified-since", ""), t.addEntry(pair("if-match", ""))
pair("last-modified", ""), t.addEntry(pair("if-modified-since", ""))
pair("link", ""), t.addEntry(pair("if-none-match", ""))
pair("location", ""), t.addEntry(pair("if-range", ""))
pair("max-forwards", ""), t.addEntry(pair("if-unmodified-since", ""))
pair("proxy-authenticate", ""), t.addEntry(pair("last-modified", ""))
pair("proxy-authorization", ""), t.addEntry(pair("link", ""))
pair("range", ""), t.addEntry(pair("location", ""))
pair("referer", ""), t.addEntry(pair("max-forwards", ""))
pair("refresh", ""), t.addEntry(pair("proxy-authenticate", ""))
pair("retry-after", ""), t.addEntry(pair("proxy-authorization", ""))
pair("server", ""), t.addEntry(pair("range", ""))
pair("set-cookie", ""), t.addEntry(pair("referer", ""))
pair("strict-transport-security", ""), t.addEntry(pair("refresh", ""))
pair("transfer-encoding", ""), t.addEntry(pair("retry-after", ""))
pair("user-agent", ""), t.addEntry(pair("server", ""))
pair("vary", ""), t.addEntry(pair("set-cookie", ""))
pair("via", ""), t.addEntry(pair("strict-transport-security", ""))
pair("www-authenticate", ""), t.addEntry(pair("transfer-encoding", ""))
t.addEntry(pair("user-agent", ""))
t.addEntry(pair("vary", ""))
t.addEntry(pair("via", ""))
t.addEntry(pair("www-authenticate", ""))
return t
} }
var huffmanCodes = [256]uint32{ var huffmanCodes = [256]uint32{
......
...@@ -13,7 +13,8 @@ ...@@ -13,7 +13,8 @@
// See https://http2.github.io/ for more information on HTTP/2. // See https://http2.github.io/ for more information on HTTP/2.
// //
// See https://http2.golang.org/ for a test server running this code. // See https://http2.golang.org/ for a test server running this code.
package http2 //
package http2 // import "golang.org/x/net/http2"
import ( import (
"bufio" "bufio"
...@@ -35,6 +36,7 @@ var ( ...@@ -35,6 +36,7 @@ var (
VerboseLogs bool VerboseLogs bool
logFrameWrites bool logFrameWrites bool
logFrameReads bool logFrameReads bool
inTests bool
) )
func init() { func init() {
...@@ -76,13 +78,23 @@ var ( ...@@ -76,13 +78,23 @@ var (
type streamState int type streamState int
// HTTP/2 stream states.
//
// See http://tools.ietf.org/html/rfc7540#section-5.1.
//
// For simplicity, the server code merges "reserved (local)" into
// "half-closed (remote)". This is one less state transition to track.
// The only downside is that we send PUSH_PROMISEs slightly less
// liberally than allowable. More discussion here:
// https://lists.w3.org/Archives/Public/ietf-http-wg/2016JulSep/0599.html
//
// "reserved (remote)" is omitted since the client code does not
// support server push.
const ( const (
stateIdle streamState = iota stateIdle streamState = iota
stateOpen stateOpen
stateHalfClosedLocal stateHalfClosedLocal
stateHalfClosedRemote stateHalfClosedRemote
stateResvLocal
stateResvRemote
stateClosed stateClosed
) )
...@@ -91,8 +103,6 @@ var stateName = [...]string{ ...@@ -91,8 +103,6 @@ var stateName = [...]string{
stateOpen: "Open", stateOpen: "Open",
stateHalfClosedLocal: "HalfClosedLocal", stateHalfClosedLocal: "HalfClosedLocal",
stateHalfClosedRemote: "HalfClosedRemote", stateHalfClosedRemote: "HalfClosedRemote",
stateResvLocal: "ResvLocal",
stateResvRemote: "ResvRemote",
stateClosed: "Closed", stateClosed: "Closed",
} }
...@@ -252,14 +262,27 @@ func newBufferedWriter(w io.Writer) *bufferedWriter { ...@@ -252,14 +262,27 @@ func newBufferedWriter(w io.Writer) *bufferedWriter {
return &bufferedWriter{w: w} return &bufferedWriter{w: w}
} }
// bufWriterPoolBufferSize is the size of bufio.Writer's
// buffers created using bufWriterPool.
//
// TODO: pick a less arbitrary value? this is a bit under
// (3 x typical 1500 byte MTU) at least. Other than that,
// not much thought went into it.
const bufWriterPoolBufferSize = 4 << 10
var bufWriterPool = sync.Pool{ var bufWriterPool = sync.Pool{
New: func() interface{} { New: func() interface{} {
// TODO: pick something better? this is a bit under return bufio.NewWriterSize(nil, bufWriterPoolBufferSize)
// (3 x typical 1500 byte MTU) at least.
return bufio.NewWriterSize(nil, 4<<10)
}, },
} }
func (w *bufferedWriter) Available() int {
if w.bw == nil {
return bufWriterPoolBufferSize
}
return w.bw.Available()
}
func (w *bufferedWriter) Write(p []byte) (n int, err error) { func (w *bufferedWriter) Write(p []byte) (n int, err error) {
if w.bw == nil { if w.bw == nil {
bw := bufWriterPool.Get().(*bufio.Writer) bw := bufWriterPool.Get().(*bufio.Writer)
...@@ -342,10 +365,23 @@ func (s *sorter) Keys(h http.Header) []string { ...@@ -342,10 +365,23 @@ func (s *sorter) Keys(h http.Header) []string {
} }
func (s *sorter) SortStrings(ss []string) { func (s *sorter) SortStrings(ss []string) {
// Our sorter works on s.v, which sorter owners, so // Our sorter works on s.v, which sorter owns, so
// stash it away while we sort the user's buffer. // stash it away while we sort the user's buffer.
save := s.v save := s.v
s.v = ss s.v = ss
sort.Sort(s) sort.Sort(s)
s.v = save s.v = save
} }
// validPseudoPath reports whether v is a valid :path pseudo-header
// value. It must be either:
//
// *) a non-empty string starting with '/', but not with with "//",
// *) the string '*', for OPTIONS requests.
//
// For now this is only used a quick check for deciding when to clean
// up Opaque URLs before sending requests from the Transport.
// See golang.org/issue/16847
func validPseudoPath(v string) bool {
return (len(v) > 0 && v[0] == '/' && (len(v) == 1 || v[1] != '/')) || v == "*"
}
...@@ -7,11 +7,16 @@ ...@@ -7,11 +7,16 @@
package http2 package http2
import ( import (
"crypto/tls"
"net" "net"
"net/http" "net/http"
"time"
) )
type contextContext interface{} type contextContext interface {
Done() <-chan struct{}
Err() error
}
type fakeContext struct{} type fakeContext struct{}
...@@ -49,3 +54,34 @@ func contextWithCancel(ctx contextContext) (_ contextContext, cancel func()) { ...@@ -49,3 +54,34 @@ func contextWithCancel(ctx contextContext) (_ contextContext, cancel func()) {
func requestWithContext(req *http.Request, ctx contextContext) *http.Request { func requestWithContext(req *http.Request, ctx contextContext) *http.Request {
return req return req
} }
// temporary copy of Go 1.6's private tls.Config.clone:
func cloneTLSConfig(c *tls.Config) *tls.Config {
return &tls.Config{
Rand: c.Rand,
Time: c.Time,
Certificates: c.Certificates,
NameToCertificate: c.NameToCertificate,
GetCertificate: c.GetCertificate,
RootCAs: c.RootCAs,
NextProtos: c.NextProtos,
ServerName: c.ServerName,
ClientAuth: c.ClientAuth,
ClientCAs: c.ClientCAs,
InsecureSkipVerify: c.InsecureSkipVerify,
CipherSuites: c.CipherSuites,
PreferServerCipherSuites: c.PreferServerCipherSuites,
SessionTicketsDisabled: c.SessionTicketsDisabled,
SessionTicketKey: c.SessionTicketKey,
ClientSessionCache: c.ClientSessionCache,
MinVersion: c.MinVersion,
MaxVersion: c.MaxVersion,
CurvePreferences: c.CurvePreferences,
}
}
func (cc *ClientConn) Ping(ctx contextContext) error {
return cc.ping(ctx)
}
func (t *Transport) idleConnTimeout() time.Duration { return 0 }
// 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.8
package http2
import (
"io"
"net/http"
)
func configureServer18(h1 *http.Server, h2 *Server) error {
// No IdleTimeout to sync prior to Go 1.8.
return nil
}
func shouldLogPanic(panicValue interface{}) bool {
return panicValue != nil
}
func reqGetBody(req *http.Request) func() (io.ReadCloser, error) {
return nil
}
func reqBodyIsNoBody(io.ReadCloser) bool { return false }
...@@ -10,7 +10,7 @@ import ( ...@@ -10,7 +10,7 @@ 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 {
......
This diff is collapsed.
This diff is collapsed.
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"net/url"
"time" "time"
"golang.org/x/net/http2/hpack" "golang.org/x/net/http2/hpack"
...@@ -18,6 +19,11 @@ import ( ...@@ -18,6 +19,11 @@ import (
// writeFramer is implemented by any type that is used to write frames. // writeFramer is implemented by any type that is used to write frames.
type writeFramer interface { type writeFramer interface {
writeFrame(writeContext) error writeFrame(writeContext) error
// staysWithinBuffer reports whether this writer promises that
// it will only write less than or equal to size bytes, and it
// won't Flush the write context.
staysWithinBuffer(size int) bool
} }
// writeContext is the interface needed by the various frame writer // writeContext is the interface needed by the various frame writer
...@@ -39,9 +45,10 @@ type writeContext interface { ...@@ -39,9 +45,10 @@ type writeContext interface {
HeaderEncoder() (*hpack.Encoder, *bytes.Buffer) HeaderEncoder() (*hpack.Encoder, *bytes.Buffer)
} }
// endsStream reports whether the given frame writer w will locally // writeEndsStream reports whether w writes a frame that will transition
// close the stream. // the stream to a half-closed local state. This returns false for RST_STREAM,
func endsStream(w writeFramer) bool { // which closes the entire stream (not just the local half).
func writeEndsStream(w writeFramer) bool {
switch v := w.(type) { switch v := w.(type) {
case *writeData: case *writeData:
return v.endStream return v.endStream
...@@ -51,7 +58,7 @@ func endsStream(w writeFramer) bool { ...@@ -51,7 +58,7 @@ func endsStream(w writeFramer) bool {
// This can only happen if the caller reuses w after it's // This can only happen if the caller reuses w after it's
// been intentionally nil'ed out to prevent use. Keep this // been intentionally nil'ed out to prevent use. Keep this
// here to catch future refactoring breaking it. // here to catch future refactoring breaking it.
panic("endsStream called on nil writeFramer") panic("writeEndsStream called on nil writeFramer")
} }
return false return false
} }
...@@ -62,8 +69,16 @@ func (flushFrameWriter) writeFrame(ctx writeContext) error { ...@@ -62,8 +69,16 @@ func (flushFrameWriter) writeFrame(ctx writeContext) error {
return ctx.Flush() return ctx.Flush()
} }
func (flushFrameWriter) staysWithinBuffer(max int) bool { return false }
type writeSettings []Setting type writeSettings []Setting
func (s writeSettings) staysWithinBuffer(max int) bool {
const settingSize = 6 // uint16 + uint32
return frameHeaderLen+settingSize*len(s) <= max
}
func (s writeSettings) writeFrame(ctx writeContext) error { func (s writeSettings) writeFrame(ctx writeContext) error {
return ctx.Framer().WriteSettings([]Setting(s)...) return ctx.Framer().WriteSettings([]Setting(s)...)
} }
...@@ -83,6 +98,8 @@ func (p *writeGoAway) writeFrame(ctx writeContext) error { ...@@ -83,6 +98,8 @@ func (p *writeGoAway) writeFrame(ctx writeContext) error {
return err return err
} }
func (*writeGoAway) staysWithinBuffer(max int) bool { return false } // flushes
type writeData struct { type writeData struct {
streamID uint32 streamID uint32
p []byte p []byte
...@@ -97,6 +114,10 @@ func (w *writeData) writeFrame(ctx writeContext) error { ...@@ -97,6 +114,10 @@ func (w *writeData) writeFrame(ctx writeContext) error {
return ctx.Framer().WriteData(w.streamID, w.endStream, w.p) return ctx.Framer().WriteData(w.streamID, w.endStream, w.p)
} }
func (w *writeData) staysWithinBuffer(max int) bool {
return frameHeaderLen+len(w.p) <= max
}
// handlerPanicRST is the message sent from handler goroutines when // handlerPanicRST is the message sent from handler goroutines when
// the handler panics. // the handler panics.
type handlerPanicRST struct { type handlerPanicRST struct {
...@@ -107,22 +128,57 @@ func (hp handlerPanicRST) writeFrame(ctx writeContext) error { ...@@ -107,22 +128,57 @@ func (hp handlerPanicRST) writeFrame(ctx writeContext) error {
return ctx.Framer().WriteRSTStream(hp.StreamID, ErrCodeInternal) return ctx.Framer().WriteRSTStream(hp.StreamID, ErrCodeInternal)
} }
func (hp handlerPanicRST) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max }
func (se StreamError) writeFrame(ctx writeContext) error { func (se StreamError) writeFrame(ctx writeContext) error {
return ctx.Framer().WriteRSTStream(se.StreamID, se.Code) return ctx.Framer().WriteRSTStream(se.StreamID, se.Code)
} }
func (se StreamError) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max }
type writePingAck struct{ pf *PingFrame } type writePingAck struct{ pf *PingFrame }
func (w writePingAck) writeFrame(ctx writeContext) error { func (w writePingAck) writeFrame(ctx writeContext) error {
return ctx.Framer().WritePing(true, w.pf.Data) return ctx.Framer().WritePing(true, w.pf.Data)
} }
func (w writePingAck) staysWithinBuffer(max int) bool { return frameHeaderLen+len(w.pf.Data) <= max }
type writeSettingsAck struct{} type writeSettingsAck struct{}
func (writeSettingsAck) writeFrame(ctx writeContext) error { func (writeSettingsAck) writeFrame(ctx writeContext) error {
return ctx.Framer().WriteSettingsAck() return ctx.Framer().WriteSettingsAck()
} }
func (writeSettingsAck) staysWithinBuffer(max int) bool { return frameHeaderLen <= max }
// splitHeaderBlock splits headerBlock into fragments so that each fragment fits
// in a single frame, then calls fn for each fragment. firstFrag/lastFrag are true
// for the first/last fragment, respectively.
func splitHeaderBlock(ctx writeContext, headerBlock []byte, fn func(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error) error {
// For now we're lazy and just pick the minimum MAX_FRAME_SIZE
// that all peers must support (16KB). Later we could care
// more and send larger frames if the peer advertised it, but
// there's little point. Most headers are small anyway (so we
// generally won't have CONTINUATION frames), and extra frames
// only waste 9 bytes anyway.
const maxFrameSize = 16384
first := true
for len(headerBlock) > 0 {
frag := headerBlock
if len(frag) > maxFrameSize {
frag = frag[:maxFrameSize]
}
headerBlock = headerBlock[len(frag):]
if err := fn(ctx, frag, first, len(headerBlock) == 0); err != nil {
return err
}
first = false
}
return nil
}
// writeResHeaders is a request to write a HEADERS and 0+ CONTINUATION frames // writeResHeaders is a request to write a HEADERS and 0+ CONTINUATION frames
// for HTTP response headers or trailers from a server handler. // for HTTP response headers or trailers from a server handler.
type writeResHeaders struct { type writeResHeaders struct {
...@@ -144,6 +200,17 @@ func encKV(enc *hpack.Encoder, k, v string) { ...@@ -144,6 +200,17 @@ func encKV(enc *hpack.Encoder, k, v string) {
enc.WriteField(hpack.HeaderField{Name: k, Value: v}) enc.WriteField(hpack.HeaderField{Name: k, Value: v})
} }
func (w *writeResHeaders) staysWithinBuffer(max int) bool {
// TODO: this is a common one. It'd be nice to return true
// here and get into the fast path if we could be clever and
// calculate the size fast enough, or at least a conservative
// uppper bound that usually fires. (Maybe if w.h and
// w.trailers are nil, so we don't need to enumerate it.)
// Otherwise I'm afraid that just calculating the length to
// answer this question would be slower than the ~2µs benefit.
return false
}
func (w *writeResHeaders) writeFrame(ctx writeContext) error { func (w *writeResHeaders) writeFrame(ctx writeContext) error {
enc, buf := ctx.HeaderEncoder() enc, buf := ctx.HeaderEncoder()
buf.Reset() buf.Reset()
...@@ -169,39 +236,69 @@ func (w *writeResHeaders) writeFrame(ctx writeContext) error { ...@@ -169,39 +236,69 @@ func (w *writeResHeaders) writeFrame(ctx writeContext) error {
panic("unexpected empty hpack") panic("unexpected empty hpack")
} }
// For now we're lazy and just pick the minimum MAX_FRAME_SIZE return splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock)
// that all peers must support (16KB). Later we could care }
// more and send larger frames if the peer advertised it, but
// there's little point. Most headers are small anyway (so we
// generally won't have CONTINUATION frames), and extra frames
// only waste 9 bytes anyway.
const maxFrameSize = 16384
first := true func (w *writeResHeaders) writeHeaderBlock(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error {
for len(headerBlock) > 0 { if firstFrag {
frag := headerBlock return ctx.Framer().WriteHeaders(HeadersFrameParam{
if len(frag) > maxFrameSize { StreamID: w.streamID,
frag = frag[:maxFrameSize] BlockFragment: frag,
} EndStream: w.endStream,
headerBlock = headerBlock[len(frag):] EndHeaders: lastFrag,
endHeaders := len(headerBlock) == 0 })
var err error } else {
if first { return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag)
first = false }
err = ctx.Framer().WriteHeaders(HeadersFrameParam{ }
StreamID: w.streamID,
BlockFragment: frag, // writePushPromise is a request to write a PUSH_PROMISE and 0+ CONTINUATION frames.
EndStream: w.endStream, type writePushPromise struct {
EndHeaders: endHeaders, streamID uint32 // pusher stream
}) method string // for :method
} else { url *url.URL // for :scheme, :authority, :path
err = ctx.Framer().WriteContinuation(w.streamID, endHeaders, frag) h http.Header
}
if err != nil { // Creates an ID for a pushed stream. This runs on serveG just before
return err // the frame is written. The returned ID is copied to promisedID.
} allocatePromisedID func() (uint32, error)
promisedID uint32
}
func (w *writePushPromise) staysWithinBuffer(max int) bool {
// TODO: see writeResHeaders.staysWithinBuffer
return false
}
func (w *writePushPromise) writeFrame(ctx writeContext) error {
enc, buf := ctx.HeaderEncoder()
buf.Reset()
encKV(enc, ":method", w.method)
encKV(enc, ":scheme", w.url.Scheme)
encKV(enc, ":authority", w.url.Host)
encKV(enc, ":path", w.url.RequestURI())
encodeHeaders(enc, w.h, nil)
headerBlock := buf.Bytes()
if len(headerBlock) == 0 {
panic("unexpected empty hpack")
}
return splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock)
}
func (w *writePushPromise) writeHeaderBlock(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error {
if firstFrag {
return ctx.Framer().WritePushPromise(PushPromiseParam{
StreamID: w.streamID,
PromiseID: w.promisedID,
BlockFragment: frag,
EndHeaders: lastFrag,
})
} else {
return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag)
} }
return nil
} }
type write100ContinueHeadersFrame struct { type write100ContinueHeadersFrame struct {
...@@ -220,15 +317,24 @@ func (w write100ContinueHeadersFrame) writeFrame(ctx writeContext) error { ...@@ -220,15 +317,24 @@ func (w write100ContinueHeadersFrame) writeFrame(ctx writeContext) error {
}) })
} }
func (w write100ContinueHeadersFrame) staysWithinBuffer(max int) bool {
// Sloppy but conservative:
return 9+2*(len(":status")+len("100")) <= max
}
type writeWindowUpdate struct { type writeWindowUpdate struct {
streamID uint32 // or 0 for conn-level streamID uint32 // or 0 for conn-level
n uint32 n uint32
} }
func (wu writeWindowUpdate) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max }
func (wu writeWindowUpdate) writeFrame(ctx writeContext) error { func (wu writeWindowUpdate) writeFrame(ctx writeContext) error {
return ctx.Framer().WriteWindowUpdate(wu.streamID, wu.n) return ctx.Framer().WriteWindowUpdate(wu.streamID, wu.n)
} }
// encodeHeaders encodes an http.Header. If keys is not nil, then (k, h[k])
// is encoded only only if k is in keys.
func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) { func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) {
if keys == nil { if keys == nil {
sorter := sorterPool.Get().(*sorter) sorter := sorterPool.Get().(*sorter)
......
This diff is collapsed.
This diff is collapsed.
// 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 "math"
// NewRandomWriteScheduler constructs a WriteScheduler that ignores HTTP/2
// priorities. Control frames like SETTINGS and PING are written before DATA
// frames, but if no control frames are queued and multiple streams have queued
// HEADERS or DATA frames, Pop selects a ready stream arbitrarily.
func NewRandomWriteScheduler() WriteScheduler {
return &randomWriteScheduler{sq: make(map[uint32]*writeQueue)}
}
type randomWriteScheduler struct {
// zero are frames not associated with a specific stream.
zero writeQueue
// sq contains the stream-specific queues, keyed by stream ID.
// When a stream is idle or closed, it's deleted from the map.
sq map[uint32]*writeQueue
// pool of empty queues for reuse.
queuePool writeQueuePool
}
func (ws *randomWriteScheduler) OpenStream(streamID uint32, options OpenStreamOptions) {
// no-op: idle streams are not tracked
}
func (ws *randomWriteScheduler) CloseStream(streamID uint32) {
q, ok := ws.sq[streamID]
if !ok {
return
}
delete(ws.sq, streamID)
ws.queuePool.put(q)
}
func (ws *randomWriteScheduler) AdjustStream(streamID uint32, priority PriorityParam) {
// no-op: priorities are ignored
}
func (ws *randomWriteScheduler) Push(wr FrameWriteRequest) {
id := wr.StreamID()
if id == 0 {
ws.zero.push(wr)
return
}
q, ok := ws.sq[id]
if !ok {
q = ws.queuePool.get()
ws.sq[id] = q
}
q.push(wr)
}
func (ws *randomWriteScheduler) Pop() (FrameWriteRequest, bool) {
// Control frames first.
if !ws.zero.empty() {
return ws.zero.shift(), true
}
// Iterate over all non-idle streams until finding one that can be consumed.
for _, q := range ws.sq {
if wr, ok := q.consume(math.MaxInt32); ok {
return wr, true
}
}
return FrameWriteRequest{}, false
}
This diff is collapsed.
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
// 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.
package idna
// This file implements the Punycode algorithm from RFC 3492.
import (
"math"
"strings"
"unicode/utf8"
)
// These parameter values are specified in section 5.
//
// All computation is done with int32s, so that overflow behavior is identical
// regardless of whether int is 32-bit or 64-bit.
const (
base int32 = 36
damp int32 = 700
initialBias int32 = 72
initialN int32 = 128
skew int32 = 38
tmax int32 = 26
tmin int32 = 1
)
func punyError(s string) error { return &labelError{s, "A3"} }
// decode decodes a string as specified in section 6.2.
func decode(encoded string) (string, error) {
if encoded == "" {
return "", nil
}
pos := 1 + strings.LastIndex(encoded, "-")
if pos == 1 {
return "", punyError(encoded)
}
if pos == len(encoded) {
return encoded[:len(encoded)-1], nil
}
output := make([]rune, 0, len(encoded))
if pos != 0 {
for _, r := range encoded[:pos-1] {
output = append(output, r)
}
}
i, n, bias := int32(0), initialN, initialBias
for pos < len(encoded) {
oldI, w := i, int32(1)
for k := base; ; k += base {
if pos == len(encoded) {
return "", punyError(encoded)
}
digit, ok := decodeDigit(encoded[pos])
if !ok {
return "", punyError(encoded)
}
pos++
i += digit * w
if i < 0 {
return "", punyError(encoded)
}
t := k - bias
if t < tmin {
t = tmin
} else if t > tmax {
t = tmax
}
if digit < t {
break
}
w *= base - t
if w >= math.MaxInt32/base {
return "", punyError(encoded)
}
}
x := int32(len(output) + 1)
bias = adapt(i-oldI, x, oldI == 0)
n += i / x
i %= x
if n > utf8.MaxRune || len(output) >= 1024 {
return "", punyError(encoded)
}
output = append(output, 0)
copy(output[i+1:], output[i:])
output[i] = n
i++
}
return string(output), nil
}
// encode encodes a string as specified in section 6.3 and prepends prefix to
// the result.
//
// The "while h < length(input)" line in the specification becomes "for
// remaining != 0" in the Go code, because len(s) in Go is in bytes, not runes.
func encode(prefix, s string) (string, error) {
output := make([]byte, len(prefix), len(prefix)+1+2*len(s))
copy(output, prefix)
delta, n, bias := int32(0), initialN, initialBias
b, remaining := int32(0), int32(0)
for _, r := range s {
if r < 0x80 {
b++
output = append(output, byte(r))
} else {
remaining++
}
}
h := b
if b > 0 {
output = append(output, '-')
}
for remaining != 0 {
m := int32(0x7fffffff)
for _, r := range s {
if m > r && r >= n {
m = r
}
}
delta += (m - n) * (h + 1)
if delta < 0 {
return "", punyError(s)
}
n = m
for _, r := range s {
if r < n {
delta++
if delta < 0 {
return "", punyError(s)
}
continue
}
if r > n {
continue
}
q := delta
for k := base; ; k += base {
t := k - bias
if t < tmin {
t = tmin
} else if t > tmax {
t = tmax
}
if q < t {
break
}
output = append(output, encodeDigit(t+(q-t)%(base-t)))
q = (q - t) / (base - t)
}
output = append(output, encodeDigit(q))
bias = adapt(delta, h+1, h == b)
delta = 0
h++
remaining--
}
delta++
n++
}
return string(output), nil
}
func decodeDigit(x byte) (digit int32, ok bool) {
switch {
case '0' <= x && x <= '9':
return int32(x - ('0' - 26)), true
case 'A' <= x && x <= 'Z':
return int32(x - 'A'), true
case 'a' <= x && x <= 'z':
return int32(x - 'a'), true
}
return 0, false
}
func encodeDigit(digit int32) byte {
switch {
case 0 <= digit && digit < 26:
return byte(digit + 'a')
case 26 <= digit && digit < 36:
return byte(digit + ('0' - 26))
}
panic("idna: internal error in punycode encoding")
}
// adapt is the bias adaptation function specified in section 6.1.
func adapt(delta, numPoints int32, firstTime bool) int32 {
if firstTime {
delta /= damp
} else {
delta /= 2
}
delta += delta / numPoints
k := int32(0)
for delta > ((base-tmin)*tmax)/2 {
delta /= base - tmin
k += base
}
return k + (base-tmin+1)*delta/(delta+skew)
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -371,7 +371,7 @@ func (ts *timeSeries) ComputeRange(start, finish time.Time, num int) []Observabl ...@@ -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)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google 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,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment