From d9d04a230f9d5831725b756c74a594f8399788fd Mon Sep 17 00:00:00 2001 From: hmol233 <82594500+hmol233@users.noreply.github.com> Date: Sat, 3 Jul 2021 16:01:59 +0800 Subject: [PATCH 1/3] Add h2 & gRPC health check --- infra/conf/grpc.go | 21 ++++++++-- infra/conf/transport_internet.go | 16 ++++++-- transport/internet/grpc/config.pb.go | 58 ++++++++++++++++++---------- transport/internet/grpc/config.proto | 2 + transport/internet/grpc/dial.go | 58 +++++++++++++++++----------- transport/internet/http/config.pb.go | 58 ++++++++++++++++++---------- transport/internet/http/config.proto | 2 + transport/internet/http/dialer.go | 30 ++++++++------ 8 files changed, 162 insertions(+), 83 deletions(-) diff --git a/infra/conf/grpc.go b/infra/conf/grpc.go index e39d314d..cae908de 100644 --- a/infra/conf/grpc.go +++ b/infra/conf/grpc.go @@ -7,10 +7,23 @@ import ( ) type GRPCConfig struct { - ServiceName string `json:"serviceName"` - MultiMode bool `json:"multiMode"` + ServiceName string `json:"serviceName" ` + MultiMode bool `json:"multiMode"` + IdleTimeout int32 `json:"idle_timeout"` + HealthCheckTimeout int32 `json:"health_check_timeout"` } -func (g GRPCConfig) Build() (proto.Message, error) { - return &grpc.Config{ServiceName: g.ServiceName, MultiMode: g.MultiMode}, nil +func (g *GRPCConfig) Build() (proto.Message, error) { + if g.IdleTimeout <= 0 { + g.IdleTimeout = 0 + } + if g.HealthCheckTimeout <= 0 { + g.HealthCheckTimeout = 0 + } + return &grpc.Config{ + ServiceName: g.ServiceName, + MultiMode: g.MultiMode, + IdleTimeout: g.IdleTimeout, + HealthCheckTimeout: g.HealthCheckTimeout, + }, nil } diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 62c3f1a8..30737f57 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -179,14 +179,24 @@ func (c *WebSocketConfig) Build() (proto.Message, error) { } type HTTPConfig struct { - Host *StringList `json:"host"` - Path string `json:"path"` + Host *StringList `json:"host"` + Path string `json:"path"` + ReadIdleTimeout int32 `json:"read_idle_timeout"` + HealthCheckTimeout int32 `json:"health_check_timeout"` } // Build implements Buildable. func (c *HTTPConfig) Build() (proto.Message, error) { + if c.ReadIdleTimeout <= 0 { + c.ReadIdleTimeout = 0 + } + if c.HealthCheckTimeout <= 0 { + c.HealthCheckTimeout = 0 + } config := &http.Config{ - Path: c.Path, + Path: c.Path, + IdleTimeout: c.ReadIdleTimeout, + HealthCheckTimeout: c.HealthCheckTimeout, } if c.Host != nil { config.Host = []string(*c.Host) diff --git a/transport/internet/grpc/config.pb.go b/transport/internet/grpc/config.pb.go index 4f8fcb81..3f558d2b 100644 --- a/transport/internet/grpc/config.pb.go +++ b/transport/internet/grpc/config.pb.go @@ -1,13 +1,12 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0 -// protoc v3.15.6 +// protoc-gen-go v1.26.0 +// protoc v3.17.3 // source: transport/internet/grpc/config.proto package grpc import ( - proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -21,18 +20,16 @@ const ( _ = 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 - type Config struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` - ServiceName string `protobuf:"bytes,2,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"` - MultiMode bool `protobuf:"varint,3,opt,name=multi_mode,json=multiMode,proto3" json:"multi_mode,omitempty"` + Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` + ServiceName string `protobuf:"bytes,2,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"` + MultiMode bool `protobuf:"varint,3,opt,name=multi_mode,json=multiMode,proto3" json:"multi_mode,omitempty"` + IdleTimeout int32 `protobuf:"varint,4,opt,name=idle_timeout,json=idleTimeout,proto3" json:"idle_timeout,omitempty"` + HealthCheckTimeout int32 `protobuf:"varint,5,opt,name=health_check_timeout,json=healthCheckTimeout,proto3" json:"health_check_timeout,omitempty"` } func (x *Config) Reset() { @@ -88,6 +85,20 @@ func (x *Config) GetMultiMode() bool { return false } +func (x *Config) GetIdleTimeout() int32 { + if x != nil { + return x.IdleTimeout + } + return 0 +} + +func (x *Config) GetHealthCheckTimeout() int32 { + if x != nil { + return x.HealthCheckTimeout + } + return 0 +} + var File_transport_internet_grpc_config_proto protoreflect.FileDescriptor var file_transport_internet_grpc_config_proto_rawDesc = []byte{ @@ -95,17 +106,22 @@ var file_transport_internet_grpc_config_proto_rawDesc = []byte{ 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x25, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, - 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x22, 0x5e, 0x0a, - 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, - 0x0a, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x09, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x6f, 0x64, 0x65, 0x42, 0x33, 0x5a, - 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, - 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, - 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x67, 0x72, - 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x22, 0xb3, 0x01, + 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x1d, 0x0a, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x09, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x21, + 0x0a, 0x0c, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x69, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x12, 0x30, 0x0a, 0x14, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x5f, 0x63, 0x68, 0x65, 0x63, + 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x12, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, + 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/transport/internet/grpc/config.proto b/transport/internet/grpc/config.proto index 9c4330b2..dffdf2fe 100644 --- a/transport/internet/grpc/config.proto +++ b/transport/internet/grpc/config.proto @@ -7,4 +7,6 @@ message Config { string host = 1; string service_name = 2; bool multi_mode = 3; + int32 idle_timeout = 4; + int32 health_check_timeout = 5; } diff --git a/transport/internet/grpc/dial.go b/transport/internet/grpc/dial.go index 99c483be..47c0b439 100644 --- a/transport/internet/grpc/dial.go +++ b/transport/internet/grpc/dial.go @@ -10,6 +10,7 @@ import ( "google.golang.org/grpc/backoff" "google.golang.org/grpc/connectivity" "google.golang.org/grpc/credentials" + "google.golang.org/grpc/keepalive" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/net" @@ -35,8 +36,7 @@ func init() { type dialerConf struct { net.Destination - *internet.SocketConfig - *tls.Config + *internet.MemoryStreamConfig } var ( @@ -47,9 +47,7 @@ var ( func dialgRPC(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (net.Conn, error) { grpcSettings := streamSettings.ProtocolSettings.(*Config) - tlsConfig := tls.ConfigFromStreamSettings(streamSettings) - - conn, err := getGrpcClient(ctx, dest, tlsConfig, streamSettings.SocketSettings) + conn, err := getGrpcClient(ctx, dest, streamSettings) if err != nil { return nil, newError("Cannot dial gRPC").Base(err) @@ -72,33 +70,22 @@ func dialgRPC(ctx context.Context, dest net.Destination, streamSettings *interne return encoding.NewHunkConn(grpcService, nil), nil } -func getGrpcClient(ctx context.Context, dest net.Destination, tlsConfig *tls.Config, sockopt *internet.SocketConfig) (*grpc.ClientConn, error) { +func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (*grpc.ClientConn, error) { globalDialerAccess.Lock() defer globalDialerAccess.Unlock() if globalDialerMap == nil { globalDialerMap = make(map[dialerConf]*grpc.ClientConn) } + tlsConfig := tls.ConfigFromStreamSettings(streamSettings) + sockopt := streamSettings.SocketSettings + grpcSettings := streamSettings.ProtocolSettings.(*Config) - if client, found := globalDialerMap[dialerConf{dest, sockopt, tlsConfig}]; found && client.GetState() != connectivity.Shutdown { + if client, found := globalDialerMap[dialerConf{dest, streamSettings}]; found && client.GetState() != connectivity.Shutdown { return client, nil } - dialOption := grpc.WithInsecure() - - if tlsConfig != nil { - dialOption = grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig.GetTLSConfig())) - } - - var grpcDestHost string - if dest.Address.Family().IsDomain() { - grpcDestHost = dest.Address.Domain() - } else { - grpcDestHost = dest.Address.IP().String() - } - conn, err := grpc.Dial( - gonet.JoinHostPort(grpcDestHost, dest.Port.String()), - dialOption, + var dialOptions = []grpc.DialOption{ grpc.WithConnectParams(grpc.ConnectParams{ Backoff: backoff.Config{ BaseDelay: 500 * time.Millisecond, @@ -132,7 +119,32 @@ func getGrpcClient(ctx context.Context, dest net.Destination, tlsConfig *tls.Con address := net.ParseAddress(rawHost) return internet.DialSystem(gctx, net.TCPDestination(address, port), sockopt) }), + } + + if tlsConfig != nil { + dialOptions = append(dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig.GetTLSConfig()))) + } else { + dialOptions = append(dialOptions, grpc.WithInsecure()) + } + + if grpcSettings.IdleTimeout > 0 || grpcSettings.HealthCheckTimeout > 0 { + dialOptions = append(dialOptions, grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: time.Second * time.Duration(grpcSettings.IdleTimeout), + Timeout: time.Second * time.Duration(grpcSettings.HealthCheckTimeout), + })) + } + + var grpcDestHost string + if dest.Address.Family().IsDomain() { + grpcDestHost = dest.Address.Domain() + } else { + grpcDestHost = dest.Address.IP().String() + } + + conn, err := grpc.Dial( + gonet.JoinHostPort(grpcDestHost, dest.Port.String()), + dialOptions..., ) - globalDialerMap[dialerConf{dest, sockopt, tlsConfig}] = conn + globalDialerMap[dialerConf{dest, streamSettings}] = conn return conn, err } diff --git a/transport/internet/http/config.pb.go b/transport/internet/http/config.pb.go index 94b7d434..fe37c6d8 100644 --- a/transport/internet/http/config.pb.go +++ b/transport/internet/http/config.pb.go @@ -1,13 +1,12 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0 -// protoc v3.14.0 +// protoc-gen-go v1.26.0 +// protoc v3.17.3 // source: transport/internet/http/config.proto package http import ( - proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -21,17 +20,15 @@ const ( _ = 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 - type Config struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Host []string `protobuf:"bytes,1,rep,name=host,proto3" json:"host,omitempty"` - Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Host []string `protobuf:"bytes,1,rep,name=host,proto3" json:"host,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + IdleTimeout int32 `protobuf:"varint,3,opt,name=idle_timeout,json=idleTimeout,proto3" json:"idle_timeout,omitempty"` + HealthCheckTimeout int32 `protobuf:"varint,4,opt,name=health_check_timeout,json=healthCheckTimeout,proto3" json:"health_check_timeout,omitempty"` } func (x *Config) Reset() { @@ -80,6 +77,20 @@ func (x *Config) GetPath() string { return "" } +func (x *Config) GetIdleTimeout() int32 { + if x != nil { + return x.IdleTimeout + } + return 0 +} + +func (x *Config) GetHealthCheckTimeout() int32 { + if x != nil { + return x.HealthCheckTimeout + } + return 0 +} + var File_transport_internet_http_config_proto protoreflect.FileDescriptor var file_transport_internet_http_config_proto_rawDesc = []byte{ @@ -87,18 +98,23 @@ var file_transport_internet_http_config_proto_rawDesc = []byte{ 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, - 0x68, 0x74, 0x74, 0x70, 0x22, 0x30, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, - 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, - 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x42, 0x76, 0x0a, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, - 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, - 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, - 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x74, 0x74, 0x70, 0xaa, - 0x02, 0x1c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x68, 0x74, 0x74, 0x70, 0x22, 0x85, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x68, + 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x64, 0x6c, 0x65, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x69, + 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x68, 0x65, + 0x61, 0x6c, 0x74, 0x68, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x42, 0x76, 0x0a, 0x20, + 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, + 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x74, 0x74, 0x70, + 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, + 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, + 0x2f, 0x68, 0x74, 0x74, 0x70, 0xaa, 0x02, 0x1c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, + 0x48, 0x74, 0x74, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/transport/internet/http/config.proto b/transport/internet/http/config.proto index 34433af5..641ed5ee 100644 --- a/transport/internet/http/config.proto +++ b/transport/internet/http/config.proto @@ -9,4 +9,6 @@ option java_multiple_files = true; message Config { repeated string host = 1; string path = 2; + int32 idle_timeout = 3; + int32 health_check_timeout = 4; } diff --git a/transport/internet/http/dialer.go b/transport/internet/http/dialer.go index ae3ba6d2..db3be736 100644 --- a/transport/internet/http/dialer.go +++ b/transport/internet/http/dialer.go @@ -6,6 +6,7 @@ import ( "net/http" "net/url" "sync" + "time" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" @@ -20,8 +21,7 @@ import ( type dialerConf struct { net.Destination - *internet.SocketConfig - *tls.Config + *internet.MemoryStreamConfig } var ( @@ -29,7 +29,7 @@ var ( globalDialerAccess sync.Mutex ) -func getHTTPClient(ctx context.Context, dest net.Destination, tlsSettings *tls.Config, sockopt *internet.SocketConfig) (*http.Client, error) { +func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (*http.Client, error) { globalDialerAccess.Lock() defer globalDialerAccess.Unlock() @@ -37,7 +37,14 @@ func getHTTPClient(ctx context.Context, dest net.Destination, tlsSettings *tls.C globalDialerMap = make(map[dialerConf]*http.Client) } - if client, found := globalDialerMap[dialerConf{dest, sockopt, tlsSettings}]; found { + httpSettings := streamSettings.ProtocolSettings.(*Config) + tlsConfig := tls.ConfigFromStreamSettings(streamSettings) + if tlsConfig == nil { + return nil, newError("TLS must be enabled for http transport.").AtWarning() + } + sockopt := streamSettings.SocketSettings + + if client, found := globalDialerMap[dialerConf{dest, streamSettings}]; found { return client, nil } @@ -86,25 +93,26 @@ func getHTTPClient(ctx context.Context, dest net.Destination, tlsSettings *tls.C } return cn, nil }, - TLSClientConfig: tlsSettings.GetTLSConfig(tls.WithDestination(dest)), + TLSClientConfig: tlsConfig.GetTLSConfig(tls.WithDestination(dest)), + } + + if httpSettings.IdleTimeout > 0 || httpSettings.HealthCheckTimeout > 0 { + transport.ReadIdleTimeout = time.Second * time.Duration(httpSettings.IdleTimeout) + transport.PingTimeout = time.Second * time.Duration(httpSettings.HealthCheckTimeout) } client := &http.Client{ Transport: transport, } - globalDialerMap[dialerConf{dest, sockopt, tlsSettings}] = client + globalDialerMap[dialerConf{dest, streamSettings}] = client return client, nil } // Dial dials a new TCP connection to the given destination. func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) { httpSettings := streamSettings.ProtocolSettings.(*Config) - tlsConfig := tls.ConfigFromStreamSettings(streamSettings) - if tlsConfig == nil { - return nil, newError("TLS must be enabled for http transport.").AtWarning() - } - client, err := getHTTPClient(ctx, dest, tlsConfig, streamSettings.SocketSettings) + client, err := getHTTPClient(ctx, dest, streamSettings) if err != nil { return nil, err } From 57b9006d26d468ba4e964d943a5f4ce34f4a050f Mon Sep 17 00:00:00 2001 From: hmol233 <82594500+hmol233@users.noreply.github.com> Date: Mon, 5 Jul 2021 21:25:03 +0800 Subject: [PATCH 2/3] gRPC: add keepalive option for server --- transport/internet/grpc/hub.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/transport/internet/grpc/hub.go b/transport/internet/grpc/hub.go index 0f7ab6ba..3ca87837 100644 --- a/transport/internet/grpc/hub.go +++ b/transport/internet/grpc/hub.go @@ -2,9 +2,11 @@ package grpc import ( "context" + "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials" + "google.golang.org/grpc/keepalive" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/net" @@ -75,12 +77,19 @@ func Listen(ctx context.Context, address net.Address, port net.Port, settings *i config := tls.ConfigFromStreamSettings(settings) + var options []grpc.ServerOption var s *grpc.Server - if config == nil { - s = grpc.NewServer() - } else { - s = grpc.NewServer(grpc.Creds(credentials.NewTLS(config.GetTLSConfig(tls.WithNextProto("h2"))))) + if config != nil { + options = append(options, grpc.Creds(credentials.NewTLS(config.GetTLSConfig(tls.WithNextProto("h2"))))) } + if grpcSettings.IdleTimeout > 0 || grpcSettings.HealthCheckTimeout > 0 { + options = append(options, grpc.KeepaliveParams(keepalive.ServerParameters{ + Time: time.Second * time.Duration(grpcSettings.IdleTimeout), + Timeout: time.Second * time.Duration(grpcSettings.HealthCheckTimeout), + })) + } + + s = grpc.NewServer(options...) listener.s = s if settings.SocketSettings != nil && settings.SocketSettings.AcceptProxyProtocol { From 31c7141feff794511c3cf3331e5259da7dc23cb6 Mon Sep 17 00:00:00 2001 From: hmol233 <82594500+hmol233@users.noreply.github.com> Date: Mon, 5 Jul 2021 21:25:21 +0800 Subject: [PATCH 3/3] gRPC: add keepalive option `PermitWithoutStream` --- infra/conf/grpc.go | 18 +++++++++------- transport/internet/grpc/config.pb.go | 32 +++++++++++++++++++--------- transport/internet/grpc/config.proto | 1 + transport/internet/grpc/dial.go | 7 +++--- 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/infra/conf/grpc.go b/infra/conf/grpc.go index cae908de..0fbe9f34 100644 --- a/infra/conf/grpc.go +++ b/infra/conf/grpc.go @@ -7,10 +7,11 @@ import ( ) type GRPCConfig struct { - ServiceName string `json:"serviceName" ` - MultiMode bool `json:"multiMode"` - IdleTimeout int32 `json:"idle_timeout"` - HealthCheckTimeout int32 `json:"health_check_timeout"` + ServiceName string `json:"serviceName" ` + MultiMode bool `json:"multiMode"` + IdleTimeout int32 `json:"idle_timeout"` + HealthCheckTimeout int32 `json:"health_check_timeout"` + PermitWithoutStream bool `json:"permit_without_stream"` } func (g *GRPCConfig) Build() (proto.Message, error) { @@ -21,9 +22,10 @@ func (g *GRPCConfig) Build() (proto.Message, error) { g.HealthCheckTimeout = 0 } return &grpc.Config{ - ServiceName: g.ServiceName, - MultiMode: g.MultiMode, - IdleTimeout: g.IdleTimeout, - HealthCheckTimeout: g.HealthCheckTimeout, + ServiceName: g.ServiceName, + MultiMode: g.MultiMode, + IdleTimeout: g.IdleTimeout, + HealthCheckTimeout: g.HealthCheckTimeout, + PermitWithoutStream: g.PermitWithoutStream, }, nil } diff --git a/transport/internet/grpc/config.pb.go b/transport/internet/grpc/config.pb.go index 3f558d2b..d2ece121 100644 --- a/transport/internet/grpc/config.pb.go +++ b/transport/internet/grpc/config.pb.go @@ -25,11 +25,12 @@ type Config struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` - ServiceName string `protobuf:"bytes,2,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"` - MultiMode bool `protobuf:"varint,3,opt,name=multi_mode,json=multiMode,proto3" json:"multi_mode,omitempty"` - IdleTimeout int32 `protobuf:"varint,4,opt,name=idle_timeout,json=idleTimeout,proto3" json:"idle_timeout,omitempty"` - HealthCheckTimeout int32 `protobuf:"varint,5,opt,name=health_check_timeout,json=healthCheckTimeout,proto3" json:"health_check_timeout,omitempty"` + Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` + ServiceName string `protobuf:"bytes,2,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"` + MultiMode bool `protobuf:"varint,3,opt,name=multi_mode,json=multiMode,proto3" json:"multi_mode,omitempty"` + IdleTimeout int32 `protobuf:"varint,4,opt,name=idle_timeout,json=idleTimeout,proto3" json:"idle_timeout,omitempty"` + HealthCheckTimeout int32 `protobuf:"varint,5,opt,name=health_check_timeout,json=healthCheckTimeout,proto3" json:"health_check_timeout,omitempty"` + PermitWithoutStream bool `protobuf:"varint,6,opt,name=permit_without_stream,json=permitWithoutStream,proto3" json:"permit_without_stream,omitempty"` } func (x *Config) Reset() { @@ -99,6 +100,13 @@ func (x *Config) GetHealthCheckTimeout() int32 { return 0 } +func (x *Config) GetPermitWithoutStream() bool { + if x != nil { + return x.PermitWithoutStream + } + return false +} + var File_transport_internet_grpc_config_proto protoreflect.FileDescriptor var file_transport_internet_grpc_config_proto_rawDesc = []byte{ @@ -106,7 +114,7 @@ var file_transport_internet_grpc_config_proto_rawDesc = []byte{ 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x25, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, - 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x22, 0xb3, 0x01, + 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x22, 0xe7, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, @@ -118,10 +126,14 @@ var file_transport_internet_grpc_config_proto_rawDesc = []byte{ 0x74, 0x12, 0x30, 0x0a, 0x14, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, - 0x6f, 0x75, 0x74, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, - 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x65, 0x74, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x75, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x5f, 0x77, 0x69, + 0x74, 0x68, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x13, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x57, 0x69, 0x74, 0x68, 0x6f, 0x75, + 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, + 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/transport/internet/grpc/config.proto b/transport/internet/grpc/config.proto index dffdf2fe..c402e1a9 100644 --- a/transport/internet/grpc/config.proto +++ b/transport/internet/grpc/config.proto @@ -9,4 +9,5 @@ message Config { bool multi_mode = 3; int32 idle_timeout = 4; int32 health_check_timeout = 5; + bool permit_without_stream = 6; } diff --git a/transport/internet/grpc/dial.go b/transport/internet/grpc/dial.go index 47c0b439..8ec79ea4 100644 --- a/transport/internet/grpc/dial.go +++ b/transport/internet/grpc/dial.go @@ -127,10 +127,11 @@ func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *in dialOptions = append(dialOptions, grpc.WithInsecure()) } - if grpcSettings.IdleTimeout > 0 || grpcSettings.HealthCheckTimeout > 0 { + if grpcSettings.IdleTimeout > 0 || grpcSettings.HealthCheckTimeout > 0 || grpcSettings.PermitWithoutStream { dialOptions = append(dialOptions, grpc.WithKeepaliveParams(keepalive.ClientParameters{ - Time: time.Second * time.Duration(grpcSettings.IdleTimeout), - Timeout: time.Second * time.Duration(grpcSettings.HealthCheckTimeout), + Time: time.Second * time.Duration(grpcSettings.IdleTimeout), + Timeout: time.Second * time.Duration(grpcSettings.HealthCheckTimeout), + PermitWithoutStream: grpcSettings.PermitWithoutStream, })) }