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] 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 }