Fix HTTP/2 transport reset with Go 1.27

This commit is contained in:
世界
2026-08-30 17:41:47 +08:00
parent de7460ec7c
commit f50ca2244b
4 changed files with 96 additions and 19 deletions
+1 -19
View File
@@ -3,7 +3,6 @@ package v2rayhttp
import ( import (
"net/http" "net/http"
"reflect" "reflect"
"sync"
"unsafe" "unsafe"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
@@ -11,12 +10,6 @@ import (
"golang.org/x/net/http2" "golang.org/x/net/http2"
) )
type clientConnPool struct {
t *http2.Transport
mu sync.Mutex
conns map[string][]*http2.ClientConn // key is host:port
}
type efaceWords struct { type efaceWords struct {
typ unsafe.Pointer typ unsafe.Pointer
data unsafe.Pointer data unsafe.Pointer
@@ -28,20 +21,9 @@ func ResetTransport(rawTransport http.RoundTripper) http.RoundTripper {
transport.CloseIdleConnections() transport.CloseIdleConnections()
return transport.Clone() return transport.Clone()
case *http2.Transport: case *http2.Transport:
connPool := transportConnPool(transport) closeHTTP2Connections(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()
}
}
return transport return transport
default: default:
panic(E.New("unknown transport type: ", reflect.TypeOf(transport))) panic(E.New("unknown transport type: ", reflect.TypeOf(transport)))
} }
} }
//go:linkname transportConnPool golang.org/x/net/http2.(*Transport).connPool
func transportConnPool(t *http2.Transport) http2.ClientConnPool
+55
View File
@@ -0,0 +1,55 @@
//go:build go1.27 && badlinkname
package v2rayhttp
import (
"net/http"
"sync"
"unsafe"
"golang.org/x/net/http2"
)
// cmd/compile creates the method symbols reachable from *http.Transport's field types with
// their package recorded when the type is first used by a declaration; a linkname pull
// processed afterwards reuses that symbol as a package-indexed reference, which the linker's
// -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) {
h2Transport := transportFromH1Transport(transportInit(transport))
t := (*internalTransport)((*efaceWords)(unsafe.Pointer(&h2Transport)).data)
if t == nil {
return
}
p := t.connPool
p.mu.Lock()
defer p.mu.Unlock()
for _, vv := range p.conns {
for _, cc := range vv {
clientConnClose(cc)
}
}
}
//go:linkname transportInit golang.org/x/net/http2.(*Transport).init
func transportInit(t *http2.Transport) *http.Transport
//go:linkname transportFromH1Transport net/http/internal/http2_test.transportFromH1Transport
func transportFromH1Transport(t *http.Transport) any
//go:linkname clientConnClose net/http/internal/http2.(*ClientConn).Close
func clientConnClose(cc unsafe.Pointer) error
@@ -0,0 +1,9 @@
//go:build go1.27 && !badlinkname
package v2rayhttp
import "golang.org/x/net/http2"
func closeHTTP2Connections(transport *http2.Transport) {
transport.CloseIdleConnections()
}
+31
View File
@@ -0,0 +1,31 @@
//go:build !go1.27
package v2rayhttp
import (
"sync"
"unsafe"
"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) {
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()
}
}
}
//go:linkname transportConnPool golang.org/x/net/http2.(*Transport).connPool
func transportConnPool(t *http2.Transport) http2.ClientConnPool