diff --git a/option/v2ray_transport.go b/option/v2ray_transport.go index f5c6b962..aa07dba9 100644 --- a/option/v2ray_transport.go +++ b/option/v2ray_transport.go @@ -61,10 +61,12 @@ func (o *V2RayTransportOptions) UnmarshalJSON(bytes []byte) error { } 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 { @@ -77,6 +79,9 @@ type V2RayWebsocketOptions struct { type V2RayQUICOptions 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 } diff --git a/transport/v2raygrpc/client.go b/transport/v2raygrpc/client.go index 8d9af3e0..17b3b5cd 100644 --- a/transport/v2raygrpc/client.go +++ b/transport/v2raygrpc/client.go @@ -18,6 +18,7 @@ import ( "google.golang.org/grpc/backoff" "google.golang.org/grpc/connectivity" "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/keepalive" ) var _ adapter.V2RayClientTransport = (*Client)(nil) @@ -40,6 +41,13 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt } else { 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{ Backoff: backoff.Config{ BaseDelay: 500 * time.Millisecond, diff --git a/transport/v2raygrpc/server.go b/transport/v2raygrpc/server.go index 66e06a91..1b6a34c1 100644 --- a/transport/v2raygrpc/server.go +++ b/transport/v2raygrpc/server.go @@ -5,6 +5,7 @@ import ( "net" "os" "strings" + "time" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" @@ -13,6 +14,7 @@ import ( N "github.com/sagernet/sing/common/network" "google.golang.org/grpc" + "google.golang.org/grpc/keepalive" gM "google.golang.org/grpc/metadata" "google.golang.org/grpc/peer" ) @@ -31,6 +33,12 @@ func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig t tlsConfig.SetNextProtos([]string{"h2"}) 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...)} RegisterGunServiceCustomNameServer(server.server, server, options.ServiceName) return server, nil diff --git a/transport/v2raygrpclite/client.go b/transport/v2raygrpclite/client.go index 313a575a..fee1fcf7 100644 --- a/transport/v2raygrpclite/client.go +++ b/transport/v2raygrpclite/client.go @@ -7,6 +7,7 @@ import ( "net" "net/http" "net/url" + "time" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" @@ -45,6 +46,8 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt } else { tlsConfig.SetNextProtos([]string{http2.NextProtoTLS}) 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) { conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr)) if err != nil { diff --git a/transport/v2raygrpclite/server.go b/transport/v2raygrpclite/server.go index ad407f74..d9b04791 100644 --- a/transport/v2raygrpclite/server.go +++ b/transport/v2raygrpclite/server.go @@ -8,6 +8,7 @@ import ( "net/url" "os" "strings" + "time" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" @@ -45,7 +46,9 @@ func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig t tlsConfig: tlsConfig, handler: handler, 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{ Handler: server, diff --git a/transport/v2rayhttp/client.go b/transport/v2rayhttp/client.go index 3f82c89e..316d76ba 100644 --- a/transport/v2rayhttp/client.go +++ b/transport/v2rayhttp/client.go @@ -9,6 +9,7 @@ import ( "net/http" "net/url" "strings" + "time" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" @@ -45,6 +46,8 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt } else { tlsConfig.SetNextProtos([]string{http2.NextProtoTLS}) 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) { conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr)) if err != nil { diff --git a/transport/v2rayhttp/server.go b/transport/v2rayhttp/server.go index 1964a21f..358d7429 100644 --- a/transport/v2rayhttp/server.go +++ b/transport/v2rayhttp/server.go @@ -6,6 +6,7 @@ import ( "net/http" "os" "strings" + "time" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" @@ -46,11 +47,13 @@ func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig t ctx: ctx, tlsConfig: tlsConfig, 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 == "" { server.method = "PUT"