Skip to content

Commit 9cf3eb3

Browse files
committed
fix: hysteria1 outbound should be closed when proxy removed
1 parent 81756fc commit 9cf3eb3

File tree

7 files changed

+109
-5
lines changed

7 files changed

+109
-5
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ jobs:
207207
if: ${{ matrix.jobs.test == 'test' }}
208208
run: |
209209
go test ./...
210+
echo "---test with_gvisor---"
211+
go test ./... -tags "with_gvisor" -count=1
210212
211213
- name: Update CA
212214
run: |

adapter/outbound/hysteria.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import (
77
"fmt"
88
"net"
99
"net/netip"
10+
"runtime"
1011
"strconv"
1112
"time"
1213

1314
"github.com/metacubex/quic-go"
1415
"github.com/metacubex/quic-go/congestion"
1516
M "github.com/sagernet/sing/common/metadata"
1617

18+
CN "github.com/metacubex/mihomo/common/net"
1719
"github.com/metacubex/mihomo/component/ca"
1820
"github.com/metacubex/mihomo/component/dialer"
1921
"github.com/metacubex/mihomo/component/proxydialer"
@@ -43,6 +45,8 @@ type Hysteria struct {
4345

4446
option *HysteriaOption
4547
client *core.Client
48+
49+
closeCh chan struct{} // for test
4650
}
4751

4852
func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
@@ -51,15 +55,15 @@ func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts .
5155
return nil, err
5256
}
5357

54-
return NewConn(tcpConn, h), nil
58+
return NewConn(CN.NewRefConn(tcpConn, h), h), nil
5559
}
5660

5761
func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
5862
udpConn, err := h.client.DialUDP(h.genHdc(ctx, opts...))
5963
if err != nil {
6064
return nil, err
6165
}
62-
return newPacketConn(&hyPacketConn{udpConn}, h), nil
66+
return newPacketConn(CN.NewRefPacketConn(&hyPacketConn{udpConn}, h), h), nil
6367
}
6468

6569
func (h *Hysteria) genHdc(ctx context.Context, opts ...dialer.Option) utils.PacketDialer {
@@ -218,7 +222,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
218222
if err != nil {
219223
return nil, fmt.Errorf("hysteria %s create error: %w", addr, err)
220224
}
221-
return &Hysteria{
225+
outbound := &Hysteria{
222226
Base: &Base{
223227
name: option.Name,
224228
addr: addr,
@@ -231,7 +235,19 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
231235
},
232236
option: &option,
233237
client: client,
234-
}, nil
238+
}
239+
runtime.SetFinalizer(outbound, closeHysteria)
240+
241+
return outbound, nil
242+
}
243+
244+
func closeHysteria(h *Hysteria) {
245+
if h.client != nil {
246+
_ = h.client.Close()
247+
}
248+
if h.closeCh != nil {
249+
close(h.closeCh)
250+
}
235251
}
236252

237253
type hyPacketConn struct {

adapter/outbound/hysteria2.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ type Hysteria2 struct {
3838
option *Hysteria2Option
3939
client *hysteria2.Client
4040
dialer proxydialer.SingDialer
41+
42+
closeCh chan struct{} // for test
4143
}
4244

4345
type Hysteria2Option struct {
@@ -89,6 +91,9 @@ func closeHysteria2(h *Hysteria2) {
8991
if h.client != nil {
9092
_ = h.client.CloseWithError(errors.New("proxy removed"))
9193
}
94+
if h.closeCh != nil {
95+
close(h.closeCh)
96+
}
9297
}
9398

9499
func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {

adapter/outbound/hysteria2_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package outbound
2+
3+
import (
4+
"context"
5+
"runtime"
6+
"testing"
7+
"time"
8+
)
9+
10+
func TestHysteria2GC(t *testing.T) {
11+
option := Hysteria2Option{}
12+
option.Server = "127.0.0.1"
13+
option.Ports = "200,204,401-429,501-503"
14+
option.HopInterval = 30
15+
option.Password = "password"
16+
option.Obfs = "salamander"
17+
option.ObfsPassword = "password"
18+
option.SNI = "example.com"
19+
option.ALPN = []string{"h3"}
20+
hy, err := NewHysteria2(option)
21+
if err != nil {
22+
t.Error(err)
23+
return
24+
}
25+
closeCh := make(chan struct{})
26+
hy.closeCh = closeCh
27+
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
28+
defer cancel()
29+
30+
hy = nil
31+
runtime.GC()
32+
select {
33+
case <-closeCh:
34+
return
35+
case <-ctx.Done():
36+
t.Error("timeout not GC")
37+
}
38+
}

adapter/outbound/hysteria_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package outbound
2+
3+
import (
4+
"context"
5+
"runtime"
6+
"testing"
7+
"time"
8+
)
9+
10+
func TestHysteriaGC(t *testing.T) {
11+
option := HysteriaOption{}
12+
option.Server = "127.0.0.1"
13+
option.Ports = "200,204,401-429,501-503"
14+
option.Protocol = "udp"
15+
option.Up = "1Mbps"
16+
option.Down = "1Mbps"
17+
option.HopInterval = 30
18+
option.Obfs = "salamander"
19+
option.SNI = "example.com"
20+
option.ALPN = []string{"h3"}
21+
hy, err := NewHysteria(option)
22+
if err != nil {
23+
t.Error(err)
24+
return
25+
}
26+
closeCh := make(chan struct{})
27+
hy.closeCh = closeCh
28+
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
29+
defer cancel()
30+
31+
hy = nil
32+
runtime.GC()
33+
select {
34+
case <-closeCh:
35+
return
36+
case <-ctx.Done():
37+
t.Error("timeout not GC")
38+
}
39+
}

adapter/outbound/wireguard_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func TestWireGuardGC(t *testing.T) {
2929
err = wg.init(ctx)
3030
if err != nil {
3131
t.Error(err)
32+
return
3233
}
3334
// must do a small sleep before test GC
3435
// because it maybe deadlocks if w.device.Close call too fast after w.device.Start

transport/hysteria/core/client.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,10 @@ func (c *Client) DialUDP(dialer utils.PacketDialer) (UDPConn, error) {
289289
func (c *Client) Close() error {
290290
c.reconnectMutex.Lock()
291291
defer c.reconnectMutex.Unlock()
292-
err := c.quicSession.CloseWithError(closeErrorCodeGeneric, "")
292+
var err error
293+
if c.quicSession != nil {
294+
err = c.quicSession.CloseWithError(closeErrorCodeGeneric, "")
295+
}
293296
c.closed = true
294297
return err
295298
}

0 commit comments

Comments
 (0)