浏览代码

Add health check support for http-based v2ray transport

世界 2 年之前
父节点
当前提交
6af9c2b3ca

+ 11 - 6
option/v2ray_transport.go

@@ -61,10 +61,12 @@ func (o *V2RayTransportOptions) UnmarshalJSON(bytes []byte) error {
 }
 }
 
 
 type V2RayHTTPOptions struct {
 type V2RayHTTPOptions struct {
-	Host    Listable[string]  `json:"host,omitempty"`
-	Path    string            `json:"path,omitempty"`
-	Method  string            `json:"method,omitempty"`
-	Headers map[string]string `json:"headers,omitempty"`
+	Host        Listable[string]  `json:"host,omitempty"`
+	Path        string            `json:"path,omitempty"`
+	Method      string            `json:"method,omitempty"`
+	Headers     map[string]string `json:"headers,omitempty"`
+	IdleTimeout Duration          `json:"idle_timeout,omitempty"`
+	PingTimeout Duration          `json:"ping_timeout,omitempty"`
 }
 }
 
 
 type V2RayWebsocketOptions struct {
 type V2RayWebsocketOptions struct {
@@ -77,6 +79,9 @@ type V2RayWebsocketOptions struct {
 type V2RayQUICOptions struct{}
 type V2RayQUICOptions struct{}
 
 
 type V2RayGRPCOptions struct {
 type V2RayGRPCOptions struct {
-	ServiceName string `json:"service_name,omitempty"`
-	ForceLite   bool   `json:"-"` // for test
+	ServiceName         string   `json:"service_name,omitempty"`
+	IdleTimeout         Duration `json:"idle_timeout,omitempty"`
+	PingTimeout         Duration `json:"ping_timeout,omitempty"`
+	PermitWithoutStream bool     `json:"permit_without_stream,omitempty"`
+	ForceLite           bool     `json:"-"` // for test
 }
 }

+ 8 - 0
transport/v2raygrpc/client.go

@@ -18,6 +18,7 @@ import (
 	"google.golang.org/grpc/backoff"
 	"google.golang.org/grpc/backoff"
 	"google.golang.org/grpc/connectivity"
 	"google.golang.org/grpc/connectivity"
 	"google.golang.org/grpc/credentials/insecure"
 	"google.golang.org/grpc/credentials/insecure"
+	"google.golang.org/grpc/keepalive"
 )
 )
 
 
 var _ adapter.V2RayClientTransport = (*Client)(nil)
 var _ adapter.V2RayClientTransport = (*Client)(nil)
@@ -40,6 +41,13 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
 	} else {
 	} else {
 		dialOptions = append(dialOptions, grpc.WithTransportCredentials(insecure.NewCredentials()))
 		dialOptions = append(dialOptions, grpc.WithTransportCredentials(insecure.NewCredentials()))
 	}
 	}
+	if options.IdleTimeout > 0 {
+		dialOptions = append(dialOptions, grpc.WithKeepaliveParams(keepalive.ClientParameters{
+			Time:                time.Duration(options.IdleTimeout),
+			Timeout:             time.Duration(options.PingTimeout),
+			PermitWithoutStream: options.PermitWithoutStream,
+		}))
+	}
 	dialOptions = append(dialOptions, grpc.WithConnectParams(grpc.ConnectParams{
 	dialOptions = append(dialOptions, grpc.WithConnectParams(grpc.ConnectParams{
 		Backoff: backoff.Config{
 		Backoff: backoff.Config{
 			BaseDelay:  500 * time.Millisecond,
 			BaseDelay:  500 * time.Millisecond,

+ 8 - 0
transport/v2raygrpc/server.go

@@ -5,6 +5,7 @@ import (
 	"net"
 	"net"
 	"os"
 	"os"
 	"strings"
 	"strings"
+	"time"
 
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/common/tls"
 	"github.com/sagernet/sing-box/common/tls"
@@ -13,6 +14,7 @@ import (
 	N "github.com/sagernet/sing/common/network"
 	N "github.com/sagernet/sing/common/network"
 
 
 	"google.golang.org/grpc"
 	"google.golang.org/grpc"
+	"google.golang.org/grpc/keepalive"
 	gM "google.golang.org/grpc/metadata"
 	gM "google.golang.org/grpc/metadata"
 	"google.golang.org/grpc/peer"
 	"google.golang.org/grpc/peer"
 )
 )
@@ -31,6 +33,12 @@ func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig t
 		tlsConfig.SetNextProtos([]string{"h2"})
 		tlsConfig.SetNextProtos([]string{"h2"})
 		serverOptions = append(serverOptions, grpc.Creds(NewTLSTransportCredentials(tlsConfig)))
 		serverOptions = append(serverOptions, grpc.Creds(NewTLSTransportCredentials(tlsConfig)))
 	}
 	}
+	if options.IdleTimeout > 0 {
+		serverOptions = append(serverOptions, grpc.KeepaliveParams(keepalive.ServerParameters{
+			Time:    time.Duration(options.IdleTimeout),
+			Timeout: time.Duration(options.PingTimeout),
+		}))
+	}
 	server := &Server{ctx, handler, grpc.NewServer(serverOptions...)}
 	server := &Server{ctx, handler, grpc.NewServer(serverOptions...)}
 	RegisterGunServiceCustomNameServer(server.server, server, options.ServiceName)
 	RegisterGunServiceCustomNameServer(server.server, server, options.ServiceName)
 	return server, nil
 	return server, nil

+ 3 - 0
transport/v2raygrpclite/client.go

@@ -7,6 +7,7 @@ import (
 	"net"
 	"net"
 	"net/http"
 	"net/http"
 	"net/url"
 	"net/url"
+	"time"
 
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/common/tls"
 	"github.com/sagernet/sing-box/common/tls"
@@ -45,6 +46,8 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
 	} else {
 	} else {
 		tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
 		tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
 		transport = &http2.Transport{
 		transport = &http2.Transport{
+			ReadIdleTimeout: time.Duration(options.IdleTimeout),
+			PingTimeout:     time.Duration(options.PingTimeout),
 			DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
 			DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
 				conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
 				conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
 				if err != nil {
 				if err != nil {

+ 4 - 1
transport/v2raygrpclite/server.go

@@ -8,6 +8,7 @@ import (
 	"net/url"
 	"net/url"
 	"os"
 	"os"
 	"strings"
 	"strings"
+	"time"
 
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/common/tls"
 	"github.com/sagernet/sing-box/common/tls"
@@ -45,7 +46,9 @@ func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig t
 		tlsConfig: tlsConfig,
 		tlsConfig: tlsConfig,
 		handler:   handler,
 		handler:   handler,
 		path:      fmt.Sprintf("/%s/Tun", url.QueryEscape(options.ServiceName)),
 		path:      fmt.Sprintf("/%s/Tun", url.QueryEscape(options.ServiceName)),
-		h2Server:  new(http2.Server),
+		h2Server: &http2.Server{
+			IdleTimeout: time.Duration(options.IdleTimeout),
+		},
 	}
 	}
 	server.httpServer = &http.Server{
 	server.httpServer = &http.Server{
 		Handler: server,
 		Handler: server,

+ 3 - 0
transport/v2rayhttp/client.go

@@ -9,6 +9,7 @@ import (
 	"net/http"
 	"net/http"
 	"net/url"
 	"net/url"
 	"strings"
 	"strings"
+	"time"
 
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/common/tls"
 	"github.com/sagernet/sing-box/common/tls"
@@ -45,6 +46,8 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
 	} else {
 	} else {
 		tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
 		tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
 		transport = &http2.Transport{
 		transport = &http2.Transport{
+			ReadIdleTimeout: time.Duration(options.IdleTimeout),
+			PingTimeout:     time.Duration(options.PingTimeout),
 			DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
 			DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
 				conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
 				conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
 				if err != nil {
 				if err != nil {

+ 8 - 5
transport/v2rayhttp/server.go

@@ -6,6 +6,7 @@ import (
 	"net/http"
 	"net/http"
 	"os"
 	"os"
 	"strings"
 	"strings"
+	"time"
 
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/common/tls"
 	"github.com/sagernet/sing-box/common/tls"
@@ -46,11 +47,13 @@ func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig t
 		ctx:       ctx,
 		ctx:       ctx,
 		tlsConfig: tlsConfig,
 		tlsConfig: tlsConfig,
 		handler:   handler,
 		handler:   handler,
-		h2Server:  new(http2.Server),
-		host:      options.Host,
-		path:      options.Path,
-		method:    options.Method,
-		headers:   make(http.Header),
+		h2Server: &http2.Server{
+			IdleTimeout: time.Duration(options.IdleTimeout),
+		},
+		host:    options.Host,
+		path:    options.Path,
+		method:  options.Method,
+		headers: make(http.Header),
 	}
 	}
 	if server.method == "" {
 	if server.method == "" {
 		server.method = "PUT"
 		server.method = "PUT"