diff --git a/transport/v2rayhttp/force_close.go b/common/force_close/force_close.go similarity index 77% rename from transport/v2rayhttp/force_close.go rename to common/force_close/force_close.go index ef89990c..6f187c1e 100644 --- a/transport/v2rayhttp/force_close.go +++ b/common/force_close/force_close.go @@ -1,29 +1,33 @@ -package v2rayhttp +package force_close import ( "net/http" "reflect" "unsafe" + "github.com/sagernet/quic-go/http3" E "github.com/sagernet/sing/common/exceptions" "golang.org/x/net/http2" ) -type efaceWords struct { - typ unsafe.Pointer - data unsafe.Pointer -} - func ResetTransport(rawTransport http.RoundTripper) http.RoundTripper { switch transport := rawTransport.(type) { case *http.Transport: transport.CloseIdleConnections() return transport.Clone() case *http2.Transport: - closeHTTP2Connections(transport) + CloseHTTP2Connections(transport) + return transport + case *http3.Transport: + transport.Close() return transport default: panic(E.New("unknown transport type: ", reflect.TypeOf(transport))) } } + +type efaceWords struct { + typ unsafe.Pointer + data unsafe.Pointer +} diff --git a/transport/v2rayhttp/force_close_go127.go b/common/force_close/force_close_go127.go similarity index 95% rename from transport/v2rayhttp/force_close_go127.go rename to common/force_close/force_close_go127.go index 76ffc70e..0bda843d 100644 --- a/transport/v2rayhttp/force_close_go127.go +++ b/common/force_close/force_close_go127.go @@ -1,6 +1,6 @@ //go:build go1.27 && badlinkname -package v2rayhttp +package force_close import ( "net/http" @@ -16,20 +16,7 @@ import ( // -checklinkname does not inspect. This declaration must precede the linkname declarations. var _ *http.Transport -// net/http/internal/http2.Transport -type internalTransport struct { - t1 [2]uintptr // TransportConfig - connPool *clientConnPool -} - -// net/http/internal/http2.clientConnPool -type clientConnPool struct { - t *internalTransport - mu sync.Mutex - conns map[string][]unsafe.Pointer // key is host:port, value is []*ClientConn -} - -func closeHTTP2Connections(transport *http2.Transport) { +func CloseHTTP2Connections(transport *http2.Transport) { h2Transport := transportFromH1Transport(transportInit(transport)) t := (*internalTransport)((*efaceWords)(unsafe.Pointer(&h2Transport)).data) if t == nil { @@ -45,6 +32,19 @@ func closeHTTP2Connections(transport *http2.Transport) { } } +// net/http/internal/http2.Transport +type internalTransport struct { + t1 [2]uintptr // TransportConfig + connPool *clientConnPool +} + +// net/http/internal/http2.clientConnPool +type clientConnPool struct { + t *internalTransport + mu sync.Mutex + conns map[string][]unsafe.Pointer // key is host:port, value is []*ClientConn +} + //go:linkname transportInit golang.org/x/net/http2.(*Transport).init func transportInit(t *http2.Transport) *http.Transport diff --git a/transport/v2rayhttp/force_close_go127_stub.go b/common/force_close/force_close_go127_stub.go similarity index 57% rename from transport/v2rayhttp/force_close_go127_stub.go rename to common/force_close/force_close_go127_stub.go index dc675636..1faf9715 100644 --- a/transport/v2rayhttp/force_close_go127_stub.go +++ b/common/force_close/force_close_go127_stub.go @@ -1,9 +1,9 @@ //go:build go1.27 && !badlinkname -package v2rayhttp +package force_close import "golang.org/x/net/http2" -func closeHTTP2Connections(transport *http2.Transport) { +func CloseHTTP2Connections(transport *http2.Transport) { transport.CloseIdleConnections() } diff --git a/transport/v2rayhttp/force_close_legacy.go b/common/force_close/force_close_legacy.go similarity index 88% rename from transport/v2rayhttp/force_close_legacy.go rename to common/force_close/force_close_legacy.go index d8807d7e..59cab39c 100644 --- a/transport/v2rayhttp/force_close_legacy.go +++ b/common/force_close/force_close_legacy.go @@ -1,6 +1,6 @@ //go:build !go1.27 -package v2rayhttp +package force_close import ( "sync" @@ -9,13 +9,7 @@ import ( "golang.org/x/net/http2" ) -type clientConnPool struct { - t *http2.Transport - mu sync.Mutex - conns map[string][]*http2.ClientConn // key is host:port -} - -func closeHTTP2Connections(transport *http2.Transport) { +func CloseHTTP2Connections(transport *http2.Transport) { connPool := transportConnPool(transport) p := (*clientConnPool)((*efaceWords)(unsafe.Pointer(&connPool)).data) p.mu.Lock() @@ -27,5 +21,11 @@ func closeHTTP2Connections(transport *http2.Transport) { } } +type clientConnPool struct { + t *http2.Transport + mu sync.Mutex + conns map[string][]*http2.ClientConn // key is host:port +} + //go:linkname transportConnPool golang.org/x/net/http2.(*Transport).connPool func transportConnPool(t *http2.Transport) http2.ClientConnPool diff --git a/transport/v2raygrpclite/client.go b/transport/v2raygrpclite/client.go index 26cbf769..5039074f 100644 --- a/transport/v2raygrpclite/client.go +++ b/transport/v2raygrpclite/client.go @@ -9,9 +9,9 @@ import ( "time" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/force_close" "github.com/sagernet/sing-box/common/tls" "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing-box/transport/v2rayhttp" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" @@ -102,6 +102,6 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) { } func (c *Client) Close() error { - v2rayhttp.ResetTransport(c.transport) + force_close.ResetTransport(c.transport) return nil } diff --git a/transport/v2rayhttp/client.go b/transport/v2rayhttp/client.go index 6c327cd6..334f85a5 100644 --- a/transport/v2rayhttp/client.go +++ b/transport/v2rayhttp/client.go @@ -11,6 +11,7 @@ import ( "time" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/force_close" "github.com/sagernet/sing-box/common/tls" "github.com/sagernet/sing-box/option" E "github.com/sagernet/sing/common/exceptions" @@ -152,6 +153,6 @@ func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) { } func (c *Client) Close() error { - c.transport = ResetTransport(c.transport) + c.transport = force_close.ResetTransport(c.transport) return nil } diff --git a/transport/v2rayxhttp/dialer.go b/transport/v2rayxhttp/dialer.go index 8adc9514..18921829 100644 --- a/transport/v2rayxhttp/dialer.go +++ b/transport/v2rayxhttp/dialer.go @@ -9,19 +9,16 @@ import ( "net" "net/http" "net/http/httptrace" - "reflect" "strings" "sync" "sync/atomic" - "unsafe" - "github.com/sagernet/quic-go/http3" + "github.com/sagernet/sing-box/common/force_close" common "github.com/sagernet/sing-box/common/xray" "github.com/sagernet/sing-box/common/xray/buf" "github.com/sagernet/sing-box/common/xray/signal/done" "github.com/sagernet/sing-box/option" E "github.com/sagernet/sing/common/exceptions" - "golang.org/x/net/http2" ) // interface to abstract between use of browser dialer, vs net/http @@ -49,20 +46,6 @@ type DefaultDialerClient struct { mtx sync.RWMutex } -type clientConnPool struct { - t *http2.Transport - mu sync.Mutex - conns map[string][]*http2.ClientConn // key is host:port -} - -type efaceWords struct { - typ unsafe.Pointer - data unsafe.Pointer -} - -//go:linkname transportConnPool golang.org/x/net/http2.(*Transport).connPool -func transportConnPool(t *http2.Transport) http2.ClientConnPool - func (c *DefaultDialerClient) Close() { c.mtx.Lock() defer c.mtx.Unlock() @@ -70,24 +53,7 @@ func (c *DefaultDialerClient) Close() { return } c.closed = true - switch transport := c.client.Transport.(type) { - case *http.Transport: - transport.CloseIdleConnections() - case *http2.Transport: - connPool := transportConnPool(transport) - p := (*clientConnPool)((*efaceWords)(unsafe.Pointer(&connPool)).data) - p.mu.Lock() - defer p.mu.Unlock() - for _, vv := range p.conns { - for _, cc := range vv { - cc.Close() - } - } - case *http3.Transport: - transport.Close() - default: - panic(E.New("unknown transport type: ", reflect.TypeOf(transport))) - } + c.client.Transport = force_close.ResetTransport(c.client.Transport) } func (c *DefaultDialerClient) IsClosed() bool {