first grpc implementation
This commit is contained in:
parent
c0b7c2b5fd
commit
d5e48e86b6
14
Makefile
14
Makefile
@ -42,3 +42,17 @@ install-tools:
|
|||||||
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \
|
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \
|
||||||
google.golang.org/protobuf/cmd/protoc-gen-go \
|
google.golang.org/protobuf/cmd/protoc-gen-go \
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc
|
||||||
|
|
||||||
|
.PHONY: protoc-gen
|
||||||
|
protoc-gen:
|
||||||
|
protoc \
|
||||||
|
--proto_path=api/proto/v1 \
|
||||||
|
--go_opt=paths=source_relative \
|
||||||
|
--go-grpc_opt=paths=source_relative \
|
||||||
|
--go-grpc_out=pkg/api/v1 \
|
||||||
|
--go_out=pkg/api/v1 \
|
||||||
|
--grpc-gateway_opt=logtostderr=true \
|
||||||
|
--grpc-gateway_opt=paths=source_relative \
|
||||||
|
--grpc-gateway_opt=generate_unbound_methods=true \
|
||||||
|
--grpc-gateway_out=pkg/api/v1 \
|
||||||
|
api/proto/v1/schnutibox.proto
|
||||||
|
15
api/proto/v1/schnutibox.proto
Normal file
15
api/proto/v1/schnutibox.proto
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
option go_package = "go.xsfx.dev/schnutibox/pkg/api/v1";
|
||||||
|
|
||||||
|
service Identifier {
|
||||||
|
rpc Identify (IdentifyRequest) returns (Tracks) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
message IdentifyRequest {
|
||||||
|
string id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Tracks {
|
||||||
|
string name = 1;
|
||||||
|
repeated string URIS = 2;
|
||||||
|
}
|
@ -67,6 +67,7 @@ func initConfig(fatal bool) {
|
|||||||
// Defaults.
|
// Defaults.
|
||||||
viper.SetDefault("box.hostname", "localhost")
|
viper.SetDefault("box.hostname", "localhost")
|
||||||
viper.SetDefault("box.port", 9999)
|
viper.SetDefault("box.port", 9999)
|
||||||
|
viper.SetDefault("box.grpc", 9998)
|
||||||
viper.SetDefault("mpd.hostname", "localhost")
|
viper.SetDefault("mpd.hostname", "localhost")
|
||||||
viper.SetDefault("mpd.port", 6600)
|
viper.SetDefault("mpd.port", 6600)
|
||||||
viper.SetDefault("reader.dev", "/dev/hidraw0")
|
viper.SetDefault("reader.dev", "/dev/hidraw0")
|
||||||
|
1
go.mod
1
go.mod
@ -19,6 +19,7 @@ require (
|
|||||||
github.com/tmc/scp v0.0.0-20170824174625-f7b48647feef
|
github.com/tmc/scp v0.0.0-20170824174625-f7b48647feef
|
||||||
go.xsfx.dev/logginghandler v0.0.4
|
go.xsfx.dev/logginghandler v0.0.4
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||||
|
google.golang.org/grpc v1.37.0
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0
|
||||||
google.golang.org/protobuf v1.26.0
|
google.golang.org/protobuf v1.26.0
|
||||||
gotest.tools/v3 v3.0.3 // indirect
|
gotest.tools/v3 v3.0.3 // indirect
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
//nolint:gochecknoglobals,goerr113
|
//nolint:gochecknoglobals,goerr113
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
api "go.xsfx.dev/schnutibox/pkg/api/v1"
|
||||||
|
)
|
||||||
|
|
||||||
var Cfg Config
|
var Cfg Config
|
||||||
|
|
||||||
@ -15,6 +19,7 @@ type Config struct {
|
|||||||
Box struct {
|
Box struct {
|
||||||
Hostname string `mapstructure:"Hostname"`
|
Hostname string `mapstructure:"Hostname"`
|
||||||
Port int `mapstructure:"Port"`
|
Port int `mapstructure:"Port"`
|
||||||
|
Grpc int `mapstructure:"Grpc"`
|
||||||
} `mapstructure:"Box"`
|
} `mapstructure:"Box"`
|
||||||
|
|
||||||
// MPD contains the connection details for the Music Player Daemon.
|
// MPD contains the connection details for the Music Player Daemon.
|
||||||
@ -29,10 +34,7 @@ type Config struct {
|
|||||||
} `mapstructure:"Meta"`
|
} `mapstructure:"Meta"`
|
||||||
|
|
||||||
// Tracks contains all RFID's and its MPD URLs.
|
// Tracks contains all RFID's and its MPD URLs.
|
||||||
Tracks map[string]struct {
|
Tracks map[string]api.Tracks `mapstructure:"Tracks"`
|
||||||
Name string `mapstructure:"Name"`
|
|
||||||
URIS []string `mapstructure:"URIS"`
|
|
||||||
} `mapstructure:"Tracks"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) Require() error {
|
func (c *Config) Require() error {
|
||||||
|
219
pkg/api/v1/schnutibox.pb.go
Normal file
219
pkg/api/v1/schnutibox.pb.go
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.26.0
|
||||||
|
// protoc v3.15.8
|
||||||
|
// source: schnutibox.proto
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type IdentifyRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *IdentifyRequest) Reset() {
|
||||||
|
*x = IdentifyRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_schnutibox_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *IdentifyRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*IdentifyRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *IdentifyRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_schnutibox_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use IdentifyRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*IdentifyRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_schnutibox_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *IdentifyRequest) GetId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Id
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tracks struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
URIS []string `protobuf:"bytes,2,rep,name=URIS,proto3" json:"URIS,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Tracks) Reset() {
|
||||||
|
*x = Tracks{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_schnutibox_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Tracks) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Tracks) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Tracks) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_schnutibox_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Tracks.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Tracks) Descriptor() ([]byte, []int) {
|
||||||
|
return file_schnutibox_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Tracks) GetName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Tracks) GetURIS() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.URIS
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_schnutibox_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_schnutibox_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x10, 0x73, 0x63, 0x68, 0x6e, 0x75, 0x74, 0x69, 0x62, 0x6f, 0x78, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x22, 0x21, 0x0a, 0x0f, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65,
|
||||||
|
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||||
|
0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x30, 0x0a, 0x06, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x12,
|
||||||
|
0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
|
||||||
|
0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x55, 0x52, 0x49, 0x53, 0x18, 0x02, 0x20, 0x03, 0x28,
|
||||||
|
0x09, 0x52, 0x04, 0x55, 0x52, 0x49, 0x53, 0x32, 0x35, 0x0a, 0x0a, 0x49, 0x64, 0x65, 0x6e, 0x74,
|
||||||
|
0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x08, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66,
|
||||||
|
0x79, 0x12, 0x10, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75,
|
||||||
|
0x65, 0x73, 0x74, 0x1a, 0x07, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x22, 0x00, 0x42, 0x23,
|
||||||
|
0x5a, 0x21, 0x67, 0x6f, 0x2e, 0x78, 0x73, 0x66, 0x78, 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x73, 0x63,
|
||||||
|
0x68, 0x6e, 0x75, 0x74, 0x69, 0x62, 0x6f, 0x78, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69,
|
||||||
|
0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_schnutibox_proto_rawDescOnce sync.Once
|
||||||
|
file_schnutibox_proto_rawDescData = file_schnutibox_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_schnutibox_proto_rawDescGZIP() []byte {
|
||||||
|
file_schnutibox_proto_rawDescOnce.Do(func() {
|
||||||
|
file_schnutibox_proto_rawDescData = protoimpl.X.CompressGZIP(file_schnutibox_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_schnutibox_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_schnutibox_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||||
|
var file_schnutibox_proto_goTypes = []interface{}{
|
||||||
|
(*IdentifyRequest)(nil), // 0: IdentifyRequest
|
||||||
|
(*Tracks)(nil), // 1: Tracks
|
||||||
|
}
|
||||||
|
var file_schnutibox_proto_depIdxs = []int32{
|
||||||
|
0, // 0: Identifier.Identify:input_type -> IdentifyRequest
|
||||||
|
1, // 1: Identifier.Identify:output_type -> Tracks
|
||||||
|
1, // [1:2] is the sub-list for method output_type
|
||||||
|
0, // [0:1] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_schnutibox_proto_init() }
|
||||||
|
func file_schnutibox_proto_init() {
|
||||||
|
if File_schnutibox_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_schnutibox_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*IdentifyRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_schnutibox_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Tracks); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_schnutibox_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 2,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 1,
|
||||||
|
},
|
||||||
|
GoTypes: file_schnutibox_proto_goTypes,
|
||||||
|
DependencyIndexes: file_schnutibox_proto_depIdxs,
|
||||||
|
MessageInfos: file_schnutibox_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_schnutibox_proto = out.File
|
||||||
|
file_schnutibox_proto_rawDesc = nil
|
||||||
|
file_schnutibox_proto_goTypes = nil
|
||||||
|
file_schnutibox_proto_depIdxs = nil
|
||||||
|
}
|
167
pkg/api/v1/schnutibox.pb.gw.go
Normal file
167
pkg/api/v1/schnutibox.pb.gw.go
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
|
||||||
|
// source: schnutibox.proto
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package v1 is a reverse proxy.
|
||||||
|
|
||||||
|
It translates gRPC into RESTful JSON APIs.
|
||||||
|
*/
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||||
|
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Suppress "imported and not used" errors
|
||||||
|
var _ codes.Code
|
||||||
|
var _ io.Reader
|
||||||
|
var _ status.Status
|
||||||
|
var _ = runtime.String
|
||||||
|
var _ = utilities.NewDoubleArray
|
||||||
|
var _ = metadata.Join
|
||||||
|
|
||||||
|
func request_Identifier_Identify_0(ctx context.Context, marshaler runtime.Marshaler, client IdentifierClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq IdentifyRequest
|
||||||
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
|
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||||
|
if berr != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||||
|
}
|
||||||
|
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := client.Identify(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func local_request_Identifier_Identify_0(ctx context.Context, marshaler runtime.Marshaler, server IdentifierServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq IdentifyRequest
|
||||||
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
|
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||||
|
if berr != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||||
|
}
|
||||||
|
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := server.Identify(ctx, &protoReq)
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterIdentifierHandlerServer registers the http handlers for service Identifier to "mux".
|
||||||
|
// UnaryRPC :call IdentifierServer directly.
|
||||||
|
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
|
||||||
|
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterIdentifierHandlerFromEndpoint instead.
|
||||||
|
func RegisterIdentifierHandlerServer(ctx context.Context, mux *runtime.ServeMux, server IdentifierServer) error {
|
||||||
|
|
||||||
|
mux.Handle("POST", pattern_Identifier_Identify_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
|
defer cancel()
|
||||||
|
var stream runtime.ServerTransportStream
|
||||||
|
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||||
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
|
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/.Identifier/Identify")
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, md, err := local_request_Identifier_Identify_0(rctx, inboundMarshaler, server, req, pathParams)
|
||||||
|
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||||
|
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_Identifier_Identify_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterIdentifierHandlerFromEndpoint is same as RegisterIdentifierHandler but
|
||||||
|
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
|
||||||
|
func RegisterIdentifierHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
|
||||||
|
conn, err := grpc.Dial(endpoint, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
if cerr := conn.Close(); cerr != nil {
|
||||||
|
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
if cerr := conn.Close(); cerr != nil {
|
||||||
|
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return RegisterIdentifierHandler(ctx, mux, conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterIdentifierHandler registers the http handlers for service Identifier to "mux".
|
||||||
|
// The handlers forward requests to the grpc endpoint over "conn".
|
||||||
|
func RegisterIdentifierHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
|
||||||
|
return RegisterIdentifierHandlerClient(ctx, mux, NewIdentifierClient(conn))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterIdentifierHandlerClient registers the http handlers for service Identifier
|
||||||
|
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "IdentifierClient".
|
||||||
|
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "IdentifierClient"
|
||||||
|
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
|
||||||
|
// "IdentifierClient" to call the correct interceptors.
|
||||||
|
func RegisterIdentifierHandlerClient(ctx context.Context, mux *runtime.ServeMux, client IdentifierClient) error {
|
||||||
|
|
||||||
|
mux.Handle("POST", pattern_Identifier_Identify_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
|
defer cancel()
|
||||||
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
|
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/.Identifier/Identify")
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, md, err := request_Identifier_Identify_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||||
|
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_Identifier_Identify_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
pattern_Identifier_Identify_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"Identifier", "Identify"}, ""))
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
forward_Identifier_Identify_0 = runtime.ForwardResponseMessage
|
||||||
|
)
|
101
pkg/api/v1/schnutibox_grpc.pb.go
Normal file
101
pkg/api/v1/schnutibox_grpc.pb.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.32.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
|
// IdentifierClient is the client API for Identifier service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type IdentifierClient interface {
|
||||||
|
Identify(ctx context.Context, in *IdentifyRequest, opts ...grpc.CallOption) (*Tracks, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type identifierClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIdentifierClient(cc grpc.ClientConnInterface) IdentifierClient {
|
||||||
|
return &identifierClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *identifierClient) Identify(ctx context.Context, in *IdentifyRequest, opts ...grpc.CallOption) (*Tracks, error) {
|
||||||
|
out := new(Tracks)
|
||||||
|
err := c.cc.Invoke(ctx, "/Identifier/Identify", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IdentifierServer is the server API for Identifier service.
|
||||||
|
// All implementations must embed UnimplementedIdentifierServer
|
||||||
|
// for forward compatibility
|
||||||
|
type IdentifierServer interface {
|
||||||
|
Identify(context.Context, *IdentifyRequest) (*Tracks, error)
|
||||||
|
mustEmbedUnimplementedIdentifierServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedIdentifierServer must be embedded to have forward compatible implementations.
|
||||||
|
type UnimplementedIdentifierServer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedIdentifierServer) Identify(context.Context, *IdentifyRequest) (*Tracks, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method Identify not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedIdentifierServer) mustEmbedUnimplementedIdentifierServer() {}
|
||||||
|
|
||||||
|
// UnsafeIdentifierServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to IdentifierServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeIdentifierServer interface {
|
||||||
|
mustEmbedUnimplementedIdentifierServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterIdentifierServer(s grpc.ServiceRegistrar, srv IdentifierServer) {
|
||||||
|
s.RegisterService(&Identifier_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Identifier_Identify_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(IdentifyRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(IdentifierServer).Identify(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/Identifier/Identify",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(IdentifierServer).Identify(ctx, req.(*IdentifyRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identifier_ServiceDesc is the grpc.ServiceDesc for Identifier service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var Identifier_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "Identifier",
|
||||||
|
HandlerType: (*IdentifierServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "Identify",
|
||||||
|
Handler: _Identifier_Identify_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{},
|
||||||
|
Metadata: "schnutibox.proto",
|
||||||
|
}
|
17
pkg/api/v1/server.go
Normal file
17
pkg/api/v1/server.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
UnimplementedIdentifierServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) Identify(ctx context.Context, r *IdentifyRequest) (*Tracks, error) {
|
||||||
|
log.Info().Msg("Tracks und so")
|
||||||
|
|
||||||
|
return &Tracks{}, nil
|
||||||
|
}
|
@ -1,17 +1,23 @@
|
|||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"go.xsfx.dev/logginghandler"
|
"go.xsfx.dev/logginghandler"
|
||||||
assets "go.xsfx.dev/schnutibox/assets/web"
|
assets "go.xsfx.dev/schnutibox/assets/web"
|
||||||
"go.xsfx.dev/schnutibox/internal/config"
|
"go.xsfx.dev/schnutibox/internal/config"
|
||||||
|
api "go.xsfx.dev/schnutibox/pkg/api/v1"
|
||||||
"go.xsfx.dev/schnutibox/pkg/sselog"
|
"go.xsfx.dev/schnutibox/pkg/sselog"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/reflection"
|
||||||
)
|
)
|
||||||
|
|
||||||
func root(w http.ResponseWriter, r *http.Request) {
|
func root(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -33,23 +39,56 @@ func root(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func gw(conn string) *runtime.ServeMux {
|
||||||
|
ctx := context.Background()
|
||||||
|
gopts := []grpc.DialOption{grpc.WithInsecure()}
|
||||||
|
s := grpc.NewServer()
|
||||||
|
|
||||||
|
api.RegisterIdentifierServer(s, api.Server{})
|
||||||
|
reflection.Register(s)
|
||||||
|
|
||||||
|
lis, err := net.Listen("tcp", conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("could not listen")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info().Msgf("serving GRPC on %s...", conn)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
log.Fatal().Err(s.Serve(lis))
|
||||||
|
}()
|
||||||
|
|
||||||
|
gwmux := runtime.NewServeMux()
|
||||||
|
if err := api.RegisterIdentifierHandlerFromEndpoint(ctx, gwmux, conn, gopts); err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("could not register grpc endpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
return gwmux
|
||||||
|
}
|
||||||
|
|
||||||
func Run(command *cobra.Command, args []string) {
|
func Run(command *cobra.Command, args []string) {
|
||||||
// Create host string for serving web.
|
// Create host string for serving web.
|
||||||
l := fmt.Sprintf("%s:%d", config.Cfg.Box.Hostname, config.Cfg.Box.Port)
|
lh := fmt.Sprintf("%s:%d", config.Cfg.Box.Hostname, config.Cfg.Box.Port)
|
||||||
|
lg := fmt.Sprintf("%s:%d", config.Cfg.Box.Hostname, config.Cfg.Box.Grpc)
|
||||||
|
|
||||||
|
// GRPC.
|
||||||
|
|
||||||
// Define http handlers.
|
// Define http handlers.
|
||||||
http.Handle("/", logginghandler.Handler(http.HandlerFunc(root)))
|
mux := http.NewServeMux()
|
||||||
http.Handle("/log", logginghandler.Handler(http.HandlerFunc(sselog.LogHandler)))
|
mux.Handle("/", logginghandler.Handler(http.HandlerFunc(root)))
|
||||||
http.Handle(
|
mux.Handle("/log", logginghandler.Handler(http.HandlerFunc(sselog.LogHandler)))
|
||||||
|
mux.Handle(
|
||||||
"/static/",
|
"/static/",
|
||||||
logginghandler.Handler(
|
logginghandler.Handler(
|
||||||
http.StripPrefix("/static/", http.FileServer(http.FS(assets.Files))),
|
http.StripPrefix("/static/", http.FileServer(http.FS(assets.Files))),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
http.Handle("/metrics", promhttp.Handler())
|
mux.Handle("/metrics", promhttp.Handler())
|
||||||
|
|
||||||
// Serving this thing.
|
mux.Handle("/api/", http.StripPrefix("/api", gw(lg)))
|
||||||
log.Info().Msgf("serving on %s...", l)
|
|
||||||
|
|
||||||
log.Fatal().Err(http.ListenAndServe(l, nil)).Msg("goodbye")
|
// Serving http.
|
||||||
|
log.Info().Msgf("serving HTTP on %s...", lh)
|
||||||
|
|
||||||
|
log.Fatal().Err(http.ListenAndServe(lh, logginghandler.Handler(mux))).Msg("goodbye")
|
||||||
}
|
}
|
||||||
|
179
vendor/github.com/golang/protobuf/ptypes/any.go
generated
vendored
Normal file
179
vendor/github.com/golang/protobuf/ptypes/any.go
generated
vendored
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
// 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 ptypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
"google.golang.org/protobuf/reflect/protoregistry"
|
||||||
|
|
||||||
|
anypb "github.com/golang/protobuf/ptypes/any"
|
||||||
|
)
|
||||||
|
|
||||||
|
const urlPrefix = "type.googleapis.com/"
|
||||||
|
|
||||||
|
// AnyMessageName returns the message name contained in an anypb.Any message.
|
||||||
|
// Most type assertions should use the Is function instead.
|
||||||
|
//
|
||||||
|
// Deprecated: Call the any.MessageName method instead.
|
||||||
|
func AnyMessageName(any *anypb.Any) (string, error) {
|
||||||
|
name, err := anyMessageName(any)
|
||||||
|
return string(name), err
|
||||||
|
}
|
||||||
|
func anyMessageName(any *anypb.Any) (protoreflect.FullName, error) {
|
||||||
|
if any == nil {
|
||||||
|
return "", fmt.Errorf("message is nil")
|
||||||
|
}
|
||||||
|
name := protoreflect.FullName(any.TypeUrl)
|
||||||
|
if i := strings.LastIndex(any.TypeUrl, "/"); i >= 0 {
|
||||||
|
name = name[i+len("/"):]
|
||||||
|
}
|
||||||
|
if !name.IsValid() {
|
||||||
|
return "", fmt.Errorf("message type url %q is invalid", any.TypeUrl)
|
||||||
|
}
|
||||||
|
return name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalAny marshals the given message m into an anypb.Any message.
|
||||||
|
//
|
||||||
|
// Deprecated: Call the anypb.New function instead.
|
||||||
|
func MarshalAny(m proto.Message) (*anypb.Any, error) {
|
||||||
|
switch dm := m.(type) {
|
||||||
|
case DynamicAny:
|
||||||
|
m = dm.Message
|
||||||
|
case *DynamicAny:
|
||||||
|
if dm == nil {
|
||||||
|
return nil, proto.ErrNil
|
||||||
|
}
|
||||||
|
m = dm.Message
|
||||||
|
}
|
||||||
|
b, err := proto.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &anypb.Any{TypeUrl: urlPrefix + proto.MessageName(m), Value: b}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty returns a new message of the type specified in an anypb.Any message.
|
||||||
|
// It returns protoregistry.NotFound if the corresponding message type could not
|
||||||
|
// be resolved in the global registry.
|
||||||
|
//
|
||||||
|
// Deprecated: Use protoregistry.GlobalTypes.FindMessageByName instead
|
||||||
|
// to resolve the message name and create a new instance of it.
|
||||||
|
func Empty(any *anypb.Any) (proto.Message, error) {
|
||||||
|
name, err := anyMessageName(any)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mt, err := protoregistry.GlobalTypes.FindMessageByName(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return proto.MessageV1(mt.New().Interface()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalAny unmarshals the encoded value contained in the anypb.Any message
|
||||||
|
// into the provided message m. It returns an error if the target message
|
||||||
|
// does not match the type in the Any message or if an unmarshal error occurs.
|
||||||
|
//
|
||||||
|
// The target message m may be a *DynamicAny message. If the underlying message
|
||||||
|
// type could not be resolved, then this returns protoregistry.NotFound.
|
||||||
|
//
|
||||||
|
// Deprecated: Call the any.UnmarshalTo method instead.
|
||||||
|
func UnmarshalAny(any *anypb.Any, m proto.Message) error {
|
||||||
|
if dm, ok := m.(*DynamicAny); ok {
|
||||||
|
if dm.Message == nil {
|
||||||
|
var err error
|
||||||
|
dm.Message, err = Empty(any)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m = dm.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
anyName, err := AnyMessageName(any)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msgName := proto.MessageName(m)
|
||||||
|
if anyName != msgName {
|
||||||
|
return fmt.Errorf("mismatched message type: got %q want %q", anyName, msgName)
|
||||||
|
}
|
||||||
|
return proto.Unmarshal(any.Value, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is reports whether the Any message contains a message of the specified type.
|
||||||
|
//
|
||||||
|
// Deprecated: Call the any.MessageIs method instead.
|
||||||
|
func Is(any *anypb.Any, m proto.Message) bool {
|
||||||
|
if any == nil || m == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
name := proto.MessageName(m)
|
||||||
|
if !strings.HasSuffix(any.TypeUrl, name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return len(any.TypeUrl) == len(name) || any.TypeUrl[len(any.TypeUrl)-len(name)-1] == '/'
|
||||||
|
}
|
||||||
|
|
||||||
|
// DynamicAny is a value that can be passed to UnmarshalAny to automatically
|
||||||
|
// allocate a proto.Message for the type specified in an anypb.Any message.
|
||||||
|
// The allocated message is stored in the embedded proto.Message.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// var x ptypes.DynamicAny
|
||||||
|
// if err := ptypes.UnmarshalAny(a, &x); err != nil { ... }
|
||||||
|
// fmt.Printf("unmarshaled message: %v", x.Message)
|
||||||
|
//
|
||||||
|
// Deprecated: Use the any.UnmarshalNew method instead to unmarshal
|
||||||
|
// the any message contents into a new instance of the underlying message.
|
||||||
|
type DynamicAny struct{ proto.Message }
|
||||||
|
|
||||||
|
func (m DynamicAny) String() string {
|
||||||
|
if m.Message == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
return m.Message.String()
|
||||||
|
}
|
||||||
|
func (m DynamicAny) Reset() {
|
||||||
|
if m.Message == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.Message.Reset()
|
||||||
|
}
|
||||||
|
func (m DynamicAny) ProtoMessage() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (m DynamicAny) ProtoReflect() protoreflect.Message {
|
||||||
|
if m.Message == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return dynamicAny{proto.MessageReflect(m.Message)}
|
||||||
|
}
|
||||||
|
|
||||||
|
type dynamicAny struct{ protoreflect.Message }
|
||||||
|
|
||||||
|
func (m dynamicAny) Type() protoreflect.MessageType {
|
||||||
|
return dynamicAnyType{m.Message.Type()}
|
||||||
|
}
|
||||||
|
func (m dynamicAny) New() protoreflect.Message {
|
||||||
|
return dynamicAnyType{m.Message.Type()}.New()
|
||||||
|
}
|
||||||
|
func (m dynamicAny) Interface() protoreflect.ProtoMessage {
|
||||||
|
return DynamicAny{proto.MessageV1(m.Message.Interface())}
|
||||||
|
}
|
||||||
|
|
||||||
|
type dynamicAnyType struct{ protoreflect.MessageType }
|
||||||
|
|
||||||
|
func (t dynamicAnyType) New() protoreflect.Message {
|
||||||
|
return dynamicAny{t.MessageType.New()}
|
||||||
|
}
|
||||||
|
func (t dynamicAnyType) Zero() protoreflect.Message {
|
||||||
|
return dynamicAny{t.MessageType.Zero()}
|
||||||
|
}
|
10
vendor/github.com/golang/protobuf/ptypes/doc.go
generated
vendored
Normal file
10
vendor/github.com/golang/protobuf/ptypes/doc.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// 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 ptypes provides functionality for interacting with well-known types.
|
||||||
|
//
|
||||||
|
// Deprecated: Well-known types have specialized functionality directly
|
||||||
|
// injected into the generated packages for each message type.
|
||||||
|
// See the deprecation notice for each function for the suggested alternative.
|
||||||
|
package ptypes
|
76
vendor/github.com/golang/protobuf/ptypes/duration.go
generated
vendored
Normal file
76
vendor/github.com/golang/protobuf/ptypes/duration.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// 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 ptypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
durationpb "github.com/golang/protobuf/ptypes/duration"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Range of google.protobuf.Duration as specified in duration.proto.
|
||||||
|
// This is about 10,000 years in seconds.
|
||||||
|
const (
|
||||||
|
maxSeconds = int64(10000 * 365.25 * 24 * 60 * 60)
|
||||||
|
minSeconds = -maxSeconds
|
||||||
|
)
|
||||||
|
|
||||||
|
// Duration converts a durationpb.Duration to a time.Duration.
|
||||||
|
// Duration returns an error if dur is invalid or overflows a time.Duration.
|
||||||
|
//
|
||||||
|
// Deprecated: Call the dur.AsDuration and dur.CheckValid methods instead.
|
||||||
|
func Duration(dur *durationpb.Duration) (time.Duration, error) {
|
||||||
|
if err := validateDuration(dur); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
d := time.Duration(dur.Seconds) * time.Second
|
||||||
|
if int64(d/time.Second) != dur.Seconds {
|
||||||
|
return 0, fmt.Errorf("duration: %v is out of range for time.Duration", dur)
|
||||||
|
}
|
||||||
|
if dur.Nanos != 0 {
|
||||||
|
d += time.Duration(dur.Nanos) * time.Nanosecond
|
||||||
|
if (d < 0) != (dur.Nanos < 0) {
|
||||||
|
return 0, fmt.Errorf("duration: %v is out of range for time.Duration", dur)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DurationProto converts a time.Duration to a durationpb.Duration.
|
||||||
|
//
|
||||||
|
// Deprecated: Call the durationpb.New function instead.
|
||||||
|
func DurationProto(d time.Duration) *durationpb.Duration {
|
||||||
|
nanos := d.Nanoseconds()
|
||||||
|
secs := nanos / 1e9
|
||||||
|
nanos -= secs * 1e9
|
||||||
|
return &durationpb.Duration{
|
||||||
|
Seconds: int64(secs),
|
||||||
|
Nanos: int32(nanos),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateDuration determines whether the durationpb.Duration is valid
|
||||||
|
// according to the definition in google/protobuf/duration.proto.
|
||||||
|
// A valid durpb.Duration may still be too large to fit into a time.Duration
|
||||||
|
// Note that the range of durationpb.Duration is about 10,000 years,
|
||||||
|
// while the range of time.Duration is about 290 years.
|
||||||
|
func validateDuration(dur *durationpb.Duration) error {
|
||||||
|
if dur == nil {
|
||||||
|
return errors.New("duration: nil Duration")
|
||||||
|
}
|
||||||
|
if dur.Seconds < minSeconds || dur.Seconds > maxSeconds {
|
||||||
|
return fmt.Errorf("duration: %v: seconds out of range", dur)
|
||||||
|
}
|
||||||
|
if dur.Nanos <= -1e9 || dur.Nanos >= 1e9 {
|
||||||
|
return fmt.Errorf("duration: %v: nanos out of range", dur)
|
||||||
|
}
|
||||||
|
// Seconds and Nanos must have the same sign, unless d.Nanos is zero.
|
||||||
|
if (dur.Seconds < 0 && dur.Nanos > 0) || (dur.Seconds > 0 && dur.Nanos < 0) {
|
||||||
|
return fmt.Errorf("duration: %v: seconds and nanos have different signs", dur)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
63
vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go
generated
vendored
Normal file
63
vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// source: github.com/golang/protobuf/ptypes/duration/duration.proto
|
||||||
|
|
||||||
|
package duration
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
durationpb "google.golang.org/protobuf/types/known/durationpb"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Symbols defined in public import of google/protobuf/duration.proto.
|
||||||
|
|
||||||
|
type Duration = durationpb.Duration
|
||||||
|
|
||||||
|
var File_github_com_golang_protobuf_ptypes_duration_duration_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_github_com_golang_protobuf_ptypes_duration_duration_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
|
||||||
|
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
|
||||||
|
0x70, 0x65, 0x73, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x64, 0x75, 0x72,
|
||||||
|
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f,
|
||||||
|
0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72,
|
||||||
|
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x35, 0x5a, 0x33, 0x67,
|
||||||
|
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67,
|
||||||
|
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x73,
|
||||||
|
0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69,
|
||||||
|
0x6f, 0x6e, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_github_com_golang_protobuf_ptypes_duration_duration_proto_goTypes = []interface{}{}
|
||||||
|
var file_github_com_golang_protobuf_ptypes_duration_duration_proto_depIdxs = []int32{
|
||||||
|
0, // [0:0] is the sub-list for method output_type
|
||||||
|
0, // [0:0] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_github_com_golang_protobuf_ptypes_duration_duration_proto_init() }
|
||||||
|
func file_github_com_golang_protobuf_ptypes_duration_duration_proto_init() {
|
||||||
|
if File_github_com_golang_protobuf_ptypes_duration_duration_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_github_com_golang_protobuf_ptypes_duration_duration_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 0,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_github_com_golang_protobuf_ptypes_duration_duration_proto_goTypes,
|
||||||
|
DependencyIndexes: file_github_com_golang_protobuf_ptypes_duration_duration_proto_depIdxs,
|
||||||
|
}.Build()
|
||||||
|
File_github_com_golang_protobuf_ptypes_duration_duration_proto = out.File
|
||||||
|
file_github_com_golang_protobuf_ptypes_duration_duration_proto_rawDesc = nil
|
||||||
|
file_github_com_golang_protobuf_ptypes_duration_duration_proto_goTypes = nil
|
||||||
|
file_github_com_golang_protobuf_ptypes_duration_duration_proto_depIdxs = nil
|
||||||
|
}
|
112
vendor/github.com/golang/protobuf/ptypes/timestamp.go
generated
vendored
Normal file
112
vendor/github.com/golang/protobuf/ptypes/timestamp.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// 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 ptypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
timestamppb "github.com/golang/protobuf/ptypes/timestamp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Range of google.protobuf.Duration as specified in timestamp.proto.
|
||||||
|
const (
|
||||||
|
// Seconds field of the earliest valid Timestamp.
|
||||||
|
// This is time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC).Unix().
|
||||||
|
minValidSeconds = -62135596800
|
||||||
|
// Seconds field just after the latest valid Timestamp.
|
||||||
|
// This is time.Date(10000, 1, 1, 0, 0, 0, 0, time.UTC).Unix().
|
||||||
|
maxValidSeconds = 253402300800
|
||||||
|
)
|
||||||
|
|
||||||
|
// Timestamp converts a timestamppb.Timestamp to a time.Time.
|
||||||
|
// It returns an error if the argument is invalid.
|
||||||
|
//
|
||||||
|
// Unlike most Go functions, if Timestamp returns an error, the first return
|
||||||
|
// value is not the zero time.Time. Instead, it is the value obtained from the
|
||||||
|
// time.Unix function when passed the contents of the Timestamp, in the UTC
|
||||||
|
// locale. This may or may not be a meaningful time; many invalid Timestamps
|
||||||
|
// do map to valid time.Times.
|
||||||
|
//
|
||||||
|
// A nil Timestamp returns an error. The first return value in that case is
|
||||||
|
// undefined.
|
||||||
|
//
|
||||||
|
// Deprecated: Call the ts.AsTime and ts.CheckValid methods instead.
|
||||||
|
func Timestamp(ts *timestamppb.Timestamp) (time.Time, error) {
|
||||||
|
// Don't return the zero value on error, because corresponds to a valid
|
||||||
|
// timestamp. Instead return whatever time.Unix gives us.
|
||||||
|
var t time.Time
|
||||||
|
if ts == nil {
|
||||||
|
t = time.Unix(0, 0).UTC() // treat nil like the empty Timestamp
|
||||||
|
} else {
|
||||||
|
t = time.Unix(ts.Seconds, int64(ts.Nanos)).UTC()
|
||||||
|
}
|
||||||
|
return t, validateTimestamp(ts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimestampNow returns a google.protobuf.Timestamp for the current time.
|
||||||
|
//
|
||||||
|
// Deprecated: Call the timestamppb.Now function instead.
|
||||||
|
func TimestampNow() *timestamppb.Timestamp {
|
||||||
|
ts, err := TimestampProto(time.Now())
|
||||||
|
if err != nil {
|
||||||
|
panic("ptypes: time.Now() out of Timestamp range")
|
||||||
|
}
|
||||||
|
return ts
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimestampProto converts the time.Time to a google.protobuf.Timestamp proto.
|
||||||
|
// It returns an error if the resulting Timestamp is invalid.
|
||||||
|
//
|
||||||
|
// Deprecated: Call the timestamppb.New function instead.
|
||||||
|
func TimestampProto(t time.Time) (*timestamppb.Timestamp, error) {
|
||||||
|
ts := ×tamppb.Timestamp{
|
||||||
|
Seconds: t.Unix(),
|
||||||
|
Nanos: int32(t.Nanosecond()),
|
||||||
|
}
|
||||||
|
if err := validateTimestamp(ts); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimestampString returns the RFC 3339 string for valid Timestamps.
|
||||||
|
// For invalid Timestamps, it returns an error message in parentheses.
|
||||||
|
//
|
||||||
|
// Deprecated: Call the ts.AsTime method instead,
|
||||||
|
// followed by a call to the Format method on the time.Time value.
|
||||||
|
func TimestampString(ts *timestamppb.Timestamp) string {
|
||||||
|
t, err := Timestamp(ts)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("(%v)", err)
|
||||||
|
}
|
||||||
|
return t.Format(time.RFC3339Nano)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateTimestamp determines whether a Timestamp is valid.
|
||||||
|
// A valid timestamp represents a time in the range [0001-01-01, 10000-01-01)
|
||||||
|
// and has a Nanos field in the range [0, 1e9).
|
||||||
|
//
|
||||||
|
// If the Timestamp is valid, validateTimestamp returns nil.
|
||||||
|
// Otherwise, it returns an error that describes the problem.
|
||||||
|
//
|
||||||
|
// Every valid Timestamp can be represented by a time.Time,
|
||||||
|
// but the converse is not true.
|
||||||
|
func validateTimestamp(ts *timestamppb.Timestamp) error {
|
||||||
|
if ts == nil {
|
||||||
|
return errors.New("timestamp: nil Timestamp")
|
||||||
|
}
|
||||||
|
if ts.Seconds < minValidSeconds {
|
||||||
|
return fmt.Errorf("timestamp: %v before 0001-01-01", ts)
|
||||||
|
}
|
||||||
|
if ts.Seconds >= maxValidSeconds {
|
||||||
|
return fmt.Errorf("timestamp: %v after 10000-01-01", ts)
|
||||||
|
}
|
||||||
|
if ts.Nanos < 0 || ts.Nanos >= 1e9 {
|
||||||
|
return fmt.Errorf("timestamp: %v: nanos not in range [0, 1e9)", ts)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
64
vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go
generated
vendored
Normal file
64
vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// source: github.com/golang/protobuf/ptypes/timestamp/timestamp.proto
|
||||||
|
|
||||||
|
package timestamp
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Symbols defined in public import of google/protobuf/timestamp.proto.
|
||||||
|
|
||||||
|
type Timestamp = timestamppb.Timestamp
|
||||||
|
|
||||||
|
var File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
|
||||||
|
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
|
||||||
|
0x70, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x74, 0x69,
|
||||||
|
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67,
|
||||||
|
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74,
|
||||||
|
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x37,
|
||||||
|
0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
|
||||||
|
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
|
||||||
|
0x70, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3b, 0x74, 0x69,
|
||||||
|
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
|
0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes = []interface{}{}
|
||||||
|
var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs = []int32{
|
||||||
|
0, // [0:0] is the sub-list for method output_type
|
||||||
|
0, // [0:0] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_init() }
|
||||||
|
func file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_init() {
|
||||||
|
if File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 0,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes,
|
||||||
|
DependencyIndexes: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs,
|
||||||
|
}.Build()
|
||||||
|
File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto = out.File
|
||||||
|
file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc = nil
|
||||||
|
file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes = nil
|
||||||
|
file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs = nil
|
||||||
|
}
|
85
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/BUILD.bazel
generated
vendored
Normal file
85
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/BUILD.bazel
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"context.go",
|
||||||
|
"convert.go",
|
||||||
|
"doc.go",
|
||||||
|
"errors.go",
|
||||||
|
"fieldmask.go",
|
||||||
|
"handler.go",
|
||||||
|
"marshal_httpbodyproto.go",
|
||||||
|
"marshal_json.go",
|
||||||
|
"marshal_jsonpb.go",
|
||||||
|
"marshal_proto.go",
|
||||||
|
"marshaler.go",
|
||||||
|
"marshaler_registry.go",
|
||||||
|
"mux.go",
|
||||||
|
"pattern.go",
|
||||||
|
"proto2_convert.go",
|
||||||
|
"query.go",
|
||||||
|
],
|
||||||
|
importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/runtime",
|
||||||
|
deps = [
|
||||||
|
"//internal/httprule:go_default_library",
|
||||||
|
"//utilities:go_default_library",
|
||||||
|
"@go_googleapis//google/api:httpbody_go_proto",
|
||||||
|
"@io_bazel_rules_go//proto/wkt:field_mask_go_proto",
|
||||||
|
"@org_golang_google_grpc//codes:go_default_library",
|
||||||
|
"@org_golang_google_grpc//grpclog:go_default_library",
|
||||||
|
"@org_golang_google_grpc//metadata:go_default_library",
|
||||||
|
"@org_golang_google_grpc//status:go_default_library",
|
||||||
|
"@org_golang_google_protobuf//encoding/protojson:go_default_library",
|
||||||
|
"@org_golang_google_protobuf//proto:go_default_library",
|
||||||
|
"@org_golang_google_protobuf//reflect/protoreflect:go_default_library",
|
||||||
|
"@org_golang_google_protobuf//reflect/protoregistry:go_default_library",
|
||||||
|
"@org_golang_google_protobuf//types/known/durationpb:go_default_library",
|
||||||
|
"@org_golang_google_protobuf//types/known/timestamppb:go_default_library",
|
||||||
|
"@org_golang_google_protobuf//types/known/wrapperspb:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
size = "small",
|
||||||
|
srcs = [
|
||||||
|
"context_test.go",
|
||||||
|
"convert_test.go",
|
||||||
|
"errors_test.go",
|
||||||
|
"fieldmask_test.go",
|
||||||
|
"handler_test.go",
|
||||||
|
"marshal_httpbodyproto_test.go",
|
||||||
|
"marshal_json_test.go",
|
||||||
|
"marshal_jsonpb_test.go",
|
||||||
|
"marshal_proto_test.go",
|
||||||
|
"marshaler_registry_test.go",
|
||||||
|
"mux_test.go",
|
||||||
|
"pattern_test.go",
|
||||||
|
"query_test.go",
|
||||||
|
],
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
deps = [
|
||||||
|
"//runtime/internal/examplepb:go_default_library",
|
||||||
|
"//utilities:go_default_library",
|
||||||
|
"@com_github_google_go_cmp//cmp:go_default_library",
|
||||||
|
"@com_github_google_go_cmp//cmp/cmpopts:go_default_library",
|
||||||
|
"@go_googleapis//google/api:httpbody_go_proto",
|
||||||
|
"@go_googleapis//google/rpc:errdetails_go_proto",
|
||||||
|
"@go_googleapis//google/rpc:status_go_proto",
|
||||||
|
"@io_bazel_rules_go//proto/wkt:field_mask_go_proto",
|
||||||
|
"@org_golang_google_grpc//codes:go_default_library",
|
||||||
|
"@org_golang_google_grpc//metadata:go_default_library",
|
||||||
|
"@org_golang_google_grpc//status:go_default_library",
|
||||||
|
"@org_golang_google_protobuf//encoding/protojson:go_default_library",
|
||||||
|
"@org_golang_google_protobuf//proto:go_default_library",
|
||||||
|
"@org_golang_google_protobuf//testing/protocmp:go_default_library",
|
||||||
|
"@org_golang_google_protobuf//types/known/durationpb:go_default_library",
|
||||||
|
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
|
||||||
|
"@org_golang_google_protobuf//types/known/structpb:go_default_library",
|
||||||
|
"@org_golang_google_protobuf//types/known/timestamppb:go_default_library",
|
||||||
|
"@org_golang_google_protobuf//types/known/wrapperspb:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
313
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/context.go
generated
vendored
Normal file
313
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/context.go
generated
vendored
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/textproto"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MetadataHeaderPrefix is the http prefix that represents custom metadata
|
||||||
|
// parameters to or from a gRPC call.
|
||||||
|
const MetadataHeaderPrefix = "Grpc-Metadata-"
|
||||||
|
|
||||||
|
// MetadataPrefix is prepended to permanent HTTP header keys (as specified
|
||||||
|
// by the IANA) when added to the gRPC context.
|
||||||
|
const MetadataPrefix = "grpcgateway-"
|
||||||
|
|
||||||
|
// MetadataTrailerPrefix is prepended to gRPC metadata as it is converted to
|
||||||
|
// HTTP headers in a response handled by grpc-gateway
|
||||||
|
const MetadataTrailerPrefix = "Grpc-Trailer-"
|
||||||
|
|
||||||
|
const metadataGrpcTimeout = "Grpc-Timeout"
|
||||||
|
const metadataHeaderBinarySuffix = "-Bin"
|
||||||
|
|
||||||
|
const xForwardedFor = "X-Forwarded-For"
|
||||||
|
const xForwardedHost = "X-Forwarded-Host"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DefaultContextTimeout is used for gRPC call context.WithTimeout whenever a Grpc-Timeout inbound
|
||||||
|
// header isn't present. If the value is 0 the sent `context` will not have a timeout.
|
||||||
|
DefaultContextTimeout = 0 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
type rpcMethodKey struct{}
|
||||||
|
|
||||||
|
func decodeBinHeader(v string) ([]byte, error) {
|
||||||
|
if len(v)%4 == 0 {
|
||||||
|
// Input was padded, or padding was not necessary.
|
||||||
|
return base64.StdEncoding.DecodeString(v)
|
||||||
|
}
|
||||||
|
return base64.RawStdEncoding.DecodeString(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
AnnotateContext adds context information such as metadata from the request.
|
||||||
|
|
||||||
|
At a minimum, the RemoteAddr is included in the fashion of "X-Forwarded-For",
|
||||||
|
except that the forwarded destination is not another HTTP service but rather
|
||||||
|
a gRPC service.
|
||||||
|
*/
|
||||||
|
func AnnotateContext(ctx context.Context, mux *ServeMux, req *http.Request, rpcMethodName string) (context.Context, error) {
|
||||||
|
ctx, md, err := annotateContext(ctx, mux, req, rpcMethodName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if md == nil {
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.NewOutgoingContext(ctx, md), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnnotateIncomingContext adds context information such as metadata from the request.
|
||||||
|
// Attach metadata as incoming context.
|
||||||
|
func AnnotateIncomingContext(ctx context.Context, mux *ServeMux, req *http.Request, rpcMethodName string) (context.Context, error) {
|
||||||
|
ctx, md, err := annotateContext(ctx, mux, req, rpcMethodName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if md == nil {
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.NewIncomingContext(ctx, md), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func annotateContext(ctx context.Context, mux *ServeMux, req *http.Request, rpcMethodName string) (context.Context, metadata.MD, error) {
|
||||||
|
ctx = withRPCMethod(ctx, rpcMethodName)
|
||||||
|
var pairs []string
|
||||||
|
timeout := DefaultContextTimeout
|
||||||
|
if tm := req.Header.Get(metadataGrpcTimeout); tm != "" {
|
||||||
|
var err error
|
||||||
|
timeout, err = timeoutDecode(tm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, status.Errorf(codes.InvalidArgument, "invalid grpc-timeout: %s", tm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, vals := range req.Header {
|
||||||
|
key = textproto.CanonicalMIMEHeaderKey(key)
|
||||||
|
for _, val := range vals {
|
||||||
|
// For backwards-compatibility, pass through 'authorization' header with no prefix.
|
||||||
|
if key == "Authorization" {
|
||||||
|
pairs = append(pairs, "authorization", val)
|
||||||
|
}
|
||||||
|
if h, ok := mux.incomingHeaderMatcher(key); ok {
|
||||||
|
// Handles "-bin" metadata in grpc, since grpc will do another base64
|
||||||
|
// encode before sending to server, we need to decode it first.
|
||||||
|
if strings.HasSuffix(key, metadataHeaderBinarySuffix) {
|
||||||
|
b, err := decodeBinHeader(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, status.Errorf(codes.InvalidArgument, "invalid binary header %s: %s", key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
val = string(b)
|
||||||
|
}
|
||||||
|
pairs = append(pairs, h, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if host := req.Header.Get(xForwardedHost); host != "" {
|
||||||
|
pairs = append(pairs, strings.ToLower(xForwardedHost), host)
|
||||||
|
} else if req.Host != "" {
|
||||||
|
pairs = append(pairs, strings.ToLower(xForwardedHost), req.Host)
|
||||||
|
}
|
||||||
|
|
||||||
|
if addr := req.RemoteAddr; addr != "" {
|
||||||
|
if remoteIP, _, err := net.SplitHostPort(addr); err == nil {
|
||||||
|
if fwd := req.Header.Get(xForwardedFor); fwd == "" {
|
||||||
|
pairs = append(pairs, strings.ToLower(xForwardedFor), remoteIP)
|
||||||
|
} else {
|
||||||
|
pairs = append(pairs, strings.ToLower(xForwardedFor), fmt.Sprintf("%s, %s", fwd, remoteIP))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if timeout != 0 {
|
||||||
|
//nolint:govet // The context outlives this function
|
||||||
|
ctx, _ = context.WithTimeout(ctx, timeout)
|
||||||
|
}
|
||||||
|
if len(pairs) == 0 {
|
||||||
|
return ctx, nil, nil
|
||||||
|
}
|
||||||
|
md := metadata.Pairs(pairs...)
|
||||||
|
for _, mda := range mux.metadataAnnotators {
|
||||||
|
md = metadata.Join(md, mda(ctx, req))
|
||||||
|
}
|
||||||
|
return ctx, md, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerMetadata consists of metadata sent from gRPC server.
|
||||||
|
type ServerMetadata struct {
|
||||||
|
HeaderMD metadata.MD
|
||||||
|
TrailerMD metadata.MD
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverMetadataKey struct{}
|
||||||
|
|
||||||
|
// NewServerMetadataContext creates a new context with ServerMetadata
|
||||||
|
func NewServerMetadataContext(ctx context.Context, md ServerMetadata) context.Context {
|
||||||
|
return context.WithValue(ctx, serverMetadataKey{}, md)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerMetadataFromContext returns the ServerMetadata in ctx
|
||||||
|
func ServerMetadataFromContext(ctx context.Context) (md ServerMetadata, ok bool) {
|
||||||
|
md, ok = ctx.Value(serverMetadataKey{}).(ServerMetadata)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerTransportStream implements grpc.ServerTransportStream.
|
||||||
|
// It should only be used by the generated files to support grpc.SendHeader
|
||||||
|
// outside of gRPC server use.
|
||||||
|
type ServerTransportStream struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
header metadata.MD
|
||||||
|
trailer metadata.MD
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method returns the method for the stream.
|
||||||
|
func (s *ServerTransportStream) Method() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header returns the header metadata of the stream.
|
||||||
|
func (s *ServerTransportStream) Header() metadata.MD {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.header.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHeader sets the header metadata.
|
||||||
|
func (s *ServerTransportStream) SetHeader(md metadata.MD) error {
|
||||||
|
if md.Len() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mu.Lock()
|
||||||
|
s.header = metadata.Join(s.header, md)
|
||||||
|
s.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendHeader sets the header metadata.
|
||||||
|
func (s *ServerTransportStream) SendHeader(md metadata.MD) error {
|
||||||
|
return s.SetHeader(md)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trailer returns the cached trailer metadata.
|
||||||
|
func (s *ServerTransportStream) Trailer() metadata.MD {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.trailer.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTrailer sets the trailer metadata.
|
||||||
|
func (s *ServerTransportStream) SetTrailer(md metadata.MD) error {
|
||||||
|
if md.Len() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mu.Lock()
|
||||||
|
s.trailer = metadata.Join(s.trailer, md)
|
||||||
|
s.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeoutDecode(s string) (time.Duration, error) {
|
||||||
|
size := len(s)
|
||||||
|
if size < 2 {
|
||||||
|
return 0, fmt.Errorf("timeout string is too short: %q", s)
|
||||||
|
}
|
||||||
|
d, ok := timeoutUnitToDuration(s[size-1])
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("timeout unit is not recognized: %q", s)
|
||||||
|
}
|
||||||
|
t, err := strconv.ParseInt(s[:size-1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return d * time.Duration(t), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeoutUnitToDuration(u uint8) (d time.Duration, ok bool) {
|
||||||
|
switch u {
|
||||||
|
case 'H':
|
||||||
|
return time.Hour, true
|
||||||
|
case 'M':
|
||||||
|
return time.Minute, true
|
||||||
|
case 'S':
|
||||||
|
return time.Second, true
|
||||||
|
case 'm':
|
||||||
|
return time.Millisecond, true
|
||||||
|
case 'u':
|
||||||
|
return time.Microsecond, true
|
||||||
|
case 'n':
|
||||||
|
return time.Nanosecond, true
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// isPermanentHTTPHeader checks whether hdr belongs to the list of
|
||||||
|
// permanent request headers maintained by IANA.
|
||||||
|
// http://www.iana.org/assignments/message-headers/message-headers.xml
|
||||||
|
func isPermanentHTTPHeader(hdr string) bool {
|
||||||
|
switch hdr {
|
||||||
|
case
|
||||||
|
"Accept",
|
||||||
|
"Accept-Charset",
|
||||||
|
"Accept-Language",
|
||||||
|
"Accept-Ranges",
|
||||||
|
"Authorization",
|
||||||
|
"Cache-Control",
|
||||||
|
"Content-Type",
|
||||||
|
"Cookie",
|
||||||
|
"Date",
|
||||||
|
"Expect",
|
||||||
|
"From",
|
||||||
|
"Host",
|
||||||
|
"If-Match",
|
||||||
|
"If-Modified-Since",
|
||||||
|
"If-None-Match",
|
||||||
|
"If-Schedule-Tag-Match",
|
||||||
|
"If-Unmodified-Since",
|
||||||
|
"Max-Forwards",
|
||||||
|
"Origin",
|
||||||
|
"Pragma",
|
||||||
|
"Referer",
|
||||||
|
"User-Agent",
|
||||||
|
"Via",
|
||||||
|
"Warning":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPCMethod returns the method string for the server context. The returned
|
||||||
|
// string is in the format of "/package.service/method".
|
||||||
|
func RPCMethod(ctx context.Context) (string, bool) {
|
||||||
|
m := ctx.Value(rpcMethodKey{})
|
||||||
|
if m == nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
ms, ok := m.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return ms, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func withRPCMethod(ctx context.Context, rpcMethodName string) context.Context {
|
||||||
|
return context.WithValue(ctx, rpcMethodKey{}, rpcMethodName)
|
||||||
|
}
|
320
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/convert.go
generated
vendored
Normal file
320
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/convert.go
generated
vendored
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
|
"google.golang.org/protobuf/types/known/durationpb"
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// String just returns the given string.
|
||||||
|
// It is just for compatibility to other types.
|
||||||
|
func String(val string) (string, error) {
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSlice converts 'val' where individual strings are separated by
|
||||||
|
// 'sep' into a string slice.
|
||||||
|
func StringSlice(val, sep string) ([]string, error) {
|
||||||
|
return strings.Split(val, sep), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool converts the given string representation of a boolean value into bool.
|
||||||
|
func Bool(val string) (bool, error) {
|
||||||
|
return strconv.ParseBool(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolSlice converts 'val' where individual booleans are separated by
|
||||||
|
// 'sep' into a bool slice.
|
||||||
|
func BoolSlice(val, sep string) ([]bool, error) {
|
||||||
|
s := strings.Split(val, sep)
|
||||||
|
values := make([]bool, len(s))
|
||||||
|
for i, v := range s {
|
||||||
|
value, err := Bool(v)
|
||||||
|
if err != nil {
|
||||||
|
return values, err
|
||||||
|
}
|
||||||
|
values[i] = value
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 converts the given string representation into representation of a floating point number into float64.
|
||||||
|
func Float64(val string) (float64, error) {
|
||||||
|
return strconv.ParseFloat(val, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Slice converts 'val' where individual floating point numbers are separated by
|
||||||
|
// 'sep' into a float64 slice.
|
||||||
|
func Float64Slice(val, sep string) ([]float64, error) {
|
||||||
|
s := strings.Split(val, sep)
|
||||||
|
values := make([]float64, len(s))
|
||||||
|
for i, v := range s {
|
||||||
|
value, err := Float64(v)
|
||||||
|
if err != nil {
|
||||||
|
return values, err
|
||||||
|
}
|
||||||
|
values[i] = value
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32 converts the given string representation of a floating point number into float32.
|
||||||
|
func Float32(val string) (float32, error) {
|
||||||
|
f, err := strconv.ParseFloat(val, 32)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return float32(f), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32Slice converts 'val' where individual floating point numbers are separated by
|
||||||
|
// 'sep' into a float32 slice.
|
||||||
|
func Float32Slice(val, sep string) ([]float32, error) {
|
||||||
|
s := strings.Split(val, sep)
|
||||||
|
values := make([]float32, len(s))
|
||||||
|
for i, v := range s {
|
||||||
|
value, err := Float32(v)
|
||||||
|
if err != nil {
|
||||||
|
return values, err
|
||||||
|
}
|
||||||
|
values[i] = value
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 converts the given string representation of an integer into int64.
|
||||||
|
func Int64(val string) (int64, error) {
|
||||||
|
return strconv.ParseInt(val, 0, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Slice converts 'val' where individual integers are separated by
|
||||||
|
// 'sep' into a int64 slice.
|
||||||
|
func Int64Slice(val, sep string) ([]int64, error) {
|
||||||
|
s := strings.Split(val, sep)
|
||||||
|
values := make([]int64, len(s))
|
||||||
|
for i, v := range s {
|
||||||
|
value, err := Int64(v)
|
||||||
|
if err != nil {
|
||||||
|
return values, err
|
||||||
|
}
|
||||||
|
values[i] = value
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32 converts the given string representation of an integer into int32.
|
||||||
|
func Int32(val string) (int32, error) {
|
||||||
|
i, err := strconv.ParseInt(val, 0, 32)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int32(i), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32Slice converts 'val' where individual integers are separated by
|
||||||
|
// 'sep' into a int32 slice.
|
||||||
|
func Int32Slice(val, sep string) ([]int32, error) {
|
||||||
|
s := strings.Split(val, sep)
|
||||||
|
values := make([]int32, len(s))
|
||||||
|
for i, v := range s {
|
||||||
|
value, err := Int32(v)
|
||||||
|
if err != nil {
|
||||||
|
return values, err
|
||||||
|
}
|
||||||
|
values[i] = value
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64 converts the given string representation of an integer into uint64.
|
||||||
|
func Uint64(val string) (uint64, error) {
|
||||||
|
return strconv.ParseUint(val, 0, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64Slice converts 'val' where individual integers are separated by
|
||||||
|
// 'sep' into a uint64 slice.
|
||||||
|
func Uint64Slice(val, sep string) ([]uint64, error) {
|
||||||
|
s := strings.Split(val, sep)
|
||||||
|
values := make([]uint64, len(s))
|
||||||
|
for i, v := range s {
|
||||||
|
value, err := Uint64(v)
|
||||||
|
if err != nil {
|
||||||
|
return values, err
|
||||||
|
}
|
||||||
|
values[i] = value
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32 converts the given string representation of an integer into uint32.
|
||||||
|
func Uint32(val string) (uint32, error) {
|
||||||
|
i, err := strconv.ParseUint(val, 0, 32)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return uint32(i), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32Slice converts 'val' where individual integers are separated by
|
||||||
|
// 'sep' into a uint32 slice.
|
||||||
|
func Uint32Slice(val, sep string) ([]uint32, error) {
|
||||||
|
s := strings.Split(val, sep)
|
||||||
|
values := make([]uint32, len(s))
|
||||||
|
for i, v := range s {
|
||||||
|
value, err := Uint32(v)
|
||||||
|
if err != nil {
|
||||||
|
return values, err
|
||||||
|
}
|
||||||
|
values[i] = value
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes converts the given string representation of a byte sequence into a slice of bytes
|
||||||
|
// A bytes sequence is encoded in URL-safe base64 without padding
|
||||||
|
func Bytes(val string) ([]byte, error) {
|
||||||
|
b, err := base64.StdEncoding.DecodeString(val)
|
||||||
|
if err != nil {
|
||||||
|
b, err = base64.URLEncoding.DecodeString(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesSlice converts 'val' where individual bytes sequences, encoded in URL-safe
|
||||||
|
// base64 without padding, are separated by 'sep' into a slice of bytes slices slice.
|
||||||
|
func BytesSlice(val, sep string) ([][]byte, error) {
|
||||||
|
s := strings.Split(val, sep)
|
||||||
|
values := make([][]byte, len(s))
|
||||||
|
for i, v := range s {
|
||||||
|
value, err := Bytes(v)
|
||||||
|
if err != nil {
|
||||||
|
return values, err
|
||||||
|
}
|
||||||
|
values[i] = value
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timestamp converts the given RFC3339 formatted string into a timestamp.Timestamp.
|
||||||
|
func Timestamp(val string) (*timestamppb.Timestamp, error) {
|
||||||
|
var r timestamppb.Timestamp
|
||||||
|
unmarshaler := &protojson.UnmarshalOptions{}
|
||||||
|
err := unmarshaler.Unmarshal([]byte(val), &r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duration converts the given string into a timestamp.Duration.
|
||||||
|
func Duration(val string) (*durationpb.Duration, error) {
|
||||||
|
var r durationpb.Duration
|
||||||
|
unmarshaler := &protojson.UnmarshalOptions{}
|
||||||
|
err := unmarshaler.Unmarshal([]byte(val), &r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enum converts the given string into an int32 that should be type casted into the
|
||||||
|
// correct enum proto type.
|
||||||
|
func Enum(val string, enumValMap map[string]int32) (int32, error) {
|
||||||
|
e, ok := enumValMap[val]
|
||||||
|
if ok {
|
||||||
|
return e, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
i, err := Int32(val)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("%s is not valid", val)
|
||||||
|
}
|
||||||
|
for _, v := range enumValMap {
|
||||||
|
if v == i {
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("%s is not valid", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnumSlice converts 'val' where individual enums are separated by 'sep'
|
||||||
|
// into a int32 slice. Each individual int32 should be type casted into the
|
||||||
|
// correct enum proto type.
|
||||||
|
func EnumSlice(val, sep string, enumValMap map[string]int32) ([]int32, error) {
|
||||||
|
s := strings.Split(val, sep)
|
||||||
|
values := make([]int32, len(s))
|
||||||
|
for i, v := range s {
|
||||||
|
value, err := Enum(v, enumValMap)
|
||||||
|
if err != nil {
|
||||||
|
return values, err
|
||||||
|
}
|
||||||
|
values[i] = value
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Support fot google.protobuf.wrappers on top of primitive types
|
||||||
|
*/
|
||||||
|
|
||||||
|
// StringValue well-known type support as wrapper around string type
|
||||||
|
func StringValue(val string) (*wrapperspb.StringValue, error) {
|
||||||
|
return &wrapperspb.StringValue{Value: val}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FloatValue well-known type support as wrapper around float32 type
|
||||||
|
func FloatValue(val string) (*wrapperspb.FloatValue, error) {
|
||||||
|
parsedVal, err := Float32(val)
|
||||||
|
return &wrapperspb.FloatValue{Value: parsedVal}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DoubleValue well-known type support as wrapper around float64 type
|
||||||
|
func DoubleValue(val string) (*wrapperspb.DoubleValue, error) {
|
||||||
|
parsedVal, err := Float64(val)
|
||||||
|
return &wrapperspb.DoubleValue{Value: parsedVal}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolValue well-known type support as wrapper around bool type
|
||||||
|
func BoolValue(val string) (*wrapperspb.BoolValue, error) {
|
||||||
|
parsedVal, err := Bool(val)
|
||||||
|
return &wrapperspb.BoolValue{Value: parsedVal}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32Value well-known type support as wrapper around int32 type
|
||||||
|
func Int32Value(val string) (*wrapperspb.Int32Value, error) {
|
||||||
|
parsedVal, err := Int32(val)
|
||||||
|
return &wrapperspb.Int32Value{Value: parsedVal}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UInt32Value well-known type support as wrapper around uint32 type
|
||||||
|
func UInt32Value(val string) (*wrapperspb.UInt32Value, error) {
|
||||||
|
parsedVal, err := Uint32(val)
|
||||||
|
return &wrapperspb.UInt32Value{Value: parsedVal}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Value well-known type support as wrapper around int64 type
|
||||||
|
func Int64Value(val string) (*wrapperspb.Int64Value, error) {
|
||||||
|
parsedVal, err := Int64(val)
|
||||||
|
return &wrapperspb.Int64Value{Value: parsedVal}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UInt64Value well-known type support as wrapper around uint64 type
|
||||||
|
func UInt64Value(val string) (*wrapperspb.UInt64Value, error) {
|
||||||
|
parsedVal, err := Uint64(val)
|
||||||
|
return &wrapperspb.UInt64Value{Value: parsedVal}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesValue well-known type support as wrapper around bytes[] type
|
||||||
|
func BytesValue(val string) (*wrapperspb.BytesValue, error) {
|
||||||
|
parsedVal, err := Bytes(val)
|
||||||
|
return &wrapperspb.BytesValue{Value: parsedVal}, err
|
||||||
|
}
|
5
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/doc.go
generated
vendored
Normal file
5
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/doc.go
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/*
|
||||||
|
Package runtime contains runtime helper functions used by
|
||||||
|
servers which protoc-gen-grpc-gateway generates.
|
||||||
|
*/
|
||||||
|
package runtime
|
178
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/errors.go
generated
vendored
Normal file
178
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/errors.go
generated
vendored
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrorHandlerFunc is the signature used to configure error handling.
|
||||||
|
type ErrorHandlerFunc func(context.Context, *ServeMux, Marshaler, http.ResponseWriter, *http.Request, error)
|
||||||
|
|
||||||
|
// StreamErrorHandlerFunc is the signature used to configure stream error handling.
|
||||||
|
type StreamErrorHandlerFunc func(context.Context, error) *status.Status
|
||||||
|
|
||||||
|
// RoutingErrorHandlerFunc is the signature used to configure error handling for routing errors.
|
||||||
|
type RoutingErrorHandlerFunc func(context.Context, *ServeMux, Marshaler, http.ResponseWriter, *http.Request, int)
|
||||||
|
|
||||||
|
// HTTPStatusError is the error to use when needing to provide a different HTTP status code for an error
|
||||||
|
// passed to the DefaultRoutingErrorHandler.
|
||||||
|
type HTTPStatusError struct {
|
||||||
|
HTTPStatus int
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HTTPStatusError) Error() string {
|
||||||
|
return e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPStatusFromCode converts a gRPC error code into the corresponding HTTP response status.
|
||||||
|
// See: https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
|
||||||
|
func HTTPStatusFromCode(code codes.Code) int {
|
||||||
|
switch code {
|
||||||
|
case codes.OK:
|
||||||
|
return http.StatusOK
|
||||||
|
case codes.Canceled:
|
||||||
|
return http.StatusRequestTimeout
|
||||||
|
case codes.Unknown:
|
||||||
|
return http.StatusInternalServerError
|
||||||
|
case codes.InvalidArgument:
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case codes.DeadlineExceeded:
|
||||||
|
return http.StatusGatewayTimeout
|
||||||
|
case codes.NotFound:
|
||||||
|
return http.StatusNotFound
|
||||||
|
case codes.AlreadyExists:
|
||||||
|
return http.StatusConflict
|
||||||
|
case codes.PermissionDenied:
|
||||||
|
return http.StatusForbidden
|
||||||
|
case codes.Unauthenticated:
|
||||||
|
return http.StatusUnauthorized
|
||||||
|
case codes.ResourceExhausted:
|
||||||
|
return http.StatusTooManyRequests
|
||||||
|
case codes.FailedPrecondition:
|
||||||
|
// Note, this deliberately doesn't translate to the similarly named '412 Precondition Failed' HTTP response status.
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case codes.Aborted:
|
||||||
|
return http.StatusConflict
|
||||||
|
case codes.OutOfRange:
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case codes.Unimplemented:
|
||||||
|
return http.StatusNotImplemented
|
||||||
|
case codes.Internal:
|
||||||
|
return http.StatusInternalServerError
|
||||||
|
case codes.Unavailable:
|
||||||
|
return http.StatusServiceUnavailable
|
||||||
|
case codes.DataLoss:
|
||||||
|
return http.StatusInternalServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
grpclog.Infof("Unknown gRPC error code: %v", code)
|
||||||
|
return http.StatusInternalServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPError uses the mux-configured error handler.
|
||||||
|
func HTTPError(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, r *http.Request, err error) {
|
||||||
|
mux.errorHandler(ctx, mux, marshaler, w, r, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultHTTPErrorHandler is the default error handler.
|
||||||
|
// If "err" is a gRPC Status, the function replies with the status code mapped by HTTPStatusFromCode.
|
||||||
|
// If "err" is a HTTPStatusError, the function replies with the status code provide by that struct. This is
|
||||||
|
// intended to allow passing through of specific statuses via the function set via WithRoutingErrorHandler
|
||||||
|
// for the ServeMux constructor to handle edge cases which the standard mappings in HTTPStatusFromCode
|
||||||
|
// are insufficient for.
|
||||||
|
// If otherwise, it replies with http.StatusInternalServerError.
|
||||||
|
//
|
||||||
|
// The response body written by this function is a Status message marshaled by the Marshaler.
|
||||||
|
func DefaultHTTPErrorHandler(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, r *http.Request, err error) {
|
||||||
|
// return Internal when Marshal failed
|
||||||
|
const fallback = `{"code": 13, "message": "failed to marshal error message"}`
|
||||||
|
|
||||||
|
var customStatus *HTTPStatusError
|
||||||
|
if errors.As(err, &customStatus) {
|
||||||
|
err = customStatus.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
s := status.Convert(err)
|
||||||
|
pb := s.Proto()
|
||||||
|
|
||||||
|
w.Header().Del("Trailer")
|
||||||
|
w.Header().Del("Transfer-Encoding")
|
||||||
|
|
||||||
|
contentType := marshaler.ContentType(pb)
|
||||||
|
w.Header().Set("Content-Type", contentType)
|
||||||
|
|
||||||
|
buf, merr := marshaler.Marshal(pb)
|
||||||
|
if merr != nil {
|
||||||
|
grpclog.Infof("Failed to marshal error message %q: %v", s, merr)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
if _, err := io.WriteString(w, fallback); err != nil {
|
||||||
|
grpclog.Infof("Failed to write response: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
md, ok := ServerMetadataFromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
grpclog.Infof("Failed to extract ServerMetadata from context")
|
||||||
|
}
|
||||||
|
|
||||||
|
handleForwardResponseServerMetadata(w, mux, md)
|
||||||
|
|
||||||
|
// RFC 7230 https://tools.ietf.org/html/rfc7230#section-4.1.2
|
||||||
|
// Unless the request includes a TE header field indicating "trailers"
|
||||||
|
// is acceptable, as described in Section 4.3, a server SHOULD NOT
|
||||||
|
// generate trailer fields that it believes are necessary for the user
|
||||||
|
// agent to receive.
|
||||||
|
var wantsTrailers bool
|
||||||
|
|
||||||
|
if te := r.Header.Get("TE"); strings.Contains(strings.ToLower(te), "trailers") {
|
||||||
|
wantsTrailers = true
|
||||||
|
handleForwardResponseTrailerHeader(w, md)
|
||||||
|
w.Header().Set("Transfer-Encoding", "chunked")
|
||||||
|
}
|
||||||
|
|
||||||
|
st := HTTPStatusFromCode(s.Code())
|
||||||
|
if customStatus != nil {
|
||||||
|
st = customStatus.HTTPStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(st)
|
||||||
|
if _, err := w.Write(buf); err != nil {
|
||||||
|
grpclog.Infof("Failed to write response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if wantsTrailers {
|
||||||
|
handleForwardResponseTrailer(w, md)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultStreamErrorHandler(_ context.Context, err error) *status.Status {
|
||||||
|
return status.Convert(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultRoutingErrorHandler is our default handler for routing errors.
|
||||||
|
// By default http error codes mapped on the following error codes:
|
||||||
|
// NotFound -> grpc.NotFound
|
||||||
|
// StatusBadRequest -> grpc.InvalidArgument
|
||||||
|
// MethodNotAllowed -> grpc.Unimplemented
|
||||||
|
// Other -> grpc.Internal, method is not expecting to be called for anything else
|
||||||
|
func DefaultRoutingErrorHandler(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, r *http.Request, httpStatus int) {
|
||||||
|
sterr := status.Error(codes.Internal, "Unexpected routing error")
|
||||||
|
switch httpStatus {
|
||||||
|
case http.StatusBadRequest:
|
||||||
|
sterr = status.Error(codes.InvalidArgument, http.StatusText(httpStatus))
|
||||||
|
case http.StatusMethodNotAllowed:
|
||||||
|
sterr = status.Error(codes.Unimplemented, http.StatusText(httpStatus))
|
||||||
|
case http.StatusNotFound:
|
||||||
|
sterr = status.Error(codes.NotFound, http.StatusText(httpStatus))
|
||||||
|
}
|
||||||
|
mux.errorHandler(ctx, mux, marshaler, w, r, sterr)
|
||||||
|
}
|
165
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/fieldmask.go
generated
vendored
Normal file
165
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/fieldmask.go
generated
vendored
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"google.golang.org/genproto/protobuf/field_mask"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getFieldByName(fields protoreflect.FieldDescriptors, name string) protoreflect.FieldDescriptor {
|
||||||
|
fd := fields.ByName(protoreflect.Name(name))
|
||||||
|
if fd != nil {
|
||||||
|
return fd
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields.ByJSONName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldMaskFromRequestBody creates a FieldMask printing all complete paths from the JSON body.
|
||||||
|
func FieldMaskFromRequestBody(r io.Reader, msg proto.Message) (*field_mask.FieldMask, error) {
|
||||||
|
fm := &field_mask.FieldMask{}
|
||||||
|
var root interface{}
|
||||||
|
|
||||||
|
if err := json.NewDecoder(r).Decode(&root); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
return fm, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
queue := []fieldMaskPathItem{{node: root, msg: msg.ProtoReflect()}}
|
||||||
|
for len(queue) > 0 {
|
||||||
|
// dequeue an item
|
||||||
|
item := queue[0]
|
||||||
|
queue = queue[1:]
|
||||||
|
|
||||||
|
m, ok := item.node.(map[string]interface{})
|
||||||
|
switch {
|
||||||
|
case ok:
|
||||||
|
// if the item is an object, then enqueue all of its children
|
||||||
|
for k, v := range m {
|
||||||
|
if item.msg == nil {
|
||||||
|
return nil, fmt.Errorf("JSON structure did not match request type")
|
||||||
|
}
|
||||||
|
|
||||||
|
fd := getFieldByName(item.msg.Descriptor().Fields(), k)
|
||||||
|
if fd == nil {
|
||||||
|
return nil, fmt.Errorf("could not find field %q in %q", k, item.msg.Descriptor().FullName())
|
||||||
|
}
|
||||||
|
|
||||||
|
if isDynamicProtoMessage(fd.Message()) {
|
||||||
|
for _, p := range buildPathsBlindly(k, v) {
|
||||||
|
newPath := p
|
||||||
|
if item.path != "" {
|
||||||
|
newPath = item.path + "." + newPath
|
||||||
|
}
|
||||||
|
queue = append(queue, fieldMaskPathItem{path: newPath})
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if isProtobufAnyMessage(fd.Message()) {
|
||||||
|
_, hasTypeField := v.(map[string]interface{})["@type"]
|
||||||
|
if hasTypeField {
|
||||||
|
queue = append(queue, fieldMaskPathItem{path: k})
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("could not find field @type in %q in message %q", k, item.msg.Descriptor().FullName())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
child := fieldMaskPathItem{
|
||||||
|
node: v,
|
||||||
|
}
|
||||||
|
if item.path == "" {
|
||||||
|
child.path = string(fd.FullName().Name())
|
||||||
|
} else {
|
||||||
|
child.path = item.path + "." + string(fd.FullName().Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case fd.IsList(), fd.IsMap():
|
||||||
|
// As per: https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/field_mask.proto#L85-L86
|
||||||
|
// Do not recurse into repeated fields. The repeated field goes on the end of the path and we stop.
|
||||||
|
fm.Paths = append(fm.Paths, child.path)
|
||||||
|
case fd.Message() != nil:
|
||||||
|
child.msg = item.msg.Get(fd).Message()
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
queue = append(queue, child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case len(item.path) > 0:
|
||||||
|
// otherwise, it's a leaf node so print its path
|
||||||
|
fm.Paths = append(fm.Paths, item.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort for deterministic output in the presence
|
||||||
|
// of repeated fields.
|
||||||
|
sort.Strings(fm.Paths)
|
||||||
|
|
||||||
|
return fm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isProtobufAnyMessage(md protoreflect.MessageDescriptor) bool {
|
||||||
|
return md != nil && (md.FullName() == "google.protobuf.Any")
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDynamicProtoMessage(md protoreflect.MessageDescriptor) bool {
|
||||||
|
return md != nil && (md.FullName() == "google.protobuf.Struct" || md.FullName() == "google.protobuf.Value")
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildPathsBlindly does not attempt to match proto field names to the
|
||||||
|
// json value keys. Instead it relies completely on the structure of
|
||||||
|
// the unmarshalled json contained within in.
|
||||||
|
// Returns a slice containing all subpaths with the root at the
|
||||||
|
// passed in name and json value.
|
||||||
|
func buildPathsBlindly(name string, in interface{}) []string {
|
||||||
|
m, ok := in.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return []string{name}
|
||||||
|
}
|
||||||
|
|
||||||
|
var paths []string
|
||||||
|
queue := []fieldMaskPathItem{{path: name, node: m}}
|
||||||
|
for len(queue) > 0 {
|
||||||
|
cur := queue[0]
|
||||||
|
queue = queue[1:]
|
||||||
|
|
||||||
|
m, ok := cur.node.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
// This should never happen since we should always check that we only add
|
||||||
|
// nodes of type map[string]interface{} to the queue.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for k, v := range m {
|
||||||
|
if mi, ok := v.(map[string]interface{}); ok {
|
||||||
|
queue = append(queue, fieldMaskPathItem{path: cur.path + "." + k, node: mi})
|
||||||
|
} else {
|
||||||
|
// This is not a struct, so there are no more levels to descend.
|
||||||
|
curPath := cur.path + "." + k
|
||||||
|
paths = append(paths, curPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return paths
|
||||||
|
}
|
||||||
|
|
||||||
|
// fieldMaskPathItem stores a in-progress deconstruction of a path for a fieldmask
|
||||||
|
type fieldMaskPathItem struct {
|
||||||
|
// the list of prior fields leading up to node connected by dots
|
||||||
|
path string
|
||||||
|
|
||||||
|
// a generic decoded json object the current item to inspect for further path extraction
|
||||||
|
node interface{}
|
||||||
|
|
||||||
|
// parent message
|
||||||
|
msg protoreflect.Message
|
||||||
|
}
|
200
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/handler.go
generated
vendored
Normal file
200
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/handler.go
generated
vendored
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/textproto"
|
||||||
|
|
||||||
|
"google.golang.org/genproto/googleapis/api/httpbody"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ForwardResponseStream forwards the stream from gRPC server to REST client.
|
||||||
|
func ForwardResponseStream(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, req *http.Request, recv func() (proto.Message, error), opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
|
||||||
|
f, ok := w.(http.Flusher)
|
||||||
|
if !ok {
|
||||||
|
grpclog.Infof("Flush not supported in %T", w)
|
||||||
|
http.Error(w, "unexpected type of web server", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
md, ok := ServerMetadataFromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
grpclog.Infof("Failed to extract ServerMetadata from context")
|
||||||
|
http.Error(w, "unexpected error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handleForwardResponseServerMetadata(w, mux, md)
|
||||||
|
|
||||||
|
w.Header().Set("Transfer-Encoding", "chunked")
|
||||||
|
if err := handleForwardResponseOptions(ctx, w, nil, opts); err != nil {
|
||||||
|
HTTPError(ctx, mux, marshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var delimiter []byte
|
||||||
|
if d, ok := marshaler.(Delimited); ok {
|
||||||
|
delimiter = d.Delimiter()
|
||||||
|
} else {
|
||||||
|
delimiter = []byte("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
var wroteHeader bool
|
||||||
|
for {
|
||||||
|
resp, err := recv()
|
||||||
|
if err == io.EOF {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
handleForwardResponseStreamError(ctx, wroteHeader, marshaler, w, req, mux, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := handleForwardResponseOptions(ctx, w, resp, opts); err != nil {
|
||||||
|
handleForwardResponseStreamError(ctx, wroteHeader, marshaler, w, req, mux, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !wroteHeader {
|
||||||
|
w.Header().Set("Content-Type", marshaler.ContentType(resp))
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf []byte
|
||||||
|
httpBody, isHTTPBody := resp.(*httpbody.HttpBody)
|
||||||
|
switch {
|
||||||
|
case resp == nil:
|
||||||
|
buf, err = marshaler.Marshal(errorChunk(status.New(codes.Internal, "empty response")))
|
||||||
|
case isHTTPBody:
|
||||||
|
buf = httpBody.GetData()
|
||||||
|
default:
|
||||||
|
result := map[string]interface{}{"result": resp}
|
||||||
|
if rb, ok := resp.(responseBody); ok {
|
||||||
|
result["result"] = rb.XXX_ResponseBody()
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err = marshaler.Marshal(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
grpclog.Infof("Failed to marshal response chunk: %v", err)
|
||||||
|
handleForwardResponseStreamError(ctx, wroteHeader, marshaler, w, req, mux, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err = w.Write(buf); err != nil {
|
||||||
|
grpclog.Infof("Failed to send response chunk: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wroteHeader = true
|
||||||
|
if _, err = w.Write(delimiter); err != nil {
|
||||||
|
grpclog.Infof("Failed to send delimiter chunk: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleForwardResponseServerMetadata(w http.ResponseWriter, mux *ServeMux, md ServerMetadata) {
|
||||||
|
for k, vs := range md.HeaderMD {
|
||||||
|
if h, ok := mux.outgoingHeaderMatcher(k); ok {
|
||||||
|
for _, v := range vs {
|
||||||
|
w.Header().Add(h, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleForwardResponseTrailerHeader(w http.ResponseWriter, md ServerMetadata) {
|
||||||
|
for k := range md.TrailerMD {
|
||||||
|
tKey := textproto.CanonicalMIMEHeaderKey(fmt.Sprintf("%s%s", MetadataTrailerPrefix, k))
|
||||||
|
w.Header().Add("Trailer", tKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleForwardResponseTrailer(w http.ResponseWriter, md ServerMetadata) {
|
||||||
|
for k, vs := range md.TrailerMD {
|
||||||
|
tKey := fmt.Sprintf("%s%s", MetadataTrailerPrefix, k)
|
||||||
|
for _, v := range vs {
|
||||||
|
w.Header().Add(tKey, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// responseBody interface contains method for getting field for marshaling to the response body
|
||||||
|
// this method is generated for response struct from the value of `response_body` in the `google.api.HttpRule`
|
||||||
|
type responseBody interface {
|
||||||
|
XXX_ResponseBody() interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForwardResponseMessage forwards the message "resp" from gRPC server to REST client.
|
||||||
|
func ForwardResponseMessage(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, req *http.Request, resp proto.Message, opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
|
||||||
|
md, ok := ServerMetadataFromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
grpclog.Infof("Failed to extract ServerMetadata from context")
|
||||||
|
}
|
||||||
|
|
||||||
|
handleForwardResponseServerMetadata(w, mux, md)
|
||||||
|
handleForwardResponseTrailerHeader(w, md)
|
||||||
|
|
||||||
|
contentType := marshaler.ContentType(resp)
|
||||||
|
w.Header().Set("Content-Type", contentType)
|
||||||
|
|
||||||
|
if err := handleForwardResponseOptions(ctx, w, resp, opts); err != nil {
|
||||||
|
HTTPError(ctx, mux, marshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var buf []byte
|
||||||
|
var err error
|
||||||
|
if rb, ok := resp.(responseBody); ok {
|
||||||
|
buf, err = marshaler.Marshal(rb.XXX_ResponseBody())
|
||||||
|
} else {
|
||||||
|
buf, err = marshaler.Marshal(resp)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
grpclog.Infof("Marshal error: %v", err)
|
||||||
|
HTTPError(ctx, mux, marshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = w.Write(buf); err != nil {
|
||||||
|
grpclog.Infof("Failed to write response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleForwardResponseTrailer(w, md)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleForwardResponseOptions(ctx context.Context, w http.ResponseWriter, resp proto.Message, opts []func(context.Context, http.ResponseWriter, proto.Message) error) error {
|
||||||
|
if len(opts) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, opt := range opts {
|
||||||
|
if err := opt(ctx, w, resp); err != nil {
|
||||||
|
grpclog.Infof("Error handling ForwardResponseOptions: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleForwardResponseStreamError(ctx context.Context, wroteHeader bool, marshaler Marshaler, w http.ResponseWriter, req *http.Request, mux *ServeMux, err error) {
|
||||||
|
st := mux.streamErrorHandler(ctx, err)
|
||||||
|
if !wroteHeader {
|
||||||
|
w.WriteHeader(HTTPStatusFromCode(st.Code()))
|
||||||
|
}
|
||||||
|
buf, merr := marshaler.Marshal(errorChunk(st))
|
||||||
|
if merr != nil {
|
||||||
|
grpclog.Infof("Failed to marshal an error: %v", merr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, werr := w.Write(buf); werr != nil {
|
||||||
|
grpclog.Infof("Failed to notify error to client: %v", werr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func errorChunk(st *status.Status) map[string]proto.Message {
|
||||||
|
return map[string]proto.Message{"error": st.Proto()}
|
||||||
|
}
|
32
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_httpbodyproto.go
generated
vendored
Normal file
32
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_httpbodyproto.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"google.golang.org/genproto/googleapis/api/httpbody"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPBodyMarshaler is a Marshaler which supports marshaling of a
|
||||||
|
// google.api.HttpBody message as the full response body if it is
|
||||||
|
// the actual message used as the response. If not, then this will
|
||||||
|
// simply fallback to the Marshaler specified as its default Marshaler.
|
||||||
|
type HTTPBodyMarshaler struct {
|
||||||
|
Marshaler
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentType returns its specified content type in case v is a
|
||||||
|
// google.api.HttpBody message, otherwise it will fall back to the default Marshalers
|
||||||
|
// content type.
|
||||||
|
func (h *HTTPBodyMarshaler) ContentType(v interface{}) string {
|
||||||
|
if httpBody, ok := v.(*httpbody.HttpBody); ok {
|
||||||
|
return httpBody.GetContentType()
|
||||||
|
}
|
||||||
|
return h.Marshaler.ContentType(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal marshals "v" by returning the body bytes if v is a
|
||||||
|
// google.api.HttpBody message, otherwise it falls back to the default Marshaler.
|
||||||
|
func (h *HTTPBodyMarshaler) Marshal(v interface{}) ([]byte, error) {
|
||||||
|
if httpBody, ok := v.(*httpbody.HttpBody); ok {
|
||||||
|
return httpBody.Data, nil
|
||||||
|
}
|
||||||
|
return h.Marshaler.Marshal(v)
|
||||||
|
}
|
45
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_json.go
generated
vendored
Normal file
45
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_json.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// JSONBuiltin is a Marshaler which marshals/unmarshals into/from JSON
|
||||||
|
// with the standard "encoding/json" package of Golang.
|
||||||
|
// Although it is generally faster for simple proto messages than JSONPb,
|
||||||
|
// it does not support advanced features of protobuf, e.g. map, oneof, ....
|
||||||
|
//
|
||||||
|
// The NewEncoder and NewDecoder types return *json.Encoder and
|
||||||
|
// *json.Decoder respectively.
|
||||||
|
type JSONBuiltin struct{}
|
||||||
|
|
||||||
|
// ContentType always Returns "application/json".
|
||||||
|
func (*JSONBuiltin) ContentType(_ interface{}) string {
|
||||||
|
return "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal marshals "v" into JSON
|
||||||
|
func (j *JSONBuiltin) Marshal(v interface{}) ([]byte, error) {
|
||||||
|
return json.Marshal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal unmarshals JSON data into "v".
|
||||||
|
func (j *JSONBuiltin) Unmarshal(data []byte, v interface{}) error {
|
||||||
|
return json.Unmarshal(data, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder returns a Decoder which reads JSON stream from "r".
|
||||||
|
func (j *JSONBuiltin) NewDecoder(r io.Reader) Decoder {
|
||||||
|
return json.NewDecoder(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder returns an Encoder which writes JSON stream into "w".
|
||||||
|
func (j *JSONBuiltin) NewEncoder(w io.Writer) Encoder {
|
||||||
|
return json.NewEncoder(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delimiter for newline encoded JSON streams.
|
||||||
|
func (j *JSONBuiltin) Delimiter() []byte {
|
||||||
|
return []byte("\n")
|
||||||
|
}
|
307
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_jsonpb.go
generated
vendored
Normal file
307
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_jsonpb.go
generated
vendored
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// JSONPb is a Marshaler which marshals/unmarshals into/from JSON
|
||||||
|
// with the "google.golang.org/protobuf/encoding/protojson" marshaler.
|
||||||
|
// It supports the full functionality of protobuf unlike JSONBuiltin.
|
||||||
|
//
|
||||||
|
// The NewDecoder method returns a DecoderWrapper, so the underlying
|
||||||
|
// *json.Decoder methods can be used.
|
||||||
|
type JSONPb struct {
|
||||||
|
protojson.MarshalOptions
|
||||||
|
protojson.UnmarshalOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentType always returns "application/json".
|
||||||
|
func (*JSONPb) ContentType(_ interface{}) string {
|
||||||
|
return "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal marshals "v" into JSON.
|
||||||
|
func (j *JSONPb) Marshal(v interface{}) ([]byte, error) {
|
||||||
|
if _, ok := v.(proto.Message); !ok {
|
||||||
|
return j.marshalNonProtoField(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := j.marshalTo(&buf, v); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JSONPb) marshalTo(w io.Writer, v interface{}) error {
|
||||||
|
p, ok := v.(proto.Message)
|
||||||
|
if !ok {
|
||||||
|
buf, err := j.marshalNonProtoField(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = w.Write(buf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b, err := j.MarshalOptions.Marshal(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = w.Write(b)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// protoMessageType is stored to prevent constant lookup of the same type at runtime.
|
||||||
|
protoMessageType = reflect.TypeOf((*proto.Message)(nil)).Elem()
|
||||||
|
)
|
||||||
|
|
||||||
|
// marshalNonProto marshals a non-message field of a protobuf message.
|
||||||
|
// This function does not correctly marshal arbitrary data structures into JSON,
|
||||||
|
// it is only capable of marshaling non-message field values of protobuf,
|
||||||
|
// i.e. primitive types, enums; pointers to primitives or enums; maps from
|
||||||
|
// integer/string types to primitives/enums/pointers to messages.
|
||||||
|
func (j *JSONPb) marshalNonProtoField(v interface{}) ([]byte, error) {
|
||||||
|
if v == nil {
|
||||||
|
return []byte("null"), nil
|
||||||
|
}
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
for rv.Kind() == reflect.Ptr {
|
||||||
|
if rv.IsNil() {
|
||||||
|
return []byte("null"), nil
|
||||||
|
}
|
||||||
|
rv = rv.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if rv.Kind() == reflect.Slice {
|
||||||
|
if rv.IsNil() {
|
||||||
|
if j.EmitUnpopulated {
|
||||||
|
return []byte("[]"), nil
|
||||||
|
}
|
||||||
|
return []byte("null"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if rv.Type().Elem().Implements(protoMessageType) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := buf.WriteByte('[')
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for i := 0; i < rv.Len(); i++ {
|
||||||
|
if i != 0 {
|
||||||
|
err = buf.WriteByte(',')
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err = j.marshalTo(&buf, rv.Index(i).Interface().(proto.Message)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = buf.WriteByte(']')
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rv.Kind() == reflect.Map {
|
||||||
|
m := make(map[string]*json.RawMessage)
|
||||||
|
for _, k := range rv.MapKeys() {
|
||||||
|
buf, err := j.Marshal(rv.MapIndex(k).Interface())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m[fmt.Sprintf("%v", k.Interface())] = (*json.RawMessage)(&buf)
|
||||||
|
}
|
||||||
|
if j.Indent != "" {
|
||||||
|
return json.MarshalIndent(m, "", j.Indent)
|
||||||
|
}
|
||||||
|
return json.Marshal(m)
|
||||||
|
}
|
||||||
|
if enum, ok := rv.Interface().(protoEnum); ok && !j.UseEnumNumbers {
|
||||||
|
return json.Marshal(enum.String())
|
||||||
|
}
|
||||||
|
return json.Marshal(rv.Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal unmarshals JSON "data" into "v"
|
||||||
|
func (j *JSONPb) Unmarshal(data []byte, v interface{}) error {
|
||||||
|
return unmarshalJSONPb(data, j.UnmarshalOptions, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder returns a Decoder which reads JSON stream from "r".
|
||||||
|
func (j *JSONPb) NewDecoder(r io.Reader) Decoder {
|
||||||
|
d := json.NewDecoder(r)
|
||||||
|
return DecoderWrapper{
|
||||||
|
Decoder: d,
|
||||||
|
UnmarshalOptions: j.UnmarshalOptions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecoderWrapper is a wrapper around a *json.Decoder that adds
|
||||||
|
// support for protos to the Decode method.
|
||||||
|
type DecoderWrapper struct {
|
||||||
|
*json.Decoder
|
||||||
|
protojson.UnmarshalOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode wraps the embedded decoder's Decode method to support
|
||||||
|
// protos using a jsonpb.Unmarshaler.
|
||||||
|
func (d DecoderWrapper) Decode(v interface{}) error {
|
||||||
|
return decodeJSONPb(d.Decoder, d.UnmarshalOptions, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder returns an Encoder which writes JSON stream into "w".
|
||||||
|
func (j *JSONPb) NewEncoder(w io.Writer) Encoder {
|
||||||
|
return EncoderFunc(func(v interface{}) error {
|
||||||
|
if err := j.marshalTo(w, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// mimic json.Encoder by adding a newline (makes output
|
||||||
|
// easier to read when it contains multiple encoded items)
|
||||||
|
_, err := w.Write(j.Delimiter())
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalJSONPb(data []byte, unmarshaler protojson.UnmarshalOptions, v interface{}) error {
|
||||||
|
d := json.NewDecoder(bytes.NewReader(data))
|
||||||
|
return decodeJSONPb(d, unmarshaler, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeJSONPb(d *json.Decoder, unmarshaler protojson.UnmarshalOptions, v interface{}) error {
|
||||||
|
p, ok := v.(proto.Message)
|
||||||
|
if !ok {
|
||||||
|
return decodeNonProtoField(d, unmarshaler, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode into bytes for marshalling
|
||||||
|
var b json.RawMessage
|
||||||
|
err := d.Decode(&b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshaler.Unmarshal([]byte(b), p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeNonProtoField(d *json.Decoder, unmarshaler protojson.UnmarshalOptions, v interface{}) error {
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
if rv.Kind() != reflect.Ptr {
|
||||||
|
return fmt.Errorf("%T is not a pointer", v)
|
||||||
|
}
|
||||||
|
for rv.Kind() == reflect.Ptr {
|
||||||
|
if rv.IsNil() {
|
||||||
|
rv.Set(reflect.New(rv.Type().Elem()))
|
||||||
|
}
|
||||||
|
if rv.Type().ConvertibleTo(typeProtoMessage) {
|
||||||
|
// Decode into bytes for marshalling
|
||||||
|
var b json.RawMessage
|
||||||
|
err := d.Decode(&b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshaler.Unmarshal([]byte(b), rv.Interface().(proto.Message))
|
||||||
|
}
|
||||||
|
rv = rv.Elem()
|
||||||
|
}
|
||||||
|
if rv.Kind() == reflect.Map {
|
||||||
|
if rv.IsNil() {
|
||||||
|
rv.Set(reflect.MakeMap(rv.Type()))
|
||||||
|
}
|
||||||
|
conv, ok := convFromType[rv.Type().Key().Kind()]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unsupported type of map field key: %v", rv.Type().Key())
|
||||||
|
}
|
||||||
|
|
||||||
|
m := make(map[string]*json.RawMessage)
|
||||||
|
if err := d.Decode(&m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for k, v := range m {
|
||||||
|
result := conv.Call([]reflect.Value{reflect.ValueOf(k)})
|
||||||
|
if err := result[1].Interface(); err != nil {
|
||||||
|
return err.(error)
|
||||||
|
}
|
||||||
|
bk := result[0]
|
||||||
|
bv := reflect.New(rv.Type().Elem())
|
||||||
|
if err := unmarshalJSONPb([]byte(*v), unmarshaler, bv.Interface()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rv.SetMapIndex(bk, bv.Elem())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if rv.Kind() == reflect.Slice {
|
||||||
|
var sl []json.RawMessage
|
||||||
|
if err := d.Decode(&sl); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if sl != nil {
|
||||||
|
rv.Set(reflect.MakeSlice(rv.Type(), 0, 0))
|
||||||
|
}
|
||||||
|
for _, item := range sl {
|
||||||
|
bv := reflect.New(rv.Type().Elem())
|
||||||
|
if err := unmarshalJSONPb([]byte(item), unmarshaler, bv.Interface()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rv.Set(reflect.Append(rv, bv.Elem()))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if _, ok := rv.Interface().(protoEnum); ok {
|
||||||
|
var repr interface{}
|
||||||
|
if err := d.Decode(&repr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch v := repr.(type) {
|
||||||
|
case string:
|
||||||
|
// TODO(yugui) Should use proto.StructProperties?
|
||||||
|
return fmt.Errorf("unmarshaling of symbolic enum %q not supported: %T", repr, rv.Interface())
|
||||||
|
case float64:
|
||||||
|
rv.Set(reflect.ValueOf(int32(v)).Convert(rv.Type()))
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("cannot assign %#v into Go type %T", repr, rv.Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d.Decode(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type protoEnum interface {
|
||||||
|
fmt.Stringer
|
||||||
|
EnumDescriptor() ([]byte, []int)
|
||||||
|
}
|
||||||
|
|
||||||
|
var typeProtoMessage = reflect.TypeOf((*proto.Message)(nil)).Elem()
|
||||||
|
|
||||||
|
// Delimiter for newline encoded JSON streams.
|
||||||
|
func (j *JSONPb) Delimiter() []byte {
|
||||||
|
return []byte("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
convFromType = map[reflect.Kind]reflect.Value{
|
||||||
|
reflect.String: reflect.ValueOf(String),
|
||||||
|
reflect.Bool: reflect.ValueOf(Bool),
|
||||||
|
reflect.Float64: reflect.ValueOf(Float64),
|
||||||
|
reflect.Float32: reflect.ValueOf(Float32),
|
||||||
|
reflect.Int64: reflect.ValueOf(Int64),
|
||||||
|
reflect.Int32: reflect.ValueOf(Int32),
|
||||||
|
reflect.Uint64: reflect.ValueOf(Uint64),
|
||||||
|
reflect.Uint32: reflect.ValueOf(Uint32),
|
||||||
|
reflect.Slice: reflect.ValueOf(Bytes),
|
||||||
|
}
|
||||||
|
)
|
63
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_proto.go
generated
vendored
Normal file
63
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshal_proto.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProtoMarshaller is a Marshaller which marshals/unmarshals into/from serialize proto bytes
|
||||||
|
type ProtoMarshaller struct{}
|
||||||
|
|
||||||
|
// ContentType always returns "application/octet-stream".
|
||||||
|
func (*ProtoMarshaller) ContentType(_ interface{}) string {
|
||||||
|
return "application/octet-stream"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal marshals "value" into Proto
|
||||||
|
func (*ProtoMarshaller) Marshal(value interface{}) ([]byte, error) {
|
||||||
|
message, ok := value.(proto.Message)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("unable to marshal non proto field")
|
||||||
|
}
|
||||||
|
return proto.Marshal(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal unmarshals proto "data" into "value"
|
||||||
|
func (*ProtoMarshaller) Unmarshal(data []byte, value interface{}) error {
|
||||||
|
message, ok := value.(proto.Message)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("unable to unmarshal non proto field")
|
||||||
|
}
|
||||||
|
return proto.Unmarshal(data, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder returns a Decoder which reads proto stream from "reader".
|
||||||
|
func (marshaller *ProtoMarshaller) NewDecoder(reader io.Reader) Decoder {
|
||||||
|
return DecoderFunc(func(value interface{}) error {
|
||||||
|
buffer, err := ioutil.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return marshaller.Unmarshal(buffer, value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder returns an Encoder which writes proto stream into "writer".
|
||||||
|
func (marshaller *ProtoMarshaller) NewEncoder(writer io.Writer) Encoder {
|
||||||
|
return EncoderFunc(func(value interface{}) error {
|
||||||
|
buffer, err := marshaller.Marshal(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = writer.Write(buffer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
50
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshaler.go
generated
vendored
Normal file
50
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshaler.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Marshaler defines a conversion between byte sequence and gRPC payloads / fields.
|
||||||
|
type Marshaler interface {
|
||||||
|
// Marshal marshals "v" into byte sequence.
|
||||||
|
Marshal(v interface{}) ([]byte, error)
|
||||||
|
// Unmarshal unmarshals "data" into "v".
|
||||||
|
// "v" must be a pointer value.
|
||||||
|
Unmarshal(data []byte, v interface{}) error
|
||||||
|
// NewDecoder returns a Decoder which reads byte sequence from "r".
|
||||||
|
NewDecoder(r io.Reader) Decoder
|
||||||
|
// NewEncoder returns an Encoder which writes bytes sequence into "w".
|
||||||
|
NewEncoder(w io.Writer) Encoder
|
||||||
|
// ContentType returns the Content-Type which this marshaler is responsible for.
|
||||||
|
// The parameter describes the type which is being marshalled, which can sometimes
|
||||||
|
// affect the content type returned.
|
||||||
|
ContentType(v interface{}) string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decoder decodes a byte sequence
|
||||||
|
type Decoder interface {
|
||||||
|
Decode(v interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encoder encodes gRPC payloads / fields into byte sequence.
|
||||||
|
type Encoder interface {
|
||||||
|
Encode(v interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecoderFunc adapts an decoder function into Decoder.
|
||||||
|
type DecoderFunc func(v interface{}) error
|
||||||
|
|
||||||
|
// Decode delegates invocations to the underlying function itself.
|
||||||
|
func (f DecoderFunc) Decode(v interface{}) error { return f(v) }
|
||||||
|
|
||||||
|
// EncoderFunc adapts an encoder function into Encoder
|
||||||
|
type EncoderFunc func(v interface{}) error
|
||||||
|
|
||||||
|
// Encode delegates invocations to the underlying function itself.
|
||||||
|
func (f EncoderFunc) Encode(v interface{}) error { return f(v) }
|
||||||
|
|
||||||
|
// Delimited defines the streaming delimiter.
|
||||||
|
type Delimited interface {
|
||||||
|
// Delimiter returns the record separator for the stream.
|
||||||
|
Delimiter() []byte
|
||||||
|
}
|
109
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshaler_registry.go
generated
vendored
Normal file
109
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/marshaler_registry.go
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"mime"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MIMEWildcard is the fallback MIME type used for requests which do not match
|
||||||
|
// a registered MIME type.
|
||||||
|
const MIMEWildcard = "*"
|
||||||
|
|
||||||
|
var (
|
||||||
|
acceptHeader = http.CanonicalHeaderKey("Accept")
|
||||||
|
contentTypeHeader = http.CanonicalHeaderKey("Content-Type")
|
||||||
|
|
||||||
|
defaultMarshaler = &HTTPBodyMarshaler{
|
||||||
|
Marshaler: &JSONPb{
|
||||||
|
MarshalOptions: protojson.MarshalOptions{
|
||||||
|
EmitUnpopulated: true,
|
||||||
|
},
|
||||||
|
UnmarshalOptions: protojson.UnmarshalOptions{
|
||||||
|
DiscardUnknown: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// MarshalerForRequest returns the inbound/outbound marshalers for this request.
|
||||||
|
// It checks the registry on the ServeMux for the MIME type set by the Content-Type header.
|
||||||
|
// If it isn't set (or the request Content-Type is empty), checks for "*".
|
||||||
|
// If there are multiple Content-Type headers set, choose the first one that it can
|
||||||
|
// exactly match in the registry.
|
||||||
|
// Otherwise, it follows the above logic for "*"/InboundMarshaler/OutboundMarshaler.
|
||||||
|
func MarshalerForRequest(mux *ServeMux, r *http.Request) (inbound Marshaler, outbound Marshaler) {
|
||||||
|
for _, acceptVal := range r.Header[acceptHeader] {
|
||||||
|
if m, ok := mux.marshalers.mimeMap[acceptVal]; ok {
|
||||||
|
outbound = m
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, contentTypeVal := range r.Header[contentTypeHeader] {
|
||||||
|
contentType, _, err := mime.ParseMediaType(contentTypeVal)
|
||||||
|
if err != nil {
|
||||||
|
grpclog.Infof("Failed to parse Content-Type %s: %v", contentTypeVal, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if m, ok := mux.marshalers.mimeMap[contentType]; ok {
|
||||||
|
inbound = m
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if inbound == nil {
|
||||||
|
inbound = mux.marshalers.mimeMap[MIMEWildcard]
|
||||||
|
}
|
||||||
|
if outbound == nil {
|
||||||
|
outbound = inbound
|
||||||
|
}
|
||||||
|
|
||||||
|
return inbound, outbound
|
||||||
|
}
|
||||||
|
|
||||||
|
// marshalerRegistry is a mapping from MIME types to Marshalers.
|
||||||
|
type marshalerRegistry struct {
|
||||||
|
mimeMap map[string]Marshaler
|
||||||
|
}
|
||||||
|
|
||||||
|
// add adds a marshaler for a case-sensitive MIME type string ("*" to match any
|
||||||
|
// MIME type).
|
||||||
|
func (m marshalerRegistry) add(mime string, marshaler Marshaler) error {
|
||||||
|
if len(mime) == 0 {
|
||||||
|
return errors.New("empty MIME type")
|
||||||
|
}
|
||||||
|
|
||||||
|
m.mimeMap[mime] = marshaler
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeMarshalerMIMERegistry returns a new registry of marshalers.
|
||||||
|
// It allows for a mapping of case-sensitive Content-Type MIME type string to runtime.Marshaler interfaces.
|
||||||
|
//
|
||||||
|
// For example, you could allow the client to specify the use of the runtime.JSONPb marshaler
|
||||||
|
// with a "application/jsonpb" Content-Type and the use of the runtime.JSONBuiltin marshaler
|
||||||
|
// with a "application/json" Content-Type.
|
||||||
|
// "*" can be used to match any Content-Type.
|
||||||
|
// This can be attached to a ServerMux with the marshaler option.
|
||||||
|
func makeMarshalerMIMERegistry() marshalerRegistry {
|
||||||
|
return marshalerRegistry{
|
||||||
|
mimeMap: map[string]Marshaler{
|
||||||
|
MIMEWildcard: defaultMarshaler,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMarshalerOption returns a ServeMuxOption which associates inbound and outbound
|
||||||
|
// Marshalers to a MIME type in mux.
|
||||||
|
func WithMarshalerOption(mime string, marshaler Marshaler) ServeMuxOption {
|
||||||
|
return func(mux *ServeMux) {
|
||||||
|
if err := mux.marshalers.add(mime, marshaler); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
299
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/mux.go
generated
vendored
Normal file
299
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/mux.go
generated
vendored
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/textproto"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A HandlerFunc handles a specific pair of path pattern and HTTP method.
|
||||||
|
type HandlerFunc func(w http.ResponseWriter, r *http.Request, pathParams map[string]string)
|
||||||
|
|
||||||
|
// ServeMux is a request multiplexer for grpc-gateway.
|
||||||
|
// It matches http requests to patterns and invokes the corresponding handler.
|
||||||
|
type ServeMux struct {
|
||||||
|
// handlers maps HTTP method to a list of handlers.
|
||||||
|
handlers map[string][]handler
|
||||||
|
forwardResponseOptions []func(context.Context, http.ResponseWriter, proto.Message) error
|
||||||
|
marshalers marshalerRegistry
|
||||||
|
incomingHeaderMatcher HeaderMatcherFunc
|
||||||
|
outgoingHeaderMatcher HeaderMatcherFunc
|
||||||
|
metadataAnnotators []func(context.Context, *http.Request) metadata.MD
|
||||||
|
errorHandler ErrorHandlerFunc
|
||||||
|
streamErrorHandler StreamErrorHandlerFunc
|
||||||
|
routingErrorHandler RoutingErrorHandlerFunc
|
||||||
|
disablePathLengthFallback bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeMuxOption is an option that can be given to a ServeMux on construction.
|
||||||
|
type ServeMuxOption func(*ServeMux)
|
||||||
|
|
||||||
|
// WithForwardResponseOption returns a ServeMuxOption representing the forwardResponseOption.
|
||||||
|
//
|
||||||
|
// forwardResponseOption is an option that will be called on the relevant context.Context,
|
||||||
|
// http.ResponseWriter, and proto.Message before every forwarded response.
|
||||||
|
//
|
||||||
|
// The message may be nil in the case where just a header is being sent.
|
||||||
|
func WithForwardResponseOption(forwardResponseOption func(context.Context, http.ResponseWriter, proto.Message) error) ServeMuxOption {
|
||||||
|
return func(serveMux *ServeMux) {
|
||||||
|
serveMux.forwardResponseOptions = append(serveMux.forwardResponseOptions, forwardResponseOption)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetQueryParameterParser sets the query parameter parser, used to populate message from query parameters.
|
||||||
|
// Configuring this will mean the generated OpenAPI output is no longer correct, and it should be
|
||||||
|
// done with careful consideration.
|
||||||
|
func SetQueryParameterParser(queryParameterParser QueryParameterParser) ServeMuxOption {
|
||||||
|
return func(serveMux *ServeMux) {
|
||||||
|
currentQueryParser = queryParameterParser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderMatcherFunc checks whether a header key should be forwarded to/from gRPC context.
|
||||||
|
type HeaderMatcherFunc func(string) (string, bool)
|
||||||
|
|
||||||
|
// DefaultHeaderMatcher is used to pass http request headers to/from gRPC context. This adds permanent HTTP header
|
||||||
|
// keys (as specified by the IANA) to gRPC context with grpcgateway- prefix. HTTP headers that start with
|
||||||
|
// 'Grpc-Metadata-' are mapped to gRPC metadata after removing prefix 'Grpc-Metadata-'.
|
||||||
|
func DefaultHeaderMatcher(key string) (string, bool) {
|
||||||
|
key = textproto.CanonicalMIMEHeaderKey(key)
|
||||||
|
if isPermanentHTTPHeader(key) {
|
||||||
|
return MetadataPrefix + key, true
|
||||||
|
} else if strings.HasPrefix(key, MetadataHeaderPrefix) {
|
||||||
|
return key[len(MetadataHeaderPrefix):], true
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithIncomingHeaderMatcher returns a ServeMuxOption representing a headerMatcher for incoming request to gateway.
|
||||||
|
//
|
||||||
|
// This matcher will be called with each header in http.Request. If matcher returns true, that header will be
|
||||||
|
// passed to gRPC context. To transform the header before passing to gRPC context, matcher should return modified header.
|
||||||
|
func WithIncomingHeaderMatcher(fn HeaderMatcherFunc) ServeMuxOption {
|
||||||
|
return func(mux *ServeMux) {
|
||||||
|
mux.incomingHeaderMatcher = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithOutgoingHeaderMatcher returns a ServeMuxOption representing a headerMatcher for outgoing response from gateway.
|
||||||
|
//
|
||||||
|
// This matcher will be called with each header in response header metadata. If matcher returns true, that header will be
|
||||||
|
// passed to http response returned from gateway. To transform the header before passing to response,
|
||||||
|
// matcher should return modified header.
|
||||||
|
func WithOutgoingHeaderMatcher(fn HeaderMatcherFunc) ServeMuxOption {
|
||||||
|
return func(mux *ServeMux) {
|
||||||
|
mux.outgoingHeaderMatcher = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMetadata returns a ServeMuxOption for passing metadata to a gRPC context.
|
||||||
|
//
|
||||||
|
// This can be used by services that need to read from http.Request and modify gRPC context. A common use case
|
||||||
|
// is reading token from cookie and adding it in gRPC context.
|
||||||
|
func WithMetadata(annotator func(context.Context, *http.Request) metadata.MD) ServeMuxOption {
|
||||||
|
return func(serveMux *ServeMux) {
|
||||||
|
serveMux.metadataAnnotators = append(serveMux.metadataAnnotators, annotator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithErrorHandler returns a ServeMuxOption for configuring a custom error handler.
|
||||||
|
//
|
||||||
|
// This can be used to configure a custom error response.
|
||||||
|
func WithErrorHandler(fn ErrorHandlerFunc) ServeMuxOption {
|
||||||
|
return func(serveMux *ServeMux) {
|
||||||
|
serveMux.errorHandler = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithStreamErrorHandler returns a ServeMuxOption that will use the given custom stream
|
||||||
|
// error handler, which allows for customizing the error trailer for server-streaming
|
||||||
|
// calls.
|
||||||
|
//
|
||||||
|
// For stream errors that occur before any response has been written, the mux's
|
||||||
|
// ErrorHandler will be invoked. However, once data has been written, the errors must
|
||||||
|
// be handled differently: they must be included in the response body. The response body's
|
||||||
|
// final message will include the error details returned by the stream error handler.
|
||||||
|
func WithStreamErrorHandler(fn StreamErrorHandlerFunc) ServeMuxOption {
|
||||||
|
return func(serveMux *ServeMux) {
|
||||||
|
serveMux.streamErrorHandler = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRoutingErrorHandler returns a ServeMuxOption for configuring a custom error handler to handle http routing errors.
|
||||||
|
//
|
||||||
|
// Method called for errors which can happen before gRPC route selected or executed.
|
||||||
|
// The following error codes: StatusMethodNotAllowed StatusNotFound StatusBadRequest
|
||||||
|
func WithRoutingErrorHandler(fn RoutingErrorHandlerFunc) ServeMuxOption {
|
||||||
|
return func(serveMux *ServeMux) {
|
||||||
|
serveMux.routingErrorHandler = fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDisablePathLengthFallback returns a ServeMuxOption for disable path length fallback.
|
||||||
|
func WithDisablePathLengthFallback() ServeMuxOption {
|
||||||
|
return func(serveMux *ServeMux) {
|
||||||
|
serveMux.disablePathLengthFallback = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServeMux returns a new ServeMux whose internal mapping is empty.
|
||||||
|
func NewServeMux(opts ...ServeMuxOption) *ServeMux {
|
||||||
|
serveMux := &ServeMux{
|
||||||
|
handlers: make(map[string][]handler),
|
||||||
|
forwardResponseOptions: make([]func(context.Context, http.ResponseWriter, proto.Message) error, 0),
|
||||||
|
marshalers: makeMarshalerMIMERegistry(),
|
||||||
|
errorHandler: DefaultHTTPErrorHandler,
|
||||||
|
streamErrorHandler: DefaultStreamErrorHandler,
|
||||||
|
routingErrorHandler: DefaultRoutingErrorHandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(serveMux)
|
||||||
|
}
|
||||||
|
|
||||||
|
if serveMux.incomingHeaderMatcher == nil {
|
||||||
|
serveMux.incomingHeaderMatcher = DefaultHeaderMatcher
|
||||||
|
}
|
||||||
|
|
||||||
|
if serveMux.outgoingHeaderMatcher == nil {
|
||||||
|
serveMux.outgoingHeaderMatcher = func(key string) (string, bool) {
|
||||||
|
return fmt.Sprintf("%s%s", MetadataHeaderPrefix, key), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return serveMux
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle associates "h" to the pair of HTTP method and path pattern.
|
||||||
|
func (s *ServeMux) Handle(meth string, pat Pattern, h HandlerFunc) {
|
||||||
|
s.handlers[meth] = append([]handler{{pat: pat, h: h}}, s.handlers[meth]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlePath allows users to configure custom path handlers.
|
||||||
|
// refer: https://grpc-ecosystem.github.io/grpc-gateway/docs/inject_router.html
|
||||||
|
func (s *ServeMux) HandlePath(meth string, pathPattern string, h HandlerFunc) error {
|
||||||
|
compiler, err := httprule.Parse(pathPattern)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing path pattern: %w", err)
|
||||||
|
}
|
||||||
|
tp := compiler.Compile()
|
||||||
|
pattern, err := NewPattern(tp.Version, tp.OpCodes, tp.Pool, tp.Verb)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("creating new pattern: %w", err)
|
||||||
|
}
|
||||||
|
s.Handle(meth, pattern, h)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP dispatches the request to the first handler whose pattern matches to r.Method and r.Path.
|
||||||
|
func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
path := r.URL.Path
|
||||||
|
if !strings.HasPrefix(path, "/") {
|
||||||
|
_, outboundMarshaler := MarshalerForRequest(s, r)
|
||||||
|
s.routingErrorHandler(ctx, s, outboundMarshaler, w, r, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
components := strings.Split(path[1:], "/")
|
||||||
|
|
||||||
|
if override := r.Header.Get("X-HTTP-Method-Override"); override != "" && s.isPathLengthFallback(r) {
|
||||||
|
r.Method = strings.ToUpper(override)
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
_, outboundMarshaler := MarshalerForRequest(s, r)
|
||||||
|
sterr := status.Error(codes.InvalidArgument, err.Error())
|
||||||
|
s.errorHandler(ctx, s, outboundMarshaler, w, r, sterr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verb out here is to memoize for the fallback case below
|
||||||
|
var verb string
|
||||||
|
|
||||||
|
for _, h := range s.handlers[r.Method] {
|
||||||
|
// If the pattern has a verb, explicitly look for a suffix in the last
|
||||||
|
// component that matches a colon plus the verb. This allows us to
|
||||||
|
// handle some cases that otherwise can't be correctly handled by the
|
||||||
|
// former LastIndex case, such as when the verb literal itself contains
|
||||||
|
// a colon. This should work for all cases that have run through the
|
||||||
|
// parser because we know what verb we're looking for, however, there
|
||||||
|
// are still some cases that the parser itself cannot disambiguate. See
|
||||||
|
// the comment there if interested.
|
||||||
|
patVerb := h.pat.Verb()
|
||||||
|
l := len(components)
|
||||||
|
lastComponent := components[l-1]
|
||||||
|
var idx int = -1
|
||||||
|
if patVerb != "" && strings.HasSuffix(lastComponent, ":"+patVerb) {
|
||||||
|
idx = len(lastComponent) - len(patVerb) - 1
|
||||||
|
}
|
||||||
|
if idx == 0 {
|
||||||
|
_, outboundMarshaler := MarshalerForRequest(s, r)
|
||||||
|
s.routingErrorHandler(ctx, s, outboundMarshaler, w, r, http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if idx > 0 {
|
||||||
|
components[l-1], verb = lastComponent[:idx], lastComponent[idx+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
pathParams, err := h.pat.Match(components, verb)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
h.h(w, r, pathParams)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookup other methods to handle fallback from GET to POST and
|
||||||
|
// to determine if it is NotImplemented or NotFound.
|
||||||
|
for m, handlers := range s.handlers {
|
||||||
|
if m == r.Method {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, h := range handlers {
|
||||||
|
pathParams, err := h.pat.Match(components, verb)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// X-HTTP-Method-Override is optional. Always allow fallback to POST.
|
||||||
|
if s.isPathLengthFallback(r) {
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
_, outboundMarshaler := MarshalerForRequest(s, r)
|
||||||
|
sterr := status.Error(codes.InvalidArgument, err.Error())
|
||||||
|
s.errorHandler(ctx, s, outboundMarshaler, w, r, sterr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.h(w, r, pathParams)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, outboundMarshaler := MarshalerForRequest(s, r)
|
||||||
|
s.routingErrorHandler(ctx, s, outboundMarshaler, w, r, http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, outboundMarshaler := MarshalerForRequest(s, r)
|
||||||
|
s.routingErrorHandler(ctx, s, outboundMarshaler, w, r, http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetForwardResponseOptions returns the ForwardResponseOptions associated with this ServeMux.
|
||||||
|
func (s *ServeMux) GetForwardResponseOptions() []func(context.Context, http.ResponseWriter, proto.Message) error {
|
||||||
|
return s.forwardResponseOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServeMux) isPathLengthFallback(r *http.Request) bool {
|
||||||
|
return !s.disablePathLengthFallback && r.Method == "POST" && r.Header.Get("Content-Type") == "application/x-www-form-urlencoded"
|
||||||
|
}
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
pat Pattern
|
||||||
|
h HandlerFunc
|
||||||
|
}
|
236
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/pattern.go
generated
vendored
Normal file
236
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/pattern.go
generated
vendored
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrNotMatch indicates that the given HTTP request path does not match to the pattern.
|
||||||
|
ErrNotMatch = errors.New("not match to the path pattern")
|
||||||
|
// ErrInvalidPattern indicates that the given definition of Pattern is not valid.
|
||||||
|
ErrInvalidPattern = errors.New("invalid pattern")
|
||||||
|
)
|
||||||
|
|
||||||
|
type op struct {
|
||||||
|
code utilities.OpCode
|
||||||
|
operand int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pattern is a template pattern of http request paths defined in
|
||||||
|
// https://github.com/googleapis/googleapis/blob/master/google/api/http.proto
|
||||||
|
type Pattern struct {
|
||||||
|
// ops is a list of operations
|
||||||
|
ops []op
|
||||||
|
// pool is a constant pool indexed by the operands or vars.
|
||||||
|
pool []string
|
||||||
|
// vars is a list of variables names to be bound by this pattern
|
||||||
|
vars []string
|
||||||
|
// stacksize is the max depth of the stack
|
||||||
|
stacksize int
|
||||||
|
// tailLen is the length of the fixed-size segments after a deep wildcard
|
||||||
|
tailLen int
|
||||||
|
// verb is the VERB part of the path pattern. It is empty if the pattern does not have VERB part.
|
||||||
|
verb string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPattern returns a new Pattern from the given definition values.
|
||||||
|
// "ops" is a sequence of op codes. "pool" is a constant pool.
|
||||||
|
// "verb" is the verb part of the pattern. It is empty if the pattern does not have the part.
|
||||||
|
// "version" must be 1 for now.
|
||||||
|
// It returns an error if the given definition is invalid.
|
||||||
|
func NewPattern(version int, ops []int, pool []string, verb string) (Pattern, error) {
|
||||||
|
if version != 1 {
|
||||||
|
grpclog.Infof("unsupported version: %d", version)
|
||||||
|
return Pattern{}, ErrInvalidPattern
|
||||||
|
}
|
||||||
|
|
||||||
|
l := len(ops)
|
||||||
|
if l%2 != 0 {
|
||||||
|
grpclog.Infof("odd number of ops codes: %d", l)
|
||||||
|
return Pattern{}, ErrInvalidPattern
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
typedOps []op
|
||||||
|
stack, maxstack int
|
||||||
|
tailLen int
|
||||||
|
pushMSeen bool
|
||||||
|
vars []string
|
||||||
|
)
|
||||||
|
for i := 0; i < l; i += 2 {
|
||||||
|
op := op{code: utilities.OpCode(ops[i]), operand: ops[i+1]}
|
||||||
|
switch op.code {
|
||||||
|
case utilities.OpNop:
|
||||||
|
continue
|
||||||
|
case utilities.OpPush:
|
||||||
|
if pushMSeen {
|
||||||
|
tailLen++
|
||||||
|
}
|
||||||
|
stack++
|
||||||
|
case utilities.OpPushM:
|
||||||
|
if pushMSeen {
|
||||||
|
grpclog.Infof("pushM appears twice")
|
||||||
|
return Pattern{}, ErrInvalidPattern
|
||||||
|
}
|
||||||
|
pushMSeen = true
|
||||||
|
stack++
|
||||||
|
case utilities.OpLitPush:
|
||||||
|
if op.operand < 0 || len(pool) <= op.operand {
|
||||||
|
grpclog.Infof("negative literal index: %d", op.operand)
|
||||||
|
return Pattern{}, ErrInvalidPattern
|
||||||
|
}
|
||||||
|
if pushMSeen {
|
||||||
|
tailLen++
|
||||||
|
}
|
||||||
|
stack++
|
||||||
|
case utilities.OpConcatN:
|
||||||
|
if op.operand <= 0 {
|
||||||
|
grpclog.Infof("negative concat size: %d", op.operand)
|
||||||
|
return Pattern{}, ErrInvalidPattern
|
||||||
|
}
|
||||||
|
stack -= op.operand
|
||||||
|
if stack < 0 {
|
||||||
|
grpclog.Info("stack underflow")
|
||||||
|
return Pattern{}, ErrInvalidPattern
|
||||||
|
}
|
||||||
|
stack++
|
||||||
|
case utilities.OpCapture:
|
||||||
|
if op.operand < 0 || len(pool) <= op.operand {
|
||||||
|
grpclog.Infof("variable name index out of bound: %d", op.operand)
|
||||||
|
return Pattern{}, ErrInvalidPattern
|
||||||
|
}
|
||||||
|
v := pool[op.operand]
|
||||||
|
op.operand = len(vars)
|
||||||
|
vars = append(vars, v)
|
||||||
|
stack--
|
||||||
|
if stack < 0 {
|
||||||
|
grpclog.Infof("stack underflow")
|
||||||
|
return Pattern{}, ErrInvalidPattern
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
grpclog.Infof("invalid opcode: %d", op.code)
|
||||||
|
return Pattern{}, ErrInvalidPattern
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxstack < stack {
|
||||||
|
maxstack = stack
|
||||||
|
}
|
||||||
|
typedOps = append(typedOps, op)
|
||||||
|
}
|
||||||
|
return Pattern{
|
||||||
|
ops: typedOps,
|
||||||
|
pool: pool,
|
||||||
|
vars: vars,
|
||||||
|
stacksize: maxstack,
|
||||||
|
tailLen: tailLen,
|
||||||
|
verb: verb,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustPattern is a helper function which makes it easier to call NewPattern in variable initialization.
|
||||||
|
func MustPattern(p Pattern, err error) Pattern {
|
||||||
|
if err != nil {
|
||||||
|
grpclog.Fatalf("Pattern initialization failed: %v", err)
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match examines components if it matches to the Pattern.
|
||||||
|
// If it matches, the function returns a mapping from field paths to their captured values.
|
||||||
|
// If otherwise, the function returns an error.
|
||||||
|
func (p Pattern) Match(components []string, verb string) (map[string]string, error) {
|
||||||
|
if p.verb != verb {
|
||||||
|
if p.verb != "" {
|
||||||
|
return nil, ErrNotMatch
|
||||||
|
}
|
||||||
|
if len(components) == 0 {
|
||||||
|
components = []string{":" + verb}
|
||||||
|
} else {
|
||||||
|
components = append([]string{}, components...)
|
||||||
|
components[len(components)-1] += ":" + verb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pos int
|
||||||
|
stack := make([]string, 0, p.stacksize)
|
||||||
|
captured := make([]string, len(p.vars))
|
||||||
|
l := len(components)
|
||||||
|
for _, op := range p.ops {
|
||||||
|
switch op.code {
|
||||||
|
case utilities.OpNop:
|
||||||
|
continue
|
||||||
|
case utilities.OpPush, utilities.OpLitPush:
|
||||||
|
if pos >= l {
|
||||||
|
return nil, ErrNotMatch
|
||||||
|
}
|
||||||
|
c := components[pos]
|
||||||
|
if op.code == utilities.OpLitPush {
|
||||||
|
if lit := p.pool[op.operand]; c != lit {
|
||||||
|
return nil, ErrNotMatch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stack = append(stack, c)
|
||||||
|
pos++
|
||||||
|
case utilities.OpPushM:
|
||||||
|
end := len(components)
|
||||||
|
if end < pos+p.tailLen {
|
||||||
|
return nil, ErrNotMatch
|
||||||
|
}
|
||||||
|
end -= p.tailLen
|
||||||
|
stack = append(stack, strings.Join(components[pos:end], "/"))
|
||||||
|
pos = end
|
||||||
|
case utilities.OpConcatN:
|
||||||
|
n := op.operand
|
||||||
|
l := len(stack) - n
|
||||||
|
stack = append(stack[:l], strings.Join(stack[l:], "/"))
|
||||||
|
case utilities.OpCapture:
|
||||||
|
n := len(stack) - 1
|
||||||
|
captured[op.operand] = stack[n]
|
||||||
|
stack = stack[:n]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pos < l {
|
||||||
|
return nil, ErrNotMatch
|
||||||
|
}
|
||||||
|
bindings := make(map[string]string)
|
||||||
|
for i, val := range captured {
|
||||||
|
bindings[p.vars[i]] = val
|
||||||
|
}
|
||||||
|
return bindings, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verb returns the verb part of the Pattern.
|
||||||
|
func (p Pattern) Verb() string { return p.verb }
|
||||||
|
|
||||||
|
func (p Pattern) String() string {
|
||||||
|
var stack []string
|
||||||
|
for _, op := range p.ops {
|
||||||
|
switch op.code {
|
||||||
|
case utilities.OpNop:
|
||||||
|
continue
|
||||||
|
case utilities.OpPush:
|
||||||
|
stack = append(stack, "*")
|
||||||
|
case utilities.OpLitPush:
|
||||||
|
stack = append(stack, p.pool[op.operand])
|
||||||
|
case utilities.OpPushM:
|
||||||
|
stack = append(stack, "**")
|
||||||
|
case utilities.OpConcatN:
|
||||||
|
n := op.operand
|
||||||
|
l := len(stack) - n
|
||||||
|
stack = append(stack[:l], strings.Join(stack[l:], "/"))
|
||||||
|
case utilities.OpCapture:
|
||||||
|
n := len(stack) - 1
|
||||||
|
stack[n] = fmt.Sprintf("{%s=%s}", p.vars[op.operand], stack[n])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
segs := strings.Join(stack, "/")
|
||||||
|
if p.verb != "" {
|
||||||
|
return fmt.Sprintf("/%s:%s", segs, p.verb)
|
||||||
|
}
|
||||||
|
return "/" + segs
|
||||||
|
}
|
80
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/proto2_convert.go
generated
vendored
Normal file
80
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/proto2_convert.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StringP returns a pointer to a string whose pointee is same as the given string value.
|
||||||
|
func StringP(val string) (*string, error) {
|
||||||
|
return proto.String(val), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolP parses the given string representation of a boolean value,
|
||||||
|
// and returns a pointer to a bool whose value is same as the parsed value.
|
||||||
|
func BoolP(val string) (*bool, error) {
|
||||||
|
b, err := Bool(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return proto.Bool(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64P parses the given string representation of a floating point number,
|
||||||
|
// and returns a pointer to a float64 whose value is same as the parsed number.
|
||||||
|
func Float64P(val string) (*float64, error) {
|
||||||
|
f, err := Float64(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return proto.Float64(f), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32P parses the given string representation of a floating point number,
|
||||||
|
// and returns a pointer to a float32 whose value is same as the parsed number.
|
||||||
|
func Float32P(val string) (*float32, error) {
|
||||||
|
f, err := Float32(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return proto.Float32(f), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64P parses the given string representation of an integer
|
||||||
|
// and returns a pointer to a int64 whose value is same as the parsed integer.
|
||||||
|
func Int64P(val string) (*int64, error) {
|
||||||
|
i, err := Int64(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return proto.Int64(i), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32P parses the given string representation of an integer
|
||||||
|
// and returns a pointer to a int32 whose value is same as the parsed integer.
|
||||||
|
func Int32P(val string) (*int32, error) {
|
||||||
|
i, err := Int32(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return proto.Int32(i), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64P parses the given string representation of an integer
|
||||||
|
// and returns a pointer to a uint64 whose value is same as the parsed integer.
|
||||||
|
func Uint64P(val string) (*uint64, error) {
|
||||||
|
i, err := Uint64(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return proto.Uint64(i), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32P parses the given string representation of an integer
|
||||||
|
// and returns a pointer to a uint32 whose value is same as the parsed integer.
|
||||||
|
func Uint32P(val string) (*uint32, error) {
|
||||||
|
i, err := Uint32(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return proto.Uint32(i), err
|
||||||
|
}
|
329
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/query.go
generated
vendored
Normal file
329
vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/query.go
generated
vendored
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
|
||||||
|
"google.golang.org/genproto/protobuf/field_mask"
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
"google.golang.org/protobuf/reflect/protoregistry"
|
||||||
|
"google.golang.org/protobuf/types/known/durationpb"
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||||
|
)
|
||||||
|
|
||||||
|
var valuesKeyRegexp = regexp.MustCompile(`^(.*)\[(.*)\]$`)
|
||||||
|
|
||||||
|
var currentQueryParser QueryParameterParser = &defaultQueryParser{}
|
||||||
|
|
||||||
|
// QueryParameterParser defines interface for all query parameter parsers
|
||||||
|
type QueryParameterParser interface {
|
||||||
|
Parse(msg proto.Message, values url.Values, filter *utilities.DoubleArray) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// PopulateQueryParameters parses query parameters
|
||||||
|
// into "msg" using current query parser
|
||||||
|
func PopulateQueryParameters(msg proto.Message, values url.Values, filter *utilities.DoubleArray) error {
|
||||||
|
return currentQueryParser.Parse(msg, values, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultQueryParser struct{}
|
||||||
|
|
||||||
|
// Parse populates "values" into "msg".
|
||||||
|
// A value is ignored if its key starts with one of the elements in "filter".
|
||||||
|
func (*defaultQueryParser) Parse(msg proto.Message, values url.Values, filter *utilities.DoubleArray) error {
|
||||||
|
for key, values := range values {
|
||||||
|
match := valuesKeyRegexp.FindStringSubmatch(key)
|
||||||
|
if len(match) == 3 {
|
||||||
|
key = match[1]
|
||||||
|
values = append([]string{match[2]}, values...)
|
||||||
|
}
|
||||||
|
fieldPath := strings.Split(key, ".")
|
||||||
|
if filter.HasCommonPrefix(fieldPath) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := populateFieldValueFromPath(msg.ProtoReflect(), fieldPath, values); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PopulateFieldFromPath sets a value in a nested Protobuf structure.
|
||||||
|
func PopulateFieldFromPath(msg proto.Message, fieldPathString string, value string) error {
|
||||||
|
fieldPath := strings.Split(fieldPathString, ".")
|
||||||
|
return populateFieldValueFromPath(msg.ProtoReflect(), fieldPath, []string{value})
|
||||||
|
}
|
||||||
|
|
||||||
|
func populateFieldValueFromPath(msgValue protoreflect.Message, fieldPath []string, values []string) error {
|
||||||
|
if len(fieldPath) < 1 {
|
||||||
|
return errors.New("no field path")
|
||||||
|
}
|
||||||
|
if len(values) < 1 {
|
||||||
|
return errors.New("no value provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
var fieldDescriptor protoreflect.FieldDescriptor
|
||||||
|
for i, fieldName := range fieldPath {
|
||||||
|
fields := msgValue.Descriptor().Fields()
|
||||||
|
|
||||||
|
// Get field by name
|
||||||
|
fieldDescriptor = fields.ByName(protoreflect.Name(fieldName))
|
||||||
|
if fieldDescriptor == nil {
|
||||||
|
fieldDescriptor = fields.ByJSONName(fieldName)
|
||||||
|
if fieldDescriptor == nil {
|
||||||
|
// We're not returning an error here because this could just be
|
||||||
|
// an extra query parameter that isn't part of the request.
|
||||||
|
grpclog.Infof("field not found in %q: %q", msgValue.Descriptor().FullName(), strings.Join(fieldPath, "."))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the last element, we're done
|
||||||
|
if i == len(fieldPath)-1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only singular message fields are allowed
|
||||||
|
if fieldDescriptor.Message() == nil || fieldDescriptor.Cardinality() == protoreflect.Repeated {
|
||||||
|
return fmt.Errorf("invalid path: %q is not a message", fieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the nested message
|
||||||
|
msgValue = msgValue.Mutable(fieldDescriptor).Message()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if oneof already set
|
||||||
|
if of := fieldDescriptor.ContainingOneof(); of != nil {
|
||||||
|
if f := msgValue.WhichOneof(of); f != nil {
|
||||||
|
return fmt.Errorf("field already set for oneof %q", of.FullName().Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case fieldDescriptor.IsList():
|
||||||
|
return populateRepeatedField(fieldDescriptor, msgValue.Mutable(fieldDescriptor).List(), values)
|
||||||
|
case fieldDescriptor.IsMap():
|
||||||
|
return populateMapField(fieldDescriptor, msgValue.Mutable(fieldDescriptor).Map(), values)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(values) > 1 {
|
||||||
|
return fmt.Errorf("too many values for field %q: %s", fieldDescriptor.FullName().Name(), strings.Join(values, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
return populateField(fieldDescriptor, msgValue, values[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func populateField(fieldDescriptor protoreflect.FieldDescriptor, msgValue protoreflect.Message, value string) error {
|
||||||
|
v, err := parseField(fieldDescriptor, value)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing field %q: %w", fieldDescriptor.FullName().Name(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgValue.Set(fieldDescriptor, v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func populateRepeatedField(fieldDescriptor protoreflect.FieldDescriptor, list protoreflect.List, values []string) error {
|
||||||
|
for _, value := range values {
|
||||||
|
v, err := parseField(fieldDescriptor, value)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing list %q: %w", fieldDescriptor.FullName().Name(), err)
|
||||||
|
}
|
||||||
|
list.Append(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func populateMapField(fieldDescriptor protoreflect.FieldDescriptor, mp protoreflect.Map, values []string) error {
|
||||||
|
if len(values) != 2 {
|
||||||
|
return fmt.Errorf("more than one value provided for key %q in map %q", values[0], fieldDescriptor.FullName())
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := parseField(fieldDescriptor.MapKey(), values[0])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing map key %q: %w", fieldDescriptor.FullName().Name(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := parseField(fieldDescriptor.MapValue(), values[1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing map value %q: %w", fieldDescriptor.FullName().Name(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mp.Set(key.MapKey(), value)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseField(fieldDescriptor protoreflect.FieldDescriptor, value string) (protoreflect.Value, error) {
|
||||||
|
switch fieldDescriptor.Kind() {
|
||||||
|
case protoreflect.BoolKind:
|
||||||
|
v, err := strconv.ParseBool(value)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
return protoreflect.ValueOfBool(v), nil
|
||||||
|
case protoreflect.EnumKind:
|
||||||
|
enum, err := protoregistry.GlobalTypes.FindEnumByName(fieldDescriptor.Enum().FullName())
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, protoregistry.NotFound):
|
||||||
|
return protoreflect.Value{}, fmt.Errorf("enum %q is not registered", fieldDescriptor.Enum().FullName())
|
||||||
|
case err != nil:
|
||||||
|
return protoreflect.Value{}, fmt.Errorf("failed to look up enum: %w", err)
|
||||||
|
}
|
||||||
|
// Look for enum by name
|
||||||
|
v := enum.Descriptor().Values().ByName(protoreflect.Name(value))
|
||||||
|
if v == nil {
|
||||||
|
i, err := strconv.Atoi(value)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, fmt.Errorf("%q is not a valid value", value)
|
||||||
|
}
|
||||||
|
// Look for enum by number
|
||||||
|
v = enum.Descriptor().Values().ByNumber(protoreflect.EnumNumber(i))
|
||||||
|
if v == nil {
|
||||||
|
return protoreflect.Value{}, fmt.Errorf("%q is not a valid value", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return protoreflect.ValueOfEnum(v.Number()), nil
|
||||||
|
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
|
||||||
|
v, err := strconv.ParseInt(value, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
return protoreflect.ValueOfInt32(int32(v)), nil
|
||||||
|
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
|
||||||
|
v, err := strconv.ParseInt(value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
return protoreflect.ValueOfInt64(v), nil
|
||||||
|
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
|
||||||
|
v, err := strconv.ParseUint(value, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
return protoreflect.ValueOfUint32(uint32(v)), nil
|
||||||
|
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
|
||||||
|
v, err := strconv.ParseUint(value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
return protoreflect.ValueOfUint64(v), nil
|
||||||
|
case protoreflect.FloatKind:
|
||||||
|
v, err := strconv.ParseFloat(value, 32)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
return protoreflect.ValueOfFloat32(float32(v)), nil
|
||||||
|
case protoreflect.DoubleKind:
|
||||||
|
v, err := strconv.ParseFloat(value, 64)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
return protoreflect.ValueOfFloat64(v), nil
|
||||||
|
case protoreflect.StringKind:
|
||||||
|
return protoreflect.ValueOfString(value), nil
|
||||||
|
case protoreflect.BytesKind:
|
||||||
|
v, err := base64.URLEncoding.DecodeString(value)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
return protoreflect.ValueOfBytes(v), nil
|
||||||
|
case protoreflect.MessageKind, protoreflect.GroupKind:
|
||||||
|
return parseMessage(fieldDescriptor.Message(), value)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unknown field kind: %v", fieldDescriptor.Kind()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseMessage(msgDescriptor protoreflect.MessageDescriptor, value string) (protoreflect.Value, error) {
|
||||||
|
var msg proto.Message
|
||||||
|
switch msgDescriptor.FullName() {
|
||||||
|
case "google.protobuf.Timestamp":
|
||||||
|
if value == "null" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
t, err := time.Parse(time.RFC3339Nano, value)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
msg = timestamppb.New(t)
|
||||||
|
case "google.protobuf.Duration":
|
||||||
|
if value == "null" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
d, err := time.ParseDuration(value)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
msg = durationpb.New(d)
|
||||||
|
case "google.protobuf.DoubleValue":
|
||||||
|
v, err := strconv.ParseFloat(value, 64)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
msg = &wrapperspb.DoubleValue{Value: v}
|
||||||
|
case "google.protobuf.FloatValue":
|
||||||
|
v, err := strconv.ParseFloat(value, 32)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
msg = &wrapperspb.FloatValue{Value: float32(v)}
|
||||||
|
case "google.protobuf.Int64Value":
|
||||||
|
v, err := strconv.ParseInt(value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
msg = &wrapperspb.Int64Value{Value: v}
|
||||||
|
case "google.protobuf.Int32Value":
|
||||||
|
v, err := strconv.ParseInt(value, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
msg = &wrapperspb.Int32Value{Value: int32(v)}
|
||||||
|
case "google.protobuf.UInt64Value":
|
||||||
|
v, err := strconv.ParseUint(value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
msg = &wrapperspb.UInt64Value{Value: v}
|
||||||
|
case "google.protobuf.UInt32Value":
|
||||||
|
v, err := strconv.ParseUint(value, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
msg = &wrapperspb.UInt32Value{Value: uint32(v)}
|
||||||
|
case "google.protobuf.BoolValue":
|
||||||
|
v, err := strconv.ParseBool(value)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
msg = &wrapperspb.BoolValue{Value: v}
|
||||||
|
case "google.protobuf.StringValue":
|
||||||
|
msg = &wrapperspb.StringValue{Value: value}
|
||||||
|
case "google.protobuf.BytesValue":
|
||||||
|
v, err := base64.URLEncoding.DecodeString(value)
|
||||||
|
if err != nil {
|
||||||
|
return protoreflect.Value{}, err
|
||||||
|
}
|
||||||
|
msg = &wrapperspb.BytesValue{Value: v}
|
||||||
|
case "google.protobuf.FieldMask":
|
||||||
|
fm := &field_mask.FieldMask{}
|
||||||
|
fm.Paths = append(fm.Paths, strings.Split(value, ",")...)
|
||||||
|
msg = fm
|
||||||
|
default:
|
||||||
|
return protoreflect.Value{}, fmt.Errorf("unsupported message type: %q", string(msgDescriptor.FullName()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return protoreflect.ValueOfMessage(msg.ProtoReflect()), nil
|
||||||
|
}
|
50
vendor/golang.org/x/net/http/httpguts/guts.go
generated
vendored
Normal file
50
vendor/golang.org/x/net/http/httpguts/guts.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2018 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 httpguts provides functions implementing various details
|
||||||
|
// of the HTTP specification.
|
||||||
|
//
|
||||||
|
// This package is shared by the standard library (which vendors it)
|
||||||
|
// and x/net/http2. It comes with no API stability promise.
|
||||||
|
package httpguts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/textproto"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidTrailerHeader reports whether name is a valid header field name to appear
|
||||||
|
// in trailers.
|
||||||
|
// See RFC 7230, Section 4.1.2
|
||||||
|
func ValidTrailerHeader(name string) bool {
|
||||||
|
name = textproto.CanonicalMIMEHeaderKey(name)
|
||||||
|
if strings.HasPrefix(name, "If-") || badTrailer[name] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var badTrailer = map[string]bool{
|
||||||
|
"Authorization": true,
|
||||||
|
"Cache-Control": true,
|
||||||
|
"Connection": true,
|
||||||
|
"Content-Encoding": true,
|
||||||
|
"Content-Length": true,
|
||||||
|
"Content-Range": true,
|
||||||
|
"Content-Type": true,
|
||||||
|
"Expect": true,
|
||||||
|
"Host": true,
|
||||||
|
"Keep-Alive": true,
|
||||||
|
"Max-Forwards": true,
|
||||||
|
"Pragma": true,
|
||||||
|
"Proxy-Authenticate": true,
|
||||||
|
"Proxy-Authorization": true,
|
||||||
|
"Proxy-Connection": true,
|
||||||
|
"Range": true,
|
||||||
|
"Realm": true,
|
||||||
|
"Te": true,
|
||||||
|
"Trailer": true,
|
||||||
|
"Transfer-Encoding": true,
|
||||||
|
"Www-Authenticate": true,
|
||||||
|
}
|
346
vendor/golang.org/x/net/http/httpguts/httplex.go
generated
vendored
Normal file
346
vendor/golang.org/x/net/http/httpguts/httplex.go
generated
vendored
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
// 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 httpguts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"golang.org/x/net/idna"
|
||||||
|
)
|
||||||
|
|
||||||
|
var isTokenTable = [127]bool{
|
||||||
|
'!': true,
|
||||||
|
'#': true,
|
||||||
|
'$': true,
|
||||||
|
'%': true,
|
||||||
|
'&': true,
|
||||||
|
'\'': true,
|
||||||
|
'*': true,
|
||||||
|
'+': true,
|
||||||
|
'-': true,
|
||||||
|
'.': true,
|
||||||
|
'0': true,
|
||||||
|
'1': true,
|
||||||
|
'2': true,
|
||||||
|
'3': true,
|
||||||
|
'4': true,
|
||||||
|
'5': true,
|
||||||
|
'6': true,
|
||||||
|
'7': true,
|
||||||
|
'8': true,
|
||||||
|
'9': true,
|
||||||
|
'A': true,
|
||||||
|
'B': true,
|
||||||
|
'C': true,
|
||||||
|
'D': true,
|
||||||
|
'E': true,
|
||||||
|
'F': true,
|
||||||
|
'G': true,
|
||||||
|
'H': true,
|
||||||
|
'I': true,
|
||||||
|
'J': true,
|
||||||
|
'K': true,
|
||||||
|
'L': true,
|
||||||
|
'M': true,
|
||||||
|
'N': true,
|
||||||
|
'O': true,
|
||||||
|
'P': true,
|
||||||
|
'Q': true,
|
||||||
|
'R': true,
|
||||||
|
'S': true,
|
||||||
|
'T': true,
|
||||||
|
'U': true,
|
||||||
|
'W': true,
|
||||||
|
'V': true,
|
||||||
|
'X': true,
|
||||||
|
'Y': true,
|
||||||
|
'Z': true,
|
||||||
|
'^': true,
|
||||||
|
'_': true,
|
||||||
|
'`': true,
|
||||||
|
'a': true,
|
||||||
|
'b': true,
|
||||||
|
'c': true,
|
||||||
|
'd': true,
|
||||||
|
'e': true,
|
||||||
|
'f': true,
|
||||||
|
'g': true,
|
||||||
|
'h': true,
|
||||||
|
'i': true,
|
||||||
|
'j': true,
|
||||||
|
'k': true,
|
||||||
|
'l': true,
|
||||||
|
'm': true,
|
||||||
|
'n': true,
|
||||||
|
'o': true,
|
||||||
|
'p': true,
|
||||||
|
'q': true,
|
||||||
|
'r': true,
|
||||||
|
's': true,
|
||||||
|
't': true,
|
||||||
|
'u': true,
|
||||||
|
'v': true,
|
||||||
|
'w': true,
|
||||||
|
'x': true,
|
||||||
|
'y': true,
|
||||||
|
'z': true,
|
||||||
|
'|': true,
|
||||||
|
'~': true,
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsTokenRune(r rune) bool {
|
||||||
|
i := int(r)
|
||||||
|
return i < len(isTokenTable) && isTokenTable[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNotToken(r rune) bool {
|
||||||
|
return !IsTokenRune(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderValuesContainsToken reports whether any string in values
|
||||||
|
// contains the provided token, ASCII case-insensitively.
|
||||||
|
func HeaderValuesContainsToken(values []string, token string) bool {
|
||||||
|
for _, v := range values {
|
||||||
|
if headerValueContainsToken(v, token) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isOWS reports whether b is an optional whitespace byte, as defined
|
||||||
|
// by RFC 7230 section 3.2.3.
|
||||||
|
func isOWS(b byte) bool { return b == ' ' || b == '\t' }
|
||||||
|
|
||||||
|
// trimOWS returns x with all optional whitespace removes from the
|
||||||
|
// beginning and end.
|
||||||
|
func trimOWS(x string) string {
|
||||||
|
// TODO: consider using strings.Trim(x, " \t") instead,
|
||||||
|
// if and when it's fast enough. See issue 10292.
|
||||||
|
// But this ASCII-only code will probably always beat UTF-8
|
||||||
|
// aware code.
|
||||||
|
for len(x) > 0 && isOWS(x[0]) {
|
||||||
|
x = x[1:]
|
||||||
|
}
|
||||||
|
for len(x) > 0 && isOWS(x[len(x)-1]) {
|
||||||
|
x = x[:len(x)-1]
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// headerValueContainsToken reports whether v (assumed to be a
|
||||||
|
// 0#element, in the ABNF extension described in RFC 7230 section 7)
|
||||||
|
// contains token amongst its comma-separated tokens, ASCII
|
||||||
|
// case-insensitively.
|
||||||
|
func headerValueContainsToken(v string, token string) bool {
|
||||||
|
v = trimOWS(v)
|
||||||
|
if comma := strings.IndexByte(v, ','); comma != -1 {
|
||||||
|
return tokenEqual(trimOWS(v[:comma]), token) || headerValueContainsToken(v[comma+1:], token)
|
||||||
|
}
|
||||||
|
return tokenEqual(v, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// lowerASCII returns the ASCII lowercase version of b.
|
||||||
|
func lowerASCII(b byte) byte {
|
||||||
|
if 'A' <= b && b <= 'Z' {
|
||||||
|
return b + ('a' - 'A')
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// tokenEqual reports whether t1 and t2 are equal, ASCII case-insensitively.
|
||||||
|
func tokenEqual(t1, t2 string) bool {
|
||||||
|
if len(t1) != len(t2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, b := range t1 {
|
||||||
|
if b >= utf8.RuneSelf {
|
||||||
|
// No UTF-8 or non-ASCII allowed in tokens.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lowerASCII(byte(b)) != lowerASCII(t2[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// isLWS reports whether b is linear white space, according
|
||||||
|
// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
|
||||||
|
// LWS = [CRLF] 1*( SP | HT )
|
||||||
|
func isLWS(b byte) bool { return b == ' ' || b == '\t' }
|
||||||
|
|
||||||
|
// isCTL reports whether b is a control byte, according
|
||||||
|
// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
|
||||||
|
// CTL = <any US-ASCII control character
|
||||||
|
// (octets 0 - 31) and DEL (127)>
|
||||||
|
func isCTL(b byte) bool {
|
||||||
|
const del = 0x7f // a CTL
|
||||||
|
return b < ' ' || b == del
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidHeaderFieldName reports whether v is a valid HTTP/1.x header name.
|
||||||
|
// HTTP/2 imposes the additional restriction that uppercase ASCII
|
||||||
|
// letters are not allowed.
|
||||||
|
//
|
||||||
|
// RFC 7230 says:
|
||||||
|
// header-field = field-name ":" OWS field-value OWS
|
||||||
|
// field-name = token
|
||||||
|
// token = 1*tchar
|
||||||
|
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
|
||||||
|
// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
|
||||||
|
func ValidHeaderFieldName(v string) bool {
|
||||||
|
if len(v) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, r := range v {
|
||||||
|
if !IsTokenRune(r) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidHostHeader reports whether h is a valid host header.
|
||||||
|
func ValidHostHeader(h string) bool {
|
||||||
|
// The latest spec is actually this:
|
||||||
|
//
|
||||||
|
// http://tools.ietf.org/html/rfc7230#section-5.4
|
||||||
|
// Host = uri-host [ ":" port ]
|
||||||
|
//
|
||||||
|
// Where uri-host is:
|
||||||
|
// http://tools.ietf.org/html/rfc3986#section-3.2.2
|
||||||
|
//
|
||||||
|
// But we're going to be much more lenient for now and just
|
||||||
|
// search for any byte that's not a valid byte in any of those
|
||||||
|
// expressions.
|
||||||
|
for i := 0; i < len(h); i++ {
|
||||||
|
if !validHostByte[h[i]] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// See the validHostHeader comment.
|
||||||
|
var validHostByte = [256]bool{
|
||||||
|
'0': true, '1': true, '2': true, '3': true, '4': true, '5': true, '6': true, '7': true,
|
||||||
|
'8': true, '9': true,
|
||||||
|
|
||||||
|
'a': true, 'b': true, 'c': true, 'd': true, 'e': true, 'f': true, 'g': true, 'h': true,
|
||||||
|
'i': true, 'j': true, 'k': true, 'l': true, 'm': true, 'n': true, 'o': true, 'p': true,
|
||||||
|
'q': true, 'r': true, 's': true, 't': true, 'u': true, 'v': true, 'w': true, 'x': true,
|
||||||
|
'y': true, 'z': true,
|
||||||
|
|
||||||
|
'A': true, 'B': true, 'C': true, 'D': true, 'E': true, 'F': true, 'G': true, 'H': true,
|
||||||
|
'I': true, 'J': true, 'K': true, 'L': true, 'M': true, 'N': true, 'O': true, 'P': true,
|
||||||
|
'Q': true, 'R': true, 'S': true, 'T': true, 'U': true, 'V': true, 'W': true, 'X': true,
|
||||||
|
'Y': true, 'Z': true,
|
||||||
|
|
||||||
|
'!': true, // sub-delims
|
||||||
|
'$': true, // sub-delims
|
||||||
|
'%': true, // pct-encoded (and used in IPv6 zones)
|
||||||
|
'&': true, // sub-delims
|
||||||
|
'(': true, // sub-delims
|
||||||
|
')': true, // sub-delims
|
||||||
|
'*': true, // sub-delims
|
||||||
|
'+': true, // sub-delims
|
||||||
|
',': true, // sub-delims
|
||||||
|
'-': true, // unreserved
|
||||||
|
'.': true, // unreserved
|
||||||
|
':': true, // IPv6address + Host expression's optional port
|
||||||
|
';': true, // sub-delims
|
||||||
|
'=': true, // sub-delims
|
||||||
|
'[': true,
|
||||||
|
'\'': true, // sub-delims
|
||||||
|
']': true,
|
||||||
|
'_': true, // unreserved
|
||||||
|
'~': true, // unreserved
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidHeaderFieldValue reports whether v is a valid "field-value" according to
|
||||||
|
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 :
|
||||||
|
//
|
||||||
|
// message-header = field-name ":" [ field-value ]
|
||||||
|
// field-value = *( field-content | LWS )
|
||||||
|
// field-content = <the OCTETs making up the field-value
|
||||||
|
// and consisting of either *TEXT or combinations
|
||||||
|
// of token, separators, and quoted-string>
|
||||||
|
//
|
||||||
|
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 :
|
||||||
|
//
|
||||||
|
// TEXT = <any OCTET except CTLs,
|
||||||
|
// but including LWS>
|
||||||
|
// LWS = [CRLF] 1*( SP | HT )
|
||||||
|
// CTL = <any US-ASCII control character
|
||||||
|
// (octets 0 - 31) and DEL (127)>
|
||||||
|
//
|
||||||
|
// RFC 7230 says:
|
||||||
|
// field-value = *( field-content / obs-fold )
|
||||||
|
// obj-fold = N/A to http2, and deprecated
|
||||||
|
// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
||||||
|
// field-vchar = VCHAR / obs-text
|
||||||
|
// obs-text = %x80-FF
|
||||||
|
// VCHAR = "any visible [USASCII] character"
|
||||||
|
//
|
||||||
|
// http2 further says: "Similarly, HTTP/2 allows header field values
|
||||||
|
// that are not valid. While most of the values that can be encoded
|
||||||
|
// will not alter header field parsing, carriage return (CR, ASCII
|
||||||
|
// 0xd), line feed (LF, ASCII 0xa), and the zero character (NUL, ASCII
|
||||||
|
// 0x0) might be exploited by an attacker if they are translated
|
||||||
|
// verbatim. Any request or response that contains a character not
|
||||||
|
// permitted in a header field value MUST be treated as malformed
|
||||||
|
// (Section 8.1.2.6). Valid characters are defined by the
|
||||||
|
// field-content ABNF rule in Section 3.2 of [RFC7230]."
|
||||||
|
//
|
||||||
|
// This function does not (yet?) properly handle the rejection of
|
||||||
|
// strings that begin or end with SP or HTAB.
|
||||||
|
func ValidHeaderFieldValue(v string) bool {
|
||||||
|
for i := 0; i < len(v); i++ {
|
||||||
|
b := v[i]
|
||||||
|
if isCTL(b) && !isLWS(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func isASCII(s string) bool {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] >= utf8.RuneSelf {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// PunycodeHostPort returns the IDNA Punycode version
|
||||||
|
// of the provided "host" or "host:port" string.
|
||||||
|
func PunycodeHostPort(v string) (string, error) {
|
||||||
|
if isASCII(v) {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
host, port, err := net.SplitHostPort(v)
|
||||||
|
if err != nil {
|
||||||
|
// The input 'v' argument was just a "host" argument,
|
||||||
|
// without a port. This error should not be returned
|
||||||
|
// to the caller.
|
||||||
|
host = v
|
||||||
|
port = ""
|
||||||
|
}
|
||||||
|
host, err = idna.ToASCII(host)
|
||||||
|
if err != nil {
|
||||||
|
// Non-UTF-8? Not representable in Punycode, in any
|
||||||
|
// case.
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if port == "" {
|
||||||
|
return host, nil
|
||||||
|
}
|
||||||
|
return net.JoinHostPort(host, port), nil
|
||||||
|
}
|
2
vendor/golang.org/x/net/http2/.gitignore
generated
vendored
Normal file
2
vendor/golang.org/x/net/http2/.gitignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*~
|
||||||
|
h2i/h2i
|
51
vendor/golang.org/x/net/http2/Dockerfile
generated
vendored
Normal file
51
vendor/golang.org/x/net/http2/Dockerfile
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#
|
||||||
|
# This Dockerfile builds a recent curl with HTTP/2 client support, using
|
||||||
|
# a recent nghttp2 build.
|
||||||
|
#
|
||||||
|
# See the Makefile for how to tag it. If Docker and that image is found, the
|
||||||
|
# Go tests use this curl binary for integration tests.
|
||||||
|
#
|
||||||
|
|
||||||
|
FROM ubuntu:trusty
|
||||||
|
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get upgrade -y && \
|
||||||
|
apt-get install -y git-core build-essential wget
|
||||||
|
|
||||||
|
RUN apt-get install -y --no-install-recommends \
|
||||||
|
autotools-dev libtool pkg-config zlib1g-dev \
|
||||||
|
libcunit1-dev libssl-dev libxml2-dev libevent-dev \
|
||||||
|
automake autoconf
|
||||||
|
|
||||||
|
# The list of packages nghttp2 recommends for h2load:
|
||||||
|
RUN apt-get install -y --no-install-recommends make binutils \
|
||||||
|
autoconf automake autotools-dev \
|
||||||
|
libtool pkg-config zlib1g-dev libcunit1-dev libssl-dev libxml2-dev \
|
||||||
|
libev-dev libevent-dev libjansson-dev libjemalloc-dev \
|
||||||
|
cython python3.4-dev python-setuptools
|
||||||
|
|
||||||
|
# Note: setting NGHTTP2_VER before the git clone, so an old git clone isn't cached:
|
||||||
|
ENV NGHTTP2_VER 895da9a
|
||||||
|
RUN cd /root && git clone https://github.com/tatsuhiro-t/nghttp2.git
|
||||||
|
|
||||||
|
WORKDIR /root/nghttp2
|
||||||
|
RUN git reset --hard $NGHTTP2_VER
|
||||||
|
RUN autoreconf -i
|
||||||
|
RUN automake
|
||||||
|
RUN autoconf
|
||||||
|
RUN ./configure
|
||||||
|
RUN make
|
||||||
|
RUN make install
|
||||||
|
|
||||||
|
WORKDIR /root
|
||||||
|
RUN wget http://curl.haxx.se/download/curl-7.45.0.tar.gz
|
||||||
|
RUN tar -zxvf curl-7.45.0.tar.gz
|
||||||
|
WORKDIR /root/curl-7.45.0
|
||||||
|
RUN ./configure --with-ssl --with-nghttp2=/usr/local
|
||||||
|
RUN make
|
||||||
|
RUN make install
|
||||||
|
RUN ldconfig
|
||||||
|
|
||||||
|
CMD ["-h"]
|
||||||
|
ENTRYPOINT ["/usr/local/bin/curl"]
|
||||||
|
|
3
vendor/golang.org/x/net/http2/Makefile
generated
vendored
Normal file
3
vendor/golang.org/x/net/http2/Makefile
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
curlimage:
|
||||||
|
docker build -t gohttp2/curl .
|
||||||
|
|
20
vendor/golang.org/x/net/http2/README
generated
vendored
Normal file
20
vendor/golang.org/x/net/http2/README
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
This is a work-in-progress HTTP/2 implementation for Go.
|
||||||
|
|
||||||
|
It will eventually live in the Go standard library and won't require
|
||||||
|
any changes to your code to use. It will just be automatic.
|
||||||
|
|
||||||
|
Status:
|
||||||
|
|
||||||
|
* The server support is pretty good. A few things are missing
|
||||||
|
but are being worked on.
|
||||||
|
* The client work has just started but shares a lot of code
|
||||||
|
is coming along much quicker.
|
||||||
|
|
||||||
|
Docs are at https://godoc.org/golang.org/x/net/http2
|
||||||
|
|
||||||
|
Demo test server at https://http2.golang.org/
|
||||||
|
|
||||||
|
Help & bug reports welcome!
|
||||||
|
|
||||||
|
Contributing: https://golang.org/doc/contribute.html
|
||||||
|
Bugs: https://golang.org/issue/new?title=x/net/http2:+
|
641
vendor/golang.org/x/net/http2/ciphers.go
generated
vendored
Normal file
641
vendor/golang.org/x/net/http2/ciphers.go
generated
vendored
Normal file
@ -0,0 +1,641 @@
|
|||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
// A list of the possible cipher suite ids. Taken from
|
||||||
|
// https://www.iana.org/assignments/tls-parameters/tls-parameters.txt
|
||||||
|
|
||||||
|
const (
|
||||||
|
cipher_TLS_NULL_WITH_NULL_NULL uint16 = 0x0000
|
||||||
|
cipher_TLS_RSA_WITH_NULL_MD5 uint16 = 0x0001
|
||||||
|
cipher_TLS_RSA_WITH_NULL_SHA uint16 = 0x0002
|
||||||
|
cipher_TLS_RSA_EXPORT_WITH_RC4_40_MD5 uint16 = 0x0003
|
||||||
|
cipher_TLS_RSA_WITH_RC4_128_MD5 uint16 = 0x0004
|
||||||
|
cipher_TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
|
||||||
|
cipher_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 uint16 = 0x0006
|
||||||
|
cipher_TLS_RSA_WITH_IDEA_CBC_SHA uint16 = 0x0007
|
||||||
|
cipher_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0008
|
||||||
|
cipher_TLS_RSA_WITH_DES_CBC_SHA uint16 = 0x0009
|
||||||
|
cipher_TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000A
|
||||||
|
cipher_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x000B
|
||||||
|
cipher_TLS_DH_DSS_WITH_DES_CBC_SHA uint16 = 0x000C
|
||||||
|
cipher_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA uint16 = 0x000D
|
||||||
|
cipher_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x000E
|
||||||
|
cipher_TLS_DH_RSA_WITH_DES_CBC_SHA uint16 = 0x000F
|
||||||
|
cipher_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x0010
|
||||||
|
cipher_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0011
|
||||||
|
cipher_TLS_DHE_DSS_WITH_DES_CBC_SHA uint16 = 0x0012
|
||||||
|
cipher_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA uint16 = 0x0013
|
||||||
|
cipher_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0014
|
||||||
|
cipher_TLS_DHE_RSA_WITH_DES_CBC_SHA uint16 = 0x0015
|
||||||
|
cipher_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x0016
|
||||||
|
cipher_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 uint16 = 0x0017
|
||||||
|
cipher_TLS_DH_anon_WITH_RC4_128_MD5 uint16 = 0x0018
|
||||||
|
cipher_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0019
|
||||||
|
cipher_TLS_DH_anon_WITH_DES_CBC_SHA uint16 = 0x001A
|
||||||
|
cipher_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA uint16 = 0x001B
|
||||||
|
// Reserved uint16 = 0x001C-1D
|
||||||
|
cipher_TLS_KRB5_WITH_DES_CBC_SHA uint16 = 0x001E
|
||||||
|
cipher_TLS_KRB5_WITH_3DES_EDE_CBC_SHA uint16 = 0x001F
|
||||||
|
cipher_TLS_KRB5_WITH_RC4_128_SHA uint16 = 0x0020
|
||||||
|
cipher_TLS_KRB5_WITH_IDEA_CBC_SHA uint16 = 0x0021
|
||||||
|
cipher_TLS_KRB5_WITH_DES_CBC_MD5 uint16 = 0x0022
|
||||||
|
cipher_TLS_KRB5_WITH_3DES_EDE_CBC_MD5 uint16 = 0x0023
|
||||||
|
cipher_TLS_KRB5_WITH_RC4_128_MD5 uint16 = 0x0024
|
||||||
|
cipher_TLS_KRB5_WITH_IDEA_CBC_MD5 uint16 = 0x0025
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA uint16 = 0x0026
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA uint16 = 0x0027
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_RC4_40_SHA uint16 = 0x0028
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 uint16 = 0x0029
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 uint16 = 0x002A
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_RC4_40_MD5 uint16 = 0x002B
|
||||||
|
cipher_TLS_PSK_WITH_NULL_SHA uint16 = 0x002C
|
||||||
|
cipher_TLS_DHE_PSK_WITH_NULL_SHA uint16 = 0x002D
|
||||||
|
cipher_TLS_RSA_PSK_WITH_NULL_SHA uint16 = 0x002E
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002F
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA uint16 = 0x0030
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA uint16 = 0x0031
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA uint16 = 0x0032
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0x0033
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA uint16 = 0x0034
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA uint16 = 0x0036
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0037
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA uint16 = 0x0038
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0039
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA uint16 = 0x003A
|
||||||
|
cipher_TLS_RSA_WITH_NULL_SHA256 uint16 = 0x003B
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003C
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x003D
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA256 uint16 = 0x003E
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003F
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 uint16 = 0x0040
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0041
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0042
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0043
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0044
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0045
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0046
|
||||||
|
// Reserved uint16 = 0x0047-4F
|
||||||
|
// Reserved uint16 = 0x0050-58
|
||||||
|
// Reserved uint16 = 0x0059-5C
|
||||||
|
// Unassigned uint16 = 0x005D-5F
|
||||||
|
// Reserved uint16 = 0x0060-66
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x0067
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA256 uint16 = 0x0068
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x0069
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 uint16 = 0x006A
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x006B
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA256 uint16 = 0x006C
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA256 uint16 = 0x006D
|
||||||
|
// Unassigned uint16 = 0x006E-83
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0084
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0085
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0086
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0087
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0088
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0089
|
||||||
|
cipher_TLS_PSK_WITH_RC4_128_SHA uint16 = 0x008A
|
||||||
|
cipher_TLS_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0x008B
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_CBC_SHA uint16 = 0x008C
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_CBC_SHA uint16 = 0x008D
|
||||||
|
cipher_TLS_DHE_PSK_WITH_RC4_128_SHA uint16 = 0x008E
|
||||||
|
cipher_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0x008F
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA uint16 = 0x0090
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA uint16 = 0x0091
|
||||||
|
cipher_TLS_RSA_PSK_WITH_RC4_128_SHA uint16 = 0x0092
|
||||||
|
cipher_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0x0093
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA uint16 = 0x0094
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA uint16 = 0x0095
|
||||||
|
cipher_TLS_RSA_WITH_SEED_CBC_SHA uint16 = 0x0096
|
||||||
|
cipher_TLS_DH_DSS_WITH_SEED_CBC_SHA uint16 = 0x0097
|
||||||
|
cipher_TLS_DH_RSA_WITH_SEED_CBC_SHA uint16 = 0x0098
|
||||||
|
cipher_TLS_DHE_DSS_WITH_SEED_CBC_SHA uint16 = 0x0099
|
||||||
|
cipher_TLS_DHE_RSA_WITH_SEED_CBC_SHA uint16 = 0x009A
|
||||||
|
cipher_TLS_DH_anon_WITH_SEED_CBC_SHA uint16 = 0x009B
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009C
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009D
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009E
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009F
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x00A0
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x00A1
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 uint16 = 0x00A2
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 uint16 = 0x00A3
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_128_GCM_SHA256 uint16 = 0x00A4
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_256_GCM_SHA384 uint16 = 0x00A5
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_128_GCM_SHA256 uint16 = 0x00A6
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_256_GCM_SHA384 uint16 = 0x00A7
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0x00A8
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_GCM_SHA384 uint16 = 0x00A9
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0x00AA
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 uint16 = 0x00AB
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0x00AC
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 uint16 = 0x00AD
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0x00AE
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0x00AF
|
||||||
|
cipher_TLS_PSK_WITH_NULL_SHA256 uint16 = 0x00B0
|
||||||
|
cipher_TLS_PSK_WITH_NULL_SHA384 uint16 = 0x00B1
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0x00B2
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0x00B3
|
||||||
|
cipher_TLS_DHE_PSK_WITH_NULL_SHA256 uint16 = 0x00B4
|
||||||
|
cipher_TLS_DHE_PSK_WITH_NULL_SHA384 uint16 = 0x00B5
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0x00B6
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0x00B7
|
||||||
|
cipher_TLS_RSA_PSK_WITH_NULL_SHA256 uint16 = 0x00B8
|
||||||
|
cipher_TLS_RSA_PSK_WITH_NULL_SHA384 uint16 = 0x00B9
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BA
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BB
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BC
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BD
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BE
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BF
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C0
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C1
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C2
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C3
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C4
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C5
|
||||||
|
// Unassigned uint16 = 0x00C6-FE
|
||||||
|
cipher_TLS_EMPTY_RENEGOTIATION_INFO_SCSV uint16 = 0x00FF
|
||||||
|
// Unassigned uint16 = 0x01-55,*
|
||||||
|
cipher_TLS_FALLBACK_SCSV uint16 = 0x5600
|
||||||
|
// Unassigned uint16 = 0x5601 - 0xC000
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_NULL_SHA uint16 = 0xC001
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_RC4_128_SHA uint16 = 0xC002
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC003
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xC004
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xC005
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_NULL_SHA uint16 = 0xC006
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xC007
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC008
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xC009
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xC00A
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_NULL_SHA uint16 = 0xC00B
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_RC4_128_SHA uint16 = 0xC00C
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC00D
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA uint16 = 0xC00E
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA uint16 = 0xC00F
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_NULL_SHA uint16 = 0xC010
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xC011
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC012
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xC013
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xC014
|
||||||
|
cipher_TLS_ECDH_anon_WITH_NULL_SHA uint16 = 0xC015
|
||||||
|
cipher_TLS_ECDH_anon_WITH_RC4_128_SHA uint16 = 0xC016
|
||||||
|
cipher_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA uint16 = 0xC017
|
||||||
|
cipher_TLS_ECDH_anon_WITH_AES_128_CBC_SHA uint16 = 0xC018
|
||||||
|
cipher_TLS_ECDH_anon_WITH_AES_256_CBC_SHA uint16 = 0xC019
|
||||||
|
cipher_TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC01A
|
||||||
|
cipher_TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC01B
|
||||||
|
cipher_TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA uint16 = 0xC01C
|
||||||
|
cipher_TLS_SRP_SHA_WITH_AES_128_CBC_SHA uint16 = 0xC01D
|
||||||
|
cipher_TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA uint16 = 0xC01E
|
||||||
|
cipher_TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA uint16 = 0xC01F
|
||||||
|
cipher_TLS_SRP_SHA_WITH_AES_256_CBC_SHA uint16 = 0xC020
|
||||||
|
cipher_TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA uint16 = 0xC021
|
||||||
|
cipher_TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA uint16 = 0xC022
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC023
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC024
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC025
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC026
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC027
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC028
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC029
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC02A
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC02B
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC02C
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC02D
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC02E
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC02F
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC030
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC031
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC032
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_RC4_128_SHA uint16 = 0xC033
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0xC034
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA uint16 = 0xC035
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA uint16 = 0xC036
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0xC037
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0xC038
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA uint16 = 0xC039
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA256 uint16 = 0xC03A
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA384 uint16 = 0xC03B
|
||||||
|
cipher_TLS_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC03C
|
||||||
|
cipher_TLS_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC03D
|
||||||
|
cipher_TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC03E
|
||||||
|
cipher_TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC03F
|
||||||
|
cipher_TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC040
|
||||||
|
cipher_TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC041
|
||||||
|
cipher_TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC042
|
||||||
|
cipher_TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC043
|
||||||
|
cipher_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC044
|
||||||
|
cipher_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC045
|
||||||
|
cipher_TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC046
|
||||||
|
cipher_TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC047
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC048
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC049
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC04A
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC04B
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC04C
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC04D
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC04E
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC04F
|
||||||
|
cipher_TLS_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC050
|
||||||
|
cipher_TLS_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC051
|
||||||
|
cipher_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC052
|
||||||
|
cipher_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC053
|
||||||
|
cipher_TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC054
|
||||||
|
cipher_TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC055
|
||||||
|
cipher_TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC056
|
||||||
|
cipher_TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC057
|
||||||
|
cipher_TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC058
|
||||||
|
cipher_TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC059
|
||||||
|
cipher_TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC05A
|
||||||
|
cipher_TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC05B
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC05C
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC05D
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC05E
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC05F
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC060
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC061
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC062
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC063
|
||||||
|
cipher_TLS_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC064
|
||||||
|
cipher_TLS_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC065
|
||||||
|
cipher_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC066
|
||||||
|
cipher_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC067
|
||||||
|
cipher_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC068
|
||||||
|
cipher_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC069
|
||||||
|
cipher_TLS_PSK_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC06A
|
||||||
|
cipher_TLS_PSK_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC06B
|
||||||
|
cipher_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC06C
|
||||||
|
cipher_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC06D
|
||||||
|
cipher_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC06E
|
||||||
|
cipher_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC06F
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC070
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC071
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC072
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC073
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC074
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC075
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC076
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC077
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC078
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC079
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC07A
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC07B
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC07C
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC07D
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC07E
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC07F
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC080
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC081
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC082
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC083
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC084
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC085
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC086
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC087
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC088
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC089
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC08A
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC08B
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC08C
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC08D
|
||||||
|
cipher_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC08E
|
||||||
|
cipher_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC08F
|
||||||
|
cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC090
|
||||||
|
cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC091
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC092
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC093
|
||||||
|
cipher_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC094
|
||||||
|
cipher_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC095
|
||||||
|
cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC096
|
||||||
|
cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC097
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC098
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC099
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC09A
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC09B
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_CCM uint16 = 0xC09C
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_CCM uint16 = 0xC09D
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_128_CCM uint16 = 0xC09E
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_256_CCM uint16 = 0xC09F
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_CCM_8 uint16 = 0xC0A0
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_CCM_8 uint16 = 0xC0A1
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_128_CCM_8 uint16 = 0xC0A2
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_256_CCM_8 uint16 = 0xC0A3
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_CCM uint16 = 0xC0A4
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_CCM uint16 = 0xC0A5
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_128_CCM uint16 = 0xC0A6
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_256_CCM uint16 = 0xC0A7
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_CCM_8 uint16 = 0xC0A8
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_CCM_8 uint16 = 0xC0A9
|
||||||
|
cipher_TLS_PSK_DHE_WITH_AES_128_CCM_8 uint16 = 0xC0AA
|
||||||
|
cipher_TLS_PSK_DHE_WITH_AES_256_CCM_8 uint16 = 0xC0AB
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CCM uint16 = 0xC0AC
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CCM uint16 = 0xC0AD
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 uint16 = 0xC0AE
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 uint16 = 0xC0AF
|
||||||
|
// Unassigned uint16 = 0xC0B0-FF
|
||||||
|
// Unassigned uint16 = 0xC1-CB,*
|
||||||
|
// Unassigned uint16 = 0xCC00-A7
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCA8
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCA9
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAA
|
||||||
|
cipher_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAB
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAC
|
||||||
|
cipher_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAD
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAE
|
||||||
|
)
|
||||||
|
|
||||||
|
// isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec.
|
||||||
|
// References:
|
||||||
|
// https://tools.ietf.org/html/rfc7540#appendix-A
|
||||||
|
// Reject cipher suites from Appendix A.
|
||||||
|
// "This list includes those cipher suites that do not
|
||||||
|
// offer an ephemeral key exchange and those that are
|
||||||
|
// based on the TLS null, stream or block cipher type"
|
||||||
|
func isBadCipher(cipher uint16) bool {
|
||||||
|
switch cipher {
|
||||||
|
case cipher_TLS_NULL_WITH_NULL_NULL,
|
||||||
|
cipher_TLS_RSA_WITH_NULL_MD5,
|
||||||
|
cipher_TLS_RSA_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_RSA_EXPORT_WITH_RC4_40_MD5,
|
||||||
|
cipher_TLS_RSA_WITH_RC4_128_MD5,
|
||||||
|
cipher_TLS_RSA_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5,
|
||||||
|
cipher_TLS_RSA_WITH_IDEA_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_WITH_DES_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA,
|
||||||
|
cipher_TLS_DH_DSS_WITH_DES_CBC_SHA,
|
||||||
|
cipher_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA,
|
||||||
|
cipher_TLS_DH_RSA_WITH_DES_CBC_SHA,
|
||||||
|
cipher_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_DES_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_DES_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5,
|
||||||
|
cipher_TLS_DH_anon_WITH_RC4_128_MD5,
|
||||||
|
cipher_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA,
|
||||||
|
cipher_TLS_DH_anon_WITH_DES_CBC_SHA,
|
||||||
|
cipher_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_KRB5_WITH_DES_CBC_SHA,
|
||||||
|
cipher_TLS_KRB5_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_KRB5_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_KRB5_WITH_IDEA_CBC_SHA,
|
||||||
|
cipher_TLS_KRB5_WITH_DES_CBC_MD5,
|
||||||
|
cipher_TLS_KRB5_WITH_3DES_EDE_CBC_MD5,
|
||||||
|
cipher_TLS_KRB5_WITH_RC4_128_MD5,
|
||||||
|
cipher_TLS_KRB5_WITH_IDEA_CBC_MD5,
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA,
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA,
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_RC4_40_SHA,
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5,
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5,
|
||||||
|
cipher_TLS_KRB5_EXPORT_WITH_RC4_40_MD5,
|
||||||
|
cipher_TLS_PSK_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_WITH_NULL_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA,
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA,
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA,
|
||||||
|
cipher_TLS_PSK_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_PSK_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_WITH_SEED_CBC_SHA,
|
||||||
|
cipher_TLS_DH_DSS_WITH_SEED_CBC_SHA,
|
||||||
|
cipher_TLS_DH_RSA_WITH_SEED_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_SEED_CBC_SHA,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_SEED_CBC_SHA,
|
||||||
|
cipher_TLS_DH_anon_WITH_SEED_CBC_SHA,
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_DSS_WITH_AES_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_anon_WITH_AES_256_GCM_SHA384,
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_GCM_SHA256,
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_GCM_SHA384,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_CBC_SHA384,
|
||||||
|
cipher_TLS_PSK_WITH_NULL_SHA256,
|
||||||
|
cipher_TLS_PSK_WITH_NULL_SHA384,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_NULL_SHA256,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_NULL_SHA384,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_NULL_SHA256,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_NULL_SHA384,
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256,
|
||||||
|
cipher_TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_ECDH_anon_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_ECDH_anon_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_RC4_128_SHA,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA256,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA384,
|
||||||
|
cipher_TLS_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_DH_anon_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DH_anon_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_RSA_WITH_ARIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_ARIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_anon_WITH_ARIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_anon_WITH_ARIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_PSK_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_PSK_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_PSK_WITH_ARIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_PSK_WITH_ARIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384,
|
||||||
|
cipher_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||||||
|
cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_CCM,
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_CCM,
|
||||||
|
cipher_TLS_RSA_WITH_AES_128_CCM_8,
|
||||||
|
cipher_TLS_RSA_WITH_AES_256_CCM_8,
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_CCM,
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_CCM,
|
||||||
|
cipher_TLS_PSK_WITH_AES_128_CCM_8,
|
||||||
|
cipher_TLS_PSK_WITH_AES_256_CCM_8:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
278
vendor/golang.org/x/net/http2/client_conn_pool.go
generated
vendored
Normal file
278
vendor/golang.org/x/net/http2/client_conn_pool.go
generated
vendored
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Transport code's client connection pooling.
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClientConnPool manages a pool of HTTP/2 client connections.
|
||||||
|
type ClientConnPool interface {
|
||||||
|
GetClientConn(req *http.Request, addr string) (*ClientConn, error)
|
||||||
|
MarkDead(*ClientConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// clientConnPoolIdleCloser is the interface implemented by ClientConnPool
|
||||||
|
// implementations which can close their idle connections.
|
||||||
|
type clientConnPoolIdleCloser interface {
|
||||||
|
ClientConnPool
|
||||||
|
closeIdleConnections()
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ clientConnPoolIdleCloser = (*clientConnPool)(nil)
|
||||||
|
_ clientConnPoolIdleCloser = noDialClientConnPool{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: use singleflight for dialing and addConnCalls?
|
||||||
|
type clientConnPool struct {
|
||||||
|
t *Transport
|
||||||
|
|
||||||
|
mu sync.Mutex // TODO: maybe switch to RWMutex
|
||||||
|
// TODO: add support for sharing conns based on cert names
|
||||||
|
// (e.g. share conn for googleapis.com and appspot.com)
|
||||||
|
conns map[string][]*ClientConn // key is host:port
|
||||||
|
dialing map[string]*dialCall // currently in-flight dials
|
||||||
|
keys map[*ClientConn][]string
|
||||||
|
addConnCalls map[string]*addConnCall // in-flight addConnIfNeede calls
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *clientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) {
|
||||||
|
return p.getClientConn(req, addr, dialOnMiss)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
dialOnMiss = true
|
||||||
|
noDialOnMiss = false
|
||||||
|
)
|
||||||
|
|
||||||
|
// shouldTraceGetConn reports whether getClientConn should call any
|
||||||
|
// ClientTrace.GetConn hook associated with the http.Request.
|
||||||
|
//
|
||||||
|
// This complexity is needed to avoid double calls of the GetConn hook
|
||||||
|
// during the back-and-forth between net/http and x/net/http2 (when the
|
||||||
|
// net/http.Transport is upgraded to also speak http2), as well as support
|
||||||
|
// the case where x/net/http2 is being used directly.
|
||||||
|
func (p *clientConnPool) shouldTraceGetConn(st clientConnIdleState) bool {
|
||||||
|
// If our Transport wasn't made via ConfigureTransport, always
|
||||||
|
// trace the GetConn hook if provided, because that means the
|
||||||
|
// http2 package is being used directly and it's the one
|
||||||
|
// dialing, as opposed to net/http.
|
||||||
|
if _, ok := p.t.ConnPool.(noDialClientConnPool); !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Otherwise, only use the GetConn hook if this connection has
|
||||||
|
// been used previously for other requests. For fresh
|
||||||
|
// connections, the net/http package does the dialing.
|
||||||
|
return !st.freshConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) {
|
||||||
|
if isConnectionCloseRequest(req) && dialOnMiss {
|
||||||
|
// It gets its own connection.
|
||||||
|
traceGetConn(req, addr)
|
||||||
|
const singleUse = true
|
||||||
|
cc, err := p.t.dialClientConn(addr, singleUse)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cc, nil
|
||||||
|
}
|
||||||
|
p.mu.Lock()
|
||||||
|
for _, cc := range p.conns[addr] {
|
||||||
|
if st := cc.idleState(); st.canTakeNewRequest {
|
||||||
|
if p.shouldTraceGetConn(st) {
|
||||||
|
traceGetConn(req, addr)
|
||||||
|
}
|
||||||
|
p.mu.Unlock()
|
||||||
|
return cc, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !dialOnMiss {
|
||||||
|
p.mu.Unlock()
|
||||||
|
return nil, ErrNoCachedConn
|
||||||
|
}
|
||||||
|
traceGetConn(req, addr)
|
||||||
|
call := p.getStartDialLocked(addr)
|
||||||
|
p.mu.Unlock()
|
||||||
|
<-call.done
|
||||||
|
return call.res, call.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// dialCall is an in-flight Transport dial call to a host.
|
||||||
|
type dialCall struct {
|
||||||
|
_ incomparable
|
||||||
|
p *clientConnPool
|
||||||
|
done chan struct{} // closed when done
|
||||||
|
res *ClientConn // valid after done is closed
|
||||||
|
err error // valid after done is closed
|
||||||
|
}
|
||||||
|
|
||||||
|
// requires p.mu is held.
|
||||||
|
func (p *clientConnPool) getStartDialLocked(addr string) *dialCall {
|
||||||
|
if call, ok := p.dialing[addr]; ok {
|
||||||
|
// A dial is already in-flight. Don't start another.
|
||||||
|
return call
|
||||||
|
}
|
||||||
|
call := &dialCall{p: p, done: make(chan struct{})}
|
||||||
|
if p.dialing == nil {
|
||||||
|
p.dialing = make(map[string]*dialCall)
|
||||||
|
}
|
||||||
|
p.dialing[addr] = call
|
||||||
|
go call.dial(addr)
|
||||||
|
return call
|
||||||
|
}
|
||||||
|
|
||||||
|
// run in its own goroutine.
|
||||||
|
func (c *dialCall) dial(addr string) {
|
||||||
|
const singleUse = false // shared conn
|
||||||
|
c.res, c.err = c.p.t.dialClientConn(addr, singleUse)
|
||||||
|
close(c.done)
|
||||||
|
|
||||||
|
c.p.mu.Lock()
|
||||||
|
delete(c.p.dialing, addr)
|
||||||
|
if c.err == nil {
|
||||||
|
c.p.addConnLocked(addr, c.res)
|
||||||
|
}
|
||||||
|
c.p.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// addConnIfNeeded makes a NewClientConn out of c if a connection for key doesn't
|
||||||
|
// already exist. It coalesces concurrent calls with the same key.
|
||||||
|
// This is used by the http1 Transport code when it creates a new connection. Because
|
||||||
|
// the http1 Transport doesn't de-dup TCP dials to outbound hosts (because it doesn't know
|
||||||
|
// the protocol), it can get into a situation where it has multiple TLS connections.
|
||||||
|
// This code decides which ones live or die.
|
||||||
|
// The return value used is whether c was used.
|
||||||
|
// c is never closed.
|
||||||
|
func (p *clientConnPool) addConnIfNeeded(key string, t *Transport, c *tls.Conn) (used bool, err error) {
|
||||||
|
p.mu.Lock()
|
||||||
|
for _, cc := range p.conns[key] {
|
||||||
|
if cc.CanTakeNewRequest() {
|
||||||
|
p.mu.Unlock()
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
call, dup := p.addConnCalls[key]
|
||||||
|
if !dup {
|
||||||
|
if p.addConnCalls == nil {
|
||||||
|
p.addConnCalls = make(map[string]*addConnCall)
|
||||||
|
}
|
||||||
|
call = &addConnCall{
|
||||||
|
p: p,
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
p.addConnCalls[key] = call
|
||||||
|
go call.run(t, key, c)
|
||||||
|
}
|
||||||
|
p.mu.Unlock()
|
||||||
|
|
||||||
|
<-call.done
|
||||||
|
if call.err != nil {
|
||||||
|
return false, call.err
|
||||||
|
}
|
||||||
|
return !dup, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type addConnCall struct {
|
||||||
|
_ incomparable
|
||||||
|
p *clientConnPool
|
||||||
|
done chan struct{} // closed when done
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *addConnCall) run(t *Transport, key string, tc *tls.Conn) {
|
||||||
|
cc, err := t.NewClientConn(tc)
|
||||||
|
|
||||||
|
p := c.p
|
||||||
|
p.mu.Lock()
|
||||||
|
if err != nil {
|
||||||
|
c.err = err
|
||||||
|
} else {
|
||||||
|
p.addConnLocked(key, cc)
|
||||||
|
}
|
||||||
|
delete(p.addConnCalls, key)
|
||||||
|
p.mu.Unlock()
|
||||||
|
close(c.done)
|
||||||
|
}
|
||||||
|
|
||||||
|
// p.mu must be held
|
||||||
|
func (p *clientConnPool) addConnLocked(key string, cc *ClientConn) {
|
||||||
|
for _, v := range p.conns[key] {
|
||||||
|
if v == cc {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.conns == nil {
|
||||||
|
p.conns = make(map[string][]*ClientConn)
|
||||||
|
}
|
||||||
|
if p.keys == nil {
|
||||||
|
p.keys = make(map[*ClientConn][]string)
|
||||||
|
}
|
||||||
|
p.conns[key] = append(p.conns[key], cc)
|
||||||
|
p.keys[cc] = append(p.keys[cc], key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *clientConnPool) MarkDead(cc *ClientConn) {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
for _, key := range p.keys[cc] {
|
||||||
|
vv, ok := p.conns[key]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newList := filterOutClientConn(vv, cc)
|
||||||
|
if len(newList) > 0 {
|
||||||
|
p.conns[key] = newList
|
||||||
|
} else {
|
||||||
|
delete(p.conns, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete(p.keys, cc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *clientConnPool) closeIdleConnections() {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
// TODO: don't close a cc if it was just added to the pool
|
||||||
|
// milliseconds ago and has never been used. There's currently
|
||||||
|
// a small race window with the HTTP/1 Transport's integration
|
||||||
|
// where it can add an idle conn just before using it, and
|
||||||
|
// somebody else can concurrently call CloseIdleConns and
|
||||||
|
// break some caller's RoundTrip.
|
||||||
|
for _, vv := range p.conns {
|
||||||
|
for _, cc := range vv {
|
||||||
|
cc.closeIfIdle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterOutClientConn(in []*ClientConn, exclude *ClientConn) []*ClientConn {
|
||||||
|
out := in[:0]
|
||||||
|
for _, v := range in {
|
||||||
|
if v != exclude {
|
||||||
|
out = append(out, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we filtered it out, zero out the last item to prevent
|
||||||
|
// the GC from seeing it.
|
||||||
|
if len(in) != len(out) {
|
||||||
|
in[len(in)-1] = nil
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// noDialClientConnPool is an implementation of http2.ClientConnPool
|
||||||
|
// which never dials. We let the HTTP/1.1 client dial and use its TLS
|
||||||
|
// connection instead.
|
||||||
|
type noDialClientConnPool struct{ *clientConnPool }
|
||||||
|
|
||||||
|
func (p noDialClientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) {
|
||||||
|
return p.getClientConn(req, addr, noDialOnMiss)
|
||||||
|
}
|
146
vendor/golang.org/x/net/http2/databuffer.go
generated
vendored
Normal file
146
vendor/golang.org/x/net/http2/databuffer.go
generated
vendored
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Buffer chunks are allocated from a pool to reduce pressure on GC.
|
||||||
|
// The maximum wasted space per dataBuffer is 2x the largest size class,
|
||||||
|
// which happens when the dataBuffer has multiple chunks and there is
|
||||||
|
// one unread byte in both the first and last chunks. We use a few size
|
||||||
|
// classes to minimize overheads for servers that typically receive very
|
||||||
|
// small request bodies.
|
||||||
|
//
|
||||||
|
// TODO: Benchmark to determine if the pools are necessary. The GC may have
|
||||||
|
// improved enough that we can instead allocate chunks like this:
|
||||||
|
// make([]byte, max(16<<10, expectedBytesRemaining))
|
||||||
|
var (
|
||||||
|
dataChunkSizeClasses = []int{
|
||||||
|
1 << 10,
|
||||||
|
2 << 10,
|
||||||
|
4 << 10,
|
||||||
|
8 << 10,
|
||||||
|
16 << 10,
|
||||||
|
}
|
||||||
|
dataChunkPools = [...]sync.Pool{
|
||||||
|
{New: func() interface{} { return make([]byte, 1<<10) }},
|
||||||
|
{New: func() interface{} { return make([]byte, 2<<10) }},
|
||||||
|
{New: func() interface{} { return make([]byte, 4<<10) }},
|
||||||
|
{New: func() interface{} { return make([]byte, 8<<10) }},
|
||||||
|
{New: func() interface{} { return make([]byte, 16<<10) }},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func getDataBufferChunk(size int64) []byte {
|
||||||
|
i := 0
|
||||||
|
for ; i < len(dataChunkSizeClasses)-1; i++ {
|
||||||
|
if size <= int64(dataChunkSizeClasses[i]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dataChunkPools[i].Get().([]byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
func putDataBufferChunk(p []byte) {
|
||||||
|
for i, n := range dataChunkSizeClasses {
|
||||||
|
if len(p) == n {
|
||||||
|
dataChunkPools[i].Put(p)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("unexpected buffer len=%v", len(p)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// dataBuffer is an io.ReadWriter backed by a list of data chunks.
|
||||||
|
// Each dataBuffer is used to read DATA frames on a single stream.
|
||||||
|
// The buffer is divided into chunks so the server can limit the
|
||||||
|
// total memory used by a single connection without limiting the
|
||||||
|
// request body size on any single stream.
|
||||||
|
type dataBuffer struct {
|
||||||
|
chunks [][]byte
|
||||||
|
r int // next byte to read is chunks[0][r]
|
||||||
|
w int // next byte to write is chunks[len(chunks)-1][w]
|
||||||
|
size int // total buffered bytes
|
||||||
|
expected int64 // we expect at least this many bytes in future Write calls (ignored if <= 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var errReadEmpty = errors.New("read from empty dataBuffer")
|
||||||
|
|
||||||
|
// Read copies bytes from the buffer into p.
|
||||||
|
// It is an error to read when no data is available.
|
||||||
|
func (b *dataBuffer) Read(p []byte) (int, error) {
|
||||||
|
if b.size == 0 {
|
||||||
|
return 0, errReadEmpty
|
||||||
|
}
|
||||||
|
var ntotal int
|
||||||
|
for len(p) > 0 && b.size > 0 {
|
||||||
|
readFrom := b.bytesFromFirstChunk()
|
||||||
|
n := copy(p, readFrom)
|
||||||
|
p = p[n:]
|
||||||
|
ntotal += n
|
||||||
|
b.r += n
|
||||||
|
b.size -= n
|
||||||
|
// If the first chunk has been consumed, advance to the next chunk.
|
||||||
|
if b.r == len(b.chunks[0]) {
|
||||||
|
putDataBufferChunk(b.chunks[0])
|
||||||
|
end := len(b.chunks) - 1
|
||||||
|
copy(b.chunks[:end], b.chunks[1:])
|
||||||
|
b.chunks[end] = nil
|
||||||
|
b.chunks = b.chunks[:end]
|
||||||
|
b.r = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ntotal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *dataBuffer) bytesFromFirstChunk() []byte {
|
||||||
|
if len(b.chunks) == 1 {
|
||||||
|
return b.chunks[0][b.r:b.w]
|
||||||
|
}
|
||||||
|
return b.chunks[0][b.r:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of bytes of the unread portion of the buffer.
|
||||||
|
func (b *dataBuffer) Len() int {
|
||||||
|
return b.size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write appends p to the buffer.
|
||||||
|
func (b *dataBuffer) Write(p []byte) (int, error) {
|
||||||
|
ntotal := len(p)
|
||||||
|
for len(p) > 0 {
|
||||||
|
// If the last chunk is empty, allocate a new chunk. Try to allocate
|
||||||
|
// enough to fully copy p plus any additional bytes we expect to
|
||||||
|
// receive. However, this may allocate less than len(p).
|
||||||
|
want := int64(len(p))
|
||||||
|
if b.expected > want {
|
||||||
|
want = b.expected
|
||||||
|
}
|
||||||
|
chunk := b.lastChunkOrAlloc(want)
|
||||||
|
n := copy(chunk[b.w:], p)
|
||||||
|
p = p[n:]
|
||||||
|
b.w += n
|
||||||
|
b.size += n
|
||||||
|
b.expected -= int64(n)
|
||||||
|
}
|
||||||
|
return ntotal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *dataBuffer) lastChunkOrAlloc(want int64) []byte {
|
||||||
|
if len(b.chunks) != 0 {
|
||||||
|
last := b.chunks[len(b.chunks)-1]
|
||||||
|
if b.w < len(last) {
|
||||||
|
return last
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chunk := getDataBufferChunk(want)
|
||||||
|
b.chunks = append(b.chunks, chunk)
|
||||||
|
b.w = 0
|
||||||
|
return chunk
|
||||||
|
}
|
133
vendor/golang.org/x/net/http2/errors.go
generated
vendored
Normal file
133
vendor/golang.org/x/net/http2/errors.go
generated
vendored
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// An ErrCode is an unsigned 32-bit error code as defined in the HTTP/2 spec.
|
||||||
|
type ErrCode uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrCodeNo ErrCode = 0x0
|
||||||
|
ErrCodeProtocol ErrCode = 0x1
|
||||||
|
ErrCodeInternal ErrCode = 0x2
|
||||||
|
ErrCodeFlowControl ErrCode = 0x3
|
||||||
|
ErrCodeSettingsTimeout ErrCode = 0x4
|
||||||
|
ErrCodeStreamClosed ErrCode = 0x5
|
||||||
|
ErrCodeFrameSize ErrCode = 0x6
|
||||||
|
ErrCodeRefusedStream ErrCode = 0x7
|
||||||
|
ErrCodeCancel ErrCode = 0x8
|
||||||
|
ErrCodeCompression ErrCode = 0x9
|
||||||
|
ErrCodeConnect ErrCode = 0xa
|
||||||
|
ErrCodeEnhanceYourCalm ErrCode = 0xb
|
||||||
|
ErrCodeInadequateSecurity ErrCode = 0xc
|
||||||
|
ErrCodeHTTP11Required ErrCode = 0xd
|
||||||
|
)
|
||||||
|
|
||||||
|
var errCodeName = map[ErrCode]string{
|
||||||
|
ErrCodeNo: "NO_ERROR",
|
||||||
|
ErrCodeProtocol: "PROTOCOL_ERROR",
|
||||||
|
ErrCodeInternal: "INTERNAL_ERROR",
|
||||||
|
ErrCodeFlowControl: "FLOW_CONTROL_ERROR",
|
||||||
|
ErrCodeSettingsTimeout: "SETTINGS_TIMEOUT",
|
||||||
|
ErrCodeStreamClosed: "STREAM_CLOSED",
|
||||||
|
ErrCodeFrameSize: "FRAME_SIZE_ERROR",
|
||||||
|
ErrCodeRefusedStream: "REFUSED_STREAM",
|
||||||
|
ErrCodeCancel: "CANCEL",
|
||||||
|
ErrCodeCompression: "COMPRESSION_ERROR",
|
||||||
|
ErrCodeConnect: "CONNECT_ERROR",
|
||||||
|
ErrCodeEnhanceYourCalm: "ENHANCE_YOUR_CALM",
|
||||||
|
ErrCodeInadequateSecurity: "INADEQUATE_SECURITY",
|
||||||
|
ErrCodeHTTP11Required: "HTTP_1_1_REQUIRED",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrCode) String() string {
|
||||||
|
if s, ok := errCodeName[e]; ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("unknown error code 0x%x", uint32(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectionError is an error that results in the termination of the
|
||||||
|
// entire connection.
|
||||||
|
type ConnectionError ErrCode
|
||||||
|
|
||||||
|
func (e ConnectionError) Error() string { return fmt.Sprintf("connection error: %s", ErrCode(e)) }
|
||||||
|
|
||||||
|
// StreamError is an error that only affects one stream within an
|
||||||
|
// HTTP/2 connection.
|
||||||
|
type StreamError struct {
|
||||||
|
StreamID uint32
|
||||||
|
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 {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6.9.1 The Flow Control Window
|
||||||
|
// "If a sender receives a WINDOW_UPDATE that causes a flow control
|
||||||
|
// window to exceed this maximum it MUST terminate either the stream
|
||||||
|
// or the connection, as appropriate. For streams, [...]; for the
|
||||||
|
// connection, a GOAWAY frame with a FLOW_CONTROL_ERROR code."
|
||||||
|
type goAwayFlowError struct{}
|
||||||
|
|
||||||
|
func (goAwayFlowError) Error() string { return "connection exceeded flow control window size" }
|
||||||
|
|
||||||
|
// connError represents an HTTP/2 ConnectionError error code, along
|
||||||
|
// with a string (for debugging) explaining why.
|
||||||
|
//
|
||||||
|
// Errors of this type are only returned by the frame parser functions
|
||||||
|
// and converted into ConnectionError(Code), after stashing away
|
||||||
|
// the Reason into the Framer's errDetail field, accessible via
|
||||||
|
// the (*Framer).ErrorDetail method.
|
||||||
|
type connError struct {
|
||||||
|
Code ErrCode // the ConnectionError error code
|
||||||
|
Reason string // additional reason
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e connError) Error() string {
|
||||||
|
return fmt.Sprintf("http2: connection error: %v: %v", e.Code, e.Reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
type pseudoHeaderError string
|
||||||
|
|
||||||
|
func (e pseudoHeaderError) Error() string {
|
||||||
|
return fmt.Sprintf("invalid pseudo-header %q", string(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
type duplicatePseudoHeaderError string
|
||||||
|
|
||||||
|
func (e duplicatePseudoHeaderError) Error() string {
|
||||||
|
return fmt.Sprintf("duplicate pseudo-header %q", string(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
type headerFieldNameError string
|
||||||
|
|
||||||
|
func (e headerFieldNameError) Error() string {
|
||||||
|
return fmt.Sprintf("invalid header field name %q", string(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
type headerFieldValueError string
|
||||||
|
|
||||||
|
func (e headerFieldValueError) Error() string {
|
||||||
|
return fmt.Sprintf("invalid header field value %q", string(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errMixPseudoHeaderTypes = errors.New("mix of request and response pseudo headers")
|
||||||
|
errPseudoAfterRegular = errors.New("pseudo header field after regular")
|
||||||
|
)
|
52
vendor/golang.org/x/net/http2/flow.go
generated
vendored
Normal file
52
vendor/golang.org/x/net/http2/flow.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Flow control
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
// flow is the flow control window's size.
|
||||||
|
type flow struct {
|
||||||
|
_ incomparable
|
||||||
|
|
||||||
|
// n is the number of DATA bytes we're allowed to send.
|
||||||
|
// A flow is kept both on a conn and a per-stream.
|
||||||
|
n int32
|
||||||
|
|
||||||
|
// conn points to the shared connection-level flow that is
|
||||||
|
// shared by all streams on that conn. It is nil for the flow
|
||||||
|
// that's on the conn directly.
|
||||||
|
conn *flow
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *flow) setConnFlow(cf *flow) { f.conn = cf }
|
||||||
|
|
||||||
|
func (f *flow) available() int32 {
|
||||||
|
n := f.n
|
||||||
|
if f.conn != nil && f.conn.n < n {
|
||||||
|
n = f.conn.n
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *flow) take(n int32) {
|
||||||
|
if n > f.available() {
|
||||||
|
panic("internal error: took too much")
|
||||||
|
}
|
||||||
|
f.n -= n
|
||||||
|
if f.conn != nil {
|
||||||
|
f.conn.n -= n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add adds n bytes (positive or negative) to the flow control window.
|
||||||
|
// It returns false if the sum would exceed 2^31-1.
|
||||||
|
func (f *flow) add(n int32) bool {
|
||||||
|
sum := f.n + n
|
||||||
|
if (sum > n) == (f.n > 0) {
|
||||||
|
f.n = sum
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
1614
vendor/golang.org/x/net/http2/frame.go
generated
vendored
Normal file
1614
vendor/golang.org/x/net/http2/frame.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
30
vendor/golang.org/x/net/http2/go111.go
generated
vendored
Normal file
30
vendor/golang.org/x/net/http2/go111.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2018 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.
|
||||||
|
|
||||||
|
//go:build go1.11
|
||||||
|
// +build go1.11
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http/httptrace"
|
||||||
|
"net/textproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool {
|
||||||
|
return trace != nil && trace.WroteHeaderField != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {
|
||||||
|
if trace != nil && trace.WroteHeaderField != nil {
|
||||||
|
trace.WroteHeaderField(k, []string{v})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error {
|
||||||
|
if trace != nil {
|
||||||
|
return trace.Got1xxResponse
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
170
vendor/golang.org/x/net/http2/gotrack.go
generated
vendored
Normal file
170
vendor/golang.org/x/net/http2/gotrack.go
generated
vendored
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Defensive debug-only utility to track that functions run on the
|
||||||
|
// goroutine that they're supposed to.
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1"
|
||||||
|
|
||||||
|
type goroutineLock uint64
|
||||||
|
|
||||||
|
func newGoroutineLock() goroutineLock {
|
||||||
|
if !DebugGoroutines {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return goroutineLock(curGoroutineID())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g goroutineLock) check() {
|
||||||
|
if !DebugGoroutines {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if curGoroutineID() != uint64(g) {
|
||||||
|
panic("running on the wrong goroutine")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g goroutineLock) checkNotOn() {
|
||||||
|
if !DebugGoroutines {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if curGoroutineID() == uint64(g) {
|
||||||
|
panic("running on the wrong goroutine")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var goroutineSpace = []byte("goroutine ")
|
||||||
|
|
||||||
|
func curGoroutineID() uint64 {
|
||||||
|
bp := littleBuf.Get().(*[]byte)
|
||||||
|
defer littleBuf.Put(bp)
|
||||||
|
b := *bp
|
||||||
|
b = b[:runtime.Stack(b, false)]
|
||||||
|
// Parse the 4707 out of "goroutine 4707 ["
|
||||||
|
b = bytes.TrimPrefix(b, goroutineSpace)
|
||||||
|
i := bytes.IndexByte(b, ' ')
|
||||||
|
if i < 0 {
|
||||||
|
panic(fmt.Sprintf("No space found in %q", b))
|
||||||
|
}
|
||||||
|
b = b[:i]
|
||||||
|
n, err := parseUintBytes(b, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
var littleBuf = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
buf := make([]byte, 64)
|
||||||
|
return &buf
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseUintBytes is like strconv.ParseUint, but using a []byte.
|
||||||
|
func parseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) {
|
||||||
|
var cutoff, maxVal uint64
|
||||||
|
|
||||||
|
if bitSize == 0 {
|
||||||
|
bitSize = int(strconv.IntSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
s0 := s
|
||||||
|
switch {
|
||||||
|
case len(s) < 1:
|
||||||
|
err = strconv.ErrSyntax
|
||||||
|
goto Error
|
||||||
|
|
||||||
|
case 2 <= base && base <= 36:
|
||||||
|
// valid base; nothing to do
|
||||||
|
|
||||||
|
case base == 0:
|
||||||
|
// Look for octal, hex prefix.
|
||||||
|
switch {
|
||||||
|
case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
|
||||||
|
base = 16
|
||||||
|
s = s[2:]
|
||||||
|
if len(s) < 1 {
|
||||||
|
err = strconv.ErrSyntax
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
case s[0] == '0':
|
||||||
|
base = 8
|
||||||
|
default:
|
||||||
|
base = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
err = errors.New("invalid base " + strconv.Itoa(base))
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
n = 0
|
||||||
|
cutoff = cutoff64(base)
|
||||||
|
maxVal = 1<<uint(bitSize) - 1
|
||||||
|
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
var v byte
|
||||||
|
d := s[i]
|
||||||
|
switch {
|
||||||
|
case '0' <= d && d <= '9':
|
||||||
|
v = d - '0'
|
||||||
|
case 'a' <= d && d <= 'z':
|
||||||
|
v = d - 'a' + 10
|
||||||
|
case 'A' <= d && d <= 'Z':
|
||||||
|
v = d - 'A' + 10
|
||||||
|
default:
|
||||||
|
n = 0
|
||||||
|
err = strconv.ErrSyntax
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
if int(v) >= base {
|
||||||
|
n = 0
|
||||||
|
err = strconv.ErrSyntax
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if n >= cutoff {
|
||||||
|
// n*base overflows
|
||||||
|
n = 1<<64 - 1
|
||||||
|
err = strconv.ErrRange
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
n *= uint64(base)
|
||||||
|
|
||||||
|
n1 := n + uint64(v)
|
||||||
|
if n1 < n || n1 > maxVal {
|
||||||
|
// n+v overflows
|
||||||
|
n = 1<<64 - 1
|
||||||
|
err = strconv.ErrRange
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
n = n1
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
|
||||||
|
Error:
|
||||||
|
return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the first number n such that n*base >= 1<<64.
|
||||||
|
func cutoff64(base int) uint64 {
|
||||||
|
if base < 2 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return (1<<64-1)/uint64(base) + 1
|
||||||
|
}
|
88
vendor/golang.org/x/net/http2/headermap.go
generated
vendored
Normal file
88
vendor/golang.org/x/net/http2/headermap.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// 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 (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
commonBuildOnce sync.Once
|
||||||
|
commonLowerHeader map[string]string // Go-Canonical-Case -> lower-case
|
||||||
|
commonCanonHeader map[string]string // lower-case -> Go-Canonical-Case
|
||||||
|
)
|
||||||
|
|
||||||
|
func buildCommonHeaderMapsOnce() {
|
||||||
|
commonBuildOnce.Do(buildCommonHeaderMaps)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildCommonHeaderMaps() {
|
||||||
|
common := []string{
|
||||||
|
"accept",
|
||||||
|
"accept-charset",
|
||||||
|
"accept-encoding",
|
||||||
|
"accept-language",
|
||||||
|
"accept-ranges",
|
||||||
|
"age",
|
||||||
|
"access-control-allow-origin",
|
||||||
|
"allow",
|
||||||
|
"authorization",
|
||||||
|
"cache-control",
|
||||||
|
"content-disposition",
|
||||||
|
"content-encoding",
|
||||||
|
"content-language",
|
||||||
|
"content-length",
|
||||||
|
"content-location",
|
||||||
|
"content-range",
|
||||||
|
"content-type",
|
||||||
|
"cookie",
|
||||||
|
"date",
|
||||||
|
"etag",
|
||||||
|
"expect",
|
||||||
|
"expires",
|
||||||
|
"from",
|
||||||
|
"host",
|
||||||
|
"if-match",
|
||||||
|
"if-modified-since",
|
||||||
|
"if-none-match",
|
||||||
|
"if-unmodified-since",
|
||||||
|
"last-modified",
|
||||||
|
"link",
|
||||||
|
"location",
|
||||||
|
"max-forwards",
|
||||||
|
"proxy-authenticate",
|
||||||
|
"proxy-authorization",
|
||||||
|
"range",
|
||||||
|
"referer",
|
||||||
|
"refresh",
|
||||||
|
"retry-after",
|
||||||
|
"server",
|
||||||
|
"set-cookie",
|
||||||
|
"strict-transport-security",
|
||||||
|
"trailer",
|
||||||
|
"transfer-encoding",
|
||||||
|
"user-agent",
|
||||||
|
"vary",
|
||||||
|
"via",
|
||||||
|
"www-authenticate",
|
||||||
|
}
|
||||||
|
commonLowerHeader = make(map[string]string, len(common))
|
||||||
|
commonCanonHeader = make(map[string]string, len(common))
|
||||||
|
for _, v := range common {
|
||||||
|
chk := http.CanonicalHeaderKey(v)
|
||||||
|
commonLowerHeader[chk] = v
|
||||||
|
commonCanonHeader[v] = chk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func lowerHeader(v string) string {
|
||||||
|
buildCommonHeaderMapsOnce()
|
||||||
|
if s, ok := commonLowerHeader[v]; ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return strings.ToLower(v)
|
||||||
|
}
|
240
vendor/golang.org/x/net/http2/hpack/encode.go
generated
vendored
Normal file
240
vendor/golang.org/x/net/http2/hpack/encode.go
generated
vendored
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
// 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 hpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
uint32Max = ^uint32(0)
|
||||||
|
initialHeaderTableSize = 4096
|
||||||
|
)
|
||||||
|
|
||||||
|
type Encoder struct {
|
||||||
|
dynTab dynamicTable
|
||||||
|
// minSize is the minimum table size set by
|
||||||
|
// SetMaxDynamicTableSize after the previous Header Table Size
|
||||||
|
// Update.
|
||||||
|
minSize uint32
|
||||||
|
// maxSizeLimit is the maximum table size this encoder
|
||||||
|
// supports. This will protect the encoder from too large
|
||||||
|
// size.
|
||||||
|
maxSizeLimit uint32
|
||||||
|
// tableSizeUpdate indicates whether "Header Table Size
|
||||||
|
// Update" is required.
|
||||||
|
tableSizeUpdate bool
|
||||||
|
w io.Writer
|
||||||
|
buf []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder returns a new Encoder which performs HPACK encoding. An
|
||||||
|
// encoded data is written to w.
|
||||||
|
func NewEncoder(w io.Writer) *Encoder {
|
||||||
|
e := &Encoder{
|
||||||
|
minSize: uint32Max,
|
||||||
|
maxSizeLimit: initialHeaderTableSize,
|
||||||
|
tableSizeUpdate: false,
|
||||||
|
w: w,
|
||||||
|
}
|
||||||
|
e.dynTab.table.init()
|
||||||
|
e.dynTab.setMaxSize(initialHeaderTableSize)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteField encodes f into a single Write to e's underlying Writer.
|
||||||
|
// This function may also produce bytes for "Header Table Size Update"
|
||||||
|
// if necessary. If produced, it is done before encoding f.
|
||||||
|
func (e *Encoder) WriteField(f HeaderField) error {
|
||||||
|
e.buf = e.buf[:0]
|
||||||
|
|
||||||
|
if e.tableSizeUpdate {
|
||||||
|
e.tableSizeUpdate = false
|
||||||
|
if e.minSize < e.dynTab.maxSize {
|
||||||
|
e.buf = appendTableSize(e.buf, e.minSize)
|
||||||
|
}
|
||||||
|
e.minSize = uint32Max
|
||||||
|
e.buf = appendTableSize(e.buf, e.dynTab.maxSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, nameValueMatch := e.searchTable(f)
|
||||||
|
if nameValueMatch {
|
||||||
|
e.buf = appendIndexed(e.buf, idx)
|
||||||
|
} else {
|
||||||
|
indexing := e.shouldIndex(f)
|
||||||
|
if indexing {
|
||||||
|
e.dynTab.add(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
if idx == 0 {
|
||||||
|
e.buf = appendNewName(e.buf, f, indexing)
|
||||||
|
} else {
|
||||||
|
e.buf = appendIndexedName(e.buf, f, idx, indexing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n, err := e.w.Write(e.buf)
|
||||||
|
if err == nil && n != len(e.buf) {
|
||||||
|
err = io.ErrShortWrite
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// searchTable searches f in both stable and dynamic header tables.
|
||||||
|
// The static header table is searched first. Only when there is no
|
||||||
|
// exact match for both name and value, the dynamic header table is
|
||||||
|
// then searched. 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.
|
||||||
|
func (e *Encoder) searchTable(f HeaderField) (i uint64, nameValueMatch bool) {
|
||||||
|
i, nameValueMatch = staticTable.search(f)
|
||||||
|
if nameValueMatch {
|
||||||
|
return i, true
|
||||||
|
}
|
||||||
|
|
||||||
|
j, nameValueMatch := e.dynTab.table.search(f)
|
||||||
|
if nameValueMatch || (i == 0 && j != 0) {
|
||||||
|
return j + uint64(staticTable.len()), nameValueMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMaxDynamicTableSize changes the dynamic header table size to v.
|
||||||
|
// The actual size is bounded by the value passed to
|
||||||
|
// SetMaxDynamicTableSizeLimit.
|
||||||
|
func (e *Encoder) SetMaxDynamicTableSize(v uint32) {
|
||||||
|
if v > e.maxSizeLimit {
|
||||||
|
v = e.maxSizeLimit
|
||||||
|
}
|
||||||
|
if v < e.minSize {
|
||||||
|
e.minSize = v
|
||||||
|
}
|
||||||
|
e.tableSizeUpdate = true
|
||||||
|
e.dynTab.setMaxSize(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMaxDynamicTableSizeLimit changes the maximum value that can be
|
||||||
|
// specified in SetMaxDynamicTableSize to v. By default, it is set to
|
||||||
|
// 4096, which is the same size of the default dynamic header table
|
||||||
|
// size described in HPACK specification. If the current maximum
|
||||||
|
// dynamic header table size is strictly greater than v, "Header Table
|
||||||
|
// Size Update" will be done in the next WriteField call and the
|
||||||
|
// maximum dynamic header table size is truncated to v.
|
||||||
|
func (e *Encoder) SetMaxDynamicTableSizeLimit(v uint32) {
|
||||||
|
e.maxSizeLimit = v
|
||||||
|
if e.dynTab.maxSize > v {
|
||||||
|
e.tableSizeUpdate = true
|
||||||
|
e.dynTab.setMaxSize(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// shouldIndex reports whether f should be indexed.
|
||||||
|
func (e *Encoder) shouldIndex(f HeaderField) bool {
|
||||||
|
return !f.Sensitive && f.Size() <= e.dynTab.maxSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendIndexed appends index i, as encoded in "Indexed Header Field"
|
||||||
|
// representation, to dst and returns the extended buffer.
|
||||||
|
func appendIndexed(dst []byte, i uint64) []byte {
|
||||||
|
first := len(dst)
|
||||||
|
dst = appendVarInt(dst, 7, i)
|
||||||
|
dst[first] |= 0x80
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendNewName appends f, as encoded in one of "Literal Header field
|
||||||
|
// - New Name" representation variants, to dst and returns the
|
||||||
|
// extended buffer.
|
||||||
|
//
|
||||||
|
// If f.Sensitive is true, "Never Indexed" representation is used. If
|
||||||
|
// f.Sensitive is false and indexing is true, "Incremental Indexing"
|
||||||
|
// representation is used.
|
||||||
|
func appendNewName(dst []byte, f HeaderField, indexing bool) []byte {
|
||||||
|
dst = append(dst, encodeTypeByte(indexing, f.Sensitive))
|
||||||
|
dst = appendHpackString(dst, f.Name)
|
||||||
|
return appendHpackString(dst, f.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendIndexedName appends f and index i referring indexed name
|
||||||
|
// entry, as encoded in one of "Literal Header field - Indexed Name"
|
||||||
|
// representation variants, to dst and returns the extended buffer.
|
||||||
|
//
|
||||||
|
// If f.Sensitive is true, "Never Indexed" representation is used. If
|
||||||
|
// f.Sensitive is false and indexing is true, "Incremental Indexing"
|
||||||
|
// representation is used.
|
||||||
|
func appendIndexedName(dst []byte, f HeaderField, i uint64, indexing bool) []byte {
|
||||||
|
first := len(dst)
|
||||||
|
var n byte
|
||||||
|
if indexing {
|
||||||
|
n = 6
|
||||||
|
} else {
|
||||||
|
n = 4
|
||||||
|
}
|
||||||
|
dst = appendVarInt(dst, n, i)
|
||||||
|
dst[first] |= encodeTypeByte(indexing, f.Sensitive)
|
||||||
|
return appendHpackString(dst, f.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendTableSize appends v, as encoded in "Header Table Size Update"
|
||||||
|
// representation, to dst and returns the extended buffer.
|
||||||
|
func appendTableSize(dst []byte, v uint32) []byte {
|
||||||
|
first := len(dst)
|
||||||
|
dst = appendVarInt(dst, 5, uint64(v))
|
||||||
|
dst[first] |= 0x20
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendVarInt appends i, as encoded in variable integer form using n
|
||||||
|
// bit prefix, to dst and returns the extended buffer.
|
||||||
|
//
|
||||||
|
// See
|
||||||
|
// http://http2.github.io/http2-spec/compression.html#integer.representation
|
||||||
|
func appendVarInt(dst []byte, n byte, i uint64) []byte {
|
||||||
|
k := uint64((1 << n) - 1)
|
||||||
|
if i < k {
|
||||||
|
return append(dst, byte(i))
|
||||||
|
}
|
||||||
|
dst = append(dst, byte(k))
|
||||||
|
i -= k
|
||||||
|
for ; i >= 128; i >>= 7 {
|
||||||
|
dst = append(dst, byte(0x80|(i&0x7f)))
|
||||||
|
}
|
||||||
|
return append(dst, byte(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendHpackString appends s, as encoded in "String Literal"
|
||||||
|
// representation, to dst and returns the extended buffer.
|
||||||
|
//
|
||||||
|
// s will be encoded in Huffman codes only when it produces strictly
|
||||||
|
// shorter byte string.
|
||||||
|
func appendHpackString(dst []byte, s string) []byte {
|
||||||
|
huffmanLength := HuffmanEncodeLength(s)
|
||||||
|
if huffmanLength < uint64(len(s)) {
|
||||||
|
first := len(dst)
|
||||||
|
dst = appendVarInt(dst, 7, huffmanLength)
|
||||||
|
dst = AppendHuffmanString(dst, s)
|
||||||
|
dst[first] |= 0x80
|
||||||
|
} else {
|
||||||
|
dst = appendVarInt(dst, 7, uint64(len(s)))
|
||||||
|
dst = append(dst, s...)
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodeTypeByte returns type byte. If sensitive is true, type byte
|
||||||
|
// for "Never Indexed" representation is returned. If sensitive is
|
||||||
|
// false and indexing is true, type byte for "Incremental Indexing"
|
||||||
|
// representation is returned. Otherwise, type byte for "Without
|
||||||
|
// Indexing" is returned.
|
||||||
|
func encodeTypeByte(indexing, sensitive bool) byte {
|
||||||
|
if sensitive {
|
||||||
|
return 0x10
|
||||||
|
}
|
||||||
|
if indexing {
|
||||||
|
return 0x40
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
504
vendor/golang.org/x/net/http2/hpack/hpack.go
generated
vendored
Normal file
504
vendor/golang.org/x/net/http2/hpack/hpack.go
generated
vendored
Normal file
@ -0,0 +1,504 @@
|
|||||||
|
// 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 hpack implements HPACK, a compression format for
|
||||||
|
// efficiently representing HTTP header fields in the context of HTTP/2.
|
||||||
|
//
|
||||||
|
// See http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09
|
||||||
|
package hpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A DecodingError is something the spec defines as a decoding error.
|
||||||
|
type DecodingError struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (de DecodingError) Error() string {
|
||||||
|
return fmt.Sprintf("decoding error: %v", de.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An InvalidIndexError is returned when an encoder references a table
|
||||||
|
// entry before the static table or after the end of the dynamic table.
|
||||||
|
type InvalidIndexError int
|
||||||
|
|
||||||
|
func (e InvalidIndexError) Error() string {
|
||||||
|
return fmt.Sprintf("invalid indexed representation index %d", int(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
// A HeaderField is a name-value pair. Both the name and value are
|
||||||
|
// treated as opaque sequences of octets.
|
||||||
|
type HeaderField struct {
|
||||||
|
Name, Value string
|
||||||
|
|
||||||
|
// Sensitive means that this header field should never be
|
||||||
|
// indexed.
|
||||||
|
Sensitive bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPseudo reports whether the header field is an http2 pseudo header.
|
||||||
|
// That is, it reports whether it starts with a colon.
|
||||||
|
// It is not otherwise guaranteed to be a valid pseudo header field,
|
||||||
|
// though.
|
||||||
|
func (hf HeaderField) IsPseudo() bool {
|
||||||
|
return len(hf.Name) != 0 && hf.Name[0] == ':'
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hf HeaderField) String() string {
|
||||||
|
var suffix string
|
||||||
|
if hf.Sensitive {
|
||||||
|
suffix = " (sensitive)"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("header field %q = %q%s", hf.Name, hf.Value, suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns the size of an entry per RFC 7541 section 4.1.
|
||||||
|
func (hf HeaderField) Size() uint32 {
|
||||||
|
// 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
|
||||||
|
// 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 (see Section 5.2), plus 32. The size of
|
||||||
|
// an entry is calculated using the length of the name and
|
||||||
|
// value without any Huffman encoding applied."
|
||||||
|
|
||||||
|
// This can overflow if somebody makes a large HeaderField
|
||||||
|
// Name and/or Value by hand, but we don't care, because that
|
||||||
|
// won't happen on the wire because the encoding doesn't allow
|
||||||
|
// it.
|
||||||
|
return uint32(len(hf.Name) + len(hf.Value) + 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Decoder is the decoding context for incremental processing of
|
||||||
|
// header blocks.
|
||||||
|
type Decoder struct {
|
||||||
|
dynTab dynamicTable
|
||||||
|
emit func(f HeaderField)
|
||||||
|
|
||||||
|
emitEnabled bool // whether calls to emit are enabled
|
||||||
|
maxStrLen int // 0 means unlimited
|
||||||
|
|
||||||
|
// buf is the unparsed buffer. It's only written to
|
||||||
|
// saveBuf if it was truncated in the middle of a header
|
||||||
|
// block. Because it's usually not owned, we can only
|
||||||
|
// process it under Write.
|
||||||
|
buf []byte // not owned; only valid during Write
|
||||||
|
|
||||||
|
// saveBuf is previous data passed to Write which we weren't able
|
||||||
|
// to fully parse before. Unlike buf, we own this data.
|
||||||
|
saveBuf bytes.Buffer
|
||||||
|
|
||||||
|
firstField bool // processing the first field of the header block
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder returns a new decoder with the provided maximum dynamic
|
||||||
|
// table size. The emitFunc will be called for each valid field
|
||||||
|
// parsed, in the same goroutine as calls to Write, before Write returns.
|
||||||
|
func NewDecoder(maxDynamicTableSize uint32, emitFunc func(f HeaderField)) *Decoder {
|
||||||
|
d := &Decoder{
|
||||||
|
emit: emitFunc,
|
||||||
|
emitEnabled: true,
|
||||||
|
firstField: true,
|
||||||
|
}
|
||||||
|
d.dynTab.table.init()
|
||||||
|
d.dynTab.allowedMaxSize = maxDynamicTableSize
|
||||||
|
d.dynTab.setMaxSize(maxDynamicTableSize)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrStringLength is returned by Decoder.Write when the max string length
|
||||||
|
// (as configured by Decoder.SetMaxStringLength) would be violated.
|
||||||
|
var ErrStringLength = errors.New("hpack: string too long")
|
||||||
|
|
||||||
|
// SetMaxStringLength sets the maximum size of a HeaderField name or
|
||||||
|
// value string. If a string exceeds this length (even after any
|
||||||
|
// decompression), Write will return ErrStringLength.
|
||||||
|
// A value of 0 means unlimited and is the default from NewDecoder.
|
||||||
|
func (d *Decoder) SetMaxStringLength(n int) {
|
||||||
|
d.maxStrLen = n
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEmitFunc changes the callback used when new header fields
|
||||||
|
// are decoded.
|
||||||
|
// It must be non-nil. It does not affect EmitEnabled.
|
||||||
|
func (d *Decoder) SetEmitFunc(emitFunc func(f HeaderField)) {
|
||||||
|
d.emit = emitFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEmitEnabled controls whether the emitFunc provided to NewDecoder
|
||||||
|
// should be called. The default is true.
|
||||||
|
//
|
||||||
|
// This facility exists to let servers enforce MAX_HEADER_LIST_SIZE
|
||||||
|
// while still decoding and keeping in-sync with decoder state, but
|
||||||
|
// without doing unnecessary decompression or generating unnecessary
|
||||||
|
// garbage for header fields past the limit.
|
||||||
|
func (d *Decoder) SetEmitEnabled(v bool) { d.emitEnabled = v }
|
||||||
|
|
||||||
|
// EmitEnabled reports whether calls to the emitFunc provided to NewDecoder
|
||||||
|
// are currently enabled. The default is true.
|
||||||
|
func (d *Decoder) EmitEnabled() bool { return d.emitEnabled }
|
||||||
|
|
||||||
|
// TODO: add method *Decoder.Reset(maxSize, emitFunc) to let callers re-use Decoders and their
|
||||||
|
// underlying buffers for garbage reasons.
|
||||||
|
|
||||||
|
func (d *Decoder) SetMaxDynamicTableSize(v uint32) {
|
||||||
|
d.dynTab.setMaxSize(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAllowedMaxDynamicTableSize sets the upper bound that the encoded
|
||||||
|
// stream (via dynamic table size updates) may set the maximum size
|
||||||
|
// to.
|
||||||
|
func (d *Decoder) SetAllowedMaxDynamicTableSize(v uint32) {
|
||||||
|
d.dynTab.allowedMaxSize = v
|
||||||
|
}
|
||||||
|
|
||||||
|
type dynamicTable struct {
|
||||||
|
// http://http2.github.io/http2-spec/compression.html#rfc.section.2.3.2
|
||||||
|
table headerFieldTable
|
||||||
|
size uint32 // in bytes
|
||||||
|
maxSize uint32 // current maxSize
|
||||||
|
allowedMaxSize uint32 // maxSize may go up to this, inclusive
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dt *dynamicTable) setMaxSize(v uint32) {
|
||||||
|
dt.maxSize = v
|
||||||
|
dt.evict()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dt *dynamicTable) add(f HeaderField) {
|
||||||
|
dt.table.addEntry(f)
|
||||||
|
dt.size += f.Size()
|
||||||
|
dt.evict()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're too big, evict old stuff.
|
||||||
|
func (dt *dynamicTable) evict() {
|
||||||
|
var n int
|
||||||
|
for dt.size > dt.maxSize && n < dt.table.len() {
|
||||||
|
dt.size -= dt.table.ents[n].Size()
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
dt.table.evictOldest(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) maxTableIndex() int {
|
||||||
|
// 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) {
|
||||||
|
// See Section 2.3.3.
|
||||||
|
if i == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if i <= uint64(staticTable.len()) {
|
||||||
|
return staticTable.ents[i-1], true
|
||||||
|
}
|
||||||
|
if i > uint64(d.maxTableIndex()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// In the dynamic table, newer entries have lower indices.
|
||||||
|
// However, dt.ents[0] is the oldest entry. Hence, dt.ents is
|
||||||
|
// the reversed dynamic table.
|
||||||
|
dt := d.dynTab.table
|
||||||
|
return dt.ents[dt.len()-(int(i)-staticTable.len())], true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode decodes an entire block.
|
||||||
|
//
|
||||||
|
// TODO: remove this method and make it incremental later? This is
|
||||||
|
// easier for debugging now.
|
||||||
|
func (d *Decoder) DecodeFull(p []byte) ([]HeaderField, error) {
|
||||||
|
var hf []HeaderField
|
||||||
|
saveFunc := d.emit
|
||||||
|
defer func() { d.emit = saveFunc }()
|
||||||
|
d.emit = func(f HeaderField) { hf = append(hf, f) }
|
||||||
|
if _, err := d.Write(p); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := d.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return hf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close declares that the decoding is complete and resets the Decoder
|
||||||
|
// to be reused again for a new header block. If there is any remaining
|
||||||
|
// data in the decoder's buffer, Close returns an error.
|
||||||
|
func (d *Decoder) Close() error {
|
||||||
|
if d.saveBuf.Len() > 0 {
|
||||||
|
d.saveBuf.Reset()
|
||||||
|
return DecodingError{errors.New("truncated headers")}
|
||||||
|
}
|
||||||
|
d.firstField = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) Write(p []byte) (n int, err error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
// Prevent state machine CPU attacks (making us redo
|
||||||
|
// work up to the point of finding out we don't have
|
||||||
|
// enough data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Only copy the data if we have to. Optimistically assume
|
||||||
|
// that p will contain a complete header block.
|
||||||
|
if d.saveBuf.Len() == 0 {
|
||||||
|
d.buf = p
|
||||||
|
} else {
|
||||||
|
d.saveBuf.Write(p)
|
||||||
|
d.buf = d.saveBuf.Bytes()
|
||||||
|
d.saveBuf.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
for len(d.buf) > 0 {
|
||||||
|
err = d.parseHeaderFieldRepr()
|
||||||
|
if err == errNeedMore {
|
||||||
|
// Extra paranoia, making sure saveBuf won't
|
||||||
|
// get too large. All the varint and string
|
||||||
|
// reading code earlier should already catch
|
||||||
|
// overlong things and return ErrStringLength,
|
||||||
|
// but keep this as a last resort.
|
||||||
|
const varIntOverhead = 8 // conservative
|
||||||
|
if d.maxStrLen != 0 && int64(len(d.buf)) > 2*(int64(d.maxStrLen)+varIntOverhead) {
|
||||||
|
return 0, ErrStringLength
|
||||||
|
}
|
||||||
|
d.saveBuf.Write(d.buf)
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
d.firstField = false
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(p), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// errNeedMore is an internal sentinel error value that means the
|
||||||
|
// buffer is truncated and we need to read more data before we can
|
||||||
|
// continue parsing.
|
||||||
|
var errNeedMore = errors.New("need more data")
|
||||||
|
|
||||||
|
type indexType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
indexedTrue indexType = iota
|
||||||
|
indexedFalse
|
||||||
|
indexedNever
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v indexType) indexed() bool { return v == indexedTrue }
|
||||||
|
func (v indexType) sensitive() bool { return v == indexedNever }
|
||||||
|
|
||||||
|
// returns errNeedMore if there isn't enough data available.
|
||||||
|
// any other error is fatal.
|
||||||
|
// consumes d.buf iff it returns nil.
|
||||||
|
// precondition: must be called with len(d.buf) > 0
|
||||||
|
func (d *Decoder) parseHeaderFieldRepr() error {
|
||||||
|
b := d.buf[0]
|
||||||
|
switch {
|
||||||
|
case b&128 != 0:
|
||||||
|
// Indexed representation.
|
||||||
|
// High bit set?
|
||||||
|
// http://http2.github.io/http2-spec/compression.html#rfc.section.6.1
|
||||||
|
return d.parseFieldIndexed()
|
||||||
|
case b&192 == 64:
|
||||||
|
// 6.2.1 Literal Header Field with Incremental Indexing
|
||||||
|
// 0b10xxxxxx: top two bits are 10
|
||||||
|
// http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.1
|
||||||
|
return d.parseFieldLiteral(6, indexedTrue)
|
||||||
|
case b&240 == 0:
|
||||||
|
// 6.2.2 Literal Header Field without Indexing
|
||||||
|
// 0b0000xxxx: top four bits are 0000
|
||||||
|
// http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.2
|
||||||
|
return d.parseFieldLiteral(4, indexedFalse)
|
||||||
|
case b&240 == 16:
|
||||||
|
// 6.2.3 Literal Header Field never Indexed
|
||||||
|
// 0b0001xxxx: top four bits are 0001
|
||||||
|
// http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.3
|
||||||
|
return d.parseFieldLiteral(4, indexedNever)
|
||||||
|
case b&224 == 32:
|
||||||
|
// 6.3 Dynamic Table Size Update
|
||||||
|
// Top three bits are '001'.
|
||||||
|
// http://http2.github.io/http2-spec/compression.html#rfc.section.6.3
|
||||||
|
return d.parseDynamicTableSizeUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
return DecodingError{errors.New("invalid encoding")}
|
||||||
|
}
|
||||||
|
|
||||||
|
// (same invariants and behavior as parseHeaderFieldRepr)
|
||||||
|
func (d *Decoder) parseFieldIndexed() error {
|
||||||
|
buf := d.buf
|
||||||
|
idx, buf, err := readVarInt(7, buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hf, ok := d.at(idx)
|
||||||
|
if !ok {
|
||||||
|
return DecodingError{InvalidIndexError(idx)}
|
||||||
|
}
|
||||||
|
d.buf = buf
|
||||||
|
return d.callEmit(HeaderField{Name: hf.Name, Value: hf.Value})
|
||||||
|
}
|
||||||
|
|
||||||
|
// (same invariants and behavior as parseHeaderFieldRepr)
|
||||||
|
func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error {
|
||||||
|
buf := d.buf
|
||||||
|
nameIdx, buf, err := readVarInt(n, buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var hf HeaderField
|
||||||
|
wantStr := d.emitEnabled || it.indexed()
|
||||||
|
if nameIdx > 0 {
|
||||||
|
ihf, ok := d.at(nameIdx)
|
||||||
|
if !ok {
|
||||||
|
return DecodingError{InvalidIndexError(nameIdx)}
|
||||||
|
}
|
||||||
|
hf.Name = ihf.Name
|
||||||
|
} else {
|
||||||
|
hf.Name, buf, err = d.readString(buf, wantStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hf.Value, buf, err = d.readString(buf, wantStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.buf = buf
|
||||||
|
if it.indexed() {
|
||||||
|
d.dynTab.add(hf)
|
||||||
|
}
|
||||||
|
hf.Sensitive = it.sensitive()
|
||||||
|
return d.callEmit(hf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) callEmit(hf HeaderField) error {
|
||||||
|
if d.maxStrLen != 0 {
|
||||||
|
if len(hf.Name) > d.maxStrLen || len(hf.Value) > d.maxStrLen {
|
||||||
|
return ErrStringLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if d.emitEnabled {
|
||||||
|
d.emit(hf)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// (same invariants and behavior as parseHeaderFieldRepr)
|
||||||
|
func (d *Decoder) parseDynamicTableSizeUpdate() error {
|
||||||
|
// RFC 7541, sec 4.2: This dynamic table size update MUST occur at the
|
||||||
|
// beginning of the first header block following the change to the dynamic table size.
|
||||||
|
if !d.firstField && d.dynTab.size > 0 {
|
||||||
|
return DecodingError{errors.New("dynamic table size update MUST occur at the beginning of a header block")}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := d.buf
|
||||||
|
size, buf, err := readVarInt(5, buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if size > uint64(d.dynTab.allowedMaxSize) {
|
||||||
|
return DecodingError{errors.New("dynamic table size update too large")}
|
||||||
|
}
|
||||||
|
d.dynTab.setMaxSize(uint32(size))
|
||||||
|
d.buf = buf
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var errVarintOverflow = DecodingError{errors.New("varint integer overflow")}
|
||||||
|
|
||||||
|
// readVarInt reads an unsigned variable length integer off the
|
||||||
|
// beginning of p. n is the parameter as described in
|
||||||
|
// http://http2.github.io/http2-spec/compression.html#rfc.section.5.1.
|
||||||
|
//
|
||||||
|
// n must always be between 1 and 8.
|
||||||
|
//
|
||||||
|
// The returned remain buffer is either a smaller suffix of p, or err != nil.
|
||||||
|
// The error is errNeedMore if p doesn't contain a complete integer.
|
||||||
|
func readVarInt(n byte, p []byte) (i uint64, remain []byte, err error) {
|
||||||
|
if n < 1 || n > 8 {
|
||||||
|
panic("bad n")
|
||||||
|
}
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, p, errNeedMore
|
||||||
|
}
|
||||||
|
i = uint64(p[0])
|
||||||
|
if n < 8 {
|
||||||
|
i &= (1 << uint64(n)) - 1
|
||||||
|
}
|
||||||
|
if i < (1<<uint64(n))-1 {
|
||||||
|
return i, p[1:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
origP := p
|
||||||
|
p = p[1:]
|
||||||
|
var m uint64
|
||||||
|
for len(p) > 0 {
|
||||||
|
b := p[0]
|
||||||
|
p = p[1:]
|
||||||
|
i += uint64(b&127) << m
|
||||||
|
if b&128 == 0 {
|
||||||
|
return i, p, nil
|
||||||
|
}
|
||||||
|
m += 7
|
||||||
|
if m >= 63 { // TODO: proper overflow check. making this up.
|
||||||
|
return 0, origP, errVarintOverflow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, origP, errNeedMore
|
||||||
|
}
|
||||||
|
|
||||||
|
// readString decodes an hpack string from p.
|
||||||
|
//
|
||||||
|
// wantStr is whether s will be used. If false, decompression and
|
||||||
|
// []byte->string garbage are skipped if s will be ignored
|
||||||
|
// anyway. This does mean that huffman decoding errors for non-indexed
|
||||||
|
// strings past the MAX_HEADER_LIST_SIZE are ignored, but the server
|
||||||
|
// is returning an error anyway, and because they're not indexed, the error
|
||||||
|
// won't affect the decoding state.
|
||||||
|
func (d *Decoder) readString(p []byte, wantStr bool) (s string, remain []byte, err error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return "", p, errNeedMore
|
||||||
|
}
|
||||||
|
isHuff := p[0]&128 != 0
|
||||||
|
strLen, p, err := readVarInt(7, p)
|
||||||
|
if err != nil {
|
||||||
|
return "", p, err
|
||||||
|
}
|
||||||
|
if d.maxStrLen != 0 && strLen > uint64(d.maxStrLen) {
|
||||||
|
return "", nil, ErrStringLength
|
||||||
|
}
|
||||||
|
if uint64(len(p)) < strLen {
|
||||||
|
return "", p, errNeedMore
|
||||||
|
}
|
||||||
|
if !isHuff {
|
||||||
|
if wantStr {
|
||||||
|
s = string(p[:strLen])
|
||||||
|
}
|
||||||
|
return s, p[strLen:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if wantStr {
|
||||||
|
buf := bufPool.Get().(*bytes.Buffer)
|
||||||
|
buf.Reset() // don't trust others
|
||||||
|
defer bufPool.Put(buf)
|
||||||
|
if err := huffmanDecode(buf, d.maxStrLen, p[:strLen]); err != nil {
|
||||||
|
buf.Reset()
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
s = buf.String()
|
||||||
|
buf.Reset() // be nice to GC
|
||||||
|
}
|
||||||
|
return s, p[strLen:], nil
|
||||||
|
}
|
229
vendor/golang.org/x/net/http2/hpack/huffman.go
generated
vendored
Normal file
229
vendor/golang.org/x/net/http2/hpack/huffman.go
generated
vendored
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
// 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 hpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bufPool = sync.Pool{
|
||||||
|
New: func() interface{} { return new(bytes.Buffer) },
|
||||||
|
}
|
||||||
|
|
||||||
|
// HuffmanDecode decodes the string in v and writes the expanded
|
||||||
|
// result to w, returning the number of bytes written to w and the
|
||||||
|
// Write call's return value. At most one Write call is made.
|
||||||
|
func HuffmanDecode(w io.Writer, v []byte) (int, error) {
|
||||||
|
buf := bufPool.Get().(*bytes.Buffer)
|
||||||
|
buf.Reset()
|
||||||
|
defer bufPool.Put(buf)
|
||||||
|
if err := huffmanDecode(buf, 0, v); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return w.Write(buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// HuffmanDecodeToString decodes the string in v.
|
||||||
|
func HuffmanDecodeToString(v []byte) (string, error) {
|
||||||
|
buf := bufPool.Get().(*bytes.Buffer)
|
||||||
|
buf.Reset()
|
||||||
|
defer bufPool.Put(buf)
|
||||||
|
if err := huffmanDecode(buf, 0, v); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrInvalidHuffman is returned for errors found decoding
|
||||||
|
// Huffman-encoded strings.
|
||||||
|
var ErrInvalidHuffman = errors.New("hpack: invalid Huffman-encoded data")
|
||||||
|
|
||||||
|
// huffmanDecode decodes v to buf.
|
||||||
|
// If maxLen is greater than 0, attempts to write more to buf than
|
||||||
|
// maxLen bytes will return ErrStringLength.
|
||||||
|
func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error {
|
||||||
|
rootHuffmanNode := getRootHuffmanNode()
|
||||||
|
n := rootHuffmanNode
|
||||||
|
// cur is the bit buffer that has not been fed into n.
|
||||||
|
// cbits is the number of low order bits in cur that are valid.
|
||||||
|
// sbits is the number of bits of the symbol prefix being decoded.
|
||||||
|
cur, cbits, sbits := uint(0), uint8(0), uint8(0)
|
||||||
|
for _, b := range v {
|
||||||
|
cur = cur<<8 | uint(b)
|
||||||
|
cbits += 8
|
||||||
|
sbits += 8
|
||||||
|
for cbits >= 8 {
|
||||||
|
idx := byte(cur >> (cbits - 8))
|
||||||
|
n = n.children[idx]
|
||||||
|
if n == nil {
|
||||||
|
return ErrInvalidHuffman
|
||||||
|
}
|
||||||
|
if n.children == nil {
|
||||||
|
if maxLen != 0 && buf.Len() == maxLen {
|
||||||
|
return ErrStringLength
|
||||||
|
}
|
||||||
|
buf.WriteByte(n.sym)
|
||||||
|
cbits -= n.codeLen
|
||||||
|
n = rootHuffmanNode
|
||||||
|
sbits = cbits
|
||||||
|
} else {
|
||||||
|
cbits -= 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for cbits > 0 {
|
||||||
|
n = n.children[byte(cur<<(8-cbits))]
|
||||||
|
if n == nil {
|
||||||
|
return ErrInvalidHuffman
|
||||||
|
}
|
||||||
|
if n.children != nil || n.codeLen > cbits {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if maxLen != 0 && buf.Len() == maxLen {
|
||||||
|
return ErrStringLength
|
||||||
|
}
|
||||||
|
buf.WriteByte(n.sym)
|
||||||
|
cbits -= n.codeLen
|
||||||
|
n = rootHuffmanNode
|
||||||
|
sbits = cbits
|
||||||
|
}
|
||||||
|
if sbits > 7 {
|
||||||
|
// Either there was an incomplete symbol, or overlong padding.
|
||||||
|
// Both are decoding errors per RFC 7541 section 5.2.
|
||||||
|
return ErrInvalidHuffman
|
||||||
|
}
|
||||||
|
if mask := uint(1<<cbits - 1); cur&mask != mask {
|
||||||
|
// Trailing bits must be a prefix of EOS per RFC 7541 section 5.2.
|
||||||
|
return ErrInvalidHuffman
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// incomparable is a zero-width, non-comparable type. Adding it to a struct
|
||||||
|
// makes that struct also non-comparable, and generally doesn't add
|
||||||
|
// any size (as long as it's first).
|
||||||
|
type incomparable [0]func()
|
||||||
|
|
||||||
|
type node struct {
|
||||||
|
_ incomparable
|
||||||
|
|
||||||
|
// children is non-nil for internal nodes
|
||||||
|
children *[256]*node
|
||||||
|
|
||||||
|
// The following are only valid if children is nil:
|
||||||
|
codeLen uint8 // number of bits that led to the output of sym
|
||||||
|
sym byte // output symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInternalNode() *node {
|
||||||
|
return &node{children: new([256]*node)}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
buildRootOnce sync.Once
|
||||||
|
lazyRootHuffmanNode *node
|
||||||
|
)
|
||||||
|
|
||||||
|
func getRootHuffmanNode() *node {
|
||||||
|
buildRootOnce.Do(buildRootHuffmanNode)
|
||||||
|
return lazyRootHuffmanNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildRootHuffmanNode() {
|
||||||
|
if len(huffmanCodes) != 256 {
|
||||||
|
panic("unexpected size")
|
||||||
|
}
|
||||||
|
lazyRootHuffmanNode = newInternalNode()
|
||||||
|
for i, code := range huffmanCodes {
|
||||||
|
addDecoderNode(byte(i), code, huffmanCodeLen[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addDecoderNode(sym byte, code uint32, codeLen uint8) {
|
||||||
|
cur := lazyRootHuffmanNode
|
||||||
|
for codeLen > 8 {
|
||||||
|
codeLen -= 8
|
||||||
|
i := uint8(code >> codeLen)
|
||||||
|
if cur.children[i] == nil {
|
||||||
|
cur.children[i] = newInternalNode()
|
||||||
|
}
|
||||||
|
cur = cur.children[i]
|
||||||
|
}
|
||||||
|
shift := 8 - codeLen
|
||||||
|
start, end := int(uint8(code<<shift)), int(1<<shift)
|
||||||
|
for i := start; i < start+end; i++ {
|
||||||
|
cur.children[i] = &node{sym: sym, codeLen: codeLen}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendHuffmanString appends s, as encoded in Huffman codes, to dst
|
||||||
|
// and returns the extended buffer.
|
||||||
|
func AppendHuffmanString(dst []byte, s string) []byte {
|
||||||
|
rembits := uint8(8)
|
||||||
|
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if rembits == 8 {
|
||||||
|
dst = append(dst, 0)
|
||||||
|
}
|
||||||
|
dst, rembits = appendByteToHuffmanCode(dst, rembits, s[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
if rembits < 8 {
|
||||||
|
// special EOS symbol
|
||||||
|
code := uint32(0x3fffffff)
|
||||||
|
nbits := uint8(30)
|
||||||
|
|
||||||
|
t := uint8(code >> (nbits - rembits))
|
||||||
|
dst[len(dst)-1] |= t
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// HuffmanEncodeLength returns the number of bytes required to encode
|
||||||
|
// s in Huffman codes. The result is round up to byte boundary.
|
||||||
|
func HuffmanEncodeLength(s string) uint64 {
|
||||||
|
n := uint64(0)
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
n += uint64(huffmanCodeLen[s[i]])
|
||||||
|
}
|
||||||
|
return (n + 7) / 8
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendByteToHuffmanCode appends Huffman code for c to dst and
|
||||||
|
// returns the extended buffer and the remaining bits in the last
|
||||||
|
// element. The appending is not byte aligned and the remaining bits
|
||||||
|
// in the last element of dst is given in rembits.
|
||||||
|
func appendByteToHuffmanCode(dst []byte, rembits uint8, c byte) ([]byte, uint8) {
|
||||||
|
code := huffmanCodes[c]
|
||||||
|
nbits := huffmanCodeLen[c]
|
||||||
|
|
||||||
|
for {
|
||||||
|
if rembits > nbits {
|
||||||
|
t := uint8(code << (rembits - nbits))
|
||||||
|
dst[len(dst)-1] |= t
|
||||||
|
rembits -= nbits
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
t := uint8(code >> (nbits - rembits))
|
||||||
|
dst[len(dst)-1] |= t
|
||||||
|
|
||||||
|
nbits -= rembits
|
||||||
|
rembits = 8
|
||||||
|
|
||||||
|
if nbits == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
dst = append(dst, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst, rembits
|
||||||
|
}
|
479
vendor/golang.org/x/net/http2/hpack/tables.go
generated
vendored
Normal file
479
vendor/golang.org/x/net/http2/hpack/tables.go
generated
vendored
Normal file
@ -0,0 +1,479 @@
|
|||||||
|
// 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 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B
|
||||||
|
var staticTable = newStaticTable()
|
||||||
|
var staticTableEntries = [...]HeaderField{
|
||||||
|
{Name: ":authority"},
|
||||||
|
{Name: ":method", Value: "GET"},
|
||||||
|
{Name: ":method", Value: "POST"},
|
||||||
|
{Name: ":path", Value: "/"},
|
||||||
|
{Name: ":path", Value: "/index.html"},
|
||||||
|
{Name: ":scheme", Value: "http"},
|
||||||
|
{Name: ":scheme", Value: "https"},
|
||||||
|
{Name: ":status", Value: "200"},
|
||||||
|
{Name: ":status", Value: "204"},
|
||||||
|
{Name: ":status", Value: "206"},
|
||||||
|
{Name: ":status", Value: "304"},
|
||||||
|
{Name: ":status", Value: "400"},
|
||||||
|
{Name: ":status", Value: "404"},
|
||||||
|
{Name: ":status", Value: "500"},
|
||||||
|
{Name: "accept-charset"},
|
||||||
|
{Name: "accept-encoding", Value: "gzip, deflate"},
|
||||||
|
{Name: "accept-language"},
|
||||||
|
{Name: "accept-ranges"},
|
||||||
|
{Name: "accept"},
|
||||||
|
{Name: "access-control-allow-origin"},
|
||||||
|
{Name: "age"},
|
||||||
|
{Name: "allow"},
|
||||||
|
{Name: "authorization"},
|
||||||
|
{Name: "cache-control"},
|
||||||
|
{Name: "content-disposition"},
|
||||||
|
{Name: "content-encoding"},
|
||||||
|
{Name: "content-language"},
|
||||||
|
{Name: "content-length"},
|
||||||
|
{Name: "content-location"},
|
||||||
|
{Name: "content-range"},
|
||||||
|
{Name: "content-type"},
|
||||||
|
{Name: "cookie"},
|
||||||
|
{Name: "date"},
|
||||||
|
{Name: "etag"},
|
||||||
|
{Name: "expect"},
|
||||||
|
{Name: "expires"},
|
||||||
|
{Name: "from"},
|
||||||
|
{Name: "host"},
|
||||||
|
{Name: "if-match"},
|
||||||
|
{Name: "if-modified-since"},
|
||||||
|
{Name: "if-none-match"},
|
||||||
|
{Name: "if-range"},
|
||||||
|
{Name: "if-unmodified-since"},
|
||||||
|
{Name: "last-modified"},
|
||||||
|
{Name: "link"},
|
||||||
|
{Name: "location"},
|
||||||
|
{Name: "max-forwards"},
|
||||||
|
{Name: "proxy-authenticate"},
|
||||||
|
{Name: "proxy-authorization"},
|
||||||
|
{Name: "range"},
|
||||||
|
{Name: "referer"},
|
||||||
|
{Name: "refresh"},
|
||||||
|
{Name: "retry-after"},
|
||||||
|
{Name: "server"},
|
||||||
|
{Name: "set-cookie"},
|
||||||
|
{Name: "strict-transport-security"},
|
||||||
|
{Name: "transfer-encoding"},
|
||||||
|
{Name: "user-agent"},
|
||||||
|
{Name: "vary"},
|
||||||
|
{Name: "via"},
|
||||||
|
{Name: "www-authenticate"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStaticTable() *headerFieldTable {
|
||||||
|
t := &headerFieldTable{}
|
||||||
|
t.init()
|
||||||
|
for _, e := range staticTableEntries[:] {
|
||||||
|
t.addEntry(e)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
var huffmanCodes = [256]uint32{
|
||||||
|
0x1ff8,
|
||||||
|
0x7fffd8,
|
||||||
|
0xfffffe2,
|
||||||
|
0xfffffe3,
|
||||||
|
0xfffffe4,
|
||||||
|
0xfffffe5,
|
||||||
|
0xfffffe6,
|
||||||
|
0xfffffe7,
|
||||||
|
0xfffffe8,
|
||||||
|
0xffffea,
|
||||||
|
0x3ffffffc,
|
||||||
|
0xfffffe9,
|
||||||
|
0xfffffea,
|
||||||
|
0x3ffffffd,
|
||||||
|
0xfffffeb,
|
||||||
|
0xfffffec,
|
||||||
|
0xfffffed,
|
||||||
|
0xfffffee,
|
||||||
|
0xfffffef,
|
||||||
|
0xffffff0,
|
||||||
|
0xffffff1,
|
||||||
|
0xffffff2,
|
||||||
|
0x3ffffffe,
|
||||||
|
0xffffff3,
|
||||||
|
0xffffff4,
|
||||||
|
0xffffff5,
|
||||||
|
0xffffff6,
|
||||||
|
0xffffff7,
|
||||||
|
0xffffff8,
|
||||||
|
0xffffff9,
|
||||||
|
0xffffffa,
|
||||||
|
0xffffffb,
|
||||||
|
0x14,
|
||||||
|
0x3f8,
|
||||||
|
0x3f9,
|
||||||
|
0xffa,
|
||||||
|
0x1ff9,
|
||||||
|
0x15,
|
||||||
|
0xf8,
|
||||||
|
0x7fa,
|
||||||
|
0x3fa,
|
||||||
|
0x3fb,
|
||||||
|
0xf9,
|
||||||
|
0x7fb,
|
||||||
|
0xfa,
|
||||||
|
0x16,
|
||||||
|
0x17,
|
||||||
|
0x18,
|
||||||
|
0x0,
|
||||||
|
0x1,
|
||||||
|
0x2,
|
||||||
|
0x19,
|
||||||
|
0x1a,
|
||||||
|
0x1b,
|
||||||
|
0x1c,
|
||||||
|
0x1d,
|
||||||
|
0x1e,
|
||||||
|
0x1f,
|
||||||
|
0x5c,
|
||||||
|
0xfb,
|
||||||
|
0x7ffc,
|
||||||
|
0x20,
|
||||||
|
0xffb,
|
||||||
|
0x3fc,
|
||||||
|
0x1ffa,
|
||||||
|
0x21,
|
||||||
|
0x5d,
|
||||||
|
0x5e,
|
||||||
|
0x5f,
|
||||||
|
0x60,
|
||||||
|
0x61,
|
||||||
|
0x62,
|
||||||
|
0x63,
|
||||||
|
0x64,
|
||||||
|
0x65,
|
||||||
|
0x66,
|
||||||
|
0x67,
|
||||||
|
0x68,
|
||||||
|
0x69,
|
||||||
|
0x6a,
|
||||||
|
0x6b,
|
||||||
|
0x6c,
|
||||||
|
0x6d,
|
||||||
|
0x6e,
|
||||||
|
0x6f,
|
||||||
|
0x70,
|
||||||
|
0x71,
|
||||||
|
0x72,
|
||||||
|
0xfc,
|
||||||
|
0x73,
|
||||||
|
0xfd,
|
||||||
|
0x1ffb,
|
||||||
|
0x7fff0,
|
||||||
|
0x1ffc,
|
||||||
|
0x3ffc,
|
||||||
|
0x22,
|
||||||
|
0x7ffd,
|
||||||
|
0x3,
|
||||||
|
0x23,
|
||||||
|
0x4,
|
||||||
|
0x24,
|
||||||
|
0x5,
|
||||||
|
0x25,
|
||||||
|
0x26,
|
||||||
|
0x27,
|
||||||
|
0x6,
|
||||||
|
0x74,
|
||||||
|
0x75,
|
||||||
|
0x28,
|
||||||
|
0x29,
|
||||||
|
0x2a,
|
||||||
|
0x7,
|
||||||
|
0x2b,
|
||||||
|
0x76,
|
||||||
|
0x2c,
|
||||||
|
0x8,
|
||||||
|
0x9,
|
||||||
|
0x2d,
|
||||||
|
0x77,
|
||||||
|
0x78,
|
||||||
|
0x79,
|
||||||
|
0x7a,
|
||||||
|
0x7b,
|
||||||
|
0x7ffe,
|
||||||
|
0x7fc,
|
||||||
|
0x3ffd,
|
||||||
|
0x1ffd,
|
||||||
|
0xffffffc,
|
||||||
|
0xfffe6,
|
||||||
|
0x3fffd2,
|
||||||
|
0xfffe7,
|
||||||
|
0xfffe8,
|
||||||
|
0x3fffd3,
|
||||||
|
0x3fffd4,
|
||||||
|
0x3fffd5,
|
||||||
|
0x7fffd9,
|
||||||
|
0x3fffd6,
|
||||||
|
0x7fffda,
|
||||||
|
0x7fffdb,
|
||||||
|
0x7fffdc,
|
||||||
|
0x7fffdd,
|
||||||
|
0x7fffde,
|
||||||
|
0xffffeb,
|
||||||
|
0x7fffdf,
|
||||||
|
0xffffec,
|
||||||
|
0xffffed,
|
||||||
|
0x3fffd7,
|
||||||
|
0x7fffe0,
|
||||||
|
0xffffee,
|
||||||
|
0x7fffe1,
|
||||||
|
0x7fffe2,
|
||||||
|
0x7fffe3,
|
||||||
|
0x7fffe4,
|
||||||
|
0x1fffdc,
|
||||||
|
0x3fffd8,
|
||||||
|
0x7fffe5,
|
||||||
|
0x3fffd9,
|
||||||
|
0x7fffe6,
|
||||||
|
0x7fffe7,
|
||||||
|
0xffffef,
|
||||||
|
0x3fffda,
|
||||||
|
0x1fffdd,
|
||||||
|
0xfffe9,
|
||||||
|
0x3fffdb,
|
||||||
|
0x3fffdc,
|
||||||
|
0x7fffe8,
|
||||||
|
0x7fffe9,
|
||||||
|
0x1fffde,
|
||||||
|
0x7fffea,
|
||||||
|
0x3fffdd,
|
||||||
|
0x3fffde,
|
||||||
|
0xfffff0,
|
||||||
|
0x1fffdf,
|
||||||
|
0x3fffdf,
|
||||||
|
0x7fffeb,
|
||||||
|
0x7fffec,
|
||||||
|
0x1fffe0,
|
||||||
|
0x1fffe1,
|
||||||
|
0x3fffe0,
|
||||||
|
0x1fffe2,
|
||||||
|
0x7fffed,
|
||||||
|
0x3fffe1,
|
||||||
|
0x7fffee,
|
||||||
|
0x7fffef,
|
||||||
|
0xfffea,
|
||||||
|
0x3fffe2,
|
||||||
|
0x3fffe3,
|
||||||
|
0x3fffe4,
|
||||||
|
0x7ffff0,
|
||||||
|
0x3fffe5,
|
||||||
|
0x3fffe6,
|
||||||
|
0x7ffff1,
|
||||||
|
0x3ffffe0,
|
||||||
|
0x3ffffe1,
|
||||||
|
0xfffeb,
|
||||||
|
0x7fff1,
|
||||||
|
0x3fffe7,
|
||||||
|
0x7ffff2,
|
||||||
|
0x3fffe8,
|
||||||
|
0x1ffffec,
|
||||||
|
0x3ffffe2,
|
||||||
|
0x3ffffe3,
|
||||||
|
0x3ffffe4,
|
||||||
|
0x7ffffde,
|
||||||
|
0x7ffffdf,
|
||||||
|
0x3ffffe5,
|
||||||
|
0xfffff1,
|
||||||
|
0x1ffffed,
|
||||||
|
0x7fff2,
|
||||||
|
0x1fffe3,
|
||||||
|
0x3ffffe6,
|
||||||
|
0x7ffffe0,
|
||||||
|
0x7ffffe1,
|
||||||
|
0x3ffffe7,
|
||||||
|
0x7ffffe2,
|
||||||
|
0xfffff2,
|
||||||
|
0x1fffe4,
|
||||||
|
0x1fffe5,
|
||||||
|
0x3ffffe8,
|
||||||
|
0x3ffffe9,
|
||||||
|
0xffffffd,
|
||||||
|
0x7ffffe3,
|
||||||
|
0x7ffffe4,
|
||||||
|
0x7ffffe5,
|
||||||
|
0xfffec,
|
||||||
|
0xfffff3,
|
||||||
|
0xfffed,
|
||||||
|
0x1fffe6,
|
||||||
|
0x3fffe9,
|
||||||
|
0x1fffe7,
|
||||||
|
0x1fffe8,
|
||||||
|
0x7ffff3,
|
||||||
|
0x3fffea,
|
||||||
|
0x3fffeb,
|
||||||
|
0x1ffffee,
|
||||||
|
0x1ffffef,
|
||||||
|
0xfffff4,
|
||||||
|
0xfffff5,
|
||||||
|
0x3ffffea,
|
||||||
|
0x7ffff4,
|
||||||
|
0x3ffffeb,
|
||||||
|
0x7ffffe6,
|
||||||
|
0x3ffffec,
|
||||||
|
0x3ffffed,
|
||||||
|
0x7ffffe7,
|
||||||
|
0x7ffffe8,
|
||||||
|
0x7ffffe9,
|
||||||
|
0x7ffffea,
|
||||||
|
0x7ffffeb,
|
||||||
|
0xffffffe,
|
||||||
|
0x7ffffec,
|
||||||
|
0x7ffffed,
|
||||||
|
0x7ffffee,
|
||||||
|
0x7ffffef,
|
||||||
|
0x7fffff0,
|
||||||
|
0x3ffffee,
|
||||||
|
}
|
||||||
|
|
||||||
|
var huffmanCodeLen = [256]uint8{
|
||||||
|
13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28,
|
||||||
|
28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||||
|
6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6,
|
||||||
|
5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 8, 15, 6, 12, 10,
|
||||||
|
13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||||
|
7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6,
|
||||||
|
15, 5, 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5,
|
||||||
|
6, 7, 6, 5, 5, 6, 7, 7, 7, 7, 7, 15, 11, 14, 13, 28,
|
||||||
|
20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23,
|
||||||
|
24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24,
|
||||||
|
22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, 21, 22, 23, 23,
|
||||||
|
21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23,
|
||||||
|
26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25,
|
||||||
|
19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, 27,
|
||||||
|
20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23,
|
||||||
|
26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26,
|
||||||
|
}
|
385
vendor/golang.org/x/net/http2/http2.go
generated
vendored
Normal file
385
vendor/golang.org/x/net/http2/http2.go
generated
vendored
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
// 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 implements the HTTP/2 protocol.
|
||||||
|
//
|
||||||
|
// This package is low-level and intended to be used directly by very
|
||||||
|
// few people. Most users will use it indirectly through the automatic
|
||||||
|
// use by the net/http package (from Go 1.6 and later).
|
||||||
|
// For use in earlier Go versions see ConfigureServer. (Transport support
|
||||||
|
// requires Go 1.6 or later)
|
||||||
|
//
|
||||||
|
// See https://http2.github.io/ for more information on HTTP/2.
|
||||||
|
//
|
||||||
|
// See https://http2.golang.org/ for a test server running this code.
|
||||||
|
//
|
||||||
|
package http2 // import "golang.org/x/net/http2"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/net/http/httpguts"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
VerboseLogs bool
|
||||||
|
logFrameWrites bool
|
||||||
|
logFrameReads bool
|
||||||
|
inTests bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
e := os.Getenv("GODEBUG")
|
||||||
|
if strings.Contains(e, "http2debug=1") {
|
||||||
|
VerboseLogs = true
|
||||||
|
}
|
||||||
|
if strings.Contains(e, "http2debug=2") {
|
||||||
|
VerboseLogs = true
|
||||||
|
logFrameWrites = true
|
||||||
|
logFrameReads = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ClientPreface is the string that must be sent by new
|
||||||
|
// connections from clients.
|
||||||
|
ClientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
|
||||||
|
|
||||||
|
// SETTINGS_MAX_FRAME_SIZE default
|
||||||
|
// http://http2.github.io/http2-spec/#rfc.section.6.5.2
|
||||||
|
initialMaxFrameSize = 16384
|
||||||
|
|
||||||
|
// NextProtoTLS is the NPN/ALPN protocol negotiated during
|
||||||
|
// HTTP/2's TLS setup.
|
||||||
|
NextProtoTLS = "h2"
|
||||||
|
|
||||||
|
// http://http2.github.io/http2-spec/#SettingValues
|
||||||
|
initialHeaderTableSize = 4096
|
||||||
|
|
||||||
|
initialWindowSize = 65535 // 6.9.2 Initial Flow Control Window Size
|
||||||
|
|
||||||
|
defaultMaxReadFrameSize = 1 << 20
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
clientPreface = []byte(ClientPreface)
|
||||||
|
)
|
||||||
|
|
||||||
|
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 (
|
||||||
|
stateIdle streamState = iota
|
||||||
|
stateOpen
|
||||||
|
stateHalfClosedLocal
|
||||||
|
stateHalfClosedRemote
|
||||||
|
stateClosed
|
||||||
|
)
|
||||||
|
|
||||||
|
var stateName = [...]string{
|
||||||
|
stateIdle: "Idle",
|
||||||
|
stateOpen: "Open",
|
||||||
|
stateHalfClosedLocal: "HalfClosedLocal",
|
||||||
|
stateHalfClosedRemote: "HalfClosedRemote",
|
||||||
|
stateClosed: "Closed",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st streamState) String() string {
|
||||||
|
return stateName[st]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setting is a setting parameter: which setting it is, and its value.
|
||||||
|
type Setting struct {
|
||||||
|
// ID is which setting is being set.
|
||||||
|
// See http://http2.github.io/http2-spec/#SettingValues
|
||||||
|
ID SettingID
|
||||||
|
|
||||||
|
// Val is the value.
|
||||||
|
Val uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Setting) String() string {
|
||||||
|
return fmt.Sprintf("[%v = %d]", s.ID, s.Val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid reports whether the setting is valid.
|
||||||
|
func (s Setting) Valid() error {
|
||||||
|
// Limits and error codes from 6.5.2 Defined SETTINGS Parameters
|
||||||
|
switch s.ID {
|
||||||
|
case SettingEnablePush:
|
||||||
|
if s.Val != 1 && s.Val != 0 {
|
||||||
|
return ConnectionError(ErrCodeProtocol)
|
||||||
|
}
|
||||||
|
case SettingInitialWindowSize:
|
||||||
|
if s.Val > 1<<31-1 {
|
||||||
|
return ConnectionError(ErrCodeFlowControl)
|
||||||
|
}
|
||||||
|
case SettingMaxFrameSize:
|
||||||
|
if s.Val < 16384 || s.Val > 1<<24-1 {
|
||||||
|
return ConnectionError(ErrCodeProtocol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A SettingID is an HTTP/2 setting as defined in
|
||||||
|
// http://http2.github.io/http2-spec/#iana-settings
|
||||||
|
type SettingID uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
SettingHeaderTableSize SettingID = 0x1
|
||||||
|
SettingEnablePush SettingID = 0x2
|
||||||
|
SettingMaxConcurrentStreams SettingID = 0x3
|
||||||
|
SettingInitialWindowSize SettingID = 0x4
|
||||||
|
SettingMaxFrameSize SettingID = 0x5
|
||||||
|
SettingMaxHeaderListSize SettingID = 0x6
|
||||||
|
)
|
||||||
|
|
||||||
|
var settingName = map[SettingID]string{
|
||||||
|
SettingHeaderTableSize: "HEADER_TABLE_SIZE",
|
||||||
|
SettingEnablePush: "ENABLE_PUSH",
|
||||||
|
SettingMaxConcurrentStreams: "MAX_CONCURRENT_STREAMS",
|
||||||
|
SettingInitialWindowSize: "INITIAL_WINDOW_SIZE",
|
||||||
|
SettingMaxFrameSize: "MAX_FRAME_SIZE",
|
||||||
|
SettingMaxHeaderListSize: "MAX_HEADER_LIST_SIZE",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SettingID) String() string {
|
||||||
|
if v, ok := settingName[s]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("UNKNOWN_SETTING_%d", uint16(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// validWireHeaderFieldName reports whether v is a valid header field
|
||||||
|
// name (key). See httpguts.ValidHeaderName for the base rules.
|
||||||
|
//
|
||||||
|
// Further, http2 says:
|
||||||
|
// "Just as in HTTP/1.x, header field names are strings of ASCII
|
||||||
|
// characters that are compared in a case-insensitive
|
||||||
|
// fashion. However, header field names MUST be converted to
|
||||||
|
// lowercase prior to their encoding in HTTP/2. "
|
||||||
|
func validWireHeaderFieldName(v string) bool {
|
||||||
|
if len(v) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, r := range v {
|
||||||
|
if !httpguts.IsTokenRune(r) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if 'A' <= r && r <= 'Z' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func httpCodeString(code int) string {
|
||||||
|
switch code {
|
||||||
|
case 200:
|
||||||
|
return "200"
|
||||||
|
case 404:
|
||||||
|
return "404"
|
||||||
|
}
|
||||||
|
return strconv.Itoa(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// from pkg io
|
||||||
|
type stringWriter interface {
|
||||||
|
WriteString(s string) (n int, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A gate lets two goroutines coordinate their activities.
|
||||||
|
type gate chan struct{}
|
||||||
|
|
||||||
|
func (g gate) Done() { g <- struct{}{} }
|
||||||
|
func (g gate) Wait() { <-g }
|
||||||
|
|
||||||
|
// A closeWaiter is like a sync.WaitGroup but only goes 1 to 0 (open to closed).
|
||||||
|
type closeWaiter chan struct{}
|
||||||
|
|
||||||
|
// Init makes a closeWaiter usable.
|
||||||
|
// It exists because so a closeWaiter value can be placed inside a
|
||||||
|
// larger struct and have the Mutex and Cond's memory in the same
|
||||||
|
// allocation.
|
||||||
|
func (cw *closeWaiter) Init() {
|
||||||
|
*cw = make(chan struct{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close marks the closeWaiter as closed and unblocks any waiters.
|
||||||
|
func (cw closeWaiter) Close() {
|
||||||
|
close(cw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait waits for the closeWaiter to become closed.
|
||||||
|
func (cw closeWaiter) Wait() {
|
||||||
|
<-cw
|
||||||
|
}
|
||||||
|
|
||||||
|
// bufferedWriter is a buffered writer that writes to w.
|
||||||
|
// Its buffered writer is lazily allocated as needed, to minimize
|
||||||
|
// idle memory usage with many connections.
|
||||||
|
type bufferedWriter struct {
|
||||||
|
_ incomparable
|
||||||
|
w io.Writer // immutable
|
||||||
|
bw *bufio.Writer // non-nil when data is buffered
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBufferedWriter(w io.Writer) *bufferedWriter {
|
||||||
|
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{
|
||||||
|
New: func() interface{} {
|
||||||
|
return bufio.NewWriterSize(nil, bufWriterPoolBufferSize)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if w.bw == nil {
|
||||||
|
bw := bufWriterPool.Get().(*bufio.Writer)
|
||||||
|
bw.Reset(w.w)
|
||||||
|
w.bw = bw
|
||||||
|
}
|
||||||
|
return w.bw.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *bufferedWriter) Flush() error {
|
||||||
|
bw := w.bw
|
||||||
|
if bw == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := bw.Flush()
|
||||||
|
bw.Reset(nil)
|
||||||
|
bufWriterPool.Put(bw)
|
||||||
|
w.bw = nil
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustUint31(v int32) uint32 {
|
||||||
|
if v < 0 || v > 2147483647 {
|
||||||
|
panic("out of range")
|
||||||
|
}
|
||||||
|
return uint32(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bodyAllowedForStatus reports whether a given response status code
|
||||||
|
// permits a body. See RFC 7230, section 3.3.
|
||||||
|
func bodyAllowedForStatus(status int) bool {
|
||||||
|
switch {
|
||||||
|
case status >= 100 && status <= 199:
|
||||||
|
return false
|
||||||
|
case status == 204:
|
||||||
|
return false
|
||||||
|
case status == 304:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpError struct {
|
||||||
|
_ incomparable
|
||||||
|
msg string
|
||||||
|
timeout bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *httpError) Error() string { return e.msg }
|
||||||
|
func (e *httpError) Timeout() bool { return e.timeout }
|
||||||
|
func (e *httpError) Temporary() bool { return true }
|
||||||
|
|
||||||
|
var errTimeout error = &httpError{msg: "http2: timeout awaiting response headers", timeout: true}
|
||||||
|
|
||||||
|
type connectionStater interface {
|
||||||
|
ConnectionState() tls.ConnectionState
|
||||||
|
}
|
||||||
|
|
||||||
|
var sorterPool = sync.Pool{New: func() interface{} { return new(sorter) }}
|
||||||
|
|
||||||
|
type sorter struct {
|
||||||
|
v []string // owned by sorter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sorter) Len() int { return len(s.v) }
|
||||||
|
func (s *sorter) Swap(i, j int) { s.v[i], s.v[j] = s.v[j], s.v[i] }
|
||||||
|
func (s *sorter) Less(i, j int) bool { return s.v[i] < s.v[j] }
|
||||||
|
|
||||||
|
// Keys returns the sorted keys of h.
|
||||||
|
//
|
||||||
|
// The returned slice is only valid until s used again or returned to
|
||||||
|
// its pool.
|
||||||
|
func (s *sorter) Keys(h http.Header) []string {
|
||||||
|
keys := s.v[:0]
|
||||||
|
for k := range h {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
s.v = keys
|
||||||
|
sort.Sort(s)
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sorter) SortStrings(ss []string) {
|
||||||
|
// Our sorter works on s.v, which sorter owns, so
|
||||||
|
// stash it away while we sort the user's buffer.
|
||||||
|
save := s.v
|
||||||
|
s.v = ss
|
||||||
|
sort.Sort(s)
|
||||||
|
s.v = save
|
||||||
|
}
|
||||||
|
|
||||||
|
// validPseudoPath reports whether v is a valid :path pseudo-header
|
||||||
|
// value. It must be either:
|
||||||
|
//
|
||||||
|
// *) a non-empty string starting 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
|
||||||
|
//
|
||||||
|
// We used to enforce that the path also didn't start with "//", but
|
||||||
|
// Google's GFE accepts such paths and Chrome sends them, so ignore
|
||||||
|
// that part of the spec. See golang.org/issue/19103.
|
||||||
|
func validPseudoPath(v string) bool {
|
||||||
|
return (len(v) > 0 && v[0] == '/') || v == "*"
|
||||||
|
}
|
||||||
|
|
||||||
|
// incomparable is a zero-width, non-comparable type. Adding it to a struct
|
||||||
|
// makes that struct also non-comparable, and generally doesn't add
|
||||||
|
// any size (as long as it's first).
|
||||||
|
type incomparable [0]func()
|
21
vendor/golang.org/x/net/http2/not_go111.go
generated
vendored
Normal file
21
vendor/golang.org/x/net/http2/not_go111.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2018 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.
|
||||||
|
|
||||||
|
//go:build !go1.11
|
||||||
|
// +build !go1.11
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http/httptrace"
|
||||||
|
"net/textproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { return false }
|
||||||
|
|
||||||
|
func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {}
|
||||||
|
|
||||||
|
func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error {
|
||||||
|
return nil
|
||||||
|
}
|
168
vendor/golang.org/x/net/http2/pipe.go
generated
vendored
Normal file
168
vendor/golang.org/x/net/http2/pipe.go
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// 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"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// pipe is a goroutine-safe io.Reader/io.Writer pair. It's like
|
||||||
|
// io.Pipe except there are no PipeReader/PipeWriter halves, and the
|
||||||
|
// underlying buffer is an interface. (io.Pipe is always unbuffered)
|
||||||
|
type pipe struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
c sync.Cond // c.L lazily initialized to &p.mu
|
||||||
|
b pipeBuffer // nil when done reading
|
||||||
|
unread int // bytes unread when done
|
||||||
|
err error // read error once empty. non-nil means closed.
|
||||||
|
breakErr error // immediate read error (caller doesn't see rest of b)
|
||||||
|
donec chan struct{} // closed on error
|
||||||
|
readFn func() // optional code to run in Read before error
|
||||||
|
}
|
||||||
|
|
||||||
|
type pipeBuffer interface {
|
||||||
|
Len() int
|
||||||
|
io.Writer
|
||||||
|
io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pipe) Len() int {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.b == nil {
|
||||||
|
return p.unread
|
||||||
|
}
|
||||||
|
return p.b.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read waits until data is available and copies bytes
|
||||||
|
// from the buffer into p.
|
||||||
|
func (p *pipe) Read(d []byte) (n int, err error) {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.c.L == nil {
|
||||||
|
p.c.L = &p.mu
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
if p.breakErr != nil {
|
||||||
|
return 0, p.breakErr
|
||||||
|
}
|
||||||
|
if p.b != nil && p.b.Len() > 0 {
|
||||||
|
return p.b.Read(d)
|
||||||
|
}
|
||||||
|
if p.err != nil {
|
||||||
|
if p.readFn != nil {
|
||||||
|
p.readFn() // e.g. copy trailers
|
||||||
|
p.readFn = nil // not sticky like p.err
|
||||||
|
}
|
||||||
|
p.b = nil
|
||||||
|
return 0, p.err
|
||||||
|
}
|
||||||
|
p.c.Wait()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var errClosedPipeWrite = errors.New("write on closed buffer")
|
||||||
|
|
||||||
|
// Write copies bytes from p into the buffer and wakes a reader.
|
||||||
|
// It is an error to write more data than the buffer can hold.
|
||||||
|
func (p *pipe) Write(d []byte) (n int, err error) {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.c.L == nil {
|
||||||
|
p.c.L = &p.mu
|
||||||
|
}
|
||||||
|
defer p.c.Signal()
|
||||||
|
if p.err != nil {
|
||||||
|
return 0, errClosedPipeWrite
|
||||||
|
}
|
||||||
|
if p.breakErr != nil {
|
||||||
|
p.unread += len(d)
|
||||||
|
return len(d), nil // discard when there is no reader
|
||||||
|
}
|
||||||
|
return p.b.Write(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseWithError causes the next Read (waking up a current blocked
|
||||||
|
// Read if needed) to return the provided err after all data has been
|
||||||
|
// read.
|
||||||
|
//
|
||||||
|
// The error must be non-nil.
|
||||||
|
func (p *pipe) CloseWithError(err error) { p.closeWithError(&p.err, err, nil) }
|
||||||
|
|
||||||
|
// BreakWithError causes the next Read (waking up a current blocked
|
||||||
|
// Read if needed) to return the provided err immediately, without
|
||||||
|
// waiting for unread data.
|
||||||
|
func (p *pipe) BreakWithError(err error) { p.closeWithError(&p.breakErr, err, nil) }
|
||||||
|
|
||||||
|
// closeWithErrorAndCode is like CloseWithError but also sets some code to run
|
||||||
|
// in the caller's goroutine before returning the error.
|
||||||
|
func (p *pipe) closeWithErrorAndCode(err error, fn func()) { p.closeWithError(&p.err, err, fn) }
|
||||||
|
|
||||||
|
func (p *pipe) closeWithError(dst *error, err error, fn func()) {
|
||||||
|
if err == nil {
|
||||||
|
panic("err must be non-nil")
|
||||||
|
}
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.c.L == nil {
|
||||||
|
p.c.L = &p.mu
|
||||||
|
}
|
||||||
|
defer p.c.Signal()
|
||||||
|
if *dst != nil {
|
||||||
|
// Already been done.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.readFn = fn
|
||||||
|
if dst == &p.breakErr {
|
||||||
|
if p.b != nil {
|
||||||
|
p.unread += p.b.Len()
|
||||||
|
}
|
||||||
|
p.b = nil
|
||||||
|
}
|
||||||
|
*dst = err
|
||||||
|
p.closeDoneLocked()
|
||||||
|
}
|
||||||
|
|
||||||
|
// requires p.mu be held.
|
||||||
|
func (p *pipe) closeDoneLocked() {
|
||||||
|
if p.donec == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Close if unclosed. This isn't racy since we always
|
||||||
|
// hold p.mu while closing.
|
||||||
|
select {
|
||||||
|
case <-p.donec:
|
||||||
|
default:
|
||||||
|
close(p.donec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns the error (if any) first set by BreakWithError or CloseWithError.
|
||||||
|
func (p *pipe) Err() error {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.breakErr != nil {
|
||||||
|
return p.breakErr
|
||||||
|
}
|
||||||
|
return p.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done returns a channel which is closed if and when this pipe is closed
|
||||||
|
// with CloseWithError.
|
||||||
|
func (p *pipe) Done() <-chan struct{} {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.donec == nil {
|
||||||
|
p.donec = make(chan struct{})
|
||||||
|
if p.err != nil || p.breakErr != nil {
|
||||||
|
// Already hit an error.
|
||||||
|
p.closeDoneLocked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.donec
|
||||||
|
}
|
2984
vendor/golang.org/x/net/http2/server.go
generated
vendored
Normal file
2984
vendor/golang.org/x/net/http2/server.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2760
vendor/golang.org/x/net/http2/transport.go
generated
vendored
Normal file
2760
vendor/golang.org/x/net/http2/transport.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
365
vendor/golang.org/x/net/http2/write.go
generated
vendored
Normal file
365
vendor/golang.org/x/net/http2/write.go
generated
vendored
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
// 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 (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"golang.org/x/net/http/httpguts"
|
||||||
|
"golang.org/x/net/http2/hpack"
|
||||||
|
)
|
||||||
|
|
||||||
|
// writeFramer is implemented by any type that is used to write frames.
|
||||||
|
type writeFramer interface {
|
||||||
|
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
|
||||||
|
// types below. All the writeFrame methods below are scheduled via the
|
||||||
|
// frame writing scheduler (see writeScheduler in writesched.go).
|
||||||
|
//
|
||||||
|
// This interface is implemented by *serverConn.
|
||||||
|
//
|
||||||
|
// TODO: decide whether to a) use this in the client code (which didn't
|
||||||
|
// end up using this yet, because it has a simpler design, not
|
||||||
|
// currently implementing priorities), or b) delete this and
|
||||||
|
// make the server code a bit more concrete.
|
||||||
|
type writeContext interface {
|
||||||
|
Framer() *Framer
|
||||||
|
Flush() error
|
||||||
|
CloseConn() error
|
||||||
|
// HeaderEncoder returns an HPACK encoder that writes to the
|
||||||
|
// returned buffer.
|
||||||
|
HeaderEncoder() (*hpack.Encoder, *bytes.Buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeEndsStream reports whether w writes a frame that will transition
|
||||||
|
// the stream to a half-closed local state. This returns false for RST_STREAM,
|
||||||
|
// which closes the entire stream (not just the local half).
|
||||||
|
func writeEndsStream(w writeFramer) bool {
|
||||||
|
switch v := w.(type) {
|
||||||
|
case *writeData:
|
||||||
|
return v.endStream
|
||||||
|
case *writeResHeaders:
|
||||||
|
return v.endStream
|
||||||
|
case nil:
|
||||||
|
// This can only happen if the caller reuses w after it's
|
||||||
|
// been intentionally nil'ed out to prevent use. Keep this
|
||||||
|
// here to catch future refactoring breaking it.
|
||||||
|
panic("writeEndsStream called on nil writeFramer")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type flushFrameWriter struct{}
|
||||||
|
|
||||||
|
func (flushFrameWriter) writeFrame(ctx writeContext) error {
|
||||||
|
return ctx.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (flushFrameWriter) staysWithinBuffer(max int) bool { return false }
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return ctx.Framer().WriteSettings([]Setting(s)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type writeGoAway struct {
|
||||||
|
maxStreamID uint32
|
||||||
|
code ErrCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *writeGoAway) writeFrame(ctx writeContext) error {
|
||||||
|
err := ctx.Framer().WriteGoAway(p.maxStreamID, p.code, nil)
|
||||||
|
ctx.Flush() // ignore error: we're hanging up on them anyway
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*writeGoAway) staysWithinBuffer(max int) bool { return false } // flushes
|
||||||
|
|
||||||
|
type writeData struct {
|
||||||
|
streamID uint32
|
||||||
|
p []byte
|
||||||
|
endStream bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *writeData) String() string {
|
||||||
|
return fmt.Sprintf("writeData(stream=%d, p=%d, endStream=%v)", w.streamID, len(w.p), w.endStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *writeData) writeFrame(ctx writeContext) error {
|
||||||
|
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
|
||||||
|
// the handler panics.
|
||||||
|
type handlerPanicRST struct {
|
||||||
|
StreamID uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hp handlerPanicRST) writeFrame(ctx writeContext) error {
|
||||||
|
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 {
|
||||||
|
return ctx.Framer().WriteRSTStream(se.StreamID, se.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se StreamError) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max }
|
||||||
|
|
||||||
|
type writePingAck struct{ pf *PingFrame }
|
||||||
|
|
||||||
|
func (w writePingAck) writeFrame(ctx writeContext) error {
|
||||||
|
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{}
|
||||||
|
|
||||||
|
func (writeSettingsAck) writeFrame(ctx writeContext) error {
|
||||||
|
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
|
||||||
|
// for HTTP response headers or trailers from a server handler.
|
||||||
|
type writeResHeaders struct {
|
||||||
|
streamID uint32
|
||||||
|
httpResCode int // 0 means no ":status" line
|
||||||
|
h http.Header // may be nil
|
||||||
|
trailers []string // if non-nil, which keys of h to write. nil means all.
|
||||||
|
endStream bool
|
||||||
|
|
||||||
|
date string
|
||||||
|
contentType string
|
||||||
|
contentLength string
|
||||||
|
}
|
||||||
|
|
||||||
|
func encKV(enc *hpack.Encoder, k, v string) {
|
||||||
|
if VerboseLogs {
|
||||||
|
log.Printf("http2: server encoding header %q = %q", k, 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
|
||||||
|
// upper 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 {
|
||||||
|
enc, buf := ctx.HeaderEncoder()
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
if w.httpResCode != 0 {
|
||||||
|
encKV(enc, ":status", httpCodeString(w.httpResCode))
|
||||||
|
}
|
||||||
|
|
||||||
|
encodeHeaders(enc, w.h, w.trailers)
|
||||||
|
|
||||||
|
if w.contentType != "" {
|
||||||
|
encKV(enc, "content-type", w.contentType)
|
||||||
|
}
|
||||||
|
if w.contentLength != "" {
|
||||||
|
encKV(enc, "content-length", w.contentLength)
|
||||||
|
}
|
||||||
|
if w.date != "" {
|
||||||
|
encKV(enc, "date", w.date)
|
||||||
|
}
|
||||||
|
|
||||||
|
headerBlock := buf.Bytes()
|
||||||
|
if len(headerBlock) == 0 && w.trailers == nil {
|
||||||
|
panic("unexpected empty hpack")
|
||||||
|
}
|
||||||
|
|
||||||
|
return splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *writeResHeaders) writeHeaderBlock(ctx writeContext, frag []byte, firstFrag, lastFrag bool) error {
|
||||||
|
if firstFrag {
|
||||||
|
return ctx.Framer().WriteHeaders(HeadersFrameParam{
|
||||||
|
StreamID: w.streamID,
|
||||||
|
BlockFragment: frag,
|
||||||
|
EndStream: w.endStream,
|
||||||
|
EndHeaders: lastFrag,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writePushPromise is a request to write a PUSH_PROMISE and 0+ CONTINUATION frames.
|
||||||
|
type writePushPromise struct {
|
||||||
|
streamID uint32 // pusher stream
|
||||||
|
method string // for :method
|
||||||
|
url *url.URL // for :scheme, :authority, :path
|
||||||
|
h http.Header
|
||||||
|
|
||||||
|
// Creates an ID for a pushed stream. This runs on serveG just before
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type write100ContinueHeadersFrame struct {
|
||||||
|
streamID uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w write100ContinueHeadersFrame) writeFrame(ctx writeContext) error {
|
||||||
|
enc, buf := ctx.HeaderEncoder()
|
||||||
|
buf.Reset()
|
||||||
|
encKV(enc, ":status", "100")
|
||||||
|
return ctx.Framer().WriteHeaders(HeadersFrameParam{
|
||||||
|
StreamID: w.streamID,
|
||||||
|
BlockFragment: buf.Bytes(),
|
||||||
|
EndStream: false,
|
||||||
|
EndHeaders: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w write100ContinueHeadersFrame) staysWithinBuffer(max int) bool {
|
||||||
|
// Sloppy but conservative:
|
||||||
|
return 9+2*(len(":status")+len("100")) <= max
|
||||||
|
}
|
||||||
|
|
||||||
|
type writeWindowUpdate struct {
|
||||||
|
streamID uint32 // or 0 for conn-level
|
||||||
|
n uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wu writeWindowUpdate) staysWithinBuffer(max int) bool { return frameHeaderLen+4 <= max }
|
||||||
|
|
||||||
|
func (wu writeWindowUpdate) writeFrame(ctx writeContext) error {
|
||||||
|
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 if k is in keys.
|
||||||
|
func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) {
|
||||||
|
if keys == nil {
|
||||||
|
sorter := sorterPool.Get().(*sorter)
|
||||||
|
// Using defer here, since the returned keys from the
|
||||||
|
// sorter.Keys method is only valid until the sorter
|
||||||
|
// is returned:
|
||||||
|
defer sorterPool.Put(sorter)
|
||||||
|
keys = sorter.Keys(h)
|
||||||
|
}
|
||||||
|
for _, k := range keys {
|
||||||
|
vv := h[k]
|
||||||
|
k = lowerHeader(k)
|
||||||
|
if !validWireHeaderFieldName(k) {
|
||||||
|
// Skip it as backup paranoia. Per
|
||||||
|
// golang.org/issue/14048, these should
|
||||||
|
// already be rejected at a higher level.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
isTE := k == "transfer-encoding"
|
||||||
|
for _, v := range vv {
|
||||||
|
if !httpguts.ValidHeaderFieldValue(v) {
|
||||||
|
// TODO: return an error? golang.org/issue/14048
|
||||||
|
// For now just omit it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// TODO: more of "8.1.2.2 Connection-Specific Header Fields"
|
||||||
|
if isTE && v != "trailers" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
encKV(enc, k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
248
vendor/golang.org/x/net/http2/writesched.go
generated
vendored
Normal file
248
vendor/golang.org/x/net/http2/writesched.go
generated
vendored
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
// 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 "fmt"
|
||||||
|
|
||||||
|
// WriteScheduler is the interface implemented by HTTP/2 write schedulers.
|
||||||
|
// Methods are never called concurrently.
|
||||||
|
type WriteScheduler interface {
|
||||||
|
// OpenStream opens a new stream in the write scheduler.
|
||||||
|
// It is illegal to call this with streamID=0 or with a streamID that is
|
||||||
|
// already open -- the call may panic.
|
||||||
|
OpenStream(streamID uint32, options OpenStreamOptions)
|
||||||
|
|
||||||
|
// CloseStream closes a stream in the write scheduler. Any frames queued on
|
||||||
|
// this stream should be discarded. It is illegal to call this on a stream
|
||||||
|
// that is not open -- the call may panic.
|
||||||
|
CloseStream(streamID uint32)
|
||||||
|
|
||||||
|
// AdjustStream adjusts the priority of the given stream. This may be called
|
||||||
|
// on a stream that has not yet been opened or has been closed. Note that
|
||||||
|
// RFC 7540 allows PRIORITY frames to be sent on streams in any state. See:
|
||||||
|
// https://tools.ietf.org/html/rfc7540#section-5.1
|
||||||
|
AdjustStream(streamID uint32, priority PriorityParam)
|
||||||
|
|
||||||
|
// Push queues a frame in the scheduler. In most cases, this will not be
|
||||||
|
// called with wr.StreamID()!=0 unless that stream is currently open. The one
|
||||||
|
// exception is RST_STREAM frames, which may be sent on idle or closed streams.
|
||||||
|
Push(wr FrameWriteRequest)
|
||||||
|
|
||||||
|
// Pop dequeues the next frame to write. Returns false if no frames can
|
||||||
|
// be written. Frames with a given wr.StreamID() are Pop'd in the same
|
||||||
|
// order they are Push'd. No frames should be discarded except by CloseStream.
|
||||||
|
Pop() (wr FrameWriteRequest, ok bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenStreamOptions specifies extra options for WriteScheduler.OpenStream.
|
||||||
|
type OpenStreamOptions struct {
|
||||||
|
// PusherID is zero if the stream was initiated by the client. Otherwise,
|
||||||
|
// PusherID names the stream that pushed the newly opened stream.
|
||||||
|
PusherID uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// FrameWriteRequest is a request to write a frame.
|
||||||
|
type FrameWriteRequest struct {
|
||||||
|
// write is the interface value that does the writing, once the
|
||||||
|
// WriteScheduler has selected this frame to write. The write
|
||||||
|
// functions are all defined in write.go.
|
||||||
|
write writeFramer
|
||||||
|
|
||||||
|
// stream is the stream on which this frame will be written.
|
||||||
|
// nil for non-stream frames like PING and SETTINGS.
|
||||||
|
stream *stream
|
||||||
|
|
||||||
|
// done, if non-nil, must be a buffered channel with space for
|
||||||
|
// 1 message and is sent the return value from write (or an
|
||||||
|
// earlier error) when the frame has been written.
|
||||||
|
done chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
// StreamID returns the id of the stream this frame will be written to.
|
||||||
|
// 0 is used for non-stream frames such as PING and SETTINGS.
|
||||||
|
func (wr FrameWriteRequest) StreamID() uint32 {
|
||||||
|
if wr.stream == nil {
|
||||||
|
if se, ok := wr.write.(StreamError); ok {
|
||||||
|
// (*serverConn).resetStream doesn't set
|
||||||
|
// stream because it doesn't necessarily have
|
||||||
|
// one. So special case this type of write
|
||||||
|
// message.
|
||||||
|
return se.StreamID
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return wr.stream.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// isControl reports whether wr is a control frame for MaxQueuedControlFrames
|
||||||
|
// purposes. That includes non-stream frames and RST_STREAM frames.
|
||||||
|
func (wr FrameWriteRequest) isControl() bool {
|
||||||
|
return wr.stream == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataSize returns the number of flow control bytes that must be consumed
|
||||||
|
// to write this entire frame. This is 0 for non-DATA frames.
|
||||||
|
func (wr FrameWriteRequest) DataSize() int {
|
||||||
|
if wd, ok := wr.write.(*writeData); ok {
|
||||||
|
return len(wd.p)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume consumes min(n, available) bytes from this frame, where available
|
||||||
|
// is the number of flow control bytes available on the stream. Consume returns
|
||||||
|
// 0, 1, or 2 frames, where the integer return value gives the number of frames
|
||||||
|
// returned.
|
||||||
|
//
|
||||||
|
// If flow control prevents consuming any bytes, this returns (_, _, 0). If
|
||||||
|
// the entire frame was consumed, this returns (wr, _, 1). Otherwise, this
|
||||||
|
// returns (consumed, rest, 2), where 'consumed' contains the consumed bytes and
|
||||||
|
// 'rest' contains the remaining bytes. The consumed bytes are deducted from the
|
||||||
|
// underlying stream's flow control budget.
|
||||||
|
func (wr FrameWriteRequest) Consume(n int32) (FrameWriteRequest, FrameWriteRequest, int) {
|
||||||
|
var empty FrameWriteRequest
|
||||||
|
|
||||||
|
// Non-DATA frames are always consumed whole.
|
||||||
|
wd, ok := wr.write.(*writeData)
|
||||||
|
if !ok || len(wd.p) == 0 {
|
||||||
|
return wr, empty, 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Might need to split after applying limits.
|
||||||
|
allowed := wr.stream.flow.available()
|
||||||
|
if n < allowed {
|
||||||
|
allowed = n
|
||||||
|
}
|
||||||
|
if wr.stream.sc.maxFrameSize < allowed {
|
||||||
|
allowed = wr.stream.sc.maxFrameSize
|
||||||
|
}
|
||||||
|
if allowed <= 0 {
|
||||||
|
return empty, empty, 0
|
||||||
|
}
|
||||||
|
if len(wd.p) > int(allowed) {
|
||||||
|
wr.stream.flow.take(allowed)
|
||||||
|
consumed := FrameWriteRequest{
|
||||||
|
stream: wr.stream,
|
||||||
|
write: &writeData{
|
||||||
|
streamID: wd.streamID,
|
||||||
|
p: wd.p[:allowed],
|
||||||
|
// Even if the original had endStream set, there
|
||||||
|
// are bytes remaining because len(wd.p) > allowed,
|
||||||
|
// so we know endStream is false.
|
||||||
|
endStream: false,
|
||||||
|
},
|
||||||
|
// Our caller is blocking on the final DATA frame, not
|
||||||
|
// this intermediate frame, so no need to wait.
|
||||||
|
done: nil,
|
||||||
|
}
|
||||||
|
rest := FrameWriteRequest{
|
||||||
|
stream: wr.stream,
|
||||||
|
write: &writeData{
|
||||||
|
streamID: wd.streamID,
|
||||||
|
p: wd.p[allowed:],
|
||||||
|
endStream: wd.endStream,
|
||||||
|
},
|
||||||
|
done: wr.done,
|
||||||
|
}
|
||||||
|
return consumed, rest, 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// The frame is consumed whole.
|
||||||
|
// NB: This cast cannot overflow because allowed is <= math.MaxInt32.
|
||||||
|
wr.stream.flow.take(int32(len(wd.p)))
|
||||||
|
return wr, empty, 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// String is for debugging only.
|
||||||
|
func (wr FrameWriteRequest) String() string {
|
||||||
|
var des string
|
||||||
|
if s, ok := wr.write.(fmt.Stringer); ok {
|
||||||
|
des = s.String()
|
||||||
|
} else {
|
||||||
|
des = fmt.Sprintf("%T", wr.write)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("[FrameWriteRequest stream=%d, ch=%v, writer=%v]", wr.StreamID(), wr.done != nil, des)
|
||||||
|
}
|
||||||
|
|
||||||
|
// replyToWriter sends err to wr.done and panics if the send must block
|
||||||
|
// This does nothing if wr.done is nil.
|
||||||
|
func (wr *FrameWriteRequest) replyToWriter(err error) {
|
||||||
|
if wr.done == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case wr.done <- err:
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unbuffered done channel passed in for type %T", wr.write))
|
||||||
|
}
|
||||||
|
wr.write = nil // prevent use (assume it's tainted after wr.done send)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeQueue is used by implementations of WriteScheduler.
|
||||||
|
type writeQueue struct {
|
||||||
|
s []FrameWriteRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *writeQueue) empty() bool { return len(q.s) == 0 }
|
||||||
|
|
||||||
|
func (q *writeQueue) push(wr FrameWriteRequest) {
|
||||||
|
q.s = append(q.s, wr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *writeQueue) shift() FrameWriteRequest {
|
||||||
|
if len(q.s) == 0 {
|
||||||
|
panic("invalid use of queue")
|
||||||
|
}
|
||||||
|
wr := q.s[0]
|
||||||
|
// TODO: less copy-happy queue.
|
||||||
|
copy(q.s, q.s[1:])
|
||||||
|
q.s[len(q.s)-1] = FrameWriteRequest{}
|
||||||
|
q.s = q.s[:len(q.s)-1]
|
||||||
|
return wr
|
||||||
|
}
|
||||||
|
|
||||||
|
// consume consumes up to n bytes from q.s[0]. If the frame is
|
||||||
|
// entirely consumed, it is removed from the queue. If the frame
|
||||||
|
// is partially consumed, the frame is kept with the consumed
|
||||||
|
// bytes removed. Returns true iff any bytes were consumed.
|
||||||
|
func (q *writeQueue) consume(n int32) (FrameWriteRequest, bool) {
|
||||||
|
if len(q.s) == 0 {
|
||||||
|
return FrameWriteRequest{}, false
|
||||||
|
}
|
||||||
|
consumed, rest, numresult := q.s[0].Consume(n)
|
||||||
|
switch numresult {
|
||||||
|
case 0:
|
||||||
|
return FrameWriteRequest{}, false
|
||||||
|
case 1:
|
||||||
|
q.shift()
|
||||||
|
case 2:
|
||||||
|
q.s[0] = rest
|
||||||
|
}
|
||||||
|
return consumed, true
|
||||||
|
}
|
||||||
|
|
||||||
|
type writeQueuePool []*writeQueue
|
||||||
|
|
||||||
|
// put inserts an unused writeQueue into the pool.
|
||||||
|
func (p *writeQueuePool) put(q *writeQueue) {
|
||||||
|
for i := range q.s {
|
||||||
|
q.s[i] = FrameWriteRequest{}
|
||||||
|
}
|
||||||
|
q.s = q.s[:0]
|
||||||
|
*p = append(*p, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get returns an empty writeQueue.
|
||||||
|
func (p *writeQueuePool) get() *writeQueue {
|
||||||
|
ln := len(*p)
|
||||||
|
if ln == 0 {
|
||||||
|
return new(writeQueue)
|
||||||
|
}
|
||||||
|
x := ln - 1
|
||||||
|
q := (*p)[x]
|
||||||
|
(*p)[x] = nil
|
||||||
|
*p = (*p)[:x]
|
||||||
|
return q
|
||||||
|
}
|
452
vendor/golang.org/x/net/http2/writesched_priority.go
generated
vendored
Normal file
452
vendor/golang.org/x/net/http2/writesched_priority.go
generated
vendored
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
// 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 http2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RFC 7540, Section 5.3.5: the default weight is 16.
|
||||||
|
const priorityDefaultWeight = 15 // 16 = 15 + 1
|
||||||
|
|
||||||
|
// PriorityWriteSchedulerConfig configures a priorityWriteScheduler.
|
||||||
|
type PriorityWriteSchedulerConfig struct {
|
||||||
|
// MaxClosedNodesInTree controls the maximum number of closed streams to
|
||||||
|
// retain in the priority tree. Setting this to zero saves a small amount
|
||||||
|
// of memory at the cost of performance.
|
||||||
|
//
|
||||||
|
// See RFC 7540, Section 5.3.4:
|
||||||
|
// "It is possible for a stream to become closed while prioritization
|
||||||
|
// information ... is in transit. ... This potentially creates suboptimal
|
||||||
|
// prioritization, since the stream could be given a priority that is
|
||||||
|
// different from what is intended. To avoid these problems, an endpoint
|
||||||
|
// SHOULD retain stream prioritization state for a period after streams
|
||||||
|
// become closed. The longer state is retained, the lower the chance that
|
||||||
|
// streams are assigned incorrect or default priority values."
|
||||||
|
MaxClosedNodesInTree int
|
||||||
|
|
||||||
|
// MaxIdleNodesInTree controls the maximum number of idle streams to
|
||||||
|
// retain in the priority tree. Setting this to zero saves a small amount
|
||||||
|
// of memory at the cost of performance.
|
||||||
|
//
|
||||||
|
// See RFC 7540, Section 5.3.4:
|
||||||
|
// Similarly, streams that are in the "idle" state can be assigned
|
||||||
|
// priority or become a parent of other streams. This allows for the
|
||||||
|
// creation of a grouping node in the dependency tree, which enables
|
||||||
|
// more flexible expressions of priority. Idle streams begin with a
|
||||||
|
// default priority (Section 5.3.5).
|
||||||
|
MaxIdleNodesInTree int
|
||||||
|
|
||||||
|
// ThrottleOutOfOrderWrites enables write throttling to help ensure that
|
||||||
|
// data is delivered in priority order. This works around a race where
|
||||||
|
// stream B depends on stream A and both streams are about to call Write
|
||||||
|
// to queue DATA frames. If B wins the race, a naive scheduler would eagerly
|
||||||
|
// write as much data from B as possible, but this is suboptimal because A
|
||||||
|
// is a higher-priority stream. With throttling enabled, we write a small
|
||||||
|
// amount of data from B to minimize the amount of bandwidth that B can
|
||||||
|
// steal from A.
|
||||||
|
ThrottleOutOfOrderWrites bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPriorityWriteScheduler constructs a WriteScheduler that schedules
|
||||||
|
// frames by following HTTP/2 priorities as described in RFC 7540 Section 5.3.
|
||||||
|
// If cfg is nil, default options are used.
|
||||||
|
func NewPriorityWriteScheduler(cfg *PriorityWriteSchedulerConfig) WriteScheduler {
|
||||||
|
if cfg == nil {
|
||||||
|
// For justification of these defaults, see:
|
||||||
|
// https://docs.google.com/document/d/1oLhNg1skaWD4_DtaoCxdSRN5erEXrH-KnLrMwEpOtFY
|
||||||
|
cfg = &PriorityWriteSchedulerConfig{
|
||||||
|
MaxClosedNodesInTree: 10,
|
||||||
|
MaxIdleNodesInTree: 10,
|
||||||
|
ThrottleOutOfOrderWrites: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ws := &priorityWriteScheduler{
|
||||||
|
nodes: make(map[uint32]*priorityNode),
|
||||||
|
maxClosedNodesInTree: cfg.MaxClosedNodesInTree,
|
||||||
|
maxIdleNodesInTree: cfg.MaxIdleNodesInTree,
|
||||||
|
enableWriteThrottle: cfg.ThrottleOutOfOrderWrites,
|
||||||
|
}
|
||||||
|
ws.nodes[0] = &ws.root
|
||||||
|
if cfg.ThrottleOutOfOrderWrites {
|
||||||
|
ws.writeThrottleLimit = 1024
|
||||||
|
} else {
|
||||||
|
ws.writeThrottleLimit = math.MaxInt32
|
||||||
|
}
|
||||||
|
return ws
|
||||||
|
}
|
||||||
|
|
||||||
|
type priorityNodeState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
priorityNodeOpen priorityNodeState = iota
|
||||||
|
priorityNodeClosed
|
||||||
|
priorityNodeIdle
|
||||||
|
)
|
||||||
|
|
||||||
|
// priorityNode is a node in an HTTP/2 priority tree.
|
||||||
|
// Each node is associated with a single stream ID.
|
||||||
|
// See RFC 7540, Section 5.3.
|
||||||
|
type priorityNode struct {
|
||||||
|
q writeQueue // queue of pending frames to write
|
||||||
|
id uint32 // id of the stream, or 0 for the root of the tree
|
||||||
|
weight uint8 // the actual weight is weight+1, so the value is in [1,256]
|
||||||
|
state priorityNodeState // open | closed | idle
|
||||||
|
bytes int64 // number of bytes written by this node, or 0 if closed
|
||||||
|
subtreeBytes int64 // sum(node.bytes) of all nodes in this subtree
|
||||||
|
|
||||||
|
// These links form the priority tree.
|
||||||
|
parent *priorityNode
|
||||||
|
kids *priorityNode // start of the kids list
|
||||||
|
prev, next *priorityNode // doubly-linked list of siblings
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *priorityNode) setParent(parent *priorityNode) {
|
||||||
|
if n == parent {
|
||||||
|
panic("setParent to self")
|
||||||
|
}
|
||||||
|
if n.parent == parent {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Unlink from current parent.
|
||||||
|
if parent := n.parent; parent != nil {
|
||||||
|
if n.prev == nil {
|
||||||
|
parent.kids = n.next
|
||||||
|
} else {
|
||||||
|
n.prev.next = n.next
|
||||||
|
}
|
||||||
|
if n.next != nil {
|
||||||
|
n.next.prev = n.prev
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Link to new parent.
|
||||||
|
// If parent=nil, remove n from the tree.
|
||||||
|
// Always insert at the head of parent.kids (this is assumed by walkReadyInOrder).
|
||||||
|
n.parent = parent
|
||||||
|
if parent == nil {
|
||||||
|
n.next = nil
|
||||||
|
n.prev = nil
|
||||||
|
} else {
|
||||||
|
n.next = parent.kids
|
||||||
|
n.prev = nil
|
||||||
|
if n.next != nil {
|
||||||
|
n.next.prev = n
|
||||||
|
}
|
||||||
|
parent.kids = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *priorityNode) addBytes(b int64) {
|
||||||
|
n.bytes += b
|
||||||
|
for ; n != nil; n = n.parent {
|
||||||
|
n.subtreeBytes += b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// walkReadyInOrder iterates over the tree in priority order, calling f for each node
|
||||||
|
// with a non-empty write queue. When f returns true, this function returns true and the
|
||||||
|
// walk halts. tmp is used as scratch space for sorting.
|
||||||
|
//
|
||||||
|
// f(n, openParent) takes two arguments: the node to visit, n, and a bool that is true
|
||||||
|
// if any ancestor p of n is still open (ignoring the root node).
|
||||||
|
func (n *priorityNode) walkReadyInOrder(openParent bool, tmp *[]*priorityNode, f func(*priorityNode, bool) bool) bool {
|
||||||
|
if !n.q.empty() && f(n, openParent) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if n.kids == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't consider the root "open" when updating openParent since
|
||||||
|
// we can't send data frames on the root stream (only control frames).
|
||||||
|
if n.id != 0 {
|
||||||
|
openParent = openParent || (n.state == priorityNodeOpen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common case: only one kid or all kids have the same weight.
|
||||||
|
// Some clients don't use weights; other clients (like web browsers)
|
||||||
|
// use mostly-linear priority trees.
|
||||||
|
w := n.kids.weight
|
||||||
|
needSort := false
|
||||||
|
for k := n.kids.next; k != nil; k = k.next {
|
||||||
|
if k.weight != w {
|
||||||
|
needSort = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !needSort {
|
||||||
|
for k := n.kids; k != nil; k = k.next {
|
||||||
|
if k.walkReadyInOrder(openParent, tmp, f) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uncommon case: sort the child nodes. We remove the kids from the parent,
|
||||||
|
// then re-insert after sorting so we can reuse tmp for future sort calls.
|
||||||
|
*tmp = (*tmp)[:0]
|
||||||
|
for n.kids != nil {
|
||||||
|
*tmp = append(*tmp, n.kids)
|
||||||
|
n.kids.setParent(nil)
|
||||||
|
}
|
||||||
|
sort.Sort(sortPriorityNodeSiblings(*tmp))
|
||||||
|
for i := len(*tmp) - 1; i >= 0; i-- {
|
||||||
|
(*tmp)[i].setParent(n) // setParent inserts at the head of n.kids
|
||||||
|
}
|
||||||
|
for k := n.kids; k != nil; k = k.next {
|
||||||
|
if k.walkReadyInOrder(openParent, tmp, f) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type sortPriorityNodeSiblings []*priorityNode
|
||||||
|
|
||||||
|
func (z sortPriorityNodeSiblings) Len() int { return len(z) }
|
||||||
|
func (z sortPriorityNodeSiblings) Swap(i, k int) { z[i], z[k] = z[k], z[i] }
|
||||||
|
func (z sortPriorityNodeSiblings) Less(i, k int) bool {
|
||||||
|
// Prefer the subtree that has sent fewer bytes relative to its weight.
|
||||||
|
// See sections 5.3.2 and 5.3.4.
|
||||||
|
wi, bi := float64(z[i].weight+1), float64(z[i].subtreeBytes)
|
||||||
|
wk, bk := float64(z[k].weight+1), float64(z[k].subtreeBytes)
|
||||||
|
if bi == 0 && bk == 0 {
|
||||||
|
return wi >= wk
|
||||||
|
}
|
||||||
|
if bk == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return bi/bk <= wi/wk
|
||||||
|
}
|
||||||
|
|
||||||
|
type priorityWriteScheduler struct {
|
||||||
|
// root is the root of the priority tree, where root.id = 0.
|
||||||
|
// The root queues control frames that are not associated with any stream.
|
||||||
|
root priorityNode
|
||||||
|
|
||||||
|
// nodes maps stream ids to priority tree nodes.
|
||||||
|
nodes map[uint32]*priorityNode
|
||||||
|
|
||||||
|
// maxID is the maximum stream id in nodes.
|
||||||
|
maxID uint32
|
||||||
|
|
||||||
|
// lists of nodes that have been closed or are idle, but are kept in
|
||||||
|
// the tree for improved prioritization. When the lengths exceed either
|
||||||
|
// maxClosedNodesInTree or maxIdleNodesInTree, old nodes are discarded.
|
||||||
|
closedNodes, idleNodes []*priorityNode
|
||||||
|
|
||||||
|
// From the config.
|
||||||
|
maxClosedNodesInTree int
|
||||||
|
maxIdleNodesInTree int
|
||||||
|
writeThrottleLimit int32
|
||||||
|
enableWriteThrottle bool
|
||||||
|
|
||||||
|
// tmp is scratch space for priorityNode.walkReadyInOrder to reduce allocations.
|
||||||
|
tmp []*priorityNode
|
||||||
|
|
||||||
|
// pool of empty queues for reuse.
|
||||||
|
queuePool writeQueuePool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ws *priorityWriteScheduler) OpenStream(streamID uint32, options OpenStreamOptions) {
|
||||||
|
// The stream may be currently idle but cannot be opened or closed.
|
||||||
|
if curr := ws.nodes[streamID]; curr != nil {
|
||||||
|
if curr.state != priorityNodeIdle {
|
||||||
|
panic(fmt.Sprintf("stream %d already opened", streamID))
|
||||||
|
}
|
||||||
|
curr.state = priorityNodeOpen
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC 7540, Section 5.3.5:
|
||||||
|
// "All streams are initially assigned a non-exclusive dependency on stream 0x0.
|
||||||
|
// Pushed streams initially depend on their associated stream. In both cases,
|
||||||
|
// streams are assigned a default weight of 16."
|
||||||
|
parent := ws.nodes[options.PusherID]
|
||||||
|
if parent == nil {
|
||||||
|
parent = &ws.root
|
||||||
|
}
|
||||||
|
n := &priorityNode{
|
||||||
|
q: *ws.queuePool.get(),
|
||||||
|
id: streamID,
|
||||||
|
weight: priorityDefaultWeight,
|
||||||
|
state: priorityNodeOpen,
|
||||||
|
}
|
||||||
|
n.setParent(parent)
|
||||||
|
ws.nodes[streamID] = n
|
||||||
|
if streamID > ws.maxID {
|
||||||
|
ws.maxID = streamID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ws *priorityWriteScheduler) CloseStream(streamID uint32) {
|
||||||
|
if streamID == 0 {
|
||||||
|
panic("violation of WriteScheduler interface: cannot close stream 0")
|
||||||
|
}
|
||||||
|
if ws.nodes[streamID] == nil {
|
||||||
|
panic(fmt.Sprintf("violation of WriteScheduler interface: unknown stream %d", streamID))
|
||||||
|
}
|
||||||
|
if ws.nodes[streamID].state != priorityNodeOpen {
|
||||||
|
panic(fmt.Sprintf("violation of WriteScheduler interface: stream %d already closed", streamID))
|
||||||
|
}
|
||||||
|
|
||||||
|
n := ws.nodes[streamID]
|
||||||
|
n.state = priorityNodeClosed
|
||||||
|
n.addBytes(-n.bytes)
|
||||||
|
|
||||||
|
q := n.q
|
||||||
|
ws.queuePool.put(&q)
|
||||||
|
n.q.s = nil
|
||||||
|
if ws.maxClosedNodesInTree > 0 {
|
||||||
|
ws.addClosedOrIdleNode(&ws.closedNodes, ws.maxClosedNodesInTree, n)
|
||||||
|
} else {
|
||||||
|
ws.removeNode(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ws *priorityWriteScheduler) AdjustStream(streamID uint32, priority PriorityParam) {
|
||||||
|
if streamID == 0 {
|
||||||
|
panic("adjustPriority on root")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If streamID does not exist, there are two cases:
|
||||||
|
// - A closed stream that has been removed (this will have ID <= maxID)
|
||||||
|
// - An idle stream that is being used for "grouping" (this will have ID > maxID)
|
||||||
|
n := ws.nodes[streamID]
|
||||||
|
if n == nil {
|
||||||
|
if streamID <= ws.maxID || ws.maxIdleNodesInTree == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ws.maxID = streamID
|
||||||
|
n = &priorityNode{
|
||||||
|
q: *ws.queuePool.get(),
|
||||||
|
id: streamID,
|
||||||
|
weight: priorityDefaultWeight,
|
||||||
|
state: priorityNodeIdle,
|
||||||
|
}
|
||||||
|
n.setParent(&ws.root)
|
||||||
|
ws.nodes[streamID] = n
|
||||||
|
ws.addClosedOrIdleNode(&ws.idleNodes, ws.maxIdleNodesInTree, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section 5.3.1: A dependency on a stream that is not currently in the tree
|
||||||
|
// results in that stream being given a default priority (Section 5.3.5).
|
||||||
|
parent := ws.nodes[priority.StreamDep]
|
||||||
|
if parent == nil {
|
||||||
|
n.setParent(&ws.root)
|
||||||
|
n.weight = priorityDefaultWeight
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore if the client tries to make a node its own parent.
|
||||||
|
if n == parent {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section 5.3.3:
|
||||||
|
// "If a stream is made dependent on one of its own dependencies, the
|
||||||
|
// formerly dependent stream is first moved to be dependent on the
|
||||||
|
// reprioritized stream's previous parent. The moved dependency retains
|
||||||
|
// its weight."
|
||||||
|
//
|
||||||
|
// That is: if parent depends on n, move parent to depend on n.parent.
|
||||||
|
for x := parent.parent; x != nil; x = x.parent {
|
||||||
|
if x == n {
|
||||||
|
parent.setParent(n.parent)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section 5.3.3: The exclusive flag causes the stream to become the sole
|
||||||
|
// dependency of its parent stream, causing other dependencies to become
|
||||||
|
// dependent on the exclusive stream.
|
||||||
|
if priority.Exclusive {
|
||||||
|
k := parent.kids
|
||||||
|
for k != nil {
|
||||||
|
next := k.next
|
||||||
|
if k != n {
|
||||||
|
k.setParent(n)
|
||||||
|
}
|
||||||
|
k = next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n.setParent(parent)
|
||||||
|
n.weight = priority.Weight
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ws *priorityWriteScheduler) Push(wr FrameWriteRequest) {
|
||||||
|
var n *priorityNode
|
||||||
|
if id := wr.StreamID(); id == 0 {
|
||||||
|
n = &ws.root
|
||||||
|
} else {
|
||||||
|
n = ws.nodes[id]
|
||||||
|
if n == nil {
|
||||||
|
// id is an idle or closed stream. wr should not be a HEADERS or
|
||||||
|
// DATA frame. However, wr can be a RST_STREAM. In this case, we
|
||||||
|
// push wr onto the root, rather than creating a new priorityNode,
|
||||||
|
// since RST_STREAM is tiny and the stream's priority is unknown
|
||||||
|
// anyway. See issue #17919.
|
||||||
|
if wr.DataSize() > 0 {
|
||||||
|
panic("add DATA on non-open stream")
|
||||||
|
}
|
||||||
|
n = &ws.root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n.q.push(wr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ws *priorityWriteScheduler) Pop() (wr FrameWriteRequest, ok bool) {
|
||||||
|
ws.root.walkReadyInOrder(false, &ws.tmp, func(n *priorityNode, openParent bool) bool {
|
||||||
|
limit := int32(math.MaxInt32)
|
||||||
|
if openParent {
|
||||||
|
limit = ws.writeThrottleLimit
|
||||||
|
}
|
||||||
|
wr, ok = n.q.consume(limit)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
n.addBytes(int64(wr.DataSize()))
|
||||||
|
// If B depends on A and B continuously has data available but A
|
||||||
|
// does not, gradually increase the throttling limit to allow B to
|
||||||
|
// steal more and more bandwidth from A.
|
||||||
|
if openParent {
|
||||||
|
ws.writeThrottleLimit += 1024
|
||||||
|
if ws.writeThrottleLimit < 0 {
|
||||||
|
ws.writeThrottleLimit = math.MaxInt32
|
||||||
|
}
|
||||||
|
} else if ws.enableWriteThrottle {
|
||||||
|
ws.writeThrottleLimit = 1024
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return wr, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ws *priorityWriteScheduler) addClosedOrIdleNode(list *[]*priorityNode, maxSize int, n *priorityNode) {
|
||||||
|
if maxSize == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(*list) == maxSize {
|
||||||
|
// Remove the oldest node, then shift left.
|
||||||
|
ws.removeNode((*list)[0])
|
||||||
|
x := (*list)[1:]
|
||||||
|
copy(*list, x)
|
||||||
|
*list = (*list)[:len(x)]
|
||||||
|
}
|
||||||
|
*list = append(*list, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ws *priorityWriteScheduler) removeNode(n *priorityNode) {
|
||||||
|
for k := n.kids; k != nil; k = k.next {
|
||||||
|
k.setParent(n.parent)
|
||||||
|
}
|
||||||
|
n.setParent(nil)
|
||||||
|
delete(ws.nodes, n.id)
|
||||||
|
}
|
77
vendor/golang.org/x/net/http2/writesched_random.go
generated
vendored
Normal file
77
vendor/golang.org/x/net/http2/writesched_random.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// 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, closed, or emptied, 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 streamID, q := range ws.sq {
|
||||||
|
if wr, ok := q.consume(math.MaxInt32); ok {
|
||||||
|
if q.empty() {
|
||||||
|
delete(ws.sq, streamID)
|
||||||
|
ws.queuePool.put(q)
|
||||||
|
}
|
||||||
|
return wr, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FrameWriteRequest{}, false
|
||||||
|
}
|
735
vendor/golang.org/x/net/idna/idna10.0.0.go
generated
vendored
Normal file
735
vendor/golang.org/x/net/idna/idna10.0.0.go
generated
vendored
Normal file
@ -0,0 +1,735 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//go:build go1.10
|
||||||
|
// +build go1.10
|
||||||
|
|
||||||
|
// Package idna implements IDNA2008 using the compatibility processing
|
||||||
|
// defined by UTS (Unicode Technical Standard) #46, which defines a standard to
|
||||||
|
// deal with the transition from IDNA2003.
|
||||||
|
//
|
||||||
|
// IDNA2008 (Internationalized Domain Names for Applications), is defined in RFC
|
||||||
|
// 5890, RFC 5891, RFC 5892, RFC 5893 and RFC 5894.
|
||||||
|
// UTS #46 is defined in https://www.unicode.org/reports/tr46.
|
||||||
|
// See https://unicode.org/cldr/utility/idna.jsp for a visualization of the
|
||||||
|
// differences between these two standards.
|
||||||
|
package idna // import "golang.org/x/net/idna"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"golang.org/x/text/secure/bidirule"
|
||||||
|
"golang.org/x/text/unicode/bidi"
|
||||||
|
"golang.org/x/text/unicode/norm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NOTE: Unlike common practice in Go APIs, the functions will return a
|
||||||
|
// sanitized domain name in case of errors. Browsers sometimes use a partially
|
||||||
|
// evaluated string as lookup.
|
||||||
|
// TODO: the current error handling is, in my opinion, the least opinionated.
|
||||||
|
// Other strategies are also viable, though:
|
||||||
|
// Option 1) Return an empty string in case of error, but allow the user to
|
||||||
|
// specify explicitly which errors to ignore.
|
||||||
|
// Option 2) Return the partially evaluated string if it is itself a valid
|
||||||
|
// string, otherwise return the empty string in case of error.
|
||||||
|
// Option 3) Option 1 and 2.
|
||||||
|
// Option 4) Always return an empty string for now and implement Option 1 as
|
||||||
|
// needed, and document that the return string may not be empty in case of
|
||||||
|
// error in the future.
|
||||||
|
// I think Option 1 is best, but it is quite opinionated.
|
||||||
|
|
||||||
|
// ToASCII is a wrapper for Punycode.ToASCII.
|
||||||
|
func ToASCII(s string) (string, error) {
|
||||||
|
return Punycode.process(s, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUnicode is a wrapper for Punycode.ToUnicode.
|
||||||
|
func ToUnicode(s string) (string, error) {
|
||||||
|
return Punycode.process(s, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Option configures a Profile at creation time.
|
||||||
|
type Option func(*options)
|
||||||
|
|
||||||
|
// Transitional sets a Profile to use the Transitional mapping as defined in UTS
|
||||||
|
// #46. This will cause, for example, "ß" to be mapped to "ss". Using the
|
||||||
|
// transitional mapping provides a compromise between IDNA2003 and IDNA2008
|
||||||
|
// compatibility. It is used by most browsers when resolving domain names. This
|
||||||
|
// option is only meaningful if combined with MapForLookup.
|
||||||
|
func Transitional(transitional bool) Option {
|
||||||
|
return func(o *options) { o.transitional = true }
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyDNSLength sets whether a Profile should fail if any of the IDN parts
|
||||||
|
// are longer than allowed by the RFC.
|
||||||
|
func VerifyDNSLength(verify bool) Option {
|
||||||
|
return func(o *options) { o.verifyDNSLength = verify }
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveLeadingDots removes leading label separators. Leading runes that map to
|
||||||
|
// dots, such as U+3002 IDEOGRAPHIC FULL STOP, are removed as well.
|
||||||
|
//
|
||||||
|
// This is the behavior suggested by the UTS #46 and is adopted by some
|
||||||
|
// browsers.
|
||||||
|
func RemoveLeadingDots(remove bool) Option {
|
||||||
|
return func(o *options) { o.removeLeadingDots = remove }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateLabels sets whether to check the mandatory label validation criteria
|
||||||
|
// as defined in Section 5.4 of RFC 5891. This includes testing for correct use
|
||||||
|
// of hyphens ('-'), normalization, validity of runes, and the context rules.
|
||||||
|
func ValidateLabels(enable bool) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
// Don't override existing mappings, but set one that at least checks
|
||||||
|
// normalization if it is not set.
|
||||||
|
if o.mapping == nil && enable {
|
||||||
|
o.mapping = normalize
|
||||||
|
}
|
||||||
|
o.trie = trie
|
||||||
|
o.validateLabels = enable
|
||||||
|
o.fromPuny = validateFromPunycode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrictDomainName limits the set of permissible ASCII characters to those
|
||||||
|
// allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the
|
||||||
|
// hyphen). This is set by default for MapForLookup and ValidateForRegistration.
|
||||||
|
//
|
||||||
|
// This option is useful, for instance, for browsers that allow characters
|
||||||
|
// outside this range, for example a '_' (U+005F LOW LINE). See
|
||||||
|
// http://www.rfc-editor.org/std/std3.txt for more details This option
|
||||||
|
// corresponds to the UseSTD3ASCIIRules option in UTS #46.
|
||||||
|
func StrictDomainName(use bool) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.trie = trie
|
||||||
|
o.useSTD3Rules = use
|
||||||
|
o.fromPuny = validateFromPunycode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: the following options pull in tables. The tables should not be linked
|
||||||
|
// in as long as the options are not used.
|
||||||
|
|
||||||
|
// BidiRule enables the Bidi rule as defined in RFC 5893. Any application
|
||||||
|
// that relies on proper validation of labels should include this rule.
|
||||||
|
func BidiRule() Option {
|
||||||
|
return func(o *options) { o.bidirule = bidirule.ValidString }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateForRegistration sets validation options to verify that a given IDN is
|
||||||
|
// properly formatted for registration as defined by Section 4 of RFC 5891.
|
||||||
|
func ValidateForRegistration() Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.mapping = validateRegistration
|
||||||
|
StrictDomainName(true)(o)
|
||||||
|
ValidateLabels(true)(o)
|
||||||
|
VerifyDNSLength(true)(o)
|
||||||
|
BidiRule()(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapForLookup sets validation and mapping options such that a given IDN is
|
||||||
|
// transformed for domain name lookup according to the requirements set out in
|
||||||
|
// Section 5 of RFC 5891. The mappings follow the recommendations of RFC 5894,
|
||||||
|
// RFC 5895 and UTS 46. It does not add the Bidi Rule. Use the BidiRule option
|
||||||
|
// to add this check.
|
||||||
|
//
|
||||||
|
// The mappings include normalization and mapping case, width and other
|
||||||
|
// compatibility mappings.
|
||||||
|
func MapForLookup() Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.mapping = validateAndMap
|
||||||
|
StrictDomainName(true)(o)
|
||||||
|
ValidateLabels(true)(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type options struct {
|
||||||
|
transitional bool
|
||||||
|
useSTD3Rules bool
|
||||||
|
validateLabels bool
|
||||||
|
verifyDNSLength bool
|
||||||
|
removeLeadingDots bool
|
||||||
|
|
||||||
|
trie *idnaTrie
|
||||||
|
|
||||||
|
// fromPuny calls validation rules when converting A-labels to U-labels.
|
||||||
|
fromPuny func(p *Profile, s string) error
|
||||||
|
|
||||||
|
// mapping implements a validation and mapping step as defined in RFC 5895
|
||||||
|
// or UTS 46, tailored to, for example, domain registration or lookup.
|
||||||
|
mapping func(p *Profile, s string) (mapped string, isBidi bool, err error)
|
||||||
|
|
||||||
|
// bidirule, if specified, checks whether s conforms to the Bidi Rule
|
||||||
|
// defined in RFC 5893.
|
||||||
|
bidirule func(s string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Profile defines the configuration of an IDNA mapper.
|
||||||
|
type Profile struct {
|
||||||
|
options
|
||||||
|
}
|
||||||
|
|
||||||
|
func apply(o *options, opts []Option) {
|
||||||
|
for _, f := range opts {
|
||||||
|
f(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Profile.
|
||||||
|
//
|
||||||
|
// With no options, the returned Profile is the most permissive and equals the
|
||||||
|
// Punycode Profile. Options can be passed to further restrict the Profile. The
|
||||||
|
// MapForLookup and ValidateForRegistration options set a collection of options,
|
||||||
|
// for lookup and registration purposes respectively, which can be tailored by
|
||||||
|
// adding more fine-grained options, where later options override earlier
|
||||||
|
// options.
|
||||||
|
func New(o ...Option) *Profile {
|
||||||
|
p := &Profile{}
|
||||||
|
apply(&p.options, o)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToASCII converts a domain or domain label to its ASCII form. For example,
|
||||||
|
// ToASCII("bücher.example.com") is "xn--bcher-kva.example.com", and
|
||||||
|
// ToASCII("golang") is "golang". If an error is encountered it will return
|
||||||
|
// an error and a (partially) processed result.
|
||||||
|
func (p *Profile) ToASCII(s string) (string, error) {
|
||||||
|
return p.process(s, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUnicode converts a domain or domain label to its Unicode form. For example,
|
||||||
|
// ToUnicode("xn--bcher-kva.example.com") is "bücher.example.com", and
|
||||||
|
// ToUnicode("golang") is "golang". If an error is encountered it will return
|
||||||
|
// an error and a (partially) processed result.
|
||||||
|
func (p *Profile) ToUnicode(s string) (string, error) {
|
||||||
|
pp := *p
|
||||||
|
pp.transitional = false
|
||||||
|
return pp.process(s, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String reports a string with a description of the profile for debugging
|
||||||
|
// purposes. The string format may change with different versions.
|
||||||
|
func (p *Profile) String() string {
|
||||||
|
s := ""
|
||||||
|
if p.transitional {
|
||||||
|
s = "Transitional"
|
||||||
|
} else {
|
||||||
|
s = "NonTransitional"
|
||||||
|
}
|
||||||
|
if p.useSTD3Rules {
|
||||||
|
s += ":UseSTD3Rules"
|
||||||
|
}
|
||||||
|
if p.validateLabels {
|
||||||
|
s += ":ValidateLabels"
|
||||||
|
}
|
||||||
|
if p.verifyDNSLength {
|
||||||
|
s += ":VerifyDNSLength"
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Punycode is a Profile that does raw punycode processing with a minimum
|
||||||
|
// of validation.
|
||||||
|
Punycode *Profile = punycode
|
||||||
|
|
||||||
|
// Lookup is the recommended profile for looking up domain names, according
|
||||||
|
// to Section 5 of RFC 5891. The exact configuration of this profile may
|
||||||
|
// change over time.
|
||||||
|
Lookup *Profile = lookup
|
||||||
|
|
||||||
|
// Display is the recommended profile for displaying domain names.
|
||||||
|
// The configuration of this profile may change over time.
|
||||||
|
Display *Profile = display
|
||||||
|
|
||||||
|
// Registration is the recommended profile for checking whether a given
|
||||||
|
// IDN is valid for registration, according to Section 4 of RFC 5891.
|
||||||
|
Registration *Profile = registration
|
||||||
|
|
||||||
|
punycode = &Profile{}
|
||||||
|
lookup = &Profile{options{
|
||||||
|
transitional: true,
|
||||||
|
useSTD3Rules: true,
|
||||||
|
validateLabels: true,
|
||||||
|
trie: trie,
|
||||||
|
fromPuny: validateFromPunycode,
|
||||||
|
mapping: validateAndMap,
|
||||||
|
bidirule: bidirule.ValidString,
|
||||||
|
}}
|
||||||
|
display = &Profile{options{
|
||||||
|
useSTD3Rules: true,
|
||||||
|
validateLabels: true,
|
||||||
|
trie: trie,
|
||||||
|
fromPuny: validateFromPunycode,
|
||||||
|
mapping: validateAndMap,
|
||||||
|
bidirule: bidirule.ValidString,
|
||||||
|
}}
|
||||||
|
registration = &Profile{options{
|
||||||
|
useSTD3Rules: true,
|
||||||
|
validateLabels: true,
|
||||||
|
verifyDNSLength: true,
|
||||||
|
trie: trie,
|
||||||
|
fromPuny: validateFromPunycode,
|
||||||
|
mapping: validateRegistration,
|
||||||
|
bidirule: bidirule.ValidString,
|
||||||
|
}}
|
||||||
|
|
||||||
|
// TODO: profiles
|
||||||
|
// Register: recommended for approving domain names: don't do any mappings
|
||||||
|
// but rather reject on invalid input. Bundle or block deviation characters.
|
||||||
|
)
|
||||||
|
|
||||||
|
type labelError struct{ label, code_ string }
|
||||||
|
|
||||||
|
func (e labelError) code() string { return e.code_ }
|
||||||
|
func (e labelError) Error() string {
|
||||||
|
return fmt.Sprintf("idna: invalid label %q", e.label)
|
||||||
|
}
|
||||||
|
|
||||||
|
type runeError rune
|
||||||
|
|
||||||
|
func (e runeError) code() string { return "P1" }
|
||||||
|
func (e runeError) Error() string {
|
||||||
|
return fmt.Sprintf("idna: disallowed rune %U", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// process implements the algorithm described in section 4 of UTS #46,
|
||||||
|
// see https://www.unicode.org/reports/tr46.
|
||||||
|
func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||||
|
var err error
|
||||||
|
var isBidi bool
|
||||||
|
if p.mapping != nil {
|
||||||
|
s, isBidi, err = p.mapping(p, s)
|
||||||
|
}
|
||||||
|
// Remove leading empty labels.
|
||||||
|
if p.removeLeadingDots {
|
||||||
|
for ; len(s) > 0 && s[0] == '.'; s = s[1:] {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: allow for a quick check of the tables data.
|
||||||
|
// It seems like we should only create this error on ToASCII, but the
|
||||||
|
// UTS 46 conformance tests suggests we should always check this.
|
||||||
|
if err == nil && p.verifyDNSLength && s == "" {
|
||||||
|
err = &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
labels := labelIter{orig: s}
|
||||||
|
for ; !labels.done(); labels.next() {
|
||||||
|
label := labels.label()
|
||||||
|
if label == "" {
|
||||||
|
// Empty labels are not okay. The label iterator skips the last
|
||||||
|
// label if it is empty.
|
||||||
|
if err == nil && p.verifyDNSLength {
|
||||||
|
err = &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(label, acePrefix) {
|
||||||
|
u, err2 := decode(label[len(acePrefix):])
|
||||||
|
if err2 != nil {
|
||||||
|
if err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
// Spec says keep the old label.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
isBidi = isBidi || bidirule.DirectionString(u) != bidi.LeftToRight
|
||||||
|
labels.set(u)
|
||||||
|
if err == nil && p.validateLabels {
|
||||||
|
err = p.fromPuny(p, u)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
// This should be called on NonTransitional, according to the
|
||||||
|
// spec, but that currently does not have any effect. Use the
|
||||||
|
// original profile to preserve options.
|
||||||
|
err = p.validateLabel(u)
|
||||||
|
}
|
||||||
|
} else if err == nil {
|
||||||
|
err = p.validateLabel(label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isBidi && p.bidirule != nil && err == nil {
|
||||||
|
for labels.reset(); !labels.done(); labels.next() {
|
||||||
|
if !p.bidirule(labels.label()) {
|
||||||
|
err = &labelError{s, "B"}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if toASCII {
|
||||||
|
for labels.reset(); !labels.done(); labels.next() {
|
||||||
|
label := labels.label()
|
||||||
|
if !ascii(label) {
|
||||||
|
a, err2 := encode(acePrefix, label)
|
||||||
|
if err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
label = a
|
||||||
|
labels.set(a)
|
||||||
|
}
|
||||||
|
n := len(label)
|
||||||
|
if p.verifyDNSLength && err == nil && (n == 0 || n > 63) {
|
||||||
|
err = &labelError{label, "A4"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = labels.result()
|
||||||
|
if toASCII && p.verifyDNSLength && err == nil {
|
||||||
|
// Compute the length of the domain name minus the root label and its dot.
|
||||||
|
n := len(s)
|
||||||
|
if n > 0 && s[n-1] == '.' {
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
if len(s) < 1 || n > 253 {
|
||||||
|
err = &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalize(p *Profile, s string) (mapped string, isBidi bool, err error) {
|
||||||
|
// TODO: consider first doing a quick check to see if any of these checks
|
||||||
|
// need to be done. This will make it slower in the general case, but
|
||||||
|
// faster in the common case.
|
||||||
|
mapped = norm.NFC.String(s)
|
||||||
|
isBidi = bidirule.DirectionString(mapped) == bidi.RightToLeft
|
||||||
|
return mapped, isBidi, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateRegistration(p *Profile, s string) (idem string, bidi bool, err error) {
|
||||||
|
// TODO: filter need for normalization in loop below.
|
||||||
|
if !norm.NFC.IsNormalString(s) {
|
||||||
|
return s, false, &labelError{s, "V1"}
|
||||||
|
}
|
||||||
|
for i := 0; i < len(s); {
|
||||||
|
v, sz := trie.lookupString(s[i:])
|
||||||
|
if sz == 0 {
|
||||||
|
return s, bidi, runeError(utf8.RuneError)
|
||||||
|
}
|
||||||
|
bidi = bidi || info(v).isBidi(s[i:])
|
||||||
|
// Copy bytes not copied so far.
|
||||||
|
switch p.simplify(info(v).category()) {
|
||||||
|
// TODO: handle the NV8 defined in the Unicode idna data set to allow
|
||||||
|
// for strict conformance to IDNA2008.
|
||||||
|
case valid, deviation:
|
||||||
|
case disallowed, mapped, unknown, ignored:
|
||||||
|
r, _ := utf8.DecodeRuneInString(s[i:])
|
||||||
|
return s, bidi, runeError(r)
|
||||||
|
}
|
||||||
|
i += sz
|
||||||
|
}
|
||||||
|
return s, bidi, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c info) isBidi(s string) bool {
|
||||||
|
if !c.isMapped() {
|
||||||
|
return c&attributesMask == rtl
|
||||||
|
}
|
||||||
|
// TODO: also store bidi info for mapped data. This is possible, but a bit
|
||||||
|
// cumbersome and not for the common case.
|
||||||
|
p, _ := bidi.LookupString(s)
|
||||||
|
switch p.Class() {
|
||||||
|
case bidi.R, bidi.AL, bidi.AN:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateAndMap(p *Profile, s string) (vm string, bidi bool, err error) {
|
||||||
|
var (
|
||||||
|
b []byte
|
||||||
|
k int
|
||||||
|
)
|
||||||
|
// combinedInfoBits contains the or-ed bits of all runes. We use this
|
||||||
|
// to derive the mayNeedNorm bit later. This may trigger normalization
|
||||||
|
// overeagerly, but it will not do so in the common case. The end result
|
||||||
|
// is another 10% saving on BenchmarkProfile for the common case.
|
||||||
|
var combinedInfoBits info
|
||||||
|
for i := 0; i < len(s); {
|
||||||
|
v, sz := trie.lookupString(s[i:])
|
||||||
|
if sz == 0 {
|
||||||
|
b = append(b, s[k:i]...)
|
||||||
|
b = append(b, "\ufffd"...)
|
||||||
|
k = len(s)
|
||||||
|
if err == nil {
|
||||||
|
err = runeError(utf8.RuneError)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
combinedInfoBits |= info(v)
|
||||||
|
bidi = bidi || info(v).isBidi(s[i:])
|
||||||
|
start := i
|
||||||
|
i += sz
|
||||||
|
// Copy bytes not copied so far.
|
||||||
|
switch p.simplify(info(v).category()) {
|
||||||
|
case valid:
|
||||||
|
continue
|
||||||
|
case disallowed:
|
||||||
|
if err == nil {
|
||||||
|
r, _ := utf8.DecodeRuneInString(s[start:])
|
||||||
|
err = runeError(r)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case mapped, deviation:
|
||||||
|
b = append(b, s[k:start]...)
|
||||||
|
b = info(v).appendMapping(b, s[start:i])
|
||||||
|
case ignored:
|
||||||
|
b = append(b, s[k:start]...)
|
||||||
|
// drop the rune
|
||||||
|
case unknown:
|
||||||
|
b = append(b, s[k:start]...)
|
||||||
|
b = append(b, "\ufffd"...)
|
||||||
|
}
|
||||||
|
k = i
|
||||||
|
}
|
||||||
|
if k == 0 {
|
||||||
|
// No changes so far.
|
||||||
|
if combinedInfoBits&mayNeedNorm != 0 {
|
||||||
|
s = norm.NFC.String(s)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
b = append(b, s[k:]...)
|
||||||
|
if norm.NFC.QuickSpan(b) != len(b) {
|
||||||
|
b = norm.NFC.Bytes(b)
|
||||||
|
}
|
||||||
|
// TODO: the punycode converters require strings as input.
|
||||||
|
s = string(b)
|
||||||
|
}
|
||||||
|
return s, bidi, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// A labelIter allows iterating over domain name labels.
|
||||||
|
type labelIter struct {
|
||||||
|
orig string
|
||||||
|
slice []string
|
||||||
|
curStart int
|
||||||
|
curEnd int
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) reset() {
|
||||||
|
l.curStart = 0
|
||||||
|
l.curEnd = 0
|
||||||
|
l.i = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) done() bool {
|
||||||
|
return l.curStart >= len(l.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) result() string {
|
||||||
|
if l.slice != nil {
|
||||||
|
return strings.Join(l.slice, ".")
|
||||||
|
}
|
||||||
|
return l.orig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) label() string {
|
||||||
|
if l.slice != nil {
|
||||||
|
return l.slice[l.i]
|
||||||
|
}
|
||||||
|
p := strings.IndexByte(l.orig[l.curStart:], '.')
|
||||||
|
l.curEnd = l.curStart + p
|
||||||
|
if p == -1 {
|
||||||
|
l.curEnd = len(l.orig)
|
||||||
|
}
|
||||||
|
return l.orig[l.curStart:l.curEnd]
|
||||||
|
}
|
||||||
|
|
||||||
|
// next sets the value to the next label. It skips the last label if it is empty.
|
||||||
|
func (l *labelIter) next() {
|
||||||
|
l.i++
|
||||||
|
if l.slice != nil {
|
||||||
|
if l.i >= len(l.slice) || l.i == len(l.slice)-1 && l.slice[l.i] == "" {
|
||||||
|
l.curStart = len(l.orig)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l.curStart = l.curEnd + 1
|
||||||
|
if l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
|
||||||
|
l.curStart = len(l.orig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) set(s string) {
|
||||||
|
if l.slice == nil {
|
||||||
|
l.slice = strings.Split(l.orig, ".")
|
||||||
|
}
|
||||||
|
l.slice[l.i] = s
|
||||||
|
}
|
||||||
|
|
||||||
|
// acePrefix is the ASCII Compatible Encoding prefix.
|
||||||
|
const acePrefix = "xn--"
|
||||||
|
|
||||||
|
func (p *Profile) simplify(cat category) category {
|
||||||
|
switch cat {
|
||||||
|
case disallowedSTD3Mapped:
|
||||||
|
if p.useSTD3Rules {
|
||||||
|
cat = disallowed
|
||||||
|
} else {
|
||||||
|
cat = mapped
|
||||||
|
}
|
||||||
|
case disallowedSTD3Valid:
|
||||||
|
if p.useSTD3Rules {
|
||||||
|
cat = disallowed
|
||||||
|
} else {
|
||||||
|
cat = valid
|
||||||
|
}
|
||||||
|
case deviation:
|
||||||
|
if !p.transitional {
|
||||||
|
cat = valid
|
||||||
|
}
|
||||||
|
case validNV8, validXV8:
|
||||||
|
// TODO: handle V2008
|
||||||
|
cat = valid
|
||||||
|
}
|
||||||
|
return cat
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateFromPunycode(p *Profile, s string) error {
|
||||||
|
if !norm.NFC.IsNormalString(s) {
|
||||||
|
return &labelError{s, "V1"}
|
||||||
|
}
|
||||||
|
// TODO: detect whether string may have to be normalized in the following
|
||||||
|
// loop.
|
||||||
|
for i := 0; i < len(s); {
|
||||||
|
v, sz := trie.lookupString(s[i:])
|
||||||
|
if sz == 0 {
|
||||||
|
return runeError(utf8.RuneError)
|
||||||
|
}
|
||||||
|
if c := p.simplify(info(v).category()); c != valid && c != deviation {
|
||||||
|
return &labelError{s, "V6"}
|
||||||
|
}
|
||||||
|
i += sz
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
zwnj = "\u200c"
|
||||||
|
zwj = "\u200d"
|
||||||
|
)
|
||||||
|
|
||||||
|
type joinState int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
stateStart joinState = iota
|
||||||
|
stateVirama
|
||||||
|
stateBefore
|
||||||
|
stateBeforeVirama
|
||||||
|
stateAfter
|
||||||
|
stateFAIL
|
||||||
|
)
|
||||||
|
|
||||||
|
var joinStates = [][numJoinTypes]joinState{
|
||||||
|
stateStart: {
|
||||||
|
joiningL: stateBefore,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
joinZWNJ: stateFAIL,
|
||||||
|
joinZWJ: stateFAIL,
|
||||||
|
joinVirama: stateVirama,
|
||||||
|
},
|
||||||
|
stateVirama: {
|
||||||
|
joiningL: stateBefore,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
},
|
||||||
|
stateBefore: {
|
||||||
|
joiningL: stateBefore,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
joiningT: stateBefore,
|
||||||
|
joinZWNJ: stateAfter,
|
||||||
|
joinZWJ: stateFAIL,
|
||||||
|
joinVirama: stateBeforeVirama,
|
||||||
|
},
|
||||||
|
stateBeforeVirama: {
|
||||||
|
joiningL: stateBefore,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
joiningT: stateBefore,
|
||||||
|
},
|
||||||
|
stateAfter: {
|
||||||
|
joiningL: stateFAIL,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
joiningT: stateAfter,
|
||||||
|
joiningR: stateStart,
|
||||||
|
joinZWNJ: stateFAIL,
|
||||||
|
joinZWJ: stateFAIL,
|
||||||
|
joinVirama: stateAfter, // no-op as we can't accept joiners here
|
||||||
|
},
|
||||||
|
stateFAIL: {
|
||||||
|
0: stateFAIL,
|
||||||
|
joiningL: stateFAIL,
|
||||||
|
joiningD: stateFAIL,
|
||||||
|
joiningT: stateFAIL,
|
||||||
|
joiningR: stateFAIL,
|
||||||
|
joinZWNJ: stateFAIL,
|
||||||
|
joinZWJ: stateFAIL,
|
||||||
|
joinVirama: stateFAIL,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateLabel validates the criteria from Section 4.1. Item 1, 4, and 6 are
|
||||||
|
// already implicitly satisfied by the overall implementation.
|
||||||
|
func (p *Profile) validateLabel(s string) (err error) {
|
||||||
|
if s == "" {
|
||||||
|
if p.verifyDNSLength {
|
||||||
|
return &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !p.validateLabels {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
trie := p.trie // p.validateLabels is only set if trie is set.
|
||||||
|
if len(s) > 4 && s[2] == '-' && s[3] == '-' {
|
||||||
|
return &labelError{s, "V2"}
|
||||||
|
}
|
||||||
|
if s[0] == '-' || s[len(s)-1] == '-' {
|
||||||
|
return &labelError{s, "V3"}
|
||||||
|
}
|
||||||
|
// TODO: merge the use of this in the trie.
|
||||||
|
v, sz := trie.lookupString(s)
|
||||||
|
x := info(v)
|
||||||
|
if x.isModifier() {
|
||||||
|
return &labelError{s, "V5"}
|
||||||
|
}
|
||||||
|
// Quickly return in the absence of zero-width (non) joiners.
|
||||||
|
if strings.Index(s, zwj) == -1 && strings.Index(s, zwnj) == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
st := stateStart
|
||||||
|
for i := 0; ; {
|
||||||
|
jt := x.joinType()
|
||||||
|
if s[i:i+sz] == zwj {
|
||||||
|
jt = joinZWJ
|
||||||
|
} else if s[i:i+sz] == zwnj {
|
||||||
|
jt = joinZWNJ
|
||||||
|
}
|
||||||
|
st = joinStates[st][jt]
|
||||||
|
if x.isViramaModifier() {
|
||||||
|
st = joinStates[st][joinVirama]
|
||||||
|
}
|
||||||
|
if i += sz; i == len(s) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
v, sz = trie.lookupString(s[i:])
|
||||||
|
x = info(v)
|
||||||
|
}
|
||||||
|
if st == stateFAIL || st == stateAfter {
|
||||||
|
return &labelError{s, "C"}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ascii(s string) bool {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] >= utf8.RuneSelf {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
683
vendor/golang.org/x/net/idna/idna9.0.0.go
generated
vendored
Normal file
683
vendor/golang.org/x/net/idna/idna9.0.0.go
generated
vendored
Normal file
@ -0,0 +1,683 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//go:build !go1.10
|
||||||
|
// +build !go1.10
|
||||||
|
|
||||||
|
// Package idna implements IDNA2008 using the compatibility processing
|
||||||
|
// defined by UTS (Unicode Technical Standard) #46, which defines a standard to
|
||||||
|
// deal with the transition from IDNA2003.
|
||||||
|
//
|
||||||
|
// IDNA2008 (Internationalized Domain Names for Applications), is defined in RFC
|
||||||
|
// 5890, RFC 5891, RFC 5892, RFC 5893 and RFC 5894.
|
||||||
|
// UTS #46 is defined in https://www.unicode.org/reports/tr46.
|
||||||
|
// See https://unicode.org/cldr/utility/idna.jsp for a visualization of the
|
||||||
|
// differences between these two standards.
|
||||||
|
package idna // import "golang.org/x/net/idna"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"golang.org/x/text/secure/bidirule"
|
||||||
|
"golang.org/x/text/unicode/norm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NOTE: Unlike common practice in Go APIs, the functions will return a
|
||||||
|
// sanitized domain name in case of errors. Browsers sometimes use a partially
|
||||||
|
// evaluated string as lookup.
|
||||||
|
// TODO: the current error handling is, in my opinion, the least opinionated.
|
||||||
|
// Other strategies are also viable, though:
|
||||||
|
// Option 1) Return an empty string in case of error, but allow the user to
|
||||||
|
// specify explicitly which errors to ignore.
|
||||||
|
// Option 2) Return the partially evaluated string if it is itself a valid
|
||||||
|
// string, otherwise return the empty string in case of error.
|
||||||
|
// Option 3) Option 1 and 2.
|
||||||
|
// Option 4) Always return an empty string for now and implement Option 1 as
|
||||||
|
// needed, and document that the return string may not be empty in case of
|
||||||
|
// error in the future.
|
||||||
|
// I think Option 1 is best, but it is quite opinionated.
|
||||||
|
|
||||||
|
// ToASCII is a wrapper for Punycode.ToASCII.
|
||||||
|
func ToASCII(s string) (string, error) {
|
||||||
|
return Punycode.process(s, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUnicode is a wrapper for Punycode.ToUnicode.
|
||||||
|
func ToUnicode(s string) (string, error) {
|
||||||
|
return Punycode.process(s, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Option configures a Profile at creation time.
|
||||||
|
type Option func(*options)
|
||||||
|
|
||||||
|
// Transitional sets a Profile to use the Transitional mapping as defined in UTS
|
||||||
|
// #46. This will cause, for example, "ß" to be mapped to "ss". Using the
|
||||||
|
// transitional mapping provides a compromise between IDNA2003 and IDNA2008
|
||||||
|
// compatibility. It is used by most browsers when resolving domain names. This
|
||||||
|
// option is only meaningful if combined with MapForLookup.
|
||||||
|
func Transitional(transitional bool) Option {
|
||||||
|
return func(o *options) { o.transitional = true }
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyDNSLength sets whether a Profile should fail if any of the IDN parts
|
||||||
|
// are longer than allowed by the RFC.
|
||||||
|
func VerifyDNSLength(verify bool) Option {
|
||||||
|
return func(o *options) { o.verifyDNSLength = verify }
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveLeadingDots removes leading label separators. Leading runes that map to
|
||||||
|
// dots, such as U+3002 IDEOGRAPHIC FULL STOP, are removed as well.
|
||||||
|
//
|
||||||
|
// This is the behavior suggested by the UTS #46 and is adopted by some
|
||||||
|
// browsers.
|
||||||
|
func RemoveLeadingDots(remove bool) Option {
|
||||||
|
return func(o *options) { o.removeLeadingDots = remove }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateLabels sets whether to check the mandatory label validation criteria
|
||||||
|
// as defined in Section 5.4 of RFC 5891. This includes testing for correct use
|
||||||
|
// of hyphens ('-'), normalization, validity of runes, and the context rules.
|
||||||
|
func ValidateLabels(enable bool) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
// Don't override existing mappings, but set one that at least checks
|
||||||
|
// normalization if it is not set.
|
||||||
|
if o.mapping == nil && enable {
|
||||||
|
o.mapping = normalize
|
||||||
|
}
|
||||||
|
o.trie = trie
|
||||||
|
o.validateLabels = enable
|
||||||
|
o.fromPuny = validateFromPunycode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrictDomainName limits the set of permissable ASCII characters to those
|
||||||
|
// allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the
|
||||||
|
// hyphen). This is set by default for MapForLookup and ValidateForRegistration.
|
||||||
|
//
|
||||||
|
// This option is useful, for instance, for browsers that allow characters
|
||||||
|
// outside this range, for example a '_' (U+005F LOW LINE). See
|
||||||
|
// http://www.rfc-editor.org/std/std3.txt for more details This option
|
||||||
|
// corresponds to the UseSTD3ASCIIRules option in UTS #46.
|
||||||
|
func StrictDomainName(use bool) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.trie = trie
|
||||||
|
o.useSTD3Rules = use
|
||||||
|
o.fromPuny = validateFromPunycode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: the following options pull in tables. The tables should not be linked
|
||||||
|
// in as long as the options are not used.
|
||||||
|
|
||||||
|
// BidiRule enables the Bidi rule as defined in RFC 5893. Any application
|
||||||
|
// that relies on proper validation of labels should include this rule.
|
||||||
|
func BidiRule() Option {
|
||||||
|
return func(o *options) { o.bidirule = bidirule.ValidString }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateForRegistration sets validation options to verify that a given IDN is
|
||||||
|
// properly formatted for registration as defined by Section 4 of RFC 5891.
|
||||||
|
func ValidateForRegistration() Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.mapping = validateRegistration
|
||||||
|
StrictDomainName(true)(o)
|
||||||
|
ValidateLabels(true)(o)
|
||||||
|
VerifyDNSLength(true)(o)
|
||||||
|
BidiRule()(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapForLookup sets validation and mapping options such that a given IDN is
|
||||||
|
// transformed for domain name lookup according to the requirements set out in
|
||||||
|
// Section 5 of RFC 5891. The mappings follow the recommendations of RFC 5894,
|
||||||
|
// RFC 5895 and UTS 46. It does not add the Bidi Rule. Use the BidiRule option
|
||||||
|
// to add this check.
|
||||||
|
//
|
||||||
|
// The mappings include normalization and mapping case, width and other
|
||||||
|
// compatibility mappings.
|
||||||
|
func MapForLookup() Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.mapping = validateAndMap
|
||||||
|
StrictDomainName(true)(o)
|
||||||
|
ValidateLabels(true)(o)
|
||||||
|
RemoveLeadingDots(true)(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type options struct {
|
||||||
|
transitional bool
|
||||||
|
useSTD3Rules bool
|
||||||
|
validateLabels bool
|
||||||
|
verifyDNSLength bool
|
||||||
|
removeLeadingDots bool
|
||||||
|
|
||||||
|
trie *idnaTrie
|
||||||
|
|
||||||
|
// fromPuny calls validation rules when converting A-labels to U-labels.
|
||||||
|
fromPuny func(p *Profile, s string) error
|
||||||
|
|
||||||
|
// mapping implements a validation and mapping step as defined in RFC 5895
|
||||||
|
// or UTS 46, tailored to, for example, domain registration or lookup.
|
||||||
|
mapping func(p *Profile, s string) (string, error)
|
||||||
|
|
||||||
|
// bidirule, if specified, checks whether s conforms to the Bidi Rule
|
||||||
|
// defined in RFC 5893.
|
||||||
|
bidirule func(s string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Profile defines the configuration of a IDNA mapper.
|
||||||
|
type Profile struct {
|
||||||
|
options
|
||||||
|
}
|
||||||
|
|
||||||
|
func apply(o *options, opts []Option) {
|
||||||
|
for _, f := range opts {
|
||||||
|
f(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Profile.
|
||||||
|
//
|
||||||
|
// With no options, the returned Profile is the most permissive and equals the
|
||||||
|
// Punycode Profile. Options can be passed to further restrict the Profile. The
|
||||||
|
// MapForLookup and ValidateForRegistration options set a collection of options,
|
||||||
|
// for lookup and registration purposes respectively, which can be tailored by
|
||||||
|
// adding more fine-grained options, where later options override earlier
|
||||||
|
// options.
|
||||||
|
func New(o ...Option) *Profile {
|
||||||
|
p := &Profile{}
|
||||||
|
apply(&p.options, o)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToASCII converts a domain or domain label to its ASCII form. For example,
|
||||||
|
// ToASCII("bücher.example.com") is "xn--bcher-kva.example.com", and
|
||||||
|
// ToASCII("golang") is "golang". If an error is encountered it will return
|
||||||
|
// an error and a (partially) processed result.
|
||||||
|
func (p *Profile) ToASCII(s string) (string, error) {
|
||||||
|
return p.process(s, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUnicode converts a domain or domain label to its Unicode form. For example,
|
||||||
|
// ToUnicode("xn--bcher-kva.example.com") is "bücher.example.com", and
|
||||||
|
// ToUnicode("golang") is "golang". If an error is encountered it will return
|
||||||
|
// an error and a (partially) processed result.
|
||||||
|
func (p *Profile) ToUnicode(s string) (string, error) {
|
||||||
|
pp := *p
|
||||||
|
pp.transitional = false
|
||||||
|
return pp.process(s, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String reports a string with a description of the profile for debugging
|
||||||
|
// purposes. The string format may change with different versions.
|
||||||
|
func (p *Profile) String() string {
|
||||||
|
s := ""
|
||||||
|
if p.transitional {
|
||||||
|
s = "Transitional"
|
||||||
|
} else {
|
||||||
|
s = "NonTransitional"
|
||||||
|
}
|
||||||
|
if p.useSTD3Rules {
|
||||||
|
s += ":UseSTD3Rules"
|
||||||
|
}
|
||||||
|
if p.validateLabels {
|
||||||
|
s += ":ValidateLabels"
|
||||||
|
}
|
||||||
|
if p.verifyDNSLength {
|
||||||
|
s += ":VerifyDNSLength"
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Punycode is a Profile that does raw punycode processing with a minimum
|
||||||
|
// of validation.
|
||||||
|
Punycode *Profile = punycode
|
||||||
|
|
||||||
|
// Lookup is the recommended profile for looking up domain names, according
|
||||||
|
// to Section 5 of RFC 5891. The exact configuration of this profile may
|
||||||
|
// change over time.
|
||||||
|
Lookup *Profile = lookup
|
||||||
|
|
||||||
|
// Display is the recommended profile for displaying domain names.
|
||||||
|
// The configuration of this profile may change over time.
|
||||||
|
Display *Profile = display
|
||||||
|
|
||||||
|
// Registration is the recommended profile for checking whether a given
|
||||||
|
// IDN is valid for registration, according to Section 4 of RFC 5891.
|
||||||
|
Registration *Profile = registration
|
||||||
|
|
||||||
|
punycode = &Profile{}
|
||||||
|
lookup = &Profile{options{
|
||||||
|
transitional: true,
|
||||||
|
useSTD3Rules: true,
|
||||||
|
validateLabels: true,
|
||||||
|
removeLeadingDots: true,
|
||||||
|
trie: trie,
|
||||||
|
fromPuny: validateFromPunycode,
|
||||||
|
mapping: validateAndMap,
|
||||||
|
bidirule: bidirule.ValidString,
|
||||||
|
}}
|
||||||
|
display = &Profile{options{
|
||||||
|
useSTD3Rules: true,
|
||||||
|
validateLabels: true,
|
||||||
|
removeLeadingDots: true,
|
||||||
|
trie: trie,
|
||||||
|
fromPuny: validateFromPunycode,
|
||||||
|
mapping: validateAndMap,
|
||||||
|
bidirule: bidirule.ValidString,
|
||||||
|
}}
|
||||||
|
registration = &Profile{options{
|
||||||
|
useSTD3Rules: true,
|
||||||
|
validateLabels: true,
|
||||||
|
verifyDNSLength: true,
|
||||||
|
trie: trie,
|
||||||
|
fromPuny: validateFromPunycode,
|
||||||
|
mapping: validateRegistration,
|
||||||
|
bidirule: bidirule.ValidString,
|
||||||
|
}}
|
||||||
|
|
||||||
|
// TODO: profiles
|
||||||
|
// Register: recommended for approving domain names: don't do any mappings
|
||||||
|
// but rather reject on invalid input. Bundle or block deviation characters.
|
||||||
|
)
|
||||||
|
|
||||||
|
type labelError struct{ label, code_ string }
|
||||||
|
|
||||||
|
func (e labelError) code() string { return e.code_ }
|
||||||
|
func (e labelError) Error() string {
|
||||||
|
return fmt.Sprintf("idna: invalid label %q", e.label)
|
||||||
|
}
|
||||||
|
|
||||||
|
type runeError rune
|
||||||
|
|
||||||
|
func (e runeError) code() string { return "P1" }
|
||||||
|
func (e runeError) Error() string {
|
||||||
|
return fmt.Sprintf("idna: disallowed rune %U", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// process implements the algorithm described in section 4 of UTS #46,
|
||||||
|
// see https://www.unicode.org/reports/tr46.
|
||||||
|
func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||||
|
var err error
|
||||||
|
if p.mapping != nil {
|
||||||
|
s, err = p.mapping(p, s)
|
||||||
|
}
|
||||||
|
// Remove leading empty labels.
|
||||||
|
if p.removeLeadingDots {
|
||||||
|
for ; len(s) > 0 && s[0] == '.'; s = s[1:] {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// It seems like we should only create this error on ToASCII, but the
|
||||||
|
// UTS 46 conformance tests suggests we should always check this.
|
||||||
|
if err == nil && p.verifyDNSLength && s == "" {
|
||||||
|
err = &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
labels := labelIter{orig: s}
|
||||||
|
for ; !labels.done(); labels.next() {
|
||||||
|
label := labels.label()
|
||||||
|
if label == "" {
|
||||||
|
// Empty labels are not okay. The label iterator skips the last
|
||||||
|
// label if it is empty.
|
||||||
|
if err == nil && p.verifyDNSLength {
|
||||||
|
err = &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(label, acePrefix) {
|
||||||
|
u, err2 := decode(label[len(acePrefix):])
|
||||||
|
if err2 != nil {
|
||||||
|
if err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
// Spec says keep the old label.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
labels.set(u)
|
||||||
|
if err == nil && p.validateLabels {
|
||||||
|
err = p.fromPuny(p, u)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
// This should be called on NonTransitional, according to the
|
||||||
|
// spec, but that currently does not have any effect. Use the
|
||||||
|
// original profile to preserve options.
|
||||||
|
err = p.validateLabel(u)
|
||||||
|
}
|
||||||
|
} else if err == nil {
|
||||||
|
err = p.validateLabel(label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if toASCII {
|
||||||
|
for labels.reset(); !labels.done(); labels.next() {
|
||||||
|
label := labels.label()
|
||||||
|
if !ascii(label) {
|
||||||
|
a, err2 := encode(acePrefix, label)
|
||||||
|
if err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
label = a
|
||||||
|
labels.set(a)
|
||||||
|
}
|
||||||
|
n := len(label)
|
||||||
|
if p.verifyDNSLength && err == nil && (n == 0 || n > 63) {
|
||||||
|
err = &labelError{label, "A4"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = labels.result()
|
||||||
|
if toASCII && p.verifyDNSLength && err == nil {
|
||||||
|
// Compute the length of the domain name minus the root label and its dot.
|
||||||
|
n := len(s)
|
||||||
|
if n > 0 && s[n-1] == '.' {
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
if len(s) < 1 || n > 253 {
|
||||||
|
err = &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalize(p *Profile, s string) (string, error) {
|
||||||
|
return norm.NFC.String(s), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateRegistration(p *Profile, s string) (string, error) {
|
||||||
|
if !norm.NFC.IsNormalString(s) {
|
||||||
|
return s, &labelError{s, "V1"}
|
||||||
|
}
|
||||||
|
for i := 0; i < len(s); {
|
||||||
|
v, sz := trie.lookupString(s[i:])
|
||||||
|
// Copy bytes not copied so far.
|
||||||
|
switch p.simplify(info(v).category()) {
|
||||||
|
// TODO: handle the NV8 defined in the Unicode idna data set to allow
|
||||||
|
// for strict conformance to IDNA2008.
|
||||||
|
case valid, deviation:
|
||||||
|
case disallowed, mapped, unknown, ignored:
|
||||||
|
r, _ := utf8.DecodeRuneInString(s[i:])
|
||||||
|
return s, runeError(r)
|
||||||
|
}
|
||||||
|
i += sz
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateAndMap(p *Profile, s string) (string, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
b []byte
|
||||||
|
k int
|
||||||
|
)
|
||||||
|
for i := 0; i < len(s); {
|
||||||
|
v, sz := trie.lookupString(s[i:])
|
||||||
|
start := i
|
||||||
|
i += sz
|
||||||
|
// Copy bytes not copied so far.
|
||||||
|
switch p.simplify(info(v).category()) {
|
||||||
|
case valid:
|
||||||
|
continue
|
||||||
|
case disallowed:
|
||||||
|
if err == nil {
|
||||||
|
r, _ := utf8.DecodeRuneInString(s[start:])
|
||||||
|
err = runeError(r)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case mapped, deviation:
|
||||||
|
b = append(b, s[k:start]...)
|
||||||
|
b = info(v).appendMapping(b, s[start:i])
|
||||||
|
case ignored:
|
||||||
|
b = append(b, s[k:start]...)
|
||||||
|
// drop the rune
|
||||||
|
case unknown:
|
||||||
|
b = append(b, s[k:start]...)
|
||||||
|
b = append(b, "\ufffd"...)
|
||||||
|
}
|
||||||
|
k = i
|
||||||
|
}
|
||||||
|
if k == 0 {
|
||||||
|
// No changes so far.
|
||||||
|
s = norm.NFC.String(s)
|
||||||
|
} else {
|
||||||
|
b = append(b, s[k:]...)
|
||||||
|
if norm.NFC.QuickSpan(b) != len(b) {
|
||||||
|
b = norm.NFC.Bytes(b)
|
||||||
|
}
|
||||||
|
// TODO: the punycode converters require strings as input.
|
||||||
|
s = string(b)
|
||||||
|
}
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// A labelIter allows iterating over domain name labels.
|
||||||
|
type labelIter struct {
|
||||||
|
orig string
|
||||||
|
slice []string
|
||||||
|
curStart int
|
||||||
|
curEnd int
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) reset() {
|
||||||
|
l.curStart = 0
|
||||||
|
l.curEnd = 0
|
||||||
|
l.i = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) done() bool {
|
||||||
|
return l.curStart >= len(l.orig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) result() string {
|
||||||
|
if l.slice != nil {
|
||||||
|
return strings.Join(l.slice, ".")
|
||||||
|
}
|
||||||
|
return l.orig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) label() string {
|
||||||
|
if l.slice != nil {
|
||||||
|
return l.slice[l.i]
|
||||||
|
}
|
||||||
|
p := strings.IndexByte(l.orig[l.curStart:], '.')
|
||||||
|
l.curEnd = l.curStart + p
|
||||||
|
if p == -1 {
|
||||||
|
l.curEnd = len(l.orig)
|
||||||
|
}
|
||||||
|
return l.orig[l.curStart:l.curEnd]
|
||||||
|
}
|
||||||
|
|
||||||
|
// next sets the value to the next label. It skips the last label if it is empty.
|
||||||
|
func (l *labelIter) next() {
|
||||||
|
l.i++
|
||||||
|
if l.slice != nil {
|
||||||
|
if l.i >= len(l.slice) || l.i == len(l.slice)-1 && l.slice[l.i] == "" {
|
||||||
|
l.curStart = len(l.orig)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l.curStart = l.curEnd + 1
|
||||||
|
if l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
|
||||||
|
l.curStart = len(l.orig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *labelIter) set(s string) {
|
||||||
|
if l.slice == nil {
|
||||||
|
l.slice = strings.Split(l.orig, ".")
|
||||||
|
}
|
||||||
|
l.slice[l.i] = s
|
||||||
|
}
|
||||||
|
|
||||||
|
// acePrefix is the ASCII Compatible Encoding prefix.
|
||||||
|
const acePrefix = "xn--"
|
||||||
|
|
||||||
|
func (p *Profile) simplify(cat category) category {
|
||||||
|
switch cat {
|
||||||
|
case disallowedSTD3Mapped:
|
||||||
|
if p.useSTD3Rules {
|
||||||
|
cat = disallowed
|
||||||
|
} else {
|
||||||
|
cat = mapped
|
||||||
|
}
|
||||||
|
case disallowedSTD3Valid:
|
||||||
|
if p.useSTD3Rules {
|
||||||
|
cat = disallowed
|
||||||
|
} else {
|
||||||
|
cat = valid
|
||||||
|
}
|
||||||
|
case deviation:
|
||||||
|
if !p.transitional {
|
||||||
|
cat = valid
|
||||||
|
}
|
||||||
|
case validNV8, validXV8:
|
||||||
|
// TODO: handle V2008
|
||||||
|
cat = valid
|
||||||
|
}
|
||||||
|
return cat
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateFromPunycode(p *Profile, s string) error {
|
||||||
|
if !norm.NFC.IsNormalString(s) {
|
||||||
|
return &labelError{s, "V1"}
|
||||||
|
}
|
||||||
|
for i := 0; i < len(s); {
|
||||||
|
v, sz := trie.lookupString(s[i:])
|
||||||
|
if c := p.simplify(info(v).category()); c != valid && c != deviation {
|
||||||
|
return &labelError{s, "V6"}
|
||||||
|
}
|
||||||
|
i += sz
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
zwnj = "\u200c"
|
||||||
|
zwj = "\u200d"
|
||||||
|
)
|
||||||
|
|
||||||
|
type joinState int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
stateStart joinState = iota
|
||||||
|
stateVirama
|
||||||
|
stateBefore
|
||||||
|
stateBeforeVirama
|
||||||
|
stateAfter
|
||||||
|
stateFAIL
|
||||||
|
)
|
||||||
|
|
||||||
|
var joinStates = [][numJoinTypes]joinState{
|
||||||
|
stateStart: {
|
||||||
|
joiningL: stateBefore,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
joinZWNJ: stateFAIL,
|
||||||
|
joinZWJ: stateFAIL,
|
||||||
|
joinVirama: stateVirama,
|
||||||
|
},
|
||||||
|
stateVirama: {
|
||||||
|
joiningL: stateBefore,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
},
|
||||||
|
stateBefore: {
|
||||||
|
joiningL: stateBefore,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
joiningT: stateBefore,
|
||||||
|
joinZWNJ: stateAfter,
|
||||||
|
joinZWJ: stateFAIL,
|
||||||
|
joinVirama: stateBeforeVirama,
|
||||||
|
},
|
||||||
|
stateBeforeVirama: {
|
||||||
|
joiningL: stateBefore,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
joiningT: stateBefore,
|
||||||
|
},
|
||||||
|
stateAfter: {
|
||||||
|
joiningL: stateFAIL,
|
||||||
|
joiningD: stateBefore,
|
||||||
|
joiningT: stateAfter,
|
||||||
|
joiningR: stateStart,
|
||||||
|
joinZWNJ: stateFAIL,
|
||||||
|
joinZWJ: stateFAIL,
|
||||||
|
joinVirama: stateAfter, // no-op as we can't accept joiners here
|
||||||
|
},
|
||||||
|
stateFAIL: {
|
||||||
|
0: stateFAIL,
|
||||||
|
joiningL: stateFAIL,
|
||||||
|
joiningD: stateFAIL,
|
||||||
|
joiningT: stateFAIL,
|
||||||
|
joiningR: stateFAIL,
|
||||||
|
joinZWNJ: stateFAIL,
|
||||||
|
joinZWJ: stateFAIL,
|
||||||
|
joinVirama: stateFAIL,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateLabel validates the criteria from Section 4.1. Item 1, 4, and 6 are
|
||||||
|
// already implicitly satisfied by the overall implementation.
|
||||||
|
func (p *Profile) validateLabel(s string) error {
|
||||||
|
if s == "" {
|
||||||
|
if p.verifyDNSLength {
|
||||||
|
return &labelError{s, "A4"}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if p.bidirule != nil && !p.bidirule(s) {
|
||||||
|
return &labelError{s, "B"}
|
||||||
|
}
|
||||||
|
if !p.validateLabels {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
trie := p.trie // p.validateLabels is only set if trie is set.
|
||||||
|
if len(s) > 4 && s[2] == '-' && s[3] == '-' {
|
||||||
|
return &labelError{s, "V2"}
|
||||||
|
}
|
||||||
|
if s[0] == '-' || s[len(s)-1] == '-' {
|
||||||
|
return &labelError{s, "V3"}
|
||||||
|
}
|
||||||
|
// TODO: merge the use of this in the trie.
|
||||||
|
v, sz := trie.lookupString(s)
|
||||||
|
x := info(v)
|
||||||
|
if x.isModifier() {
|
||||||
|
return &labelError{s, "V5"}
|
||||||
|
}
|
||||||
|
// Quickly return in the absence of zero-width (non) joiners.
|
||||||
|
if strings.Index(s, zwj) == -1 && strings.Index(s, zwnj) == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
st := stateStart
|
||||||
|
for i := 0; ; {
|
||||||
|
jt := x.joinType()
|
||||||
|
if s[i:i+sz] == zwj {
|
||||||
|
jt = joinZWJ
|
||||||
|
} else if s[i:i+sz] == zwnj {
|
||||||
|
jt = joinZWNJ
|
||||||
|
}
|
||||||
|
st = joinStates[st][jt]
|
||||||
|
if x.isViramaModifier() {
|
||||||
|
st = joinStates[st][joinVirama]
|
||||||
|
}
|
||||||
|
if i += sz; i == len(s) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
v, sz = trie.lookupString(s[i:])
|
||||||
|
x = info(v)
|
||||||
|
}
|
||||||
|
if st == stateFAIL || st == stateAfter {
|
||||||
|
return &labelError{s, "C"}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ascii(s string) bool {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] >= utf8.RuneSelf {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
203
vendor/golang.org/x/net/idna/punycode.go
generated
vendored
Normal file
203
vendor/golang.org/x/net/idna/punycode.go
generated
vendored
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
// 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)
|
||||||
|
}
|
4560
vendor/golang.org/x/net/idna/tables10.0.0.go
generated
vendored
Normal file
4560
vendor/golang.org/x/net/idna/tables10.0.0.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4654
vendor/golang.org/x/net/idna/tables11.0.0.go
generated
vendored
Normal file
4654
vendor/golang.org/x/net/idna/tables11.0.0.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4734
vendor/golang.org/x/net/idna/tables12.0.0.go
generated
vendored
Normal file
4734
vendor/golang.org/x/net/idna/tables12.0.0.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4840
vendor/golang.org/x/net/idna/tables13.0.0.go
generated
vendored
Normal file
4840
vendor/golang.org/x/net/idna/tables13.0.0.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4487
vendor/golang.org/x/net/idna/tables9.0.0.go
generated
vendored
Normal file
4487
vendor/golang.org/x/net/idna/tables9.0.0.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
72
vendor/golang.org/x/net/idna/trie.go
generated
vendored
Normal file
72
vendor/golang.org/x/net/idna/trie.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
// appendMapping appends the mapping for the respective rune. isMapped must be
|
||||||
|
// true. A mapping is a categorization of a rune as defined in UTS #46.
|
||||||
|
func (c info) appendMapping(b []byte, s string) []byte {
|
||||||
|
index := int(c >> indexShift)
|
||||||
|
if c&xorBit == 0 {
|
||||||
|
s := mappings[index:]
|
||||||
|
return append(b, s[1:s[0]+1]...)
|
||||||
|
}
|
||||||
|
b = append(b, s...)
|
||||||
|
if c&inlineXOR == inlineXOR {
|
||||||
|
// TODO: support and handle two-byte inline masks
|
||||||
|
b[len(b)-1] ^= byte(index)
|
||||||
|
} else {
|
||||||
|
for p := len(b) - int(xorData[index]); p < len(b); p++ {
|
||||||
|
index++
|
||||||
|
b[p] ^= xorData[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sparse block handling code.
|
||||||
|
|
||||||
|
type valueRange struct {
|
||||||
|
value uint16 // header: value:stride
|
||||||
|
lo, hi byte // header: lo:n
|
||||||
|
}
|
||||||
|
|
||||||
|
type sparseBlocks struct {
|
||||||
|
values []valueRange
|
||||||
|
offset []uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
var idnaSparse = sparseBlocks{
|
||||||
|
values: idnaSparseValues[:],
|
||||||
|
offset: idnaSparseOffset[:],
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't use newIdnaTrie to avoid unconditional linking in of the table.
|
||||||
|
var trie = &idnaTrie{}
|
||||||
|
|
||||||
|
// lookup determines the type of block n and looks up the value for b.
|
||||||
|
// For n < t.cutoff, the block is a simple lookup table. Otherwise, the block
|
||||||
|
// is a list of ranges with an accompanying value. Given a matching range r,
|
||||||
|
// the value for b is by r.value + (b - r.lo) * stride.
|
||||||
|
func (t *sparseBlocks) lookup(n uint32, b byte) uint16 {
|
||||||
|
offset := t.offset[n]
|
||||||
|
header := t.values[offset]
|
||||||
|
lo := offset + 1
|
||||||
|
hi := lo + uint16(header.lo)
|
||||||
|
for lo < hi {
|
||||||
|
m := lo + (hi-lo)/2
|
||||||
|
r := t.values[m]
|
||||||
|
if r.lo <= b && b <= r.hi {
|
||||||
|
return r.value + uint16(b-r.lo)*header.value
|
||||||
|
}
|
||||||
|
if b < r.lo {
|
||||||
|
hi = m
|
||||||
|
} else {
|
||||||
|
lo = m + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
119
vendor/golang.org/x/net/idna/trieval.go
generated
vendored
Normal file
119
vendor/golang.org/x/net/idna/trieval.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||||
|
|
||||||
|
package idna
|
||||||
|
|
||||||
|
// This file contains definitions for interpreting the trie value of the idna
|
||||||
|
// trie generated by "go run gen*.go". It is shared by both the generator
|
||||||
|
// program and the resultant package. Sharing is achieved by the generator
|
||||||
|
// copying gen_trieval.go to trieval.go and changing what's above this comment.
|
||||||
|
|
||||||
|
// info holds information from the IDNA mapping table for a single rune. It is
|
||||||
|
// the value returned by a trie lookup. In most cases, all information fits in
|
||||||
|
// a 16-bit value. For mappings, this value may contain an index into a slice
|
||||||
|
// with the mapped string. Such mappings can consist of the actual mapped value
|
||||||
|
// or an XOR pattern to be applied to the bytes of the UTF8 encoding of the
|
||||||
|
// input rune. This technique is used by the cases packages and reduces the
|
||||||
|
// table size significantly.
|
||||||
|
//
|
||||||
|
// The per-rune values have the following format:
|
||||||
|
//
|
||||||
|
// if mapped {
|
||||||
|
// if inlinedXOR {
|
||||||
|
// 15..13 inline XOR marker
|
||||||
|
// 12..11 unused
|
||||||
|
// 10..3 inline XOR mask
|
||||||
|
// } else {
|
||||||
|
// 15..3 index into xor or mapping table
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// 15..14 unused
|
||||||
|
// 13 mayNeedNorm
|
||||||
|
// 12..11 attributes
|
||||||
|
// 10..8 joining type
|
||||||
|
// 7..3 category type
|
||||||
|
// }
|
||||||
|
// 2 use xor pattern
|
||||||
|
// 1..0 mapped category
|
||||||
|
//
|
||||||
|
// See the definitions below for a more detailed description of the various
|
||||||
|
// bits.
|
||||||
|
type info uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
catSmallMask = 0x3
|
||||||
|
catBigMask = 0xF8
|
||||||
|
indexShift = 3
|
||||||
|
xorBit = 0x4 // interpret the index as an xor pattern
|
||||||
|
inlineXOR = 0xE000 // These bits are set if the XOR pattern is inlined.
|
||||||
|
|
||||||
|
joinShift = 8
|
||||||
|
joinMask = 0x07
|
||||||
|
|
||||||
|
// Attributes
|
||||||
|
attributesMask = 0x1800
|
||||||
|
viramaModifier = 0x1800
|
||||||
|
modifier = 0x1000
|
||||||
|
rtl = 0x0800
|
||||||
|
|
||||||
|
mayNeedNorm = 0x2000
|
||||||
|
)
|
||||||
|
|
||||||
|
// A category corresponds to a category defined in the IDNA mapping table.
|
||||||
|
type category uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
unknown category = 0 // not currently defined in unicode.
|
||||||
|
mapped category = 1
|
||||||
|
disallowedSTD3Mapped category = 2
|
||||||
|
deviation category = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
valid category = 0x08
|
||||||
|
validNV8 category = 0x18
|
||||||
|
validXV8 category = 0x28
|
||||||
|
disallowed category = 0x40
|
||||||
|
disallowedSTD3Valid category = 0x80
|
||||||
|
ignored category = 0xC0
|
||||||
|
)
|
||||||
|
|
||||||
|
// join types and additional rune information
|
||||||
|
const (
|
||||||
|
joiningL = (iota + 1)
|
||||||
|
joiningD
|
||||||
|
joiningT
|
||||||
|
joiningR
|
||||||
|
|
||||||
|
//the following types are derived during processing
|
||||||
|
joinZWJ
|
||||||
|
joinZWNJ
|
||||||
|
joinVirama
|
||||||
|
numJoinTypes
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c info) isMapped() bool {
|
||||||
|
return c&0x3 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c info) category() category {
|
||||||
|
small := c & catSmallMask
|
||||||
|
if small != 0 {
|
||||||
|
return category(small)
|
||||||
|
}
|
||||||
|
return category(c & catBigMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c info) joinType() info {
|
||||||
|
if c.isMapped() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return (c >> joinShift) & joinMask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c info) isModifier() bool {
|
||||||
|
return c&(modifier|catSmallMask) == modifier
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c info) isViramaModifier() bool {
|
||||||
|
return c&(attributesMask|catSmallMask) == viramaModifier
|
||||||
|
}
|
525
vendor/golang.org/x/net/internal/timeseries/timeseries.go
generated
vendored
Normal file
525
vendor/golang.org/x/net/internal/timeseries/timeseries.go
generated
vendored
Normal file
@ -0,0 +1,525 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Package timeseries implements a time series structure for stats collection.
|
||||||
|
package timeseries // import "golang.org/x/net/internal/timeseries"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
timeSeriesNumBuckets = 64
|
||||||
|
minuteHourSeriesNumBuckets = 60
|
||||||
|
)
|
||||||
|
|
||||||
|
var timeSeriesResolutions = []time.Duration{
|
||||||
|
1 * time.Second,
|
||||||
|
10 * time.Second,
|
||||||
|
1 * time.Minute,
|
||||||
|
10 * time.Minute,
|
||||||
|
1 * time.Hour,
|
||||||
|
6 * time.Hour,
|
||||||
|
24 * time.Hour, // 1 day
|
||||||
|
7 * 24 * time.Hour, // 1 week
|
||||||
|
4 * 7 * 24 * time.Hour, // 4 weeks
|
||||||
|
16 * 7 * 24 * time.Hour, // 16 weeks
|
||||||
|
}
|
||||||
|
|
||||||
|
var minuteHourSeriesResolutions = []time.Duration{
|
||||||
|
1 * time.Second,
|
||||||
|
1 * time.Minute,
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Observable is a kind of data that can be aggregated in a time series.
|
||||||
|
type Observable interface {
|
||||||
|
Multiply(ratio float64) // Multiplies the data in self by a given ratio
|
||||||
|
Add(other Observable) // Adds the data from a different observation to self
|
||||||
|
Clear() // Clears the observation so it can be reused.
|
||||||
|
CopyFrom(other Observable) // Copies the contents of a given observation to self
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float attaches the methods of Observable to a float64.
|
||||||
|
type Float float64
|
||||||
|
|
||||||
|
// NewFloat returns a Float.
|
||||||
|
func NewFloat() Observable {
|
||||||
|
f := Float(0)
|
||||||
|
return &f
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the float as a string.
|
||||||
|
func (f *Float) String() string { return fmt.Sprintf("%g", f.Value()) }
|
||||||
|
|
||||||
|
// Value returns the float's value.
|
||||||
|
func (f *Float) Value() float64 { return float64(*f) }
|
||||||
|
|
||||||
|
func (f *Float) Multiply(ratio float64) { *f *= Float(ratio) }
|
||||||
|
|
||||||
|
func (f *Float) Add(other Observable) {
|
||||||
|
o := other.(*Float)
|
||||||
|
*f += *o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Float) Clear() { *f = 0 }
|
||||||
|
|
||||||
|
func (f *Float) CopyFrom(other Observable) {
|
||||||
|
o := other.(*Float)
|
||||||
|
*f = *o
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Clock tells the current time.
|
||||||
|
type Clock interface {
|
||||||
|
Time() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultClock int
|
||||||
|
|
||||||
|
var defaultClockInstance defaultClock
|
||||||
|
|
||||||
|
func (defaultClock) Time() time.Time { return time.Now() }
|
||||||
|
|
||||||
|
// Information kept per level. Each level consists of a circular list of
|
||||||
|
// observations. The start of the level may be derived from end and the
|
||||||
|
// len(buckets) * sizeInMillis.
|
||||||
|
type tsLevel struct {
|
||||||
|
oldest int // index to oldest bucketed Observable
|
||||||
|
newest int // index to newest bucketed Observable
|
||||||
|
end time.Time // end timestamp for this level
|
||||||
|
size time.Duration // duration of the bucketed Observable
|
||||||
|
buckets []Observable // collections of observations
|
||||||
|
provider func() Observable // used for creating new Observable
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tsLevel) Clear() {
|
||||||
|
l.oldest = 0
|
||||||
|
l.newest = len(l.buckets) - 1
|
||||||
|
l.end = time.Time{}
|
||||||
|
for i := range l.buckets {
|
||||||
|
if l.buckets[i] != nil {
|
||||||
|
l.buckets[i].Clear()
|
||||||
|
l.buckets[i] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tsLevel) InitLevel(size time.Duration, numBuckets int, f func() Observable) {
|
||||||
|
l.size = size
|
||||||
|
l.provider = f
|
||||||
|
l.buckets = make([]Observable, numBuckets)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keeps a sequence of levels. Each level is responsible for storing data at
|
||||||
|
// a given resolution. For example, the first level stores data at a one
|
||||||
|
// minute resolution while the second level stores data at a one hour
|
||||||
|
// resolution.
|
||||||
|
|
||||||
|
// Each level is represented by a sequence of buckets. Each bucket spans an
|
||||||
|
// interval equal to the resolution of the level. New observations are added
|
||||||
|
// to the last bucket.
|
||||||
|
type timeSeries struct {
|
||||||
|
provider func() Observable // make more Observable
|
||||||
|
numBuckets int // number of buckets in each level
|
||||||
|
levels []*tsLevel // levels of bucketed Observable
|
||||||
|
lastAdd time.Time // time of last Observable tracked
|
||||||
|
total Observable // convenient aggregation of all Observable
|
||||||
|
clock Clock // Clock for getting current time
|
||||||
|
pending Observable // observations not yet bucketed
|
||||||
|
pendingTime time.Time // what time are we keeping in pending
|
||||||
|
dirty bool // if there are pending observations
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes a level according to the supplied criteria.
|
||||||
|
func (ts *timeSeries) init(resolutions []time.Duration, f func() Observable, numBuckets int, clock Clock) {
|
||||||
|
ts.provider = f
|
||||||
|
ts.numBuckets = numBuckets
|
||||||
|
ts.clock = clock
|
||||||
|
ts.levels = make([]*tsLevel, len(resolutions))
|
||||||
|
|
||||||
|
for i := range resolutions {
|
||||||
|
if i > 0 && resolutions[i-1] >= resolutions[i] {
|
||||||
|
log.Print("timeseries: resolutions must be monotonically increasing")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
newLevel := new(tsLevel)
|
||||||
|
newLevel.InitLevel(resolutions[i], ts.numBuckets, ts.provider)
|
||||||
|
ts.levels[i] = newLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
ts.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear removes all observations from the time series.
|
||||||
|
func (ts *timeSeries) Clear() {
|
||||||
|
ts.lastAdd = time.Time{}
|
||||||
|
ts.total = ts.resetObservation(ts.total)
|
||||||
|
ts.pending = ts.resetObservation(ts.pending)
|
||||||
|
ts.pendingTime = time.Time{}
|
||||||
|
ts.dirty = false
|
||||||
|
|
||||||
|
for i := range ts.levels {
|
||||||
|
ts.levels[i].Clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add records an observation at the current time.
|
||||||
|
func (ts *timeSeries) Add(observation Observable) {
|
||||||
|
ts.AddWithTime(observation, ts.clock.Time())
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddWithTime records an observation at the specified time.
|
||||||
|
func (ts *timeSeries) AddWithTime(observation Observable, t time.Time) {
|
||||||
|
|
||||||
|
smallBucketDuration := ts.levels[0].size
|
||||||
|
|
||||||
|
if t.After(ts.lastAdd) {
|
||||||
|
ts.lastAdd = t
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.After(ts.pendingTime) {
|
||||||
|
ts.advance(t)
|
||||||
|
ts.mergePendingUpdates()
|
||||||
|
ts.pendingTime = ts.levels[0].end
|
||||||
|
ts.pending.CopyFrom(observation)
|
||||||
|
ts.dirty = true
|
||||||
|
} else if t.After(ts.pendingTime.Add(-1 * smallBucketDuration)) {
|
||||||
|
// The observation is close enough to go into the pending bucket.
|
||||||
|
// This compensates for clock skewing and small scheduling delays
|
||||||
|
// by letting the update stay in the fast path.
|
||||||
|
ts.pending.Add(observation)
|
||||||
|
ts.dirty = true
|
||||||
|
} else {
|
||||||
|
ts.mergeValue(observation, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeValue inserts the observation at the specified time in the past into all levels.
|
||||||
|
func (ts *timeSeries) mergeValue(observation Observable, t time.Time) {
|
||||||
|
for _, level := range ts.levels {
|
||||||
|
index := (ts.numBuckets - 1) - int(level.end.Sub(t)/level.size)
|
||||||
|
if 0 <= index && index < ts.numBuckets {
|
||||||
|
bucketNumber := (level.oldest + index) % ts.numBuckets
|
||||||
|
if level.buckets[bucketNumber] == nil {
|
||||||
|
level.buckets[bucketNumber] = level.provider()
|
||||||
|
}
|
||||||
|
level.buckets[bucketNumber].Add(observation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ts.total.Add(observation)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergePendingUpdates applies the pending updates into all levels.
|
||||||
|
func (ts *timeSeries) mergePendingUpdates() {
|
||||||
|
if ts.dirty {
|
||||||
|
ts.mergeValue(ts.pending, ts.pendingTime)
|
||||||
|
ts.pending = ts.resetObservation(ts.pending)
|
||||||
|
ts.dirty = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// advance cycles the buckets at each level until the latest bucket in
|
||||||
|
// each level can hold the time specified.
|
||||||
|
func (ts *timeSeries) advance(t time.Time) {
|
||||||
|
if !t.After(ts.levels[0].end) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i < len(ts.levels); i++ {
|
||||||
|
level := ts.levels[i]
|
||||||
|
if !level.end.Before(t) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the time is sufficiently far, just clear the level and advance
|
||||||
|
// directly.
|
||||||
|
if !t.Before(level.end.Add(level.size * time.Duration(ts.numBuckets))) {
|
||||||
|
for _, b := range level.buckets {
|
||||||
|
ts.resetObservation(b)
|
||||||
|
}
|
||||||
|
level.end = time.Unix(0, (t.UnixNano()/level.size.Nanoseconds())*level.size.Nanoseconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
for t.After(level.end) {
|
||||||
|
level.end = level.end.Add(level.size)
|
||||||
|
level.newest = level.oldest
|
||||||
|
level.oldest = (level.oldest + 1) % ts.numBuckets
|
||||||
|
ts.resetObservation(level.buckets[level.newest])
|
||||||
|
}
|
||||||
|
|
||||||
|
t = level.end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Latest returns the sum of the num latest buckets from the level.
|
||||||
|
func (ts *timeSeries) Latest(level, num int) Observable {
|
||||||
|
now := ts.clock.Time()
|
||||||
|
if ts.levels[0].end.Before(now) {
|
||||||
|
ts.advance(now)
|
||||||
|
}
|
||||||
|
|
||||||
|
ts.mergePendingUpdates()
|
||||||
|
|
||||||
|
result := ts.provider()
|
||||||
|
l := ts.levels[level]
|
||||||
|
index := l.newest
|
||||||
|
|
||||||
|
for i := 0; i < num; i++ {
|
||||||
|
if l.buckets[index] != nil {
|
||||||
|
result.Add(l.buckets[index])
|
||||||
|
}
|
||||||
|
if index == 0 {
|
||||||
|
index = ts.numBuckets
|
||||||
|
}
|
||||||
|
index--
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// LatestBuckets returns a copy of the num latest buckets from level.
|
||||||
|
func (ts *timeSeries) LatestBuckets(level, num int) []Observable {
|
||||||
|
if level < 0 || level > len(ts.levels) {
|
||||||
|
log.Print("timeseries: bad level argument: ", level)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if num < 0 || num >= ts.numBuckets {
|
||||||
|
log.Print("timeseries: bad num argument: ", num)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
results := make([]Observable, num)
|
||||||
|
now := ts.clock.Time()
|
||||||
|
if ts.levels[0].end.Before(now) {
|
||||||
|
ts.advance(now)
|
||||||
|
}
|
||||||
|
|
||||||
|
ts.mergePendingUpdates()
|
||||||
|
|
||||||
|
l := ts.levels[level]
|
||||||
|
index := l.newest
|
||||||
|
|
||||||
|
for i := 0; i < num; i++ {
|
||||||
|
result := ts.provider()
|
||||||
|
results[i] = result
|
||||||
|
if l.buckets[index] != nil {
|
||||||
|
result.CopyFrom(l.buckets[index])
|
||||||
|
}
|
||||||
|
|
||||||
|
if index == 0 {
|
||||||
|
index = ts.numBuckets
|
||||||
|
}
|
||||||
|
index -= 1
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScaleBy updates observations by scaling by factor.
|
||||||
|
func (ts *timeSeries) ScaleBy(factor float64) {
|
||||||
|
for _, l := range ts.levels {
|
||||||
|
for i := 0; i < ts.numBuckets; i++ {
|
||||||
|
l.buckets[i].Multiply(factor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ts.total.Multiply(factor)
|
||||||
|
ts.pending.Multiply(factor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range returns the sum of observations added over the specified time range.
|
||||||
|
// If start or finish times don't fall on bucket boundaries of the same
|
||||||
|
// level, then return values are approximate answers.
|
||||||
|
func (ts *timeSeries) Range(start, finish time.Time) Observable {
|
||||||
|
return ts.ComputeRange(start, finish, 1)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recent returns the sum of observations from the last delta.
|
||||||
|
func (ts *timeSeries) Recent(delta time.Duration) Observable {
|
||||||
|
now := ts.clock.Time()
|
||||||
|
return ts.Range(now.Add(-delta), now)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total returns the total of all observations.
|
||||||
|
func (ts *timeSeries) Total() Observable {
|
||||||
|
ts.mergePendingUpdates()
|
||||||
|
return ts.total
|
||||||
|
}
|
||||||
|
|
||||||
|
// ComputeRange computes a specified number of values into a slice using
|
||||||
|
// the observations recorded over the specified time period. The return
|
||||||
|
// values are approximate if the start or finish times don't fall on the
|
||||||
|
// bucket boundaries at the same level or if the number of buckets spanning
|
||||||
|
// the range is not an integral multiple of num.
|
||||||
|
func (ts *timeSeries) ComputeRange(start, finish time.Time, num int) []Observable {
|
||||||
|
if start.After(finish) {
|
||||||
|
log.Printf("timeseries: start > finish, %v>%v", start, finish)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if num < 0 {
|
||||||
|
log.Printf("timeseries: num < 0, %v", num)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
results := make([]Observable, num)
|
||||||
|
|
||||||
|
for _, l := range ts.levels {
|
||||||
|
if !start.Before(l.end.Add(-l.size * time.Duration(ts.numBuckets))) {
|
||||||
|
ts.extract(l, start, finish, num, results)
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// desired range.
|
||||||
|
ts.extract(ts.levels[len(ts.levels)-1], start, finish, num, results)
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecentList returns the specified number of values in slice over the most
|
||||||
|
// recent time period of the specified range.
|
||||||
|
func (ts *timeSeries) RecentList(delta time.Duration, num int) []Observable {
|
||||||
|
if delta < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
now := ts.clock.Time()
|
||||||
|
return ts.ComputeRange(now.Add(-delta), now, num)
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract returns a slice of specified number of observations from a given
|
||||||
|
// level over a given range.
|
||||||
|
func (ts *timeSeries) extract(l *tsLevel, start, finish time.Time, num int, results []Observable) {
|
||||||
|
ts.mergePendingUpdates()
|
||||||
|
|
||||||
|
srcInterval := l.size
|
||||||
|
dstInterval := finish.Sub(start) / time.Duration(num)
|
||||||
|
dstStart := start
|
||||||
|
srcStart := l.end.Add(-srcInterval * time.Duration(ts.numBuckets))
|
||||||
|
|
||||||
|
srcIndex := 0
|
||||||
|
|
||||||
|
// Where should scanning start?
|
||||||
|
if dstStart.After(srcStart) {
|
||||||
|
advance := int(dstStart.Sub(srcStart) / srcInterval)
|
||||||
|
srcIndex += advance
|
||||||
|
srcStart = srcStart.Add(time.Duration(advance) * srcInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The i'th value is computed as show below.
|
||||||
|
// interval = (finish/start)/num
|
||||||
|
// i'th value = sum of observation in range
|
||||||
|
// [ start + i * interval,
|
||||||
|
// start + (i + 1) * interval )
|
||||||
|
for i := 0; i < num; i++ {
|
||||||
|
results[i] = ts.resetObservation(results[i])
|
||||||
|
dstEnd := dstStart.Add(dstInterval)
|
||||||
|
for srcIndex < ts.numBuckets && srcStart.Before(dstEnd) {
|
||||||
|
srcEnd := srcStart.Add(srcInterval)
|
||||||
|
if srcEnd.After(ts.lastAdd) {
|
||||||
|
srcEnd = ts.lastAdd
|
||||||
|
}
|
||||||
|
|
||||||
|
if !srcEnd.Before(dstStart) {
|
||||||
|
srcValue := l.buckets[(srcIndex+l.oldest)%ts.numBuckets]
|
||||||
|
if !srcStart.Before(dstStart) && !srcEnd.After(dstEnd) {
|
||||||
|
// dst completely contains src.
|
||||||
|
if srcValue != nil {
|
||||||
|
results[i].Add(srcValue)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// dst partially overlaps src.
|
||||||
|
overlapStart := maxTime(srcStart, dstStart)
|
||||||
|
overlapEnd := minTime(srcEnd, dstEnd)
|
||||||
|
base := srcEnd.Sub(srcStart)
|
||||||
|
fraction := overlapEnd.Sub(overlapStart).Seconds() / base.Seconds()
|
||||||
|
|
||||||
|
used := ts.provider()
|
||||||
|
if srcValue != nil {
|
||||||
|
used.CopyFrom(srcValue)
|
||||||
|
}
|
||||||
|
used.Multiply(fraction)
|
||||||
|
results[i].Add(used)
|
||||||
|
}
|
||||||
|
|
||||||
|
if srcEnd.After(dstEnd) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
srcIndex++
|
||||||
|
srcStart = srcStart.Add(srcInterval)
|
||||||
|
}
|
||||||
|
dstStart = dstStart.Add(dstInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resetObservation clears the content so the struct may be reused.
|
||||||
|
func (ts *timeSeries) resetObservation(observation Observable) Observable {
|
||||||
|
if observation == nil {
|
||||||
|
observation = ts.provider()
|
||||||
|
} else {
|
||||||
|
observation.Clear()
|
||||||
|
}
|
||||||
|
return observation
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeSeries tracks data at granularities from 1 second to 16 weeks.
|
||||||
|
type TimeSeries struct {
|
||||||
|
timeSeries
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTimeSeries creates a new TimeSeries using the function provided for creating new Observable.
|
||||||
|
func NewTimeSeries(f func() Observable) *TimeSeries {
|
||||||
|
return NewTimeSeriesWithClock(f, defaultClockInstance)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTimeSeriesWithClock creates a new TimeSeries using the function provided for creating new Observable and the clock for
|
||||||
|
// assigning timestamps.
|
||||||
|
func NewTimeSeriesWithClock(f func() Observable, clock Clock) *TimeSeries {
|
||||||
|
ts := new(TimeSeries)
|
||||||
|
ts.timeSeries.init(timeSeriesResolutions, f, timeSeriesNumBuckets, clock)
|
||||||
|
return ts
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinuteHourSeries tracks data at granularities of 1 minute and 1 hour.
|
||||||
|
type MinuteHourSeries struct {
|
||||||
|
timeSeries
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMinuteHourSeries creates a new MinuteHourSeries using the function provided for creating new Observable.
|
||||||
|
func NewMinuteHourSeries(f func() Observable) *MinuteHourSeries {
|
||||||
|
return NewMinuteHourSeriesWithClock(f, defaultClockInstance)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMinuteHourSeriesWithClock creates a new MinuteHourSeries using the function provided for creating new Observable and the clock for
|
||||||
|
// assigning timestamps.
|
||||||
|
func NewMinuteHourSeriesWithClock(f func() Observable, clock Clock) *MinuteHourSeries {
|
||||||
|
ts := new(MinuteHourSeries)
|
||||||
|
ts.timeSeries.init(minuteHourSeriesResolutions, f,
|
||||||
|
minuteHourSeriesNumBuckets, clock)
|
||||||
|
return ts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts *MinuteHourSeries) Minute() Observable {
|
||||||
|
return ts.timeSeries.Latest(0, 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts *MinuteHourSeries) Hour() Observable {
|
||||||
|
return ts.timeSeries.Latest(1, 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
func minTime(a, b time.Time) time.Time {
|
||||||
|
if a.Before(b) {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func maxTime(a, b time.Time) time.Time {
|
||||||
|
if a.After(b) {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
532
vendor/golang.org/x/net/trace/events.go
generated
vendored
Normal file
532
vendor/golang.org/x/net/trace/events.go
generated
vendored
Normal file
@ -0,0 +1,532 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package trace
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"runtime"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"text/tabwriter"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const maxEventsPerLog = 100
|
||||||
|
|
||||||
|
type bucket struct {
|
||||||
|
MaxErrAge time.Duration
|
||||||
|
String string
|
||||||
|
}
|
||||||
|
|
||||||
|
var buckets = []bucket{
|
||||||
|
{0, "total"},
|
||||||
|
{10 * time.Second, "errs<10s"},
|
||||||
|
{1 * time.Minute, "errs<1m"},
|
||||||
|
{10 * time.Minute, "errs<10m"},
|
||||||
|
{1 * time.Hour, "errs<1h"},
|
||||||
|
{10 * time.Hour, "errs<10h"},
|
||||||
|
{24000 * time.Hour, "errors"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderEvents renders the HTML page typically served at /debug/events.
|
||||||
|
// It does not do any auth checking. The request may be nil.
|
||||||
|
//
|
||||||
|
// Most users will use the Events handler.
|
||||||
|
func RenderEvents(w http.ResponseWriter, req *http.Request, sensitive bool) {
|
||||||
|
now := time.Now()
|
||||||
|
data := &struct {
|
||||||
|
Families []string // family names
|
||||||
|
Buckets []bucket
|
||||||
|
Counts [][]int // eventLog count per family/bucket
|
||||||
|
|
||||||
|
// Set when a bucket has been selected.
|
||||||
|
Family string
|
||||||
|
Bucket int
|
||||||
|
EventLogs eventLogs
|
||||||
|
Expanded bool
|
||||||
|
}{
|
||||||
|
Buckets: buckets,
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Families = make([]string, 0, len(families))
|
||||||
|
famMu.RLock()
|
||||||
|
for name := range families {
|
||||||
|
data.Families = append(data.Families, name)
|
||||||
|
}
|
||||||
|
famMu.RUnlock()
|
||||||
|
sort.Strings(data.Families)
|
||||||
|
|
||||||
|
// Count the number of eventLogs in each family for each error age.
|
||||||
|
data.Counts = make([][]int, len(data.Families))
|
||||||
|
for i, name := range data.Families {
|
||||||
|
// TODO(sameer): move this loop under the family lock.
|
||||||
|
f := getEventFamily(name)
|
||||||
|
data.Counts[i] = make([]int, len(data.Buckets))
|
||||||
|
for j, b := range data.Buckets {
|
||||||
|
data.Counts[i][j] = f.Count(now, b.MaxErrAge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if req != nil {
|
||||||
|
var ok bool
|
||||||
|
data.Family, data.Bucket, ok = parseEventsArgs(req)
|
||||||
|
if !ok {
|
||||||
|
// No-op
|
||||||
|
} else {
|
||||||
|
data.EventLogs = getEventFamily(data.Family).Copy(now, buckets[data.Bucket].MaxErrAge)
|
||||||
|
}
|
||||||
|
if data.EventLogs != nil {
|
||||||
|
defer data.EventLogs.Free()
|
||||||
|
sort.Sort(data.EventLogs)
|
||||||
|
}
|
||||||
|
if exp, err := strconv.ParseBool(req.FormValue("exp")); err == nil {
|
||||||
|
data.Expanded = exp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
famMu.RLock()
|
||||||
|
defer famMu.RUnlock()
|
||||||
|
if err := eventsTmpl().Execute(w, data); err != nil {
|
||||||
|
log.Printf("net/trace: Failed executing template: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEventsArgs(req *http.Request) (fam string, b int, ok bool) {
|
||||||
|
fam, bStr := req.FormValue("fam"), req.FormValue("b")
|
||||||
|
if fam == "" || bStr == "" {
|
||||||
|
return "", 0, false
|
||||||
|
}
|
||||||
|
b, err := strconv.Atoi(bStr)
|
||||||
|
if err != nil || b < 0 || b >= len(buckets) {
|
||||||
|
return "", 0, false
|
||||||
|
}
|
||||||
|
return fam, b, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// An EventLog provides a log of events associated with a specific object.
|
||||||
|
type EventLog interface {
|
||||||
|
// Printf formats its arguments with fmt.Sprintf and adds the
|
||||||
|
// result to the event log.
|
||||||
|
Printf(format string, a ...interface{})
|
||||||
|
|
||||||
|
// Errorf is like Printf, but it marks this event as an error.
|
||||||
|
Errorf(format string, a ...interface{})
|
||||||
|
|
||||||
|
// Finish declares that this event log is complete.
|
||||||
|
// The event log should not be used after calling this method.
|
||||||
|
Finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEventLog returns a new EventLog with the specified family name
|
||||||
|
// and title.
|
||||||
|
func NewEventLog(family, title string) EventLog {
|
||||||
|
el := newEventLog()
|
||||||
|
el.ref()
|
||||||
|
el.Family, el.Title = family, title
|
||||||
|
el.Start = time.Now()
|
||||||
|
el.events = make([]logEntry, 0, maxEventsPerLog)
|
||||||
|
el.stack = make([]uintptr, 32)
|
||||||
|
n := runtime.Callers(2, el.stack)
|
||||||
|
el.stack = el.stack[:n]
|
||||||
|
|
||||||
|
getEventFamily(family).add(el)
|
||||||
|
return el
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *eventLog) Finish() {
|
||||||
|
getEventFamily(el.Family).remove(el)
|
||||||
|
el.unref() // matches ref in New
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
famMu sync.RWMutex
|
||||||
|
families = make(map[string]*eventFamily) // family name => family
|
||||||
|
)
|
||||||
|
|
||||||
|
func getEventFamily(fam string) *eventFamily {
|
||||||
|
famMu.Lock()
|
||||||
|
defer famMu.Unlock()
|
||||||
|
f := families[fam]
|
||||||
|
if f == nil {
|
||||||
|
f = &eventFamily{}
|
||||||
|
families[fam] = f
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
type eventFamily struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
eventLogs eventLogs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *eventFamily) add(el *eventLog) {
|
||||||
|
f.mu.Lock()
|
||||||
|
f.eventLogs = append(f.eventLogs, el)
|
||||||
|
f.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *eventFamily) remove(el *eventLog) {
|
||||||
|
f.mu.Lock()
|
||||||
|
defer f.mu.Unlock()
|
||||||
|
for i, el0 := range f.eventLogs {
|
||||||
|
if el == el0 {
|
||||||
|
copy(f.eventLogs[i:], f.eventLogs[i+1:])
|
||||||
|
f.eventLogs = f.eventLogs[:len(f.eventLogs)-1]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *eventFamily) Count(now time.Time, maxErrAge time.Duration) (n int) {
|
||||||
|
f.mu.RLock()
|
||||||
|
defer f.mu.RUnlock()
|
||||||
|
for _, el := range f.eventLogs {
|
||||||
|
if el.hasRecentError(now, maxErrAge) {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *eventFamily) Copy(now time.Time, maxErrAge time.Duration) (els eventLogs) {
|
||||||
|
f.mu.RLock()
|
||||||
|
defer f.mu.RUnlock()
|
||||||
|
els = make(eventLogs, 0, len(f.eventLogs))
|
||||||
|
for _, el := range f.eventLogs {
|
||||||
|
if el.hasRecentError(now, maxErrAge) {
|
||||||
|
el.ref()
|
||||||
|
els = append(els, el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type eventLogs []*eventLog
|
||||||
|
|
||||||
|
// Free calls unref on each element of the list.
|
||||||
|
func (els eventLogs) Free() {
|
||||||
|
for _, el := range els {
|
||||||
|
el.unref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eventLogs may be sorted in reverse chronological order.
|
||||||
|
func (els eventLogs) Len() int { return len(els) }
|
||||||
|
func (els eventLogs) Less(i, j int) bool { return els[i].Start.After(els[j].Start) }
|
||||||
|
func (els eventLogs) Swap(i, j int) { els[i], els[j] = els[j], els[i] }
|
||||||
|
|
||||||
|
// A logEntry is a timestamped log entry in an event log.
|
||||||
|
type logEntry struct {
|
||||||
|
When time.Time
|
||||||
|
Elapsed time.Duration // since previous event in log
|
||||||
|
NewDay bool // whether this event is on a different day to the previous event
|
||||||
|
What string
|
||||||
|
IsErr bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhenString returns a string representation of the elapsed time of the event.
|
||||||
|
// It will include the date if midnight was crossed.
|
||||||
|
func (e logEntry) WhenString() string {
|
||||||
|
if e.NewDay {
|
||||||
|
return e.When.Format("2006/01/02 15:04:05.000000")
|
||||||
|
}
|
||||||
|
return e.When.Format("15:04:05.000000")
|
||||||
|
}
|
||||||
|
|
||||||
|
// An eventLog represents an active event log.
|
||||||
|
type eventLog struct {
|
||||||
|
// Family is the top-level grouping of event logs to which this belongs.
|
||||||
|
Family string
|
||||||
|
|
||||||
|
// Title is the title of this event log.
|
||||||
|
Title string
|
||||||
|
|
||||||
|
// Timing information.
|
||||||
|
Start time.Time
|
||||||
|
|
||||||
|
// Call stack where this event log was created.
|
||||||
|
stack []uintptr
|
||||||
|
|
||||||
|
// Append-only sequence of events.
|
||||||
|
//
|
||||||
|
// TODO(sameer): change this to a ring buffer to avoid the array copy
|
||||||
|
// when we hit maxEventsPerLog.
|
||||||
|
mu sync.RWMutex
|
||||||
|
events []logEntry
|
||||||
|
LastErrorTime time.Time
|
||||||
|
discarded int
|
||||||
|
|
||||||
|
refs int32 // how many buckets this is in
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *eventLog) reset() {
|
||||||
|
// Clear all but the mutex. Mutexes may not be copied, even when unlocked.
|
||||||
|
el.Family = ""
|
||||||
|
el.Title = ""
|
||||||
|
el.Start = time.Time{}
|
||||||
|
el.stack = nil
|
||||||
|
el.events = nil
|
||||||
|
el.LastErrorTime = time.Time{}
|
||||||
|
el.discarded = 0
|
||||||
|
el.refs = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *eventLog) hasRecentError(now time.Time, maxErrAge time.Duration) bool {
|
||||||
|
if maxErrAge == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
el.mu.RLock()
|
||||||
|
defer el.mu.RUnlock()
|
||||||
|
return now.Sub(el.LastErrorTime) < maxErrAge
|
||||||
|
}
|
||||||
|
|
||||||
|
// delta returns the elapsed time since the last event or the log start,
|
||||||
|
// and whether it spans midnight.
|
||||||
|
// L >= el.mu
|
||||||
|
func (el *eventLog) delta(t time.Time) (time.Duration, bool) {
|
||||||
|
if len(el.events) == 0 {
|
||||||
|
return t.Sub(el.Start), false
|
||||||
|
}
|
||||||
|
prev := el.events[len(el.events)-1].When
|
||||||
|
return t.Sub(prev), prev.Day() != t.Day()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *eventLog) Printf(format string, a ...interface{}) {
|
||||||
|
el.printf(false, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *eventLog) Errorf(format string, a ...interface{}) {
|
||||||
|
el.printf(true, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *eventLog) printf(isErr bool, format string, a ...interface{}) {
|
||||||
|
e := logEntry{When: time.Now(), IsErr: isErr, What: fmt.Sprintf(format, a...)}
|
||||||
|
el.mu.Lock()
|
||||||
|
e.Elapsed, e.NewDay = el.delta(e.When)
|
||||||
|
if len(el.events) < maxEventsPerLog {
|
||||||
|
el.events = append(el.events, e)
|
||||||
|
} else {
|
||||||
|
// Discard the oldest event.
|
||||||
|
if el.discarded == 0 {
|
||||||
|
// el.discarded starts at two to count for the event it
|
||||||
|
// is replacing, plus the next one that we are about to
|
||||||
|
// drop.
|
||||||
|
el.discarded = 2
|
||||||
|
} else {
|
||||||
|
el.discarded++
|
||||||
|
}
|
||||||
|
// TODO(sameer): if this causes allocations on a critical path,
|
||||||
|
// change eventLog.What to be a fmt.Stringer, as in trace.go.
|
||||||
|
el.events[0].What = fmt.Sprintf("(%d events discarded)", el.discarded)
|
||||||
|
// The timestamp of the discarded meta-event should be
|
||||||
|
// the time of the last event it is representing.
|
||||||
|
el.events[0].When = el.events[1].When
|
||||||
|
copy(el.events[1:], el.events[2:])
|
||||||
|
el.events[maxEventsPerLog-1] = e
|
||||||
|
}
|
||||||
|
if e.IsErr {
|
||||||
|
el.LastErrorTime = e.When
|
||||||
|
}
|
||||||
|
el.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *eventLog) ref() {
|
||||||
|
atomic.AddInt32(&el.refs, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *eventLog) unref() {
|
||||||
|
if atomic.AddInt32(&el.refs, -1) == 0 {
|
||||||
|
freeEventLog(el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *eventLog) When() string {
|
||||||
|
return el.Start.Format("2006/01/02 15:04:05.000000")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *eventLog) ElapsedTime() string {
|
||||||
|
elapsed := time.Since(el.Start)
|
||||||
|
return fmt.Sprintf("%.6f", elapsed.Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *eventLog) Stack() string {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
tw := tabwriter.NewWriter(buf, 1, 8, 1, '\t', 0)
|
||||||
|
printStackRecord(tw, el.stack)
|
||||||
|
tw.Flush()
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// printStackRecord prints the function + source line information
|
||||||
|
// for a single stack trace.
|
||||||
|
// Adapted from runtime/pprof/pprof.go.
|
||||||
|
func printStackRecord(w io.Writer, stk []uintptr) {
|
||||||
|
for _, pc := range stk {
|
||||||
|
f := runtime.FuncForPC(pc)
|
||||||
|
if f == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
file, line := f.FileLine(pc)
|
||||||
|
name := f.Name()
|
||||||
|
// Hide runtime.goexit and any runtime functions at the beginning.
|
||||||
|
if strings.HasPrefix(name, "runtime.") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "# %s\t%s:%d\n", name, file, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *eventLog) Events() []logEntry {
|
||||||
|
el.mu.RLock()
|
||||||
|
defer el.mu.RUnlock()
|
||||||
|
return el.events
|
||||||
|
}
|
||||||
|
|
||||||
|
// freeEventLogs is a freelist of *eventLog
|
||||||
|
var freeEventLogs = make(chan *eventLog, 1000)
|
||||||
|
|
||||||
|
// newEventLog returns a event log ready to use.
|
||||||
|
func newEventLog() *eventLog {
|
||||||
|
select {
|
||||||
|
case el := <-freeEventLogs:
|
||||||
|
return el
|
||||||
|
default:
|
||||||
|
return new(eventLog)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// freeEventLog adds el to freeEventLogs if there's room.
|
||||||
|
// This is non-blocking.
|
||||||
|
func freeEventLog(el *eventLog) {
|
||||||
|
el.reset()
|
||||||
|
select {
|
||||||
|
case freeEventLogs <- el:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventsTmplCache *template.Template
|
||||||
|
var eventsTmplOnce sync.Once
|
||||||
|
|
||||||
|
func eventsTmpl() *template.Template {
|
||||||
|
eventsTmplOnce.Do(func() {
|
||||||
|
eventsTmplCache = template.Must(template.New("events").Funcs(template.FuncMap{
|
||||||
|
"elapsed": elapsed,
|
||||||
|
"trimSpace": strings.TrimSpace,
|
||||||
|
}).Parse(eventsHTML))
|
||||||
|
})
|
||||||
|
return eventsTmplCache
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventsHTML = `
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>events</title>
|
||||||
|
</head>
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
table#req-status td.family {
|
||||||
|
padding-right: 2em;
|
||||||
|
}
|
||||||
|
table#req-status td.active {
|
||||||
|
padding-right: 1em;
|
||||||
|
}
|
||||||
|
table#req-status td.empty {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
table#reqs {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
table#reqs tr.first {
|
||||||
|
{{if $.Expanded}}font-weight: bold;{{end}}
|
||||||
|
}
|
||||||
|
table#reqs td {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
table#reqs td.when {
|
||||||
|
text-align: right;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
table#reqs td.elapsed {
|
||||||
|
padding: 0 0.5em;
|
||||||
|
text-align: right;
|
||||||
|
white-space: pre;
|
||||||
|
width: 10em;
|
||||||
|
}
|
||||||
|
address {
|
||||||
|
font-size: smaller;
|
||||||
|
margin-top: 5em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>/debug/events</h1>
|
||||||
|
|
||||||
|
<table id="req-status">
|
||||||
|
{{range $i, $fam := .Families}}
|
||||||
|
<tr>
|
||||||
|
<td class="family">{{$fam}}</td>
|
||||||
|
|
||||||
|
{{range $j, $bucket := $.Buckets}}
|
||||||
|
{{$n := index $.Counts $i $j}}
|
||||||
|
<td class="{{if not $bucket.MaxErrAge}}active{{end}}{{if not $n}}empty{{end}}">
|
||||||
|
{{if $n}}<a href="?fam={{$fam}}&b={{$j}}{{if $.Expanded}}&exp=1{{end}}">{{end}}
|
||||||
|
[{{$n}} {{$bucket.String}}]
|
||||||
|
{{if $n}}</a>{{end}}
|
||||||
|
</td>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
</tr>{{end}}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{{if $.EventLogs}}
|
||||||
|
<hr />
|
||||||
|
<h3>Family: {{$.Family}}</h3>
|
||||||
|
|
||||||
|
{{if $.Expanded}}<a href="?fam={{$.Family}}&b={{$.Bucket}}">{{end}}
|
||||||
|
[Summary]{{if $.Expanded}}</a>{{end}}
|
||||||
|
|
||||||
|
{{if not $.Expanded}}<a href="?fam={{$.Family}}&b={{$.Bucket}}&exp=1">{{end}}
|
||||||
|
[Expanded]{{if not $.Expanded}}</a>{{end}}
|
||||||
|
|
||||||
|
<table id="reqs">
|
||||||
|
<tr><th>When</th><th>Elapsed</th></tr>
|
||||||
|
{{range $el := $.EventLogs}}
|
||||||
|
<tr class="first">
|
||||||
|
<td class="when">{{$el.When}}</td>
|
||||||
|
<td class="elapsed">{{$el.ElapsedTime}}</td>
|
||||||
|
<td>{{$el.Title}}
|
||||||
|
</tr>
|
||||||
|
{{if $.Expanded}}
|
||||||
|
<tr>
|
||||||
|
<td class="when"></td>
|
||||||
|
<td class="elapsed"></td>
|
||||||
|
<td><pre>{{$el.Stack|trimSpace}}</pre></td>
|
||||||
|
</tr>
|
||||||
|
{{range $el.Events}}
|
||||||
|
<tr>
|
||||||
|
<td class="when">{{.WhenString}}</td>
|
||||||
|
<td class="elapsed">{{elapsed .Elapsed}}</td>
|
||||||
|
<td>.{{if .IsErr}}E{{else}}.{{end}}. {{.What}}</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</table>
|
||||||
|
{{end}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`
|
365
vendor/golang.org/x/net/trace/histogram.go
generated
vendored
Normal file
365
vendor/golang.org/x/net/trace/histogram.go
generated
vendored
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package trace
|
||||||
|
|
||||||
|
// This file implements histogramming for RPC statistics collection.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/net/internal/timeseries"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
bucketCount = 38
|
||||||
|
)
|
||||||
|
|
||||||
|
// histogram keeps counts of values in buckets that are spaced
|
||||||
|
// out in powers of 2: 0-1, 2-3, 4-7...
|
||||||
|
// histogram implements timeseries.Observable
|
||||||
|
type histogram struct {
|
||||||
|
sum int64 // running total of measurements
|
||||||
|
sumOfSquares float64 // square of running total
|
||||||
|
buckets []int64 // bucketed values for histogram
|
||||||
|
value int // holds a single value as an optimization
|
||||||
|
valueCount int64 // number of values recorded for single value
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMeasurement records a value measurement observation to the histogram.
|
||||||
|
func (h *histogram) addMeasurement(value int64) {
|
||||||
|
// TODO: assert invariant
|
||||||
|
h.sum += value
|
||||||
|
h.sumOfSquares += float64(value) * float64(value)
|
||||||
|
|
||||||
|
bucketIndex := getBucket(value)
|
||||||
|
|
||||||
|
if h.valueCount == 0 || (h.valueCount > 0 && h.value == bucketIndex) {
|
||||||
|
h.value = bucketIndex
|
||||||
|
h.valueCount++
|
||||||
|
} else {
|
||||||
|
h.allocateBuckets()
|
||||||
|
h.buckets[bucketIndex]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *histogram) allocateBuckets() {
|
||||||
|
if h.buckets == nil {
|
||||||
|
h.buckets = make([]int64, bucketCount)
|
||||||
|
h.buckets[h.value] = h.valueCount
|
||||||
|
h.value = 0
|
||||||
|
h.valueCount = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func log2(i int64) int {
|
||||||
|
n := 0
|
||||||
|
for ; i >= 0x100; i >>= 8 {
|
||||||
|
n += 8
|
||||||
|
}
|
||||||
|
for ; i > 0; i >>= 1 {
|
||||||
|
n += 1
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBucket(i int64) (index int) {
|
||||||
|
index = log2(i) - 1
|
||||||
|
if index < 0 {
|
||||||
|
index = 0
|
||||||
|
}
|
||||||
|
if index >= bucketCount {
|
||||||
|
index = bucketCount - 1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total returns the number of recorded observations.
|
||||||
|
func (h *histogram) total() (total int64) {
|
||||||
|
if h.valueCount >= 0 {
|
||||||
|
total = h.valueCount
|
||||||
|
}
|
||||||
|
for _, val := range h.buckets {
|
||||||
|
total += int64(val)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Average returns the average value of recorded observations.
|
||||||
|
func (h *histogram) average() float64 {
|
||||||
|
t := h.total()
|
||||||
|
if t == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return float64(h.sum) / float64(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variance returns the variance of recorded observations.
|
||||||
|
func (h *histogram) variance() float64 {
|
||||||
|
t := float64(h.total())
|
||||||
|
if t == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
s := float64(h.sum) / t
|
||||||
|
return h.sumOfSquares/t - s*s
|
||||||
|
}
|
||||||
|
|
||||||
|
// StandardDeviation returns the standard deviation of recorded observations.
|
||||||
|
func (h *histogram) standardDeviation() float64 {
|
||||||
|
return math.Sqrt(h.variance())
|
||||||
|
}
|
||||||
|
|
||||||
|
// PercentileBoundary estimates the value that the given fraction of recorded
|
||||||
|
// observations are less than.
|
||||||
|
func (h *histogram) percentileBoundary(percentile float64) int64 {
|
||||||
|
total := h.total()
|
||||||
|
|
||||||
|
// Corner cases (make sure result is strictly less than Total())
|
||||||
|
if total == 0 {
|
||||||
|
return 0
|
||||||
|
} else if total == 1 {
|
||||||
|
return int64(h.average())
|
||||||
|
}
|
||||||
|
|
||||||
|
percentOfTotal := round(float64(total) * percentile)
|
||||||
|
var runningTotal int64
|
||||||
|
|
||||||
|
for i := range h.buckets {
|
||||||
|
value := h.buckets[i]
|
||||||
|
runningTotal += value
|
||||||
|
if runningTotal == percentOfTotal {
|
||||||
|
// We hit an exact bucket boundary. If the next bucket has data, it is a
|
||||||
|
// good estimate of the value. If the bucket is empty, we interpolate the
|
||||||
|
// midpoint between the next bucket's boundary and the next non-zero
|
||||||
|
// bucket. If the remaining buckets are all empty, then we use the
|
||||||
|
// boundary for the next bucket as the estimate.
|
||||||
|
j := uint8(i + 1)
|
||||||
|
min := bucketBoundary(j)
|
||||||
|
if runningTotal < total {
|
||||||
|
for h.buckets[j] == 0 {
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
max := bucketBoundary(j)
|
||||||
|
return min + round(float64(max-min)/2)
|
||||||
|
} else if runningTotal > percentOfTotal {
|
||||||
|
// The value is in this bucket. Interpolate the value.
|
||||||
|
delta := runningTotal - percentOfTotal
|
||||||
|
percentBucket := float64(value-delta) / float64(value)
|
||||||
|
bucketMin := bucketBoundary(uint8(i))
|
||||||
|
nextBucketMin := bucketBoundary(uint8(i + 1))
|
||||||
|
bucketSize := nextBucketMin - bucketMin
|
||||||
|
return bucketMin + round(percentBucket*float64(bucketSize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bucketBoundary(bucketCount - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Median returns the estimated median of the observed values.
|
||||||
|
func (h *histogram) median() int64 {
|
||||||
|
return h.percentileBoundary(0.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds other to h.
|
||||||
|
func (h *histogram) Add(other timeseries.Observable) {
|
||||||
|
o := other.(*histogram)
|
||||||
|
if o.valueCount == 0 {
|
||||||
|
// Other histogram is empty
|
||||||
|
} else if h.valueCount >= 0 && o.valueCount > 0 && h.value == o.value {
|
||||||
|
// Both have a single bucketed value, aggregate them
|
||||||
|
h.valueCount += o.valueCount
|
||||||
|
} else {
|
||||||
|
// Two different values necessitate buckets in this histogram
|
||||||
|
h.allocateBuckets()
|
||||||
|
if o.valueCount >= 0 {
|
||||||
|
h.buckets[o.value] += o.valueCount
|
||||||
|
} else {
|
||||||
|
for i := range h.buckets {
|
||||||
|
h.buckets[i] += o.buckets[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h.sumOfSquares += o.sumOfSquares
|
||||||
|
h.sum += o.sum
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear resets the histogram to an empty state, removing all observed values.
|
||||||
|
func (h *histogram) Clear() {
|
||||||
|
h.buckets = nil
|
||||||
|
h.value = 0
|
||||||
|
h.valueCount = 0
|
||||||
|
h.sum = 0
|
||||||
|
h.sumOfSquares = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyFrom copies from other, which must be a *histogram, into h.
|
||||||
|
func (h *histogram) CopyFrom(other timeseries.Observable) {
|
||||||
|
o := other.(*histogram)
|
||||||
|
if o.valueCount == -1 {
|
||||||
|
h.allocateBuckets()
|
||||||
|
copy(h.buckets, o.buckets)
|
||||||
|
}
|
||||||
|
h.sum = o.sum
|
||||||
|
h.sumOfSquares = o.sumOfSquares
|
||||||
|
h.value = o.value
|
||||||
|
h.valueCount = o.valueCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiply scales the histogram by the specified ratio.
|
||||||
|
func (h *histogram) Multiply(ratio float64) {
|
||||||
|
if h.valueCount == -1 {
|
||||||
|
for i := range h.buckets {
|
||||||
|
h.buckets[i] = int64(float64(h.buckets[i]) * ratio)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
h.valueCount = int64(float64(h.valueCount) * ratio)
|
||||||
|
}
|
||||||
|
h.sum = int64(float64(h.sum) * ratio)
|
||||||
|
h.sumOfSquares = h.sumOfSquares * ratio
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new histogram.
|
||||||
|
func (h *histogram) New() timeseries.Observable {
|
||||||
|
r := new(histogram)
|
||||||
|
r.Clear()
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *histogram) String() string {
|
||||||
|
return fmt.Sprintf("%d, %f, %d, %d, %v",
|
||||||
|
h.sum, h.sumOfSquares, h.value, h.valueCount, h.buckets)
|
||||||
|
}
|
||||||
|
|
||||||
|
// round returns the closest int64 to the argument
|
||||||
|
func round(in float64) int64 {
|
||||||
|
return int64(math.Floor(in + 0.5))
|
||||||
|
}
|
||||||
|
|
||||||
|
// bucketBoundary returns the first value in the bucket.
|
||||||
|
func bucketBoundary(bucket uint8) int64 {
|
||||||
|
if bucket == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 1 << bucket
|
||||||
|
}
|
||||||
|
|
||||||
|
// bucketData holds data about a specific bucket for use in distTmpl.
|
||||||
|
type bucketData struct {
|
||||||
|
Lower, Upper int64
|
||||||
|
N int64
|
||||||
|
Pct, CumulativePct float64
|
||||||
|
GraphWidth int
|
||||||
|
}
|
||||||
|
|
||||||
|
// data holds data about a Distribution for use in distTmpl.
|
||||||
|
type data struct {
|
||||||
|
Buckets []*bucketData
|
||||||
|
Count, Median int64
|
||||||
|
Mean, StandardDeviation float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// maxHTMLBarWidth is the maximum width of the HTML bar for visualizing buckets.
|
||||||
|
const maxHTMLBarWidth = 350.0
|
||||||
|
|
||||||
|
// newData returns data representing h for use in distTmpl.
|
||||||
|
func (h *histogram) newData() *data {
|
||||||
|
// Force the allocation of buckets to simplify the rendering implementation
|
||||||
|
h.allocateBuckets()
|
||||||
|
// We scale the bars on the right so that the largest bar is
|
||||||
|
// maxHTMLBarWidth pixels in width.
|
||||||
|
maxBucket := int64(0)
|
||||||
|
for _, n := range h.buckets {
|
||||||
|
if n > maxBucket {
|
||||||
|
maxBucket = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total := h.total()
|
||||||
|
barsizeMult := maxHTMLBarWidth / float64(maxBucket)
|
||||||
|
var pctMult float64
|
||||||
|
if total == 0 {
|
||||||
|
pctMult = 1.0
|
||||||
|
} else {
|
||||||
|
pctMult = 100.0 / float64(total)
|
||||||
|
}
|
||||||
|
|
||||||
|
buckets := make([]*bucketData, len(h.buckets))
|
||||||
|
runningTotal := int64(0)
|
||||||
|
for i, n := range h.buckets {
|
||||||
|
if n == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
runningTotal += n
|
||||||
|
var upperBound int64
|
||||||
|
if i < bucketCount-1 {
|
||||||
|
upperBound = bucketBoundary(uint8(i + 1))
|
||||||
|
} else {
|
||||||
|
upperBound = math.MaxInt64
|
||||||
|
}
|
||||||
|
buckets[i] = &bucketData{
|
||||||
|
Lower: bucketBoundary(uint8(i)),
|
||||||
|
Upper: upperBound,
|
||||||
|
N: n,
|
||||||
|
Pct: float64(n) * pctMult,
|
||||||
|
CumulativePct: float64(runningTotal) * pctMult,
|
||||||
|
GraphWidth: int(float64(n) * barsizeMult),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &data{
|
||||||
|
Buckets: buckets,
|
||||||
|
Count: total,
|
||||||
|
Median: h.median(),
|
||||||
|
Mean: h.average(),
|
||||||
|
StandardDeviation: h.standardDeviation(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *histogram) html() template.HTML {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
if err := distTmpl().Execute(buf, h.newData()); err != nil {
|
||||||
|
buf.Reset()
|
||||||
|
log.Printf("net/trace: couldn't execute template: %v", err)
|
||||||
|
}
|
||||||
|
return template.HTML(buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
var distTmplCache *template.Template
|
||||||
|
var distTmplOnce sync.Once
|
||||||
|
|
||||||
|
func distTmpl() *template.Template {
|
||||||
|
distTmplOnce.Do(func() {
|
||||||
|
// Input: data
|
||||||
|
distTmplCache = template.Must(template.New("distTmpl").Parse(`
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0.25em">Count: {{.Count}}</td>
|
||||||
|
<td style="padding:0.25em">Mean: {{printf "%.0f" .Mean}}</td>
|
||||||
|
<td style="padding:0.25em">StdDev: {{printf "%.0f" .StandardDeviation}}</td>
|
||||||
|
<td style="padding:0.25em">Median: {{.Median}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<hr>
|
||||||
|
<table>
|
||||||
|
{{range $b := .Buckets}}
|
||||||
|
{{if $b}}
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 0 0 0.25em">[</td>
|
||||||
|
<td style="text-align:right;padding:0 0.25em">{{.Lower}},</td>
|
||||||
|
<td style="text-align:right;padding:0 0.25em">{{.Upper}})</td>
|
||||||
|
<td style="text-align:right;padding:0 0.25em">{{.N}}</td>
|
||||||
|
<td style="text-align:right;padding:0 0.25em">{{printf "%#.3f" .Pct}}%</td>
|
||||||
|
<td style="text-align:right;padding:0 0.25em">{{printf "%#.3f" .CumulativePct}}%</td>
|
||||||
|
<td><div style="background-color: blue; height: 1em; width: {{.GraphWidth}};"></div></td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</table>
|
||||||
|
`))
|
||||||
|
})
|
||||||
|
return distTmplCache
|
||||||
|
}
|
1130
vendor/golang.org/x/net/trace/trace.go
generated
vendored
Normal file
1130
vendor/golang.org/x/net/trace/trace.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
336
vendor/golang.org/x/text/secure/bidirule/bidirule.go
generated
vendored
Normal file
336
vendor/golang.org/x/text/secure/bidirule/bidirule.go
generated
vendored
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
// 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 bidirule implements the Bidi Rule defined by RFC 5893.
|
||||||
|
//
|
||||||
|
// This package is under development. The API may change without notice and
|
||||||
|
// without preserving backward compatibility.
|
||||||
|
package bidirule
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"golang.org/x/text/transform"
|
||||||
|
"golang.org/x/text/unicode/bidi"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file contains an implementation of RFC 5893: Right-to-Left Scripts for
|
||||||
|
// Internationalized Domain Names for Applications (IDNA)
|
||||||
|
//
|
||||||
|
// A label is an individual component of a domain name. Labels are usually
|
||||||
|
// shown separated by dots; for example, the domain name "www.example.com" is
|
||||||
|
// composed of three labels: "www", "example", and "com".
|
||||||
|
//
|
||||||
|
// An RTL label is a label that contains at least one character of class R, AL,
|
||||||
|
// or AN. An LTR label is any label that is not an RTL label.
|
||||||
|
//
|
||||||
|
// A "Bidi domain name" is a domain name that contains at least one RTL label.
|
||||||
|
//
|
||||||
|
// The following guarantees can be made based on the above:
|
||||||
|
//
|
||||||
|
// o In a domain name consisting of only labels that satisfy the rule,
|
||||||
|
// the requirements of Section 3 are satisfied. Note that even LTR
|
||||||
|
// labels and pure ASCII labels have to be tested.
|
||||||
|
//
|
||||||
|
// o In a domain name consisting of only LDH labels (as defined in the
|
||||||
|
// Definitions document [RFC5890]) and labels that satisfy the rule,
|
||||||
|
// the requirements of Section 3 are satisfied as long as a label
|
||||||
|
// that starts with an ASCII digit does not come after a
|
||||||
|
// right-to-left label.
|
||||||
|
//
|
||||||
|
// No guarantee is given for other combinations.
|
||||||
|
|
||||||
|
// ErrInvalid indicates a label is invalid according to the Bidi Rule.
|
||||||
|
var ErrInvalid = errors.New("bidirule: failed Bidi Rule")
|
||||||
|
|
||||||
|
type ruleState uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
ruleInitial ruleState = iota
|
||||||
|
ruleLTR
|
||||||
|
ruleLTRFinal
|
||||||
|
ruleRTL
|
||||||
|
ruleRTLFinal
|
||||||
|
ruleInvalid
|
||||||
|
)
|
||||||
|
|
||||||
|
type ruleTransition struct {
|
||||||
|
next ruleState
|
||||||
|
mask uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
var transitions = [...][2]ruleTransition{
|
||||||
|
// [2.1] The first character must be a character with Bidi property L, R, or
|
||||||
|
// AL. If it has the R or AL property, it is an RTL label; if it has the L
|
||||||
|
// property, it is an LTR label.
|
||||||
|
ruleInitial: {
|
||||||
|
{ruleLTRFinal, 1 << bidi.L},
|
||||||
|
{ruleRTLFinal, 1<<bidi.R | 1<<bidi.AL},
|
||||||
|
},
|
||||||
|
ruleRTL: {
|
||||||
|
// [2.3] In an RTL label, the end of the label must be a character with
|
||||||
|
// Bidi property R, AL, EN, or AN, followed by zero or more characters
|
||||||
|
// with Bidi property NSM.
|
||||||
|
{ruleRTLFinal, 1<<bidi.R | 1<<bidi.AL | 1<<bidi.EN | 1<<bidi.AN},
|
||||||
|
|
||||||
|
// [2.2] In an RTL label, only characters with the Bidi properties R,
|
||||||
|
// AL, AN, EN, ES, CS, ET, ON, BN, or NSM are allowed.
|
||||||
|
// We exclude the entries from [2.3]
|
||||||
|
{ruleRTL, 1<<bidi.ES | 1<<bidi.CS | 1<<bidi.ET | 1<<bidi.ON | 1<<bidi.BN | 1<<bidi.NSM},
|
||||||
|
},
|
||||||
|
ruleRTLFinal: {
|
||||||
|
// [2.3] In an RTL label, the end of the label must be a character with
|
||||||
|
// Bidi property R, AL, EN, or AN, followed by zero or more characters
|
||||||
|
// with Bidi property NSM.
|
||||||
|
{ruleRTLFinal, 1<<bidi.R | 1<<bidi.AL | 1<<bidi.EN | 1<<bidi.AN | 1<<bidi.NSM},
|
||||||
|
|
||||||
|
// [2.2] In an RTL label, only characters with the Bidi properties R,
|
||||||
|
// AL, AN, EN, ES, CS, ET, ON, BN, or NSM are allowed.
|
||||||
|
// We exclude the entries from [2.3] and NSM.
|
||||||
|
{ruleRTL, 1<<bidi.ES | 1<<bidi.CS | 1<<bidi.ET | 1<<bidi.ON | 1<<bidi.BN},
|
||||||
|
},
|
||||||
|
ruleLTR: {
|
||||||
|
// [2.6] In an LTR label, the end of the label must be a character with
|
||||||
|
// Bidi property L or EN, followed by zero or more characters with Bidi
|
||||||
|
// property NSM.
|
||||||
|
{ruleLTRFinal, 1<<bidi.L | 1<<bidi.EN},
|
||||||
|
|
||||||
|
// [2.5] In an LTR label, only characters with the Bidi properties L,
|
||||||
|
// EN, ES, CS, ET, ON, BN, or NSM are allowed.
|
||||||
|
// We exclude the entries from [2.6].
|
||||||
|
{ruleLTR, 1<<bidi.ES | 1<<bidi.CS | 1<<bidi.ET | 1<<bidi.ON | 1<<bidi.BN | 1<<bidi.NSM},
|
||||||
|
},
|
||||||
|
ruleLTRFinal: {
|
||||||
|
// [2.6] In an LTR label, the end of the label must be a character with
|
||||||
|
// Bidi property L or EN, followed by zero or more characters with Bidi
|
||||||
|
// property NSM.
|
||||||
|
{ruleLTRFinal, 1<<bidi.L | 1<<bidi.EN | 1<<bidi.NSM},
|
||||||
|
|
||||||
|
// [2.5] In an LTR label, only characters with the Bidi properties L,
|
||||||
|
// EN, ES, CS, ET, ON, BN, or NSM are allowed.
|
||||||
|
// We exclude the entries from [2.6].
|
||||||
|
{ruleLTR, 1<<bidi.ES | 1<<bidi.CS | 1<<bidi.ET | 1<<bidi.ON | 1<<bidi.BN},
|
||||||
|
},
|
||||||
|
ruleInvalid: {
|
||||||
|
{ruleInvalid, 0},
|
||||||
|
{ruleInvalid, 0},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// [2.4] In an RTL label, if an EN is present, no AN may be present, and
|
||||||
|
// vice versa.
|
||||||
|
const exclusiveRTL = uint16(1<<bidi.EN | 1<<bidi.AN)
|
||||||
|
|
||||||
|
// From RFC 5893
|
||||||
|
// An RTL label is a label that contains at least one character of type
|
||||||
|
// R, AL, or AN.
|
||||||
|
//
|
||||||
|
// An LTR label is any label that is not an RTL label.
|
||||||
|
|
||||||
|
// Direction reports the direction of the given label as defined by RFC 5893.
|
||||||
|
// The Bidi Rule does not have to be applied to labels of the category
|
||||||
|
// LeftToRight.
|
||||||
|
func Direction(b []byte) bidi.Direction {
|
||||||
|
for i := 0; i < len(b); {
|
||||||
|
e, sz := bidi.Lookup(b[i:])
|
||||||
|
if sz == 0 {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
c := e.Class()
|
||||||
|
if c == bidi.R || c == bidi.AL || c == bidi.AN {
|
||||||
|
return bidi.RightToLeft
|
||||||
|
}
|
||||||
|
i += sz
|
||||||
|
}
|
||||||
|
return bidi.LeftToRight
|
||||||
|
}
|
||||||
|
|
||||||
|
// DirectionString reports the direction of the given label as defined by RFC
|
||||||
|
// 5893. The Bidi Rule does not have to be applied to labels of the category
|
||||||
|
// LeftToRight.
|
||||||
|
func DirectionString(s string) bidi.Direction {
|
||||||
|
for i := 0; i < len(s); {
|
||||||
|
e, sz := bidi.LookupString(s[i:])
|
||||||
|
if sz == 0 {
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c := e.Class()
|
||||||
|
if c == bidi.R || c == bidi.AL || c == bidi.AN {
|
||||||
|
return bidi.RightToLeft
|
||||||
|
}
|
||||||
|
i += sz
|
||||||
|
}
|
||||||
|
return bidi.LeftToRight
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid reports whether b conforms to the BiDi rule.
|
||||||
|
func Valid(b []byte) bool {
|
||||||
|
var t Transformer
|
||||||
|
if n, ok := t.advance(b); !ok || n < len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return t.isFinal()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidString reports whether s conforms to the BiDi rule.
|
||||||
|
func ValidString(s string) bool {
|
||||||
|
var t Transformer
|
||||||
|
if n, ok := t.advanceString(s); !ok || n < len(s) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return t.isFinal()
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a Transformer that verifies that input adheres to the Bidi Rule.
|
||||||
|
func New() *Transformer {
|
||||||
|
return &Transformer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transformer implements transform.Transform.
|
||||||
|
type Transformer struct {
|
||||||
|
state ruleState
|
||||||
|
hasRTL bool
|
||||||
|
seen uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// A rule can only be violated for "Bidi Domain names", meaning if one of the
|
||||||
|
// following categories has been observed.
|
||||||
|
func (t *Transformer) isRTL() bool {
|
||||||
|
const isRTL = 1<<bidi.R | 1<<bidi.AL | 1<<bidi.AN
|
||||||
|
return t.seen&isRTL != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset implements transform.Transformer.
|
||||||
|
func (t *Transformer) Reset() { *t = Transformer{} }
|
||||||
|
|
||||||
|
// Transform implements transform.Transformer. This Transformer has state and
|
||||||
|
// needs to be reset between uses.
|
||||||
|
func (t *Transformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||||
|
if len(dst) < len(src) {
|
||||||
|
src = src[:len(dst)]
|
||||||
|
atEOF = false
|
||||||
|
err = transform.ErrShortDst
|
||||||
|
}
|
||||||
|
n, err1 := t.Span(src, atEOF)
|
||||||
|
copy(dst, src[:n])
|
||||||
|
if err == nil || err1 != nil && err1 != transform.ErrShortSrc {
|
||||||
|
err = err1
|
||||||
|
}
|
||||||
|
return n, n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Span returns the first n bytes of src that conform to the Bidi rule.
|
||||||
|
func (t *Transformer) Span(src []byte, atEOF bool) (n int, err error) {
|
||||||
|
if t.state == ruleInvalid && t.isRTL() {
|
||||||
|
return 0, ErrInvalid
|
||||||
|
}
|
||||||
|
n, ok := t.advance(src)
|
||||||
|
switch {
|
||||||
|
case !ok:
|
||||||
|
err = ErrInvalid
|
||||||
|
case n < len(src):
|
||||||
|
if !atEOF {
|
||||||
|
err = transform.ErrShortSrc
|
||||||
|
break
|
||||||
|
}
|
||||||
|
err = ErrInvalid
|
||||||
|
case !t.isFinal():
|
||||||
|
err = ErrInvalid
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precomputing the ASCII values decreases running time for the ASCII fast path
|
||||||
|
// by about 30%.
|
||||||
|
var asciiTable [128]bidi.Properties
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for i := range asciiTable {
|
||||||
|
p, _ := bidi.LookupRune(rune(i))
|
||||||
|
asciiTable[i] = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transformer) advance(s []byte) (n int, ok bool) {
|
||||||
|
var e bidi.Properties
|
||||||
|
var sz int
|
||||||
|
for n < len(s) {
|
||||||
|
if s[n] < utf8.RuneSelf {
|
||||||
|
e, sz = asciiTable[s[n]], 1
|
||||||
|
} else {
|
||||||
|
e, sz = bidi.Lookup(s[n:])
|
||||||
|
if sz <= 1 {
|
||||||
|
if sz == 1 {
|
||||||
|
// We always consider invalid UTF-8 to be invalid, even if
|
||||||
|
// the string has not yet been determined to be RTL.
|
||||||
|
// TODO: is this correct?
|
||||||
|
return n, false
|
||||||
|
}
|
||||||
|
return n, true // incomplete UTF-8 encoding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: using CompactClass would result in noticeable speedup.
|
||||||
|
// See unicode/bidi/prop.go:Properties.CompactClass.
|
||||||
|
c := uint16(1 << e.Class())
|
||||||
|
t.seen |= c
|
||||||
|
if t.seen&exclusiveRTL == exclusiveRTL {
|
||||||
|
t.state = ruleInvalid
|
||||||
|
return n, false
|
||||||
|
}
|
||||||
|
switch tr := transitions[t.state]; {
|
||||||
|
case tr[0].mask&c != 0:
|
||||||
|
t.state = tr[0].next
|
||||||
|
case tr[1].mask&c != 0:
|
||||||
|
t.state = tr[1].next
|
||||||
|
default:
|
||||||
|
t.state = ruleInvalid
|
||||||
|
if t.isRTL() {
|
||||||
|
return n, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n += sz
|
||||||
|
}
|
||||||
|
return n, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transformer) advanceString(s string) (n int, ok bool) {
|
||||||
|
var e bidi.Properties
|
||||||
|
var sz int
|
||||||
|
for n < len(s) {
|
||||||
|
if s[n] < utf8.RuneSelf {
|
||||||
|
e, sz = asciiTable[s[n]], 1
|
||||||
|
} else {
|
||||||
|
e, sz = bidi.LookupString(s[n:])
|
||||||
|
if sz <= 1 {
|
||||||
|
if sz == 1 {
|
||||||
|
return n, false // invalid UTF-8
|
||||||
|
}
|
||||||
|
return n, true // incomplete UTF-8 encoding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: using CompactClass results in noticeable speedup.
|
||||||
|
// See unicode/bidi/prop.go:Properties.CompactClass.
|
||||||
|
c := uint16(1 << e.Class())
|
||||||
|
t.seen |= c
|
||||||
|
if t.seen&exclusiveRTL == exclusiveRTL {
|
||||||
|
t.state = ruleInvalid
|
||||||
|
return n, false
|
||||||
|
}
|
||||||
|
switch tr := transitions[t.state]; {
|
||||||
|
case tr[0].mask&c != 0:
|
||||||
|
t.state = tr[0].next
|
||||||
|
case tr[1].mask&c != 0:
|
||||||
|
t.state = tr[1].next
|
||||||
|
default:
|
||||||
|
t.state = ruleInvalid
|
||||||
|
if t.isRTL() {
|
||||||
|
return n, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n += sz
|
||||||
|
}
|
||||||
|
return n, true
|
||||||
|
}
|
11
vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go
generated
vendored
Normal file
11
vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// 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.10
|
||||||
|
|
||||||
|
package bidirule
|
||||||
|
|
||||||
|
func (t *Transformer) isFinal() bool {
|
||||||
|
return t.state == ruleLTRFinal || t.state == ruleRTLFinal || t.state == ruleInitial
|
||||||
|
}
|
14
vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go
generated
vendored
Normal file
14
vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// 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.10
|
||||||
|
|
||||||
|
package bidirule
|
||||||
|
|
||||||
|
func (t *Transformer) isFinal() bool {
|
||||||
|
if !t.isRTL() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return t.state == ruleLTRFinal || t.state == ruleRTLFinal || t.state == ruleInitial
|
||||||
|
}
|
359
vendor/golang.org/x/text/unicode/bidi/bidi.go
generated
vendored
Normal file
359
vendor/golang.org/x/text/unicode/bidi/bidi.go
generated
vendored
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//go:generate go run gen.go gen_trieval.go gen_ranges.go
|
||||||
|
|
||||||
|
// Package bidi contains functionality for bidirectional text support.
|
||||||
|
//
|
||||||
|
// See https://www.unicode.org/reports/tr9.
|
||||||
|
//
|
||||||
|
// NOTE: UNDER CONSTRUCTION. This API may change in backwards incompatible ways
|
||||||
|
// and without notice.
|
||||||
|
package bidi // import "golang.org/x/text/unicode/bidi"
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// - Transformer for reordering?
|
||||||
|
// - Transformer (validator, really) for Bidi Rule.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This API tries to avoid dealing with embedding levels for now. Under the hood
|
||||||
|
// these will be computed, but the question is to which extent the user should
|
||||||
|
// know they exist. We should at some point allow the user to specify an
|
||||||
|
// embedding hierarchy, though.
|
||||||
|
|
||||||
|
// A Direction indicates the overall flow of text.
|
||||||
|
type Direction int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// LeftToRight indicates the text contains no right-to-left characters and
|
||||||
|
// that either there are some left-to-right characters or the option
|
||||||
|
// DefaultDirection(LeftToRight) was passed.
|
||||||
|
LeftToRight Direction = iota
|
||||||
|
|
||||||
|
// RightToLeft indicates the text contains no left-to-right characters and
|
||||||
|
// that either there are some right-to-left characters or the option
|
||||||
|
// DefaultDirection(RightToLeft) was passed.
|
||||||
|
RightToLeft
|
||||||
|
|
||||||
|
// Mixed indicates text contains both left-to-right and right-to-left
|
||||||
|
// characters.
|
||||||
|
Mixed
|
||||||
|
|
||||||
|
// Neutral means that text contains no left-to-right and right-to-left
|
||||||
|
// characters and that no default direction has been set.
|
||||||
|
Neutral
|
||||||
|
)
|
||||||
|
|
||||||
|
type options struct {
|
||||||
|
defaultDirection Direction
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Option is an option for Bidi processing.
|
||||||
|
type Option func(*options)
|
||||||
|
|
||||||
|
// ICU allows the user to define embedding levels. This may be used, for example,
|
||||||
|
// to use hierarchical structure of markup languages to define embeddings.
|
||||||
|
// The following option may be a way to expose this functionality in this API.
|
||||||
|
// // LevelFunc sets a function that associates nesting levels with the given text.
|
||||||
|
// // The levels function will be called with monotonically increasing values for p.
|
||||||
|
// func LevelFunc(levels func(p int) int) Option {
|
||||||
|
// panic("unimplemented")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// DefaultDirection sets the default direction for a Paragraph. The direction is
|
||||||
|
// overridden if the text contains directional characters.
|
||||||
|
func DefaultDirection(d Direction) Option {
|
||||||
|
return func(opts *options) {
|
||||||
|
opts.defaultDirection = d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Paragraph holds a single Paragraph for Bidi processing.
|
||||||
|
type Paragraph struct {
|
||||||
|
p []byte
|
||||||
|
o Ordering
|
||||||
|
opts []Option
|
||||||
|
types []Class
|
||||||
|
pairTypes []bracketType
|
||||||
|
pairValues []rune
|
||||||
|
runes []rune
|
||||||
|
options options
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the p.pairTypes, p.pairValues and p.types from the input previously
|
||||||
|
// set by p.SetBytes() or p.SetString(). Also limit the input up to (and including) a paragraph
|
||||||
|
// separator (bidi class B).
|
||||||
|
//
|
||||||
|
// The function p.Order() needs these values to be set, so this preparation could be postponed.
|
||||||
|
// But since the SetBytes and SetStrings functions return the length of the input up to the paragraph
|
||||||
|
// separator, the whole input needs to be processed anyway and should not be done twice.
|
||||||
|
//
|
||||||
|
// The function has the same return values as SetBytes() / SetString()
|
||||||
|
func (p *Paragraph) prepareInput() (n int, err error) {
|
||||||
|
p.runes = bytes.Runes(p.p)
|
||||||
|
bytecount := 0
|
||||||
|
// clear slices from previous SetString or SetBytes
|
||||||
|
p.pairTypes = nil
|
||||||
|
p.pairValues = nil
|
||||||
|
p.types = nil
|
||||||
|
|
||||||
|
for _, r := range p.runes {
|
||||||
|
props, i := LookupRune(r)
|
||||||
|
bytecount += i
|
||||||
|
cls := props.Class()
|
||||||
|
if cls == B {
|
||||||
|
return bytecount, nil
|
||||||
|
}
|
||||||
|
p.types = append(p.types, cls)
|
||||||
|
if props.IsOpeningBracket() {
|
||||||
|
p.pairTypes = append(p.pairTypes, bpOpen)
|
||||||
|
p.pairValues = append(p.pairValues, r)
|
||||||
|
} else if props.IsBracket() {
|
||||||
|
// this must be a closing bracket,
|
||||||
|
// since IsOpeningBracket is not true
|
||||||
|
p.pairTypes = append(p.pairTypes, bpClose)
|
||||||
|
p.pairValues = append(p.pairValues, r)
|
||||||
|
} else {
|
||||||
|
p.pairTypes = append(p.pairTypes, bpNone)
|
||||||
|
p.pairValues = append(p.pairValues, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bytecount, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBytes configures p for the given paragraph text. It replaces text
|
||||||
|
// previously set by SetBytes or SetString. If b contains a paragraph separator
|
||||||
|
// it will only process the first paragraph and report the number of bytes
|
||||||
|
// consumed from b including this separator. Error may be non-nil if options are
|
||||||
|
// given.
|
||||||
|
func (p *Paragraph) SetBytes(b []byte, opts ...Option) (n int, err error) {
|
||||||
|
p.p = b
|
||||||
|
p.opts = opts
|
||||||
|
return p.prepareInput()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetString configures s for the given paragraph text. It replaces text
|
||||||
|
// previously set by SetBytes or SetString. If s contains a paragraph separator
|
||||||
|
// it will only process the first paragraph and report the number of bytes
|
||||||
|
// consumed from s including this separator. Error may be non-nil if options are
|
||||||
|
// given.
|
||||||
|
func (p *Paragraph) SetString(s string, opts ...Option) (n int, err error) {
|
||||||
|
p.p = []byte(s)
|
||||||
|
p.opts = opts
|
||||||
|
return p.prepareInput()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLeftToRight reports whether the principle direction of rendering for this
|
||||||
|
// paragraphs is left-to-right. If this returns false, the principle direction
|
||||||
|
// of rendering is right-to-left.
|
||||||
|
func (p *Paragraph) IsLeftToRight() bool {
|
||||||
|
return p.Direction() == LeftToRight
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direction returns the direction of the text of this paragraph.
|
||||||
|
//
|
||||||
|
// The direction may be LeftToRight, RightToLeft, Mixed, or Neutral.
|
||||||
|
func (p *Paragraph) Direction() Direction {
|
||||||
|
return p.o.Direction()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: what happens if the position is > len(input)? This should return an error.
|
||||||
|
|
||||||
|
// RunAt reports the Run at the given position of the input text.
|
||||||
|
//
|
||||||
|
// This method can be used for computing line breaks on paragraphs.
|
||||||
|
func (p *Paragraph) RunAt(pos int) Run {
|
||||||
|
c := 0
|
||||||
|
runNumber := 0
|
||||||
|
for i, r := range p.o.runes {
|
||||||
|
c += len(r)
|
||||||
|
if pos < c {
|
||||||
|
runNumber = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.o.Run(runNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateOrdering(levels []level, runes []rune) Ordering {
|
||||||
|
var curDir Direction
|
||||||
|
|
||||||
|
prevDir := Neutral
|
||||||
|
prevI := 0
|
||||||
|
|
||||||
|
o := Ordering{}
|
||||||
|
// lvl = 0,2,4,...: left to right
|
||||||
|
// lvl = 1,3,5,...: right to left
|
||||||
|
for i, lvl := range levels {
|
||||||
|
if lvl%2 == 0 {
|
||||||
|
curDir = LeftToRight
|
||||||
|
} else {
|
||||||
|
curDir = RightToLeft
|
||||||
|
}
|
||||||
|
if curDir != prevDir {
|
||||||
|
if i > 0 {
|
||||||
|
o.runes = append(o.runes, runes[prevI:i])
|
||||||
|
o.directions = append(o.directions, prevDir)
|
||||||
|
o.startpos = append(o.startpos, prevI)
|
||||||
|
}
|
||||||
|
prevI = i
|
||||||
|
prevDir = curDir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
o.runes = append(o.runes, runes[prevI:])
|
||||||
|
o.directions = append(o.directions, prevDir)
|
||||||
|
o.startpos = append(o.startpos, prevI)
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order computes the visual ordering of all the runs in a Paragraph.
|
||||||
|
func (p *Paragraph) Order() (Ordering, error) {
|
||||||
|
if len(p.types) == 0 {
|
||||||
|
return Ordering{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fn := range p.opts {
|
||||||
|
fn(&p.options)
|
||||||
|
}
|
||||||
|
lvl := level(-1)
|
||||||
|
if p.options.defaultDirection == RightToLeft {
|
||||||
|
lvl = 1
|
||||||
|
}
|
||||||
|
para, err := newParagraph(p.types, p.pairTypes, p.pairValues, lvl)
|
||||||
|
if err != nil {
|
||||||
|
return Ordering{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
levels := para.getLevels([]int{len(p.types)})
|
||||||
|
|
||||||
|
p.o = calculateOrdering(levels, p.runes)
|
||||||
|
return p.o, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line computes the visual ordering of runs for a single line starting and
|
||||||
|
// ending at the given positions in the original text.
|
||||||
|
func (p *Paragraph) Line(start, end int) (Ordering, error) {
|
||||||
|
lineTypes := p.types[start:end]
|
||||||
|
para, err := newParagraph(lineTypes, p.pairTypes[start:end], p.pairValues[start:end], -1)
|
||||||
|
if err != nil {
|
||||||
|
return Ordering{}, err
|
||||||
|
}
|
||||||
|
levels := para.getLevels([]int{len(lineTypes)})
|
||||||
|
o := calculateOrdering(levels, p.runes[start:end])
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Ordering holds the computed visual order of runs of a Paragraph. Calling
|
||||||
|
// SetBytes or SetString on the originating Paragraph invalidates an Ordering.
|
||||||
|
// The methods of an Ordering should only be called by one goroutine at a time.
|
||||||
|
type Ordering struct {
|
||||||
|
runes [][]rune
|
||||||
|
directions []Direction
|
||||||
|
startpos []int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direction reports the directionality of the runs.
|
||||||
|
//
|
||||||
|
// The direction may be LeftToRight, RightToLeft, Mixed, or Neutral.
|
||||||
|
func (o *Ordering) Direction() Direction {
|
||||||
|
return o.directions[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumRuns returns the number of runs.
|
||||||
|
func (o *Ordering) NumRuns() int {
|
||||||
|
return len(o.runes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run returns the ith run within the ordering.
|
||||||
|
func (o *Ordering) Run(i int) Run {
|
||||||
|
r := Run{
|
||||||
|
runes: o.runes[i],
|
||||||
|
direction: o.directions[i],
|
||||||
|
startpos: o.startpos[i],
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: perhaps with options.
|
||||||
|
// // Reorder creates a reader that reads the runes in visual order per character.
|
||||||
|
// // Modifiers remain after the runes they modify.
|
||||||
|
// func (l *Runs) Reorder() io.Reader {
|
||||||
|
// panic("unimplemented")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// A Run is a continuous sequence of characters of a single direction.
|
||||||
|
type Run struct {
|
||||||
|
runes []rune
|
||||||
|
direction Direction
|
||||||
|
startpos int
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the text of the run in its original order.
|
||||||
|
func (r *Run) String() string {
|
||||||
|
return string(r.runes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the text of the run in its original order.
|
||||||
|
func (r *Run) Bytes() []byte {
|
||||||
|
return []byte(r.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: methods for
|
||||||
|
// - Display order
|
||||||
|
// - headers and footers
|
||||||
|
// - bracket replacement.
|
||||||
|
|
||||||
|
// Direction reports the direction of the run.
|
||||||
|
func (r *Run) Direction() Direction {
|
||||||
|
return r.direction
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pos returns the position of the Run within the text passed to SetBytes or SetString of the
|
||||||
|
// originating Paragraph value.
|
||||||
|
func (r *Run) Pos() (start, end int) {
|
||||||
|
return r.startpos, r.startpos + len(r.runes) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendReverse reverses the order of characters of in, appends them to out,
|
||||||
|
// and returns the result. Modifiers will still follow the runes they modify.
|
||||||
|
// Brackets are replaced with their counterparts.
|
||||||
|
func AppendReverse(out, in []byte) []byte {
|
||||||
|
ret := make([]byte, len(in)+len(out))
|
||||||
|
copy(ret, out)
|
||||||
|
inRunes := bytes.Runes(in)
|
||||||
|
|
||||||
|
for i, r := range inRunes {
|
||||||
|
prop, _ := LookupRune(r)
|
||||||
|
if prop.IsBracket() {
|
||||||
|
inRunes[i] = prop.reverseBracket(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, j := 0, len(inRunes)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
inRunes[i], inRunes[j] = inRunes[j], inRunes[i]
|
||||||
|
}
|
||||||
|
copy(ret[len(out):], string(inRunes))
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReverseString reverses the order of characters in s and returns a new string.
|
||||||
|
// Modifiers will still follow the runes they modify. Brackets are replaced with
|
||||||
|
// their counterparts.
|
||||||
|
func ReverseString(s string) string {
|
||||||
|
input := []rune(s)
|
||||||
|
li := len(input)
|
||||||
|
ret := make([]rune, li)
|
||||||
|
for i, r := range input {
|
||||||
|
prop, _ := LookupRune(r)
|
||||||
|
if prop.IsBracket() {
|
||||||
|
ret[li-i-1] = prop.reverseBracket(r)
|
||||||
|
} else {
|
||||||
|
ret[li-i-1] = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(ret)
|
||||||
|
}
|
335
vendor/golang.org/x/text/unicode/bidi/bracket.go
generated
vendored
Normal file
335
vendor/golang.org/x/text/unicode/bidi/bracket.go
generated
vendored
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package bidi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file contains a port of the reference implementation of the
|
||||||
|
// Bidi Parentheses Algorithm:
|
||||||
|
// https://www.unicode.org/Public/PROGRAMS/BidiReferenceJava/BidiPBAReference.java
|
||||||
|
//
|
||||||
|
// The implementation in this file covers definitions BD14-BD16 and rule N0
|
||||||
|
// of UAX#9.
|
||||||
|
//
|
||||||
|
// Some preprocessing is done for each rune before data is passed to this
|
||||||
|
// algorithm:
|
||||||
|
// - opening and closing brackets are identified
|
||||||
|
// - a bracket pair type, like '(' and ')' is assigned a unique identifier that
|
||||||
|
// is identical for the opening and closing bracket. It is left to do these
|
||||||
|
// mappings.
|
||||||
|
// - The BPA algorithm requires that bracket characters that are canonical
|
||||||
|
// equivalents of each other be able to be substituted for each other.
|
||||||
|
// It is the responsibility of the caller to do this canonicalization.
|
||||||
|
//
|
||||||
|
// In implementing BD16, this implementation departs slightly from the "logical"
|
||||||
|
// algorithm defined in UAX#9. In particular, the stack referenced there
|
||||||
|
// supports operations that go beyond a "basic" stack. An equivalent
|
||||||
|
// implementation based on a linked list is used here.
|
||||||
|
|
||||||
|
// Bidi_Paired_Bracket_Type
|
||||||
|
// BD14. An opening paired bracket is a character whose
|
||||||
|
// Bidi_Paired_Bracket_Type property value is Open.
|
||||||
|
//
|
||||||
|
// BD15. A closing paired bracket is a character whose
|
||||||
|
// Bidi_Paired_Bracket_Type property value is Close.
|
||||||
|
type bracketType byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
bpNone bracketType = iota
|
||||||
|
bpOpen
|
||||||
|
bpClose
|
||||||
|
)
|
||||||
|
|
||||||
|
// bracketPair holds a pair of index values for opening and closing bracket
|
||||||
|
// location of a bracket pair.
|
||||||
|
type bracketPair struct {
|
||||||
|
opener int
|
||||||
|
closer int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bracketPair) String() string {
|
||||||
|
return fmt.Sprintf("(%v, %v)", b.opener, b.closer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bracketPairs is a slice of bracketPairs with a sort.Interface implementation.
|
||||||
|
type bracketPairs []bracketPair
|
||||||
|
|
||||||
|
func (b bracketPairs) Len() int { return len(b) }
|
||||||
|
func (b bracketPairs) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||||
|
func (b bracketPairs) Less(i, j int) bool { return b[i].opener < b[j].opener }
|
||||||
|
|
||||||
|
// resolvePairedBrackets runs the paired bracket part of the UBA algorithm.
|
||||||
|
//
|
||||||
|
// For each rune, it takes the indexes into the original string, the class the
|
||||||
|
// bracket type (in pairTypes) and the bracket identifier (pairValues). It also
|
||||||
|
// takes the direction type for the start-of-sentence and the embedding level.
|
||||||
|
//
|
||||||
|
// The identifiers for bracket types are the rune of the canonicalized opening
|
||||||
|
// bracket for brackets (open or close) or 0 for runes that are not brackets.
|
||||||
|
func resolvePairedBrackets(s *isolatingRunSequence) {
|
||||||
|
p := bracketPairer{
|
||||||
|
sos: s.sos,
|
||||||
|
openers: list.New(),
|
||||||
|
codesIsolatedRun: s.types,
|
||||||
|
indexes: s.indexes,
|
||||||
|
}
|
||||||
|
dirEmbed := L
|
||||||
|
if s.level&1 != 0 {
|
||||||
|
dirEmbed = R
|
||||||
|
}
|
||||||
|
p.locateBrackets(s.p.pairTypes, s.p.pairValues)
|
||||||
|
p.resolveBrackets(dirEmbed, s.p.initialTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
type bracketPairer struct {
|
||||||
|
sos Class // direction corresponding to start of sequence
|
||||||
|
|
||||||
|
// The following is a restatement of BD 16 using non-algorithmic language.
|
||||||
|
//
|
||||||
|
// A bracket pair is a pair of characters consisting of an opening
|
||||||
|
// paired bracket and a closing paired bracket such that the
|
||||||
|
// Bidi_Paired_Bracket property value of the former equals the latter,
|
||||||
|
// subject to the following constraints.
|
||||||
|
// - both characters of a pair occur in the same isolating run sequence
|
||||||
|
// - the closing character of a pair follows the opening character
|
||||||
|
// - any bracket character can belong at most to one pair, the earliest possible one
|
||||||
|
// - any bracket character not part of a pair is treated like an ordinary character
|
||||||
|
// - pairs may nest properly, but their spans may not overlap otherwise
|
||||||
|
|
||||||
|
// Bracket characters with canonical decompositions are supposed to be
|
||||||
|
// treated as if they had been normalized, to allow normalized and non-
|
||||||
|
// normalized text to give the same result. In this implementation that step
|
||||||
|
// is pushed out to the caller. The caller has to ensure that the pairValue
|
||||||
|
// slices contain the rune of the opening bracket after normalization for
|
||||||
|
// any opening or closing bracket.
|
||||||
|
|
||||||
|
openers *list.List // list of positions for opening brackets
|
||||||
|
|
||||||
|
// bracket pair positions sorted by location of opening bracket
|
||||||
|
pairPositions bracketPairs
|
||||||
|
|
||||||
|
codesIsolatedRun []Class // directional bidi codes for an isolated run
|
||||||
|
indexes []int // array of index values into the original string
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchOpener reports whether characters at given positions form a matching
|
||||||
|
// bracket pair.
|
||||||
|
func (p *bracketPairer) matchOpener(pairValues []rune, opener, closer int) bool {
|
||||||
|
return pairValues[p.indexes[opener]] == pairValues[p.indexes[closer]]
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxPairingDepth = 63
|
||||||
|
|
||||||
|
// locateBrackets locates matching bracket pairs according to BD16.
|
||||||
|
//
|
||||||
|
// This implementation uses a linked list instead of a stack, because, while
|
||||||
|
// elements are added at the front (like a push) they are not generally removed
|
||||||
|
// in atomic 'pop' operations, reducing the benefit of the stack archetype.
|
||||||
|
func (p *bracketPairer) locateBrackets(pairTypes []bracketType, pairValues []rune) {
|
||||||
|
// traverse the run
|
||||||
|
// do that explicitly (not in a for-each) so we can record position
|
||||||
|
for i, index := range p.indexes {
|
||||||
|
|
||||||
|
// look at the bracket type for each character
|
||||||
|
if pairTypes[index] == bpNone || p.codesIsolatedRun[i] != ON {
|
||||||
|
// continue scanning
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch pairTypes[index] {
|
||||||
|
case bpOpen:
|
||||||
|
// check if maximum pairing depth reached
|
||||||
|
if p.openers.Len() == maxPairingDepth {
|
||||||
|
p.openers.Init()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// remember opener location, most recent first
|
||||||
|
p.openers.PushFront(i)
|
||||||
|
|
||||||
|
case bpClose:
|
||||||
|
// see if there is a match
|
||||||
|
count := 0
|
||||||
|
for elem := p.openers.Front(); elem != nil; elem = elem.Next() {
|
||||||
|
count++
|
||||||
|
opener := elem.Value.(int)
|
||||||
|
if p.matchOpener(pairValues, opener, i) {
|
||||||
|
// if the opener matches, add nested pair to the ordered list
|
||||||
|
p.pairPositions = append(p.pairPositions, bracketPair{opener, i})
|
||||||
|
// remove up to and including matched opener
|
||||||
|
for ; count > 0; count-- {
|
||||||
|
p.openers.Remove(p.openers.Front())
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Sort(p.pairPositions)
|
||||||
|
// if we get here, the closing bracket matched no openers
|
||||||
|
// and gets ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bracket pairs within an isolating run sequence are processed as units so
|
||||||
|
// that both the opening and the closing paired bracket in a pair resolve to
|
||||||
|
// the same direction.
|
||||||
|
//
|
||||||
|
// N0. Process bracket pairs in an isolating run sequence sequentially in
|
||||||
|
// the logical order of the text positions of the opening paired brackets
|
||||||
|
// using the logic given below. Within this scope, bidirectional types EN
|
||||||
|
// and AN are treated as R.
|
||||||
|
//
|
||||||
|
// Identify the bracket pairs in the current isolating run sequence
|
||||||
|
// according to BD16. For each bracket-pair element in the list of pairs of
|
||||||
|
// text positions:
|
||||||
|
//
|
||||||
|
// a Inspect the bidirectional types of the characters enclosed within the
|
||||||
|
// bracket pair.
|
||||||
|
//
|
||||||
|
// b If any strong type (either L or R) matching the embedding direction is
|
||||||
|
// found, set the type for both brackets in the pair to match the embedding
|
||||||
|
// direction.
|
||||||
|
//
|
||||||
|
// o [ e ] o -> o e e e o
|
||||||
|
//
|
||||||
|
// o [ o e ] -> o e o e e
|
||||||
|
//
|
||||||
|
// o [ NI e ] -> o e NI e e
|
||||||
|
//
|
||||||
|
// c Otherwise, if a strong type (opposite the embedding direction) is
|
||||||
|
// found, test for adjacent strong types as follows: 1 First, check
|
||||||
|
// backwards before the opening paired bracket until the first strong type
|
||||||
|
// (L, R, or sos) is found. If that first preceding strong type is opposite
|
||||||
|
// the embedding direction, then set the type for both brackets in the pair
|
||||||
|
// to that type. 2 Otherwise, set the type for both brackets in the pair to
|
||||||
|
// the embedding direction.
|
||||||
|
//
|
||||||
|
// o [ o ] e -> o o o o e
|
||||||
|
//
|
||||||
|
// o [ o NI ] o -> o o o NI o o
|
||||||
|
//
|
||||||
|
// e [ o ] o -> e e o e o
|
||||||
|
//
|
||||||
|
// e [ o ] e -> e e o e e
|
||||||
|
//
|
||||||
|
// e ( o [ o ] NI ) e -> e e o o o o NI e e
|
||||||
|
//
|
||||||
|
// d Otherwise, do not set the type for the current bracket pair. Note that
|
||||||
|
// if the enclosed text contains no strong types the paired brackets will
|
||||||
|
// both resolve to the same level when resolved individually using rules N1
|
||||||
|
// and N2.
|
||||||
|
//
|
||||||
|
// e ( NI ) o -> e ( NI ) o
|
||||||
|
|
||||||
|
// getStrongTypeN0 maps character's directional code to strong type as required
|
||||||
|
// by rule N0.
|
||||||
|
//
|
||||||
|
// TODO: have separate type for "strong" directionality.
|
||||||
|
func (p *bracketPairer) getStrongTypeN0(index int) Class {
|
||||||
|
switch p.codesIsolatedRun[index] {
|
||||||
|
// in the scope of N0, number types are treated as R
|
||||||
|
case EN, AN, AL, R:
|
||||||
|
return R
|
||||||
|
case L:
|
||||||
|
return L
|
||||||
|
default:
|
||||||
|
return ON
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// classifyPairContent reports the strong types contained inside a Bracket Pair,
|
||||||
|
// assuming the given embedding direction.
|
||||||
|
//
|
||||||
|
// It returns ON if no strong type is found. If a single strong type is found,
|
||||||
|
// it returns this type. Otherwise it returns the embedding direction.
|
||||||
|
//
|
||||||
|
// TODO: use separate type for "strong" directionality.
|
||||||
|
func (p *bracketPairer) classifyPairContent(loc bracketPair, dirEmbed Class) Class {
|
||||||
|
dirOpposite := ON
|
||||||
|
for i := loc.opener + 1; i < loc.closer; i++ {
|
||||||
|
dir := p.getStrongTypeN0(i)
|
||||||
|
if dir == ON {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if dir == dirEmbed {
|
||||||
|
return dir // type matching embedding direction found
|
||||||
|
}
|
||||||
|
dirOpposite = dir
|
||||||
|
}
|
||||||
|
// return ON if no strong type found, or class opposite to dirEmbed
|
||||||
|
return dirOpposite
|
||||||
|
}
|
||||||
|
|
||||||
|
// classBeforePair determines which strong types are present before a Bracket
|
||||||
|
// Pair. Return R or L if strong type found, otherwise ON.
|
||||||
|
func (p *bracketPairer) classBeforePair(loc bracketPair) Class {
|
||||||
|
for i := loc.opener - 1; i >= 0; i-- {
|
||||||
|
if dir := p.getStrongTypeN0(i); dir != ON {
|
||||||
|
return dir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no strong types found, return sos
|
||||||
|
return p.sos
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignBracketType implements rule N0 for a single bracket pair.
|
||||||
|
func (p *bracketPairer) assignBracketType(loc bracketPair, dirEmbed Class, initialTypes []Class) {
|
||||||
|
// rule "N0, a", inspect contents of pair
|
||||||
|
dirPair := p.classifyPairContent(loc, dirEmbed)
|
||||||
|
|
||||||
|
// dirPair is now L, R, or N (no strong type found)
|
||||||
|
|
||||||
|
// the following logical tests are performed out of order compared to
|
||||||
|
// the statement of the rules but yield the same results
|
||||||
|
if dirPair == ON {
|
||||||
|
return // case "d" - nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
if dirPair != dirEmbed {
|
||||||
|
// case "c": strong type found, opposite - check before (c.1)
|
||||||
|
dirPair = p.classBeforePair(loc)
|
||||||
|
if dirPair == dirEmbed || dirPair == ON {
|
||||||
|
// no strong opposite type found before - use embedding (c.2)
|
||||||
|
dirPair = dirEmbed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// else: case "b", strong type found matching embedding,
|
||||||
|
// no explicit action needed, as dirPair is already set to embedding
|
||||||
|
// direction
|
||||||
|
|
||||||
|
// set the bracket types to the type found
|
||||||
|
p.setBracketsToType(loc, dirPair, initialTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *bracketPairer) setBracketsToType(loc bracketPair, dirPair Class, initialTypes []Class) {
|
||||||
|
p.codesIsolatedRun[loc.opener] = dirPair
|
||||||
|
p.codesIsolatedRun[loc.closer] = dirPair
|
||||||
|
|
||||||
|
for i := loc.opener + 1; i < loc.closer; i++ {
|
||||||
|
index := p.indexes[i]
|
||||||
|
if initialTypes[index] != NSM {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p.codesIsolatedRun[i] = dirPair
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := loc.closer + 1; i < len(p.indexes); i++ {
|
||||||
|
index := p.indexes[i]
|
||||||
|
if initialTypes[index] != NSM {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p.codesIsolatedRun[i] = dirPair
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveBrackets implements rule N0 for a list of pairs.
|
||||||
|
func (p *bracketPairer) resolveBrackets(dirEmbed Class, initialTypes []Class) {
|
||||||
|
for _, loc := range p.pairPositions {
|
||||||
|
p.assignBracketType(loc, dirEmbed, initialTypes)
|
||||||
|
}
|
||||||
|
}
|
1071
vendor/golang.org/x/text/unicode/bidi/core.go
generated
vendored
Normal file
1071
vendor/golang.org/x/text/unicode/bidi/core.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
206
vendor/golang.org/x/text/unicode/bidi/prop.go
generated
vendored
Normal file
206
vendor/golang.org/x/text/unicode/bidi/prop.go
generated
vendored
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
// 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 bidi
|
||||||
|
|
||||||
|
import "unicode/utf8"
|
||||||
|
|
||||||
|
// Properties provides access to BiDi properties of runes.
|
||||||
|
type Properties struct {
|
||||||
|
entry uint8
|
||||||
|
last uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
var trie = newBidiTrie(0)
|
||||||
|
|
||||||
|
// TODO: using this for bidirule reduces the running time by about 5%. Consider
|
||||||
|
// if this is worth exposing or if we can find a way to speed up the Class
|
||||||
|
// method.
|
||||||
|
//
|
||||||
|
// // CompactClass is like Class, but maps all of the BiDi control classes
|
||||||
|
// // (LRO, RLO, LRE, RLE, PDF, LRI, RLI, FSI, PDI) to the class Control.
|
||||||
|
// func (p Properties) CompactClass() Class {
|
||||||
|
// return Class(p.entry & 0x0F)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Class returns the Bidi class for p.
|
||||||
|
func (p Properties) Class() Class {
|
||||||
|
c := Class(p.entry & 0x0F)
|
||||||
|
if c == Control {
|
||||||
|
c = controlByteToClass[p.last&0xF]
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBracket reports whether the rune is a bracket.
|
||||||
|
func (p Properties) IsBracket() bool { return p.entry&0xF0 != 0 }
|
||||||
|
|
||||||
|
// IsOpeningBracket reports whether the rune is an opening bracket.
|
||||||
|
// IsBracket must return true.
|
||||||
|
func (p Properties) IsOpeningBracket() bool { return p.entry&openMask != 0 }
|
||||||
|
|
||||||
|
// TODO: find a better API and expose.
|
||||||
|
func (p Properties) reverseBracket(r rune) rune {
|
||||||
|
return xorMasks[p.entry>>xorMaskShift] ^ r
|
||||||
|
}
|
||||||
|
|
||||||
|
var controlByteToClass = [16]Class{
|
||||||
|
0xD: LRO, // U+202D LeftToRightOverride,
|
||||||
|
0xE: RLO, // U+202E RightToLeftOverride,
|
||||||
|
0xA: LRE, // U+202A LeftToRightEmbedding,
|
||||||
|
0xB: RLE, // U+202B RightToLeftEmbedding,
|
||||||
|
0xC: PDF, // U+202C PopDirectionalFormat,
|
||||||
|
0x6: LRI, // U+2066 LeftToRightIsolate,
|
||||||
|
0x7: RLI, // U+2067 RightToLeftIsolate,
|
||||||
|
0x8: FSI, // U+2068 FirstStrongIsolate,
|
||||||
|
0x9: PDI, // U+2069 PopDirectionalIsolate,
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupRune returns properties for r.
|
||||||
|
func LookupRune(r rune) (p Properties, size int) {
|
||||||
|
var buf [4]byte
|
||||||
|
n := utf8.EncodeRune(buf[:], r)
|
||||||
|
return Lookup(buf[:n])
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: these lookup methods are based on the generated trie code. The returned
|
||||||
|
// sizes have slightly different semantics from the generated code, in that it
|
||||||
|
// always returns size==1 for an illegal UTF-8 byte (instead of the length
|
||||||
|
// of the maximum invalid subsequence). Most Transformers, like unicode/norm,
|
||||||
|
// leave invalid UTF-8 untouched, in which case it has performance benefits to
|
||||||
|
// do so (without changing the semantics). Bidi requires the semantics used here
|
||||||
|
// for the bidirule implementation to be compatible with the Go semantics.
|
||||||
|
// They ultimately should perhaps be adopted by all trie implementations, for
|
||||||
|
// convenience sake.
|
||||||
|
// This unrolled code also boosts performance of the secure/bidirule package by
|
||||||
|
// about 30%.
|
||||||
|
// So, to remove this code:
|
||||||
|
// - add option to trie generator to define return type.
|
||||||
|
// - always return 1 byte size for ill-formed UTF-8 runes.
|
||||||
|
|
||||||
|
// Lookup returns properties for the first rune in s and the width in bytes of
|
||||||
|
// its encoding. The size will be 0 if s does not hold enough bytes to complete
|
||||||
|
// the encoding.
|
||||||
|
func Lookup(s []byte) (p Properties, sz int) {
|
||||||
|
c0 := s[0]
|
||||||
|
switch {
|
||||||
|
case c0 < 0x80: // is ASCII
|
||||||
|
return Properties{entry: bidiValues[c0]}, 1
|
||||||
|
case c0 < 0xC2:
|
||||||
|
return Properties{}, 1
|
||||||
|
case c0 < 0xE0: // 2-byte UTF-8
|
||||||
|
if len(s) < 2 {
|
||||||
|
return Properties{}, 0
|
||||||
|
}
|
||||||
|
i := bidiIndex[c0]
|
||||||
|
c1 := s[1]
|
||||||
|
if c1 < 0x80 || 0xC0 <= c1 {
|
||||||
|
return Properties{}, 1
|
||||||
|
}
|
||||||
|
return Properties{entry: trie.lookupValue(uint32(i), c1)}, 2
|
||||||
|
case c0 < 0xF0: // 3-byte UTF-8
|
||||||
|
if len(s) < 3 {
|
||||||
|
return Properties{}, 0
|
||||||
|
}
|
||||||
|
i := bidiIndex[c0]
|
||||||
|
c1 := s[1]
|
||||||
|
if c1 < 0x80 || 0xC0 <= c1 {
|
||||||
|
return Properties{}, 1
|
||||||
|
}
|
||||||
|
o := uint32(i)<<6 + uint32(c1)
|
||||||
|
i = bidiIndex[o]
|
||||||
|
c2 := s[2]
|
||||||
|
if c2 < 0x80 || 0xC0 <= c2 {
|
||||||
|
return Properties{}, 1
|
||||||
|
}
|
||||||
|
return Properties{entry: trie.lookupValue(uint32(i), c2), last: c2}, 3
|
||||||
|
case c0 < 0xF8: // 4-byte UTF-8
|
||||||
|
if len(s) < 4 {
|
||||||
|
return Properties{}, 0
|
||||||
|
}
|
||||||
|
i := bidiIndex[c0]
|
||||||
|
c1 := s[1]
|
||||||
|
if c1 < 0x80 || 0xC0 <= c1 {
|
||||||
|
return Properties{}, 1
|
||||||
|
}
|
||||||
|
o := uint32(i)<<6 + uint32(c1)
|
||||||
|
i = bidiIndex[o]
|
||||||
|
c2 := s[2]
|
||||||
|
if c2 < 0x80 || 0xC0 <= c2 {
|
||||||
|
return Properties{}, 1
|
||||||
|
}
|
||||||
|
o = uint32(i)<<6 + uint32(c2)
|
||||||
|
i = bidiIndex[o]
|
||||||
|
c3 := s[3]
|
||||||
|
if c3 < 0x80 || 0xC0 <= c3 {
|
||||||
|
return Properties{}, 1
|
||||||
|
}
|
||||||
|
return Properties{entry: trie.lookupValue(uint32(i), c3)}, 4
|
||||||
|
}
|
||||||
|
// Illegal rune
|
||||||
|
return Properties{}, 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupString returns properties for the first rune in s and the width in
|
||||||
|
// bytes of its encoding. The size will be 0 if s does not hold enough bytes to
|
||||||
|
// complete the encoding.
|
||||||
|
func LookupString(s string) (p Properties, sz int) {
|
||||||
|
c0 := s[0]
|
||||||
|
switch {
|
||||||
|
case c0 < 0x80: // is ASCII
|
||||||
|
return Properties{entry: bidiValues[c0]}, 1
|
||||||
|
case c0 < 0xC2:
|
||||||
|
return Properties{}, 1
|
||||||
|
case c0 < 0xE0: // 2-byte UTF-8
|
||||||
|
if len(s) < 2 {
|
||||||
|
return Properties{}, 0
|
||||||
|
}
|
||||||
|
i := bidiIndex[c0]
|
||||||
|
c1 := s[1]
|
||||||
|
if c1 < 0x80 || 0xC0 <= c1 {
|
||||||
|
return Properties{}, 1
|
||||||
|
}
|
||||||
|
return Properties{entry: trie.lookupValue(uint32(i), c1)}, 2
|
||||||
|
case c0 < 0xF0: // 3-byte UTF-8
|
||||||
|
if len(s) < 3 {
|
||||||
|
return Properties{}, 0
|
||||||
|
}
|
||||||
|
i := bidiIndex[c0]
|
||||||
|
c1 := s[1]
|
||||||
|
if c1 < 0x80 || 0xC0 <= c1 {
|
||||||
|
return Properties{}, 1
|
||||||
|
}
|
||||||
|
o := uint32(i)<<6 + uint32(c1)
|
||||||
|
i = bidiIndex[o]
|
||||||
|
c2 := s[2]
|
||||||
|
if c2 < 0x80 || 0xC0 <= c2 {
|
||||||
|
return Properties{}, 1
|
||||||
|
}
|
||||||
|
return Properties{entry: trie.lookupValue(uint32(i), c2), last: c2}, 3
|
||||||
|
case c0 < 0xF8: // 4-byte UTF-8
|
||||||
|
if len(s) < 4 {
|
||||||
|
return Properties{}, 0
|
||||||
|
}
|
||||||
|
i := bidiIndex[c0]
|
||||||
|
c1 := s[1]
|
||||||
|
if c1 < 0x80 || 0xC0 <= c1 {
|
||||||
|
return Properties{}, 1
|
||||||
|
}
|
||||||
|
o := uint32(i)<<6 + uint32(c1)
|
||||||
|
i = bidiIndex[o]
|
||||||
|
c2 := s[2]
|
||||||
|
if c2 < 0x80 || 0xC0 <= c2 {
|
||||||
|
return Properties{}, 1
|
||||||
|
}
|
||||||
|
o = uint32(i)<<6 + uint32(c2)
|
||||||
|
i = bidiIndex[o]
|
||||||
|
c3 := s[3]
|
||||||
|
if c3 < 0x80 || 0xC0 <= c3 {
|
||||||
|
return Properties{}, 1
|
||||||
|
}
|
||||||
|
return Properties{entry: trie.lookupValue(uint32(i), c3)}, 4
|
||||||
|
}
|
||||||
|
// Illegal rune
|
||||||
|
return Properties{}, 1
|
||||||
|
}
|
1815
vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go
generated
vendored
Normal file
1815
vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1887
vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go
generated
vendored
Normal file
1887
vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1923
vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go
generated
vendored
Normal file
1923
vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1955
vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go
generated
vendored
Normal file
1955
vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1781
vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go
generated
vendored
Normal file
1781
vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
60
vendor/golang.org/x/text/unicode/bidi/trieval.go
generated
vendored
Normal file
60
vendor/golang.org/x/text/unicode/bidi/trieval.go
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||||
|
|
||||||
|
package bidi
|
||||||
|
|
||||||
|
// Class is the Unicode BiDi class. Each rune has a single class.
|
||||||
|
type Class uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
L Class = iota // LeftToRight
|
||||||
|
R // RightToLeft
|
||||||
|
EN // EuropeanNumber
|
||||||
|
ES // EuropeanSeparator
|
||||||
|
ET // EuropeanTerminator
|
||||||
|
AN // ArabicNumber
|
||||||
|
CS // CommonSeparator
|
||||||
|
B // ParagraphSeparator
|
||||||
|
S // SegmentSeparator
|
||||||
|
WS // WhiteSpace
|
||||||
|
ON // OtherNeutral
|
||||||
|
BN // BoundaryNeutral
|
||||||
|
NSM // NonspacingMark
|
||||||
|
AL // ArabicLetter
|
||||||
|
Control // Control LRO - PDI
|
||||||
|
|
||||||
|
numClass
|
||||||
|
|
||||||
|
LRO // LeftToRightOverride
|
||||||
|
RLO // RightToLeftOverride
|
||||||
|
LRE // LeftToRightEmbedding
|
||||||
|
RLE // RightToLeftEmbedding
|
||||||
|
PDF // PopDirectionalFormat
|
||||||
|
LRI // LeftToRightIsolate
|
||||||
|
RLI // RightToLeftIsolate
|
||||||
|
FSI // FirstStrongIsolate
|
||||||
|
PDI // PopDirectionalIsolate
|
||||||
|
|
||||||
|
unknownClass = ^Class(0)
|
||||||
|
)
|
||||||
|
|
||||||
|
var controlToClass = map[rune]Class{
|
||||||
|
0x202D: LRO, // LeftToRightOverride,
|
||||||
|
0x202E: RLO, // RightToLeftOverride,
|
||||||
|
0x202A: LRE, // LeftToRightEmbedding,
|
||||||
|
0x202B: RLE, // RightToLeftEmbedding,
|
||||||
|
0x202C: PDF, // PopDirectionalFormat,
|
||||||
|
0x2066: LRI, // LeftToRightIsolate,
|
||||||
|
0x2067: RLI, // RightToLeftIsolate,
|
||||||
|
0x2068: FSI, // FirstStrongIsolate,
|
||||||
|
0x2069: PDI, // PopDirectionalIsolate,
|
||||||
|
}
|
||||||
|
|
||||||
|
// A trie entry has the following bits:
|
||||||
|
// 7..5 XOR mask for brackets
|
||||||
|
// 4 1: Bracket open, 0: Bracket close
|
||||||
|
// 3..0 Class type
|
||||||
|
|
||||||
|
const (
|
||||||
|
openMask = 0x10
|
||||||
|
xorMaskShift = 5
|
||||||
|
)
|
237
vendor/google.golang.org/genproto/googleapis/api/httpbody/httpbody.pb.go
generated
vendored
Normal file
237
vendor/google.golang.org/genproto/googleapis/api/httpbody/httpbody.pb.go
generated
vendored
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
// Copyright 2020 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.25.0
|
||||||
|
// protoc v3.13.0
|
||||||
|
// source: google/api/httpbody.proto
|
||||||
|
|
||||||
|
package httpbody
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
anypb "google.golang.org/protobuf/types/known/anypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
|
// Message that represents an arbitrary HTTP body. It should only be used for
|
||||||
|
// payload formats that can't be represented as JSON, such as raw binary or
|
||||||
|
// an HTML page.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// This message can be used both in streaming and non-streaming API methods in
|
||||||
|
// the request as well as the response.
|
||||||
|
//
|
||||||
|
// It can be used as a top-level request field, which is convenient if one
|
||||||
|
// wants to extract parameters from either the URL or HTTP template into the
|
||||||
|
// request fields and also want access to the raw HTTP body.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// message GetResourceRequest {
|
||||||
|
// // A unique request id.
|
||||||
|
// string request_id = 1;
|
||||||
|
//
|
||||||
|
// // The raw HTTP body is bound to this field.
|
||||||
|
// google.api.HttpBody http_body = 2;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// service ResourceService {
|
||||||
|
// rpc GetResource(GetResourceRequest) returns (google.api.HttpBody);
|
||||||
|
// rpc UpdateResource(google.api.HttpBody) returns
|
||||||
|
// (google.protobuf.Empty);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Example with streaming methods:
|
||||||
|
//
|
||||||
|
// service CaldavService {
|
||||||
|
// rpc GetCalendar(stream google.api.HttpBody)
|
||||||
|
// returns (stream google.api.HttpBody);
|
||||||
|
// rpc UpdateCalendar(stream google.api.HttpBody)
|
||||||
|
// returns (stream google.api.HttpBody);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Use of this type only changes how the request and response bodies are
|
||||||
|
// handled, all other features will continue to work unchanged.
|
||||||
|
type HttpBody struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// The HTTP Content-Type header value specifying the content type of the body.
|
||||||
|
ContentType string `protobuf:"bytes,1,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
|
||||||
|
// The HTTP request/response body as raw binary.
|
||||||
|
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
|
||||||
|
// Application specific response metadata. Must be set in the first response
|
||||||
|
// for streaming APIs.
|
||||||
|
Extensions []*anypb.Any `protobuf:"bytes,3,rep,name=extensions,proto3" json:"extensions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HttpBody) Reset() {
|
||||||
|
*x = HttpBody{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_google_api_httpbody_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HttpBody) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*HttpBody) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *HttpBody) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_google_api_httpbody_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use HttpBody.ProtoReflect.Descriptor instead.
|
||||||
|
func (*HttpBody) Descriptor() ([]byte, []int) {
|
||||||
|
return file_google_api_httpbody_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HttpBody) GetContentType() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ContentType
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HttpBody) GetData() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.Data
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HttpBody) GetExtensions() []*anypb.Any {
|
||||||
|
if x != nil {
|
||||||
|
return x.Extensions
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_google_api_httpbody_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_google_api_httpbody_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x68, 0x74, 0x74,
|
||||||
|
0x70, 0x62, 0x6f, 0x64, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x67, 0x6f, 0x6f,
|
||||||
|
0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
|
||||||
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x22, 0x77, 0x0a, 0x08, 0x48, 0x74, 0x74, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x21,
|
||||||
|
0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01,
|
||||||
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,
|
||||||
|
0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||||
|
0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x34, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
|
||||||
|
0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
||||||
|
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52,
|
||||||
|
0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x68, 0x0a, 0x0e, 0x63,
|
||||||
|
0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x42, 0x0d, 0x48,
|
||||||
|
0x74, 0x74, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3b,
|
||||||
|
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72,
|
||||||
|
0x67, 0x2f, 0x67, 0x65, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
|
0x65, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x62, 0x6f,
|
||||||
|
0x64, 0x79, 0x3b, 0x68, 0x74, 0x74, 0x70, 0x62, 0x6f, 0x64, 0x79, 0xf8, 0x01, 0x01, 0xa2, 0x02,
|
||||||
|
0x04, 0x47, 0x41, 0x50, 0x49, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_google_api_httpbody_proto_rawDescOnce sync.Once
|
||||||
|
file_google_api_httpbody_proto_rawDescData = file_google_api_httpbody_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_google_api_httpbody_proto_rawDescGZIP() []byte {
|
||||||
|
file_google_api_httpbody_proto_rawDescOnce.Do(func() {
|
||||||
|
file_google_api_httpbody_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_api_httpbody_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_google_api_httpbody_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_google_api_httpbody_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
|
var file_google_api_httpbody_proto_goTypes = []interface{}{
|
||||||
|
(*HttpBody)(nil), // 0: google.api.HttpBody
|
||||||
|
(*anypb.Any)(nil), // 1: google.protobuf.Any
|
||||||
|
}
|
||||||
|
var file_google_api_httpbody_proto_depIdxs = []int32{
|
||||||
|
1, // 0: google.api.HttpBody.extensions:type_name -> google.protobuf.Any
|
||||||
|
1, // [1:1] is the sub-list for method output_type
|
||||||
|
1, // [1:1] is the sub-list for method input_type
|
||||||
|
1, // [1:1] is the sub-list for extension type_name
|
||||||
|
1, // [1:1] is the sub-list for extension extendee
|
||||||
|
0, // [0:1] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_google_api_httpbody_proto_init() }
|
||||||
|
func file_google_api_httpbody_proto_init() {
|
||||||
|
if File_google_api_httpbody_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_google_api_httpbody_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*HttpBody); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_google_api_httpbody_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 1,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_google_api_httpbody_proto_goTypes,
|
||||||
|
DependencyIndexes: file_google_api_httpbody_proto_depIdxs,
|
||||||
|
MessageInfos: file_google_api_httpbody_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_google_api_httpbody_proto = out.File
|
||||||
|
file_google_api_httpbody_proto_rawDesc = nil
|
||||||
|
file_google_api_httpbody_proto_goTypes = nil
|
||||||
|
file_google_api_httpbody_proto_depIdxs = nil
|
||||||
|
}
|
23
vendor/google.golang.org/genproto/protobuf/field_mask/field_mask.go
generated
vendored
Normal file
23
vendor/google.golang.org/genproto/protobuf/field_mask/field_mask.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2020 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package field_mask aliases all exported identifiers in
|
||||||
|
// package "google.golang.org/protobuf/types/known/fieldmaskpb".
|
||||||
|
package field_mask
|
||||||
|
|
||||||
|
import "google.golang.org/protobuf/types/known/fieldmaskpb"
|
||||||
|
|
||||||
|
type FieldMask = fieldmaskpb.FieldMask
|
||||||
|
|
||||||
|
var File_google_protobuf_field_mask_proto = fieldmaskpb.File_google_protobuf_field_mask_proto
|
42
vendor/google.golang.org/grpc/.travis.yml
generated
vendored
Normal file
42
vendor/google.golang.org/grpc/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- go: 1.14.x
|
||||||
|
env: VET=1 GO111MODULE=on
|
||||||
|
- go: 1.14.x
|
||||||
|
env: RACE=1 GO111MODULE=on
|
||||||
|
- go: 1.14.x
|
||||||
|
env: RUN386=1
|
||||||
|
- go: 1.14.x
|
||||||
|
env: GRPC_GO_RETRY=on
|
||||||
|
- go: 1.14.x
|
||||||
|
env: TESTEXTRAS=1
|
||||||
|
- go: 1.13.x
|
||||||
|
env: GO111MODULE=on
|
||||||
|
- go: 1.12.x
|
||||||
|
env: GO111MODULE=on
|
||||||
|
- go: 1.11.x # Keep until interop tests no longer require Go1.11
|
||||||
|
env: GO111MODULE=on
|
||||||
|
|
||||||
|
go_import_path: google.golang.org/grpc
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- if [[ "${GO111MODULE}" = "on" ]]; then mkdir "${HOME}/go"; export GOPATH="${HOME}/go"; fi
|
||||||
|
- if [[ -n "${RUN386}" ]]; then export GOARCH=386; fi
|
||||||
|
- if [[ "${TRAVIS_EVENT_TYPE}" = "cron" && -z "${RUN386}" ]]; then RACE=1; fi
|
||||||
|
- if [[ "${TRAVIS_EVENT_TYPE}" != "cron" ]]; then export VET_SKIP_PROTO=1; fi
|
||||||
|
|
||||||
|
install:
|
||||||
|
- try3() { eval "$*" || eval "$*" || eval "$*"; }
|
||||||
|
- try3 'if [[ "${GO111MODULE}" = "on" ]]; then go mod download; else make testdeps; fi'
|
||||||
|
- if [[ -n "${GAE}" ]]; then source ./install_gae.sh; make testappenginedeps; fi
|
||||||
|
- if [[ -n "${VET}" ]]; then ./vet.sh -install; fi
|
||||||
|
|
||||||
|
script:
|
||||||
|
- set -e
|
||||||
|
- if [[ -n "${TESTEXTRAS}" ]]; then examples/examples_test.sh; security/advancedtls/examples/examples_test.sh; interop/interop_test.sh; make testsubmodule; exit 0; fi
|
||||||
|
- if [[ -n "${VET}" ]]; then ./vet.sh; fi
|
||||||
|
- if [[ -n "${GAE}" ]]; then make testappengine; exit 0; fi
|
||||||
|
- if [[ -n "${RACE}" ]]; then make testrace; exit 0; fi
|
||||||
|
- make test
|
1
vendor/google.golang.org/grpc/AUTHORS
generated
vendored
Normal file
1
vendor/google.golang.org/grpc/AUTHORS
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Google Inc.
|
3
vendor/google.golang.org/grpc/CODE-OF-CONDUCT.md
generated
vendored
Normal file
3
vendor/google.golang.org/grpc/CODE-OF-CONDUCT.md
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
## Community Code of Conduct
|
||||||
|
|
||||||
|
gRPC follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
61
vendor/google.golang.org/grpc/CONTRIBUTING.md
generated
vendored
Normal file
61
vendor/google.golang.org/grpc/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# How to contribute
|
||||||
|
|
||||||
|
We definitely welcome your patches and contributions to gRPC! Please read the gRPC
|
||||||
|
organization's [governance rules](https://github.com/grpc/grpc-community/blob/master/governance.md)
|
||||||
|
and [contribution guidelines](https://github.com/grpc/grpc-community/blob/master/CONTRIBUTING.md) before proceeding.
|
||||||
|
|
||||||
|
If you are new to github, please start by reading [Pull Request howto](https://help.github.com/articles/about-pull-requests/)
|
||||||
|
|
||||||
|
## Legal requirements
|
||||||
|
|
||||||
|
In order to protect both you and ourselves, you will need to sign the
|
||||||
|
[Contributor License Agreement](https://identity.linuxfoundation.org/projects/cncf).
|
||||||
|
|
||||||
|
## Guidelines for Pull Requests
|
||||||
|
How to get your contributions merged smoothly and quickly.
|
||||||
|
|
||||||
|
- Create **small PRs** that are narrowly focused on **addressing a single
|
||||||
|
concern**. We often times receive PRs that are trying to fix several things at
|
||||||
|
a time, but only one fix is considered acceptable, nothing gets merged and
|
||||||
|
both author's & review's time is wasted. Create more PRs to address different
|
||||||
|
concerns and everyone will be happy.
|
||||||
|
|
||||||
|
- The grpc package should only depend on standard Go packages and a small number
|
||||||
|
of exceptions. If your contribution introduces new dependencies which are NOT
|
||||||
|
in the [list](https://godoc.org/google.golang.org/grpc?imports), you need a
|
||||||
|
discussion with gRPC-Go authors and consultants.
|
||||||
|
|
||||||
|
- For speculative changes, consider opening an issue and discussing it first. If
|
||||||
|
you are suggesting a behavioral or API change, consider starting with a [gRFC
|
||||||
|
proposal](https://github.com/grpc/proposal).
|
||||||
|
|
||||||
|
- Provide a good **PR description** as a record of **what** change is being made
|
||||||
|
and **why** it was made. Link to a github issue if it exists.
|
||||||
|
|
||||||
|
- Don't fix code style and formatting unless you are already changing that line
|
||||||
|
to address an issue. PRs with irrelevant changes won't be merged. If you do
|
||||||
|
want to fix formatting or style, do that in a separate PR.
|
||||||
|
|
||||||
|
- Unless your PR is trivial, you should expect there will be reviewer comments
|
||||||
|
that you'll need to address before merging. We expect you to be reasonably
|
||||||
|
responsive to those comments, otherwise the PR will be closed after 2-3 weeks
|
||||||
|
of inactivity.
|
||||||
|
|
||||||
|
- Maintain **clean commit history** and use **meaningful commit messages**. PRs
|
||||||
|
with messy commit history are difficult to review and won't be merged. Use
|
||||||
|
`rebase -i upstream/master` to curate your commit history and/or to bring in
|
||||||
|
latest changes from master (but avoid rebasing in the middle of a code
|
||||||
|
review).
|
||||||
|
|
||||||
|
- Keep your PR up to date with upstream/master (if there are merge conflicts, we
|
||||||
|
can't really merge your change).
|
||||||
|
|
||||||
|
- **All tests need to be passing** before your change can be merged. We
|
||||||
|
recommend you **run tests locally** before creating your PR to catch breakages
|
||||||
|
early on.
|
||||||
|
- `make all` to test everything, OR
|
||||||
|
- `make vet` to catch vet errors
|
||||||
|
- `make test` to run the tests
|
||||||
|
- `make testrace` to run tests in race mode
|
||||||
|
|
||||||
|
- Exceptions to the rules can be made if there's a compelling reason for doing so.
|
1
vendor/google.golang.org/grpc/GOVERNANCE.md
generated
vendored
Normal file
1
vendor/google.golang.org/grpc/GOVERNANCE.md
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
This repository is governed by the gRPC organization's [governance rules](https://github.com/grpc/grpc-community/blob/master/governance.md).
|
202
vendor/google.golang.org/grpc/LICENSE
generated
vendored
Normal file
202
vendor/google.golang.org/grpc/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
27
vendor/google.golang.org/grpc/MAINTAINERS.md
generated
vendored
Normal file
27
vendor/google.golang.org/grpc/MAINTAINERS.md
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
This page lists all active maintainers of this repository. If you were a
|
||||||
|
maintainer and would like to add your name to the Emeritus list, please send us a
|
||||||
|
PR.
|
||||||
|
|
||||||
|
See [GOVERNANCE.md](https://github.com/grpc/grpc-community/blob/master/governance.md)
|
||||||
|
for governance guidelines and how to become a maintainer.
|
||||||
|
See [CONTRIBUTING.md](https://github.com/grpc/grpc-community/blob/master/CONTRIBUTING.md)
|
||||||
|
for general contribution guidelines.
|
||||||
|
|
||||||
|
## Maintainers (in alphabetical order)
|
||||||
|
- [canguler](https://github.com/canguler), Google LLC
|
||||||
|
- [cesarghali](https://github.com/cesarghali), Google LLC
|
||||||
|
- [dfawley](https://github.com/dfawley), Google LLC
|
||||||
|
- [easwars](https://github.com/easwars), Google LLC
|
||||||
|
- [jadekler](https://github.com/jadekler), Google LLC
|
||||||
|
- [menghanl](https://github.com/menghanl), Google LLC
|
||||||
|
- [srini100](https://github.com/srini100), Google LLC
|
||||||
|
|
||||||
|
## Emeritus Maintainers (in alphabetical order)
|
||||||
|
- [adelez](https://github.com/adelez), Google LLC
|
||||||
|
- [iamqizhao](https://github.com/iamqizhao), Google LLC
|
||||||
|
- [jtattermusch](https://github.com/jtattermusch), Google LLC
|
||||||
|
- [lyuxuan](https://github.com/lyuxuan), Google LLC
|
||||||
|
- [makmukhi](https://github.com/makmukhi), Google LLC
|
||||||
|
- [matt-kwong](https://github.com/matt-kwong), Google LLC
|
||||||
|
- [nicolasnoble](https://github.com/nicolasnoble), Google LLC
|
||||||
|
- [yongni](https://github.com/yongni), Google LLC
|
48
vendor/google.golang.org/grpc/Makefile
generated
vendored
Normal file
48
vendor/google.golang.org/grpc/Makefile
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
all: vet test testrace
|
||||||
|
|
||||||
|
build:
|
||||||
|
go build google.golang.org/grpc/...
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean -i google.golang.org/grpc/...
|
||||||
|
|
||||||
|
deps:
|
||||||
|
GO111MODULE=on go get -d -v google.golang.org/grpc/...
|
||||||
|
|
||||||
|
proto:
|
||||||
|
@ if ! which protoc > /dev/null; then \
|
||||||
|
echo "error: protoc not installed" >&2; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
go generate google.golang.org/grpc/...
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test -cpu 1,4 -timeout 7m google.golang.org/grpc/...
|
||||||
|
|
||||||
|
testsubmodule:
|
||||||
|
cd security/advancedtls && go test -cpu 1,4 -timeout 7m google.golang.org/grpc/security/advancedtls/...
|
||||||
|
cd security/authorization && go test -cpu 1,4 -timeout 7m google.golang.org/grpc/security/authorization/...
|
||||||
|
|
||||||
|
testrace:
|
||||||
|
go test -race -cpu 1,4 -timeout 7m google.golang.org/grpc/...
|
||||||
|
|
||||||
|
testdeps:
|
||||||
|
GO111MODULE=on go get -d -v -t google.golang.org/grpc/...
|
||||||
|
|
||||||
|
vet: vetdeps
|
||||||
|
./vet.sh
|
||||||
|
|
||||||
|
vetdeps:
|
||||||
|
./vet.sh -install
|
||||||
|
|
||||||
|
.PHONY: \
|
||||||
|
all \
|
||||||
|
build \
|
||||||
|
clean \
|
||||||
|
proto \
|
||||||
|
test \
|
||||||
|
testappengine \
|
||||||
|
testappenginedeps \
|
||||||
|
testrace \
|
||||||
|
vet \
|
||||||
|
vetdeps
|
141
vendor/google.golang.org/grpc/README.md
generated
vendored
Normal file
141
vendor/google.golang.org/grpc/README.md
generated
vendored
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
# gRPC-Go
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/grpc/grpc-go.svg)](https://travis-ci.org/grpc/grpc-go)
|
||||||
|
[![GoDoc](https://pkg.go.dev/badge/google.golang.org/grpc)][API]
|
||||||
|
[![GoReportCard](https://goreportcard.com/badge/grpc/grpc-go)](https://goreportcard.com/report/github.com/grpc/grpc-go)
|
||||||
|
|
||||||
|
The [Go][] implementation of [gRPC][]: A high performance, open source, general
|
||||||
|
RPC framework that puts mobile and HTTP/2 first. For more information see the
|
||||||
|
[Go gRPC docs][], or jump directly into the [quick start][].
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- **[Go][]**: any one of the **three latest major** [releases][go-releases].
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
With [Go module][] support (Go 1.11+), simply add the following import
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "google.golang.org/grpc"
|
||||||
|
```
|
||||||
|
|
||||||
|
to your code, and then `go [build|run|test]` will automatically fetch the
|
||||||
|
necessary dependencies.
|
||||||
|
|
||||||
|
Otherwise, to install the `grpc-go` package, run the following command:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ go get -u google.golang.org/grpc
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note:** If you are trying to access `grpc-go` from **China**, see the
|
||||||
|
> [FAQ](#FAQ) below.
|
||||||
|
|
||||||
|
## Learn more
|
||||||
|
|
||||||
|
- [Go gRPC docs][], which include a [quick start][] and [API
|
||||||
|
reference][API] among other resources
|
||||||
|
- [Low-level technical docs](Documentation) from this repository
|
||||||
|
- [Performance benchmark][]
|
||||||
|
- [Examples](examples)
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
### I/O Timeout Errors
|
||||||
|
|
||||||
|
The `golang.org` domain may be blocked from some countries. `go get` usually
|
||||||
|
produces an error like the following when this happens:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ go get -u google.golang.org/grpc
|
||||||
|
package google.golang.org/grpc: unrecognized import path "google.golang.org/grpc" (https fetch: Get https://google.golang.org/grpc?go-get=1: dial tcp 216.239.37.1:443: i/o timeout)
|
||||||
|
```
|
||||||
|
|
||||||
|
To build Go code, there are several options:
|
||||||
|
|
||||||
|
- Set up a VPN and access google.golang.org through that.
|
||||||
|
|
||||||
|
- Without Go module support: `git clone` the repo manually:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
|
||||||
|
```
|
||||||
|
|
||||||
|
You will need to do the same for all of grpc's dependencies in `golang.org`,
|
||||||
|
e.g. `golang.org/x/net`.
|
||||||
|
|
||||||
|
- With Go module support: it is possible to use the `replace` feature of `go
|
||||||
|
mod` to create aliases for golang.org packages. In your project's directory:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go mod edit -replace=google.golang.org/grpc=github.com/grpc/grpc-go@latest
|
||||||
|
go mod tidy
|
||||||
|
go mod vendor
|
||||||
|
go build -mod=vendor
|
||||||
|
```
|
||||||
|
|
||||||
|
Again, this will need to be done for all transitive dependencies hosted on
|
||||||
|
golang.org as well. For details, refer to [golang/go issue #28652](https://github.com/golang/go/issues/28652).
|
||||||
|
|
||||||
|
### Compiling error, undefined: grpc.SupportPackageIsVersion
|
||||||
|
|
||||||
|
#### If you are using Go modules:
|
||||||
|
|
||||||
|
Ensure your gRPC-Go version is `require`d at the appropriate version in
|
||||||
|
the same module containing the generated `.pb.go` files. For example,
|
||||||
|
`SupportPackageIsVersion6` needs `v1.27.0`, so in your `go.mod` file:
|
||||||
|
|
||||||
|
```go
|
||||||
|
module <your module name>
|
||||||
|
|
||||||
|
require (
|
||||||
|
google.golang.org/grpc v1.27.0
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### If you are *not* using Go modules:
|
||||||
|
|
||||||
|
Update the `proto` package, gRPC package, and rebuild the `.proto` files:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
|
||||||
|
go get -u google.golang.org/grpc
|
||||||
|
protoc --go_out=plugins=grpc:. *.proto
|
||||||
|
```
|
||||||
|
|
||||||
|
### How to turn on logging
|
||||||
|
|
||||||
|
The default logger is controlled by environment variables. Turn everything on
|
||||||
|
like this:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ export GRPC_GO_LOG_VERBOSITY_LEVEL=99
|
||||||
|
$ export GRPC_GO_LOG_SEVERITY_LEVEL=info
|
||||||
|
```
|
||||||
|
|
||||||
|
### The RPC failed with error `"code = Unavailable desc = transport is closing"`
|
||||||
|
|
||||||
|
This error means the connection the RPC is using was closed, and there are many
|
||||||
|
possible reasons, including:
|
||||||
|
1. mis-configured transport credentials, connection failed on handshaking
|
||||||
|
1. bytes disrupted, possibly by a proxy in between
|
||||||
|
1. server shutdown
|
||||||
|
1. Keepalive parameters caused connection shutdown, for example if you have configured
|
||||||
|
your server to terminate connections regularly to [trigger DNS lookups](https://github.com/grpc/grpc-go/issues/3170#issuecomment-552517779).
|
||||||
|
If this is the case, you may want to increase your [MaxConnectionAgeGrace](https://pkg.go.dev/google.golang.org/grpc/keepalive?tab=doc#ServerParameters),
|
||||||
|
to allow longer RPC calls to finish.
|
||||||
|
|
||||||
|
It can be tricky to debug this because the error happens on the client side but
|
||||||
|
the root cause of the connection being closed is on the server side. Turn on
|
||||||
|
logging on __both client and server__, and see if there are any transport
|
||||||
|
errors.
|
||||||
|
|
||||||
|
[API]: https://pkg.go.dev/google.golang.org/grpc
|
||||||
|
[Go]: https://golang.org
|
||||||
|
[Go module]: https://github.com/golang/go/wiki/Modules
|
||||||
|
[gRPC]: https://grpc.io
|
||||||
|
[Go gRPC docs]: https://grpc.io/docs/languages/go
|
||||||
|
[Performance benchmark]: https://performance-dot-grpc-testing.appspot.com/explore?dashboard=5652536396611584&widget=490377658&container=1286539696
|
||||||
|
[quick start]: https://grpc.io/docs/languages/go/quickstart
|
||||||
|
[go-releases]: https://golang.org/doc/devel/release.html
|
3
vendor/google.golang.org/grpc/SECURITY.md
generated
vendored
Normal file
3
vendor/google.golang.org/grpc/SECURITY.md
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
For information on gRPC Security Policy and reporting potentional security issues, please see [gRPC CVE Process](https://github.com/grpc/proposal/blob/master/P4-grpc-cve-process.md).
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user