瀏覽代碼

Add grpc compatibility test

世界 3 年之前
父節點
當前提交
d440a01792

+ 12 - 0
common/baderror/baderror.go

@@ -0,0 +1,12 @@
+package baderror
+
+import "strings"
+
+func Contains(err error, msgList ...string) bool {
+	for _, msg := range msgList {
+		if strings.Contains(err.Error(), msg) {
+			return true
+		}
+	}
+	return false
+}

+ 26 - 0
common/baderror/grpc.go

@@ -0,0 +1,26 @@
+package baderror
+
+import (
+	"context"
+	"io"
+	"net"
+)
+
+func WrapGRPC(err error) error {
+	// grpc uses stupid internal error types
+	if err == nil {
+		return nil
+	}
+	if Contains(err, "EOF") {
+		return io.EOF
+	}
+	if Contains(err, "Canceled") {
+		return context.Canceled
+	}
+	if Contains(err,
+		"the client connection is closing",
+		"server closed the stream without sending trailers") {
+		return net.ErrClosed
+	}
+	return err
+}

+ 22 - 0
common/baderror/h2.go

@@ -0,0 +1,22 @@
+package baderror
+
+import (
+	"io"
+	"net"
+
+	E "github.com/sagernet/sing/common/exceptions"
+)
+
+func WrapH2(err error) error {
+	if err == nil {
+		return nil
+	}
+	err = E.Unwrap(err)
+	if err == io.ErrUnexpectedEOF {
+		return io.EOF
+	}
+	if Contains(err, "client disconnected", "body closed by handler") {
+		return net.ErrClosed
+	}
+	return err
+}

+ 1 - 1
go.mod

@@ -24,7 +24,7 @@ require (
 	github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
 	github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83
-	github.com/sagernet/sing-vmess v0.0.0-20220827032426-01665c9c4e31
+	github.com/sagernet/sing-vmess v0.0.0-20220829020559-33915075430c
 	github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939
 	github.com/spf13/cobra v1.5.0
 	github.com/stretchr/testify v1.8.0

+ 2 - 2
go.sum

@@ -143,8 +143,8 @@ github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDe
 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
 github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 h1:SoWiHYuOCVedqA7T/CJSZUUrcPGKQb2wFKEq8DphiAI=
 github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83/go.mod h1:76r07HS1WRcEI4mE9pFsohfTBUt1j/G9Avz6DaOP3VU=
-github.com/sagernet/sing-vmess v0.0.0-20220827032426-01665c9c4e31 h1:FAsJsVwpPcoITcj6/9JxRKxy8n3bIKLqKmDGVzmfeOo=
-github.com/sagernet/sing-vmess v0.0.0-20220827032426-01665c9c4e31/go.mod h1:82O6gzbxLha/W/jxSVQbsqf2lVdRTjMIgyLug0lpJps=
+github.com/sagernet/sing-vmess v0.0.0-20220829020559-33915075430c h1:92Gn78/z/t6CkzZ4XWG/uPiCxhUmjPULFEHFMDY6K8k=
+github.com/sagernet/sing-vmess v0.0.0-20220829020559-33915075430c/go.mod h1:82O6gzbxLha/W/jxSVQbsqf2lVdRTjMIgyLug0lpJps=
 github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939 h1:pB1Dh1NbwVrLhQhotr4O4Hs3yhiBzmg3AvnUyYjL4x4=
 github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
 github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=

+ 6 - 6
inbound/trojan.go

@@ -145,18 +145,18 @@ func (h *Trojan) Close() error {
 	)
 }
 
+func (h *Trojan) newTransportConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
+	h.injectTCP(conn)
+	return nil
+}
+
 func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
-	if h.tlsConfig != nil {
+	if h.tlsConfig != nil && h.transport == nil {
 		conn = tls.Server(conn, h.tlsConfig.Config())
 	}
 	return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
 }
 
-func (h *Trojan) newTransportConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
-	metadata = h.createMetadata(conn, metadata)
-	return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
-}
-
 func (h *Trojan) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
 	userIndex, loaded := auth.UserFromContext[int](ctx)
 	if !loaded {

+ 6 - 6
inbound/vmess.go

@@ -125,18 +125,18 @@ func (h *VMess) Close() error {
 	)
 }
 
+func (h *VMess) newTransportConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
+	h.injectTCP(conn)
+	return nil
+}
+
 func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
-	if h.tlsConfig != nil {
+	if h.tlsConfig != nil && h.transport == nil {
 		conn = tls.Server(conn, h.tlsConfig.Config())
 	}
 	return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
 }
 
-func (h *VMess) newTransportConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
-	metadata = h.createMetadata(conn, metadata)
-	return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
-}
-
 func (h *VMess) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
 	userIndex, loaded := auth.UserFromContext[int](ctx)
 	if !loaded {

+ 1 - 1
test/box_test.go

@@ -60,7 +60,7 @@ func testTCP(t *testing.T, clientPort uint16, testPort uint16) {
 	require.NoError(t, testLargeDataWithConn(t, testPort, dialTCP))
 }
 
-func testSuitQUIC(t *testing.T, clientPort uint16, testPort uint16) {
+func testSuitSimple(t *testing.T, clientPort uint16, testPort uint16) {
 	dialer := socks.NewClient(N.SystemDialer, M.ParseSocksaddrHostPort("127.0.0.1", clientPort), socks.Version5, "", "")
 	dialTCP := func() (net.Conn, error) {
 		return dialer.DialContext(context.Background(), "tcp", M.ParseSocksaddrHostPort("127.0.0.1", testPort))

+ 1 - 1
test/config/vmess-client.json

@@ -26,7 +26,7 @@
               {
                 "id": "",
                 "alterId": 0,
-                "security": "",
+                "security": "none",
                 "experiments": ""
               }
             ]

+ 51 - 0
test/config/vmess-grpc-client.json

@@ -0,0 +1,51 @@
+{
+  "log": {
+    "loglevel": "debug"
+  },
+  "inbounds": [
+    {
+      "listen": "127.0.0.1",
+      "port": "1080",
+      "protocol": "socks",
+      "settings": {
+        "auth": "noauth",
+        "udp": true,
+        "ip": "127.0.0.1"
+      }
+    }
+  ],
+  "outbounds": [
+    {
+      "protocol": "vmess",
+      "settings": {
+        "vnext": [
+          {
+            "address": "127.0.0.1",
+            "port": 1234,
+            "users": [
+              {
+                "id": ""
+              }
+            ]
+          }
+        ]
+      },
+      "streamSettings": {
+        "network": "gun",
+        "security": "tls",
+        "tlsSettings": {
+          "serverName": "example.org",
+          "certificates": [
+            {
+              "certificateFile": "/path/to/certificate.crt",
+              "keyFile": "/path/to/private.key"
+            }
+          ]
+        },
+        "grpcSettings": {
+          "serviceName": "TunService"
+        }
+      }
+    }
+  ]
+}

+ 40 - 0
test/config/vmess-grpc-server.json

@@ -0,0 +1,40 @@
+{
+  "log": {
+    "loglevel": "debug"
+  },
+  "inbounds": [
+    {
+      "listen": "0.0.0.0",
+      "port": 1234,
+      "protocol": "vmess",
+      "settings": {
+        "clients": [
+          {
+            "id": "b831381d-6324-4d53-ad4f-8cda48b30811"
+          }
+        ]
+      },
+      "streamSettings": {
+        "network": "gun",
+        "security": "tls",
+        "tlsSettings": {
+          "serverName": "example.org",
+          "certificates": [
+            {
+              "certificateFile": "/path/to/certificate.crt",
+              "keyFile": "/path/to/private.key"
+            }
+          ]
+        },
+        "grpcSettings": {
+          "serviceName": "TunService"
+        }
+      }
+    }
+  ],
+  "outbounds": [
+    {
+      "protocol": "freedom"
+    }
+  ]
+}

+ 7 - 7
test/go.mod

@@ -14,7 +14,7 @@ require (
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
 	github.com/spyzhov/ajson v0.7.1
 	github.com/stretchr/testify v1.8.0
-	golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c
+	golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
 )
 
 require (
@@ -60,8 +60,8 @@ require (
 	github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 // indirect
 	github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb // indirect
 	github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 // indirect
-	github.com/sagernet/sing-tun v0.0.0-20220827013030-e01ce3a8a70e // indirect
-	github.com/sagernet/sing-vmess v0.0.0-20220827032426-01665c9c4e31 // indirect
+	github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 // indirect
+	github.com/sagernet/sing-vmess v0.0.0-20220829020559-33915075430c // indirect
 	github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939 // indirect
 	github.com/sirupsen/logrus v1.8.1 // indirect
 	github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
@@ -69,22 +69,22 @@ require (
 	go.uber.org/multierr v1.6.0 // indirect
 	go.uber.org/zap v1.22.0 // indirect
 	go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d // indirect
-	golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 // indirect
+	golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d // indirect
 	golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
 	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
-	golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect
+	golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect
 	golang.org/x/text v0.3.7 // indirect
 	golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
 	golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f // indirect
 	golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
 	golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478 // indirect
 	google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
-	google.golang.org/grpc v1.48.0 // indirect
+	google.golang.org/grpc v1.49.0 // indirect
 	google.golang.org/protobuf v1.28.1 // indirect
 	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	gotest.tools/v3 v3.3.0 // indirect
-	gvisor.dev/gvisor v0.0.0-20220812001733-b5c0f23893fb // indirect
+	gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a // indirect
 	lukechampine.com/blake3 v1.1.7 // indirect
 )

+ 14 - 21
test/go.sum

@@ -13,15 +13,10 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd
 github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
 github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
 github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
 github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
@@ -44,7 +39,6 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
 github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
-github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -89,7 +83,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
@@ -168,10 +161,10 @@ github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFx
 github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
-github.com/sagernet/sing-tun v0.0.0-20220827013030-e01ce3a8a70e h1:7GGZfIhbTAiUmVsWVLEccrKbwsgocUaJDJ859RVFNTA=
-github.com/sagernet/sing-tun v0.0.0-20220827013030-e01ce3a8a70e/go.mod h1:B9BsLZmK01+9Dzhl634lM6YU80aTqOZ2yyrOzhA/Bto=
-github.com/sagernet/sing-vmess v0.0.0-20220827032426-01665c9c4e31 h1:FAsJsVwpPcoITcj6/9JxRKxy8n3bIKLqKmDGVzmfeOo=
-github.com/sagernet/sing-vmess v0.0.0-20220827032426-01665c9c4e31/go.mod h1:82O6gzbxLha/W/jxSVQbsqf2lVdRTjMIgyLug0lpJps=
+github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 h1:SoWiHYuOCVedqA7T/CJSZUUrcPGKQb2wFKEq8DphiAI=
+github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83/go.mod h1:76r07HS1WRcEI4mE9pFsohfTBUt1j/G9Avz6DaOP3VU=
+github.com/sagernet/sing-vmess v0.0.0-20220829020559-33915075430c h1:92Gn78/z/t6CkzZ4XWG/uPiCxhUmjPULFEHFMDY6K8k=
+github.com/sagernet/sing-vmess v0.0.0-20220829020559-33915075430c/go.mod h1:82O6gzbxLha/W/jxSVQbsqf2lVdRTjMIgyLug0lpJps=
 github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939 h1:pB1Dh1NbwVrLhQhotr4O4Hs3yhiBzmg3AvnUyYjL4x4=
 github.com/sagernet/smux v0.0.0-20220812084127-e2d085ee3939/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@@ -212,8 +205,8 @@ golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaE
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
-golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c=
-golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
+golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
 golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
@@ -245,8 +238,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c h1:JVAXQ10yGGVbSyoer5VILysz6YKjdNT2bsvlayjqhes=
-golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
+golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -282,8 +275,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U=
-golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
+golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -335,8 +328,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
 google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
 google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
-google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
-google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
+google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -371,8 +364,8 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
 gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
 gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
-gvisor.dev/gvisor v0.0.0-20220812001733-b5c0f23893fb h1:Z7S1dQX1RX+Tq55/Jq5kJQnPBdxA19ZmhgCmFjyK+yA=
-gvisor.dev/gvisor v0.0.0-20220812001733-b5c0f23893fb/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
+gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a h1:W1h3JsEzYWg7eD4908iHv49p7AOx7JPKsoh/fsxgylM=
+gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=

+ 2 - 2
test/hysteria_test.go

@@ -83,7 +83,7 @@ func TestHysteriaSelf(t *testing.T) {
 			},
 		},
 	})
-	testSuitQUIC(t, clientPort, testPort)
+	testSuitSimple(t, clientPort, testPort)
 }
 
 func TestHysteriaInbound(t *testing.T) {
@@ -180,5 +180,5 @@ func TestHysteriaOutbound(t *testing.T) {
 			},
 		},
 	})
-	testSuitQUIC(t, clientPort, testPort)
+	testSuitSimple(t, clientPort, testPort)
 }

+ 217 - 0
test/v2ray_grpc_test.go

@@ -0,0 +1,217 @@
+package main
+
+import (
+	"net/netip"
+	"os"
+	"testing"
+
+	C "github.com/sagernet/sing-box/constant"
+	"github.com/sagernet/sing-box/option"
+
+	"github.com/gofrs/uuid"
+	"github.com/spyzhov/ajson"
+	"github.com/stretchr/testify/require"
+)
+
+func TestV2RayGRPCInbound(t *testing.T) {
+	t.Run("origin", func(t *testing.T) {
+		testV2RayGRPCInbound(t, false)
+	})
+	t.Run("lite", func(t *testing.T) {
+		testV2RayGRPCInbound(t, true)
+	})
+}
+
+func testV2RayGRPCInbound(t *testing.T, forceLite bool) {
+	userId, err := uuid.DefaultGenerator.NewV4()
+	require.NoError(t, err)
+	_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
+	startInstance(t, option.Options{
+		Log: &option.LogOptions{
+			Level: "error",
+		},
+		Inbounds: []option.Inbound{
+			{
+				Type: C.TypeVMess,
+				VMessOptions: option.VMessInboundOptions{
+					ListenOptions: option.ListenOptions{
+						Listen:     option.ListenAddress(netip.IPv4Unspecified()),
+						ListenPort: serverPort,
+					},
+					Users: []option.VMessUser{
+						{
+							Name: "sekai",
+							UUID: userId.String(),
+						},
+					},
+					TLS: &option.InboundTLSOptions{
+						Enabled:         true,
+						ServerName:      "example.org",
+						CertificatePath: certPem,
+						KeyPath:         keyPem,
+					},
+					Transport: &option.V2RayTransportOptions{
+						Type: C.V2RayTransportTypeGRPC,
+						GRPCOptions: option.V2RayGRPCOptions{
+							ServiceName: "TunService",
+							ForceLite:   forceLite,
+						},
+					},
+				},
+			},
+		},
+	})
+	content, err := os.ReadFile("config/vmess-grpc-client.json")
+	require.NoError(t, err)
+	config, err := ajson.Unmarshal(content)
+	require.NoError(t, err)
+
+	config.MustKey("inbounds").MustIndex(0).MustKey("port").SetNumeric(float64(clientPort))
+	outbound := config.MustKey("outbounds").MustIndex(0).MustKey("settings").MustKey("vnext").MustIndex(0)
+	outbound.MustKey("port").SetNumeric(float64(serverPort))
+	user := outbound.MustKey("users").MustIndex(0)
+	user.MustKey("id").SetString(userId.String())
+	content, err = ajson.Marshal(config)
+	require.NoError(t, err)
+
+	startDockerContainer(t, DockerOptions{
+		Image:      ImageV2RayCore,
+		Ports:      []uint16{serverPort, testPort},
+		EntryPoint: "v2ray",
+		Stdin:      content,
+		Bind: map[string]string{
+			certPem: "/path/to/certificate.crt",
+			keyPem:  "/path/to/private.key",
+		},
+	})
+
+	testSuitSimple(t, clientPort, testPort)
+}
+
+func TestV2RayGRPCOutbound(t *testing.T) {
+	t.Run("origin", func(t *testing.T) {
+		testV2RayGRPCOutbound(t, false)
+	})
+	t.Run("lite", func(t *testing.T) {
+		testV2RayGRPCOutbound(t, true)
+	})
+}
+
+func testV2RayGRPCOutbound(t *testing.T, forceLite bool) {
+	userId, err := uuid.DefaultGenerator.NewV4()
+	require.NoError(t, err)
+	_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
+
+	content, err := os.ReadFile("config/vmess-grpc-server.json")
+	require.NoError(t, err)
+	config, err := ajson.Unmarshal(content)
+	require.NoError(t, err)
+
+	inbound := config.MustKey("inbounds").MustIndex(0)
+	inbound.MustKey("port").SetNumeric(float64(serverPort))
+	inbound.MustKey("settings").MustKey("clients").MustIndex(0).MustKey("id").SetString(userId.String())
+	content, err = ajson.Marshal(config)
+	require.NoError(t, err)
+
+	startDockerContainer(t, DockerOptions{
+		Image:      ImageV2RayCore,
+		Ports:      []uint16{serverPort, testPort},
+		EntryPoint: "v2ray",
+		Stdin:      content,
+		Env:        []string{"V2RAY_VMESS_AEAD_FORCED=false"},
+		Bind: map[string]string{
+			certPem: "/path/to/certificate.crt",
+			keyPem:  "/path/to/private.key",
+		},
+	})
+	startInstance(t, option.Options{
+		Log: &option.LogOptions{
+			Level: "error",
+		},
+		Inbounds: []option.Inbound{
+			{
+				Type: C.TypeMixed,
+				Tag:  "mixed-in",
+				MixedOptions: option.HTTPMixedInboundOptions{
+					ListenOptions: option.ListenOptions{
+						Listen:     option.ListenAddress(netip.IPv4Unspecified()),
+						ListenPort: clientPort,
+					},
+				},
+			},
+		},
+		Outbounds: []option.Outbound{
+			{
+				Type: C.TypeVMess,
+				Tag:  "vmess-out",
+				VMessOptions: option.VMessOutboundOptions{
+					ServerOptions: option.ServerOptions{
+						Server:     "127.0.0.1",
+						ServerPort: serverPort,
+					},
+					UUID:     userId.String(),
+					Security: "zero",
+					TLS: &option.OutboundTLSOptions{
+						Enabled:         true,
+						ServerName:      "example.org",
+						CertificatePath: certPem,
+					},
+					Transport: &option.V2RayTransportOptions{
+						Type: C.V2RayTransportTypeGRPC,
+						GRPCOptions: option.V2RayGRPCOptions{
+							ServiceName: "TunService",
+							ForceLite:   forceLite,
+						},
+					},
+				},
+			},
+		},
+	})
+	testSuitSimple(t, clientPort, testPort)
+}
+
+func TestV2RayGRPCLite(t *testing.T) {
+	t.Run("server", func(t *testing.T) {
+		testV2RayTransportSelfWith(t, &option.V2RayTransportOptions{
+			Type: C.V2RayTransportTypeGRPC,
+			GRPCOptions: option.V2RayGRPCOptions{
+				ServiceName: "TunService",
+				ForceLite:   true,
+			},
+		}, &option.V2RayTransportOptions{
+			Type: C.V2RayTransportTypeGRPC,
+			GRPCOptions: option.V2RayGRPCOptions{
+				ServiceName: "TunService",
+			},
+		})
+	})
+	t.Run("client", func(t *testing.T) {
+		testV2RayTransportSelfWith(t, &option.V2RayTransportOptions{
+			Type: C.V2RayTransportTypeGRPC,
+			GRPCOptions: option.V2RayGRPCOptions{
+				ServiceName: "TunService",
+			},
+		}, &option.V2RayTransportOptions{
+			Type: C.V2RayTransportTypeGRPC,
+			GRPCOptions: option.V2RayGRPCOptions{
+				ServiceName: "TunService",
+				ForceLite:   true,
+			},
+		})
+	})
+	t.Run("self", func(t *testing.T) {
+		testV2RayTransportSelfWith(t, &option.V2RayTransportOptions{
+			Type: C.V2RayTransportTypeGRPC,
+			GRPCOptions: option.V2RayGRPCOptions{
+				ServiceName: "TunService",
+				ForceLite:   true,
+			},
+		}, &option.V2RayTransportOptions{
+			Type: C.V2RayTransportTypeGRPC,
+			GRPCOptions: option.V2RayGRPCOptions{
+				ServiceName: "TunService",
+				ForceLite:   true,
+			},
+		})
+	})
+}

+ 2 - 57
test/v2ray_transport_test.go

@@ -11,61 +11,6 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
-func TestV2RayGRPCSelf(t *testing.T) {
-	testV2RayTransportSelf(t, &option.V2RayTransportOptions{
-		Type: C.V2RayTransportTypeGRPC,
-		GRPCOptions: option.V2RayGRPCOptions{
-			ServiceName: "TunService",
-		},
-	})
-}
-
-func TestV2RayGRPCLite(t *testing.T) {
-	t.Run("server", func(t *testing.T) {
-		testV2RayTransportSelfWith(t, &option.V2RayTransportOptions{
-			Type: C.V2RayTransportTypeGRPC,
-			GRPCOptions: option.V2RayGRPCOptions{
-				ServiceName: "TunService",
-				ForceLite:   true,
-			},
-		}, &option.V2RayTransportOptions{
-			Type: C.V2RayTransportTypeGRPC,
-			GRPCOptions: option.V2RayGRPCOptions{
-				ServiceName: "TunService",
-			},
-		})
-	})
-	t.Run("client", func(t *testing.T) {
-		testV2RayTransportSelfWith(t, &option.V2RayTransportOptions{
-			Type: C.V2RayTransportTypeGRPC,
-			GRPCOptions: option.V2RayGRPCOptions{
-				ServiceName: "TunService",
-			},
-		}, &option.V2RayTransportOptions{
-			Type: C.V2RayTransportTypeGRPC,
-			GRPCOptions: option.V2RayGRPCOptions{
-				ServiceName: "TunService",
-				ForceLite:   true,
-			},
-		})
-	})
-	t.Run("self", func(t *testing.T) {
-		testV2RayTransportSelfWith(t, &option.V2RayTransportOptions{
-			Type: C.V2RayTransportTypeGRPC,
-			GRPCOptions: option.V2RayGRPCOptions{
-				ServiceName: "TunService",
-				ForceLite:   true,
-			},
-		}, &option.V2RayTransportOptions{
-			Type: C.V2RayTransportTypeGRPC,
-			GRPCOptions: option.V2RayGRPCOptions{
-				ServiceName: "TunService",
-				ForceLite:   true,
-			},
-		})
-	})
-}
-
 func TestV2RayWebscoketSelf(t *testing.T) {
 	t.Run("basic", func(t *testing.T) {
 		testV2RayTransportSelf(t, &option.V2RayTransportOptions{
@@ -355,7 +300,7 @@ func TestVMessQUICSelf(t *testing.T) {
 			},
 		},
 	})
-	testSuitQUIC(t, clientPort, testPort)
+	testSuitSimple(t, clientPort, testPort)
 }
 
 func testV2RayTransportNOTLSSelf(t *testing.T, transport *option.V2RayTransportOptions) {
@@ -422,5 +367,5 @@ func testV2RayTransportNOTLSSelf(t *testing.T, transport *option.V2RayTransportO
 			},
 		},
 	})
-	testSuitQUIC(t, clientPort, testPort)
+	testSuitSimple(t, clientPort, testPort)
 }

+ 36 - 6
test/vmess_test.go

@@ -13,7 +13,7 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
-func TestVMessAuto(t *testing.T) {
+func _TestVMessAuto(t *testing.T) {
 	security := "auto"
 	user, err := uuid.DefaultGenerator.NewV4()
 	require.NoError(t, err)
@@ -31,7 +31,7 @@ func TestVMessAuto(t *testing.T) {
 	})
 }
 
-func _TestVMess(t *testing.T) {
+func TestVMess(t *testing.T) {
 	for _, security := range []string{
 		"zero",
 	} {
@@ -40,12 +40,19 @@ func _TestVMess(t *testing.T) {
 		})
 	}
 	for _, security := range []string{
-		"aes-128-gcm", "chacha20-poly1305", "aes-128-cfb",
+		"none",
 	} {
 		t.Run(security, func(t *testing.T) {
 			testVMess1(t, security)
 		})
 	}
+	for _, security := range []string{
+		"aes-128-gcm", "chacha20-poly1305", "aes-128-cfb",
+	} {
+		t.Run(security, func(t *testing.T) {
+			testVMess2(t, security)
+		})
+	}
 }
 
 func testVMess0(t *testing.T, security string) {
@@ -69,6 +76,29 @@ func testVMess0(t *testing.T, security string) {
 }
 
 func testVMess1(t *testing.T, security string) {
+	user, err := uuid.DefaultGenerator.NewV4()
+	require.NoError(t, err)
+	t.Run("self", func(t *testing.T) {
+		testVMessSelf(t, security, user, 0, false, false, false)
+	})
+	t.Run("self-legacy", func(t *testing.T) {
+		testVMessSelf(t, security, user, 1, false, false, false)
+	})
+	t.Run("packetaddr", func(t *testing.T) {
+		testVMessSelf(t, security, user, 0, false, false, true)
+	})
+	t.Run("inbound", func(t *testing.T) {
+		testVMessInboundWithV2Ray(t, security, user, 0, false)
+	})
+	t.Run("outbound", func(t *testing.T) {
+		testVMessOutboundWithV2Ray(t, security, user, false, false, 0)
+	})
+	t.Run("outbound-legacy", func(t *testing.T) {
+		testVMessOutboundWithV2Ray(t, security, user, false, false, 1)
+	})
+}
+
+func testVMess2(t *testing.T, security string) {
 	user, err := uuid.DefaultGenerator.NewV4()
 	require.NoError(t, err)
 	t.Run("self", func(t *testing.T) {
@@ -175,7 +205,7 @@ func testVMessInboundWithV2Ray(t *testing.T, security string, uuid uuid.UUID, al
 		},
 	})
 
-	testSuit(t, clientPort, testPort)
+	testSuitSimple(t, clientPort, testPort)
 }
 
 func testVMessOutboundWithV2Ray(t *testing.T, security string, uuid uuid.UUID, globalPadding bool, authenticatedLength bool, alterId int) {
@@ -232,13 +262,13 @@ func testVMessOutboundWithV2Ray(t *testing.T, security string, uuid uuid.UUID, g
 			},
 		},
 	})
-	testSuit(t, clientPort, testPort)
+	testSuitSimple(t, clientPort, testPort)
 }
 
 func testVMessSelf(t *testing.T, security string, uuid uuid.UUID, alterId int, globalPadding bool, authenticatedLength bool, packetAddr bool) {
 	startInstance(t, option.Options{
 		Log: &option.LogOptions{
-			Level: "trace",
+			Level: "error",
 		},
 		Inbounds: []option.Inbound{
 			{

+ 3 - 21
transport/v2raygrpc/conn.go

@@ -2,12 +2,11 @@ package v2raygrpc
 
 import (
 	"context"
-	"io"
 	"net"
 	"os"
-	"strings"
 	"time"
 
+	"github.com/sagernet/sing-box/common/baderror"
 	"github.com/sagernet/sing/common/rw"
 )
 
@@ -36,7 +35,7 @@ func (c *GRPCConn) Read(b []byte) (n int, err error) {
 		return
 	}
 	hunk, err := c.Recv()
-	err = wrapError(err)
+	err = baderror.WrapGRPC(err)
 	if err != nil {
 		return
 	}
@@ -48,7 +47,7 @@ func (c *GRPCConn) Read(b []byte) (n int, err error) {
 }
 
 func (c *GRPCConn) Write(b []byte) (n int, err error) {
-	err = wrapError(c.Send(&Hunk{Data: b}))
+	err = baderror.WrapGRPC(c.Send(&Hunk{Data: b}))
 	if err != nil {
 		return
 	}
@@ -93,20 +92,3 @@ type clientConnWrapper struct {
 func (c *clientConnWrapper) CloseWrite() error {
 	return c.CloseSend()
 }
-
-func wrapError(err error) error {
-	// grpc uses stupid internal error types
-	if err == nil {
-		return nil
-	}
-	if strings.Contains(err.Error(), "EOF") {
-		return io.EOF
-	}
-	if strings.Contains(err.Error(), "the client connection is closing") {
-		return net.ErrClosed
-	}
-	if strings.Contains(err.Error(), "server closed the stream without sending trailers") {
-		return net.ErrClosed
-	}
-	return err
-}

+ 4 - 11
transport/v2raygrpclite/conn.go

@@ -10,10 +10,10 @@ import (
 	"os"
 	"time"
 
+	"github.com/sagernet/sing-box/common/baderror"
 	"github.com/sagernet/sing/common"
 	"github.com/sagernet/sing/common/buf"
 	"github.com/sagernet/sing/common/bufio"
-	E "github.com/sagernet/sing/common/exceptions"
 	"github.com/sagernet/sing/common/rw"
 )
 
@@ -53,7 +53,7 @@ func (c *GunConn) setup(reader io.Reader, err error) {
 
 func (c *GunConn) Read(b []byte) (n int, err error) {
 	n, err = c.read(b)
-	return n, wrapError(err)
+	return n, baderror.WrapH2(err)
 }
 
 func (c *GunConn) read(b []byte) (n int, err error) {
@@ -105,7 +105,7 @@ func (c *GunConn) Write(b []byte) (n int, err error) {
 	if c.flusher != nil {
 		c.flusher.Flush()
 	}
-	return len(b), wrapError(err)
+	return len(b), baderror.WrapH2(err)
 }
 
 func uLen(x uint64) int {
@@ -129,7 +129,7 @@ func (c *GunConn) WriteBuffer(buffer *buf.Buffer) error {
 	if c.flusher != nil {
 		c.flusher.Flush()
 	}
-	return wrapError(err)
+	return baderror.WrapH2(err)
 }
 
 func (c *GunConn) FrontHeadroom() int {
@@ -159,10 +159,3 @@ func (c *GunConn) SetReadDeadline(t time.Time) error {
 func (c *GunConn) SetWriteDeadline(t time.Time) error {
 	return os.ErrInvalid
 }
-
-func wrapError(err error) error {
-	if E.IsMulti(err, io.ErrUnexpectedEOF) {
-		return io.EOF
-	}
-	return err
-}

+ 3 - 10
transport/v2rayhttp/conn.go

@@ -7,8 +7,8 @@ import (
 	"os"
 	"time"
 
+	"github.com/sagernet/sing-box/common/baderror"
 	"github.com/sagernet/sing/common"
-	E "github.com/sagernet/sing/common/exceptions"
 )
 
 type HTTPConn struct {
@@ -18,12 +18,12 @@ type HTTPConn struct {
 
 func (c *HTTPConn) Read(b []byte) (n int, err error) {
 	n, err = c.reader.Read(b)
-	return n, wrapError(err)
+	return n, baderror.WrapH2(err)
 }
 
 func (c *HTTPConn) Write(b []byte) (n int, err error) {
 	n, err = c.writer.Write(b)
-	return n, wrapError(err)
+	return n, baderror.WrapH2(err)
 }
 
 func (c *HTTPConn) Close() error {
@@ -62,10 +62,3 @@ func (c *ServerHTTPConn) Write(b []byte) (n int, err error) {
 	}
 	return
 }
-
-func wrapError(err error) error {
-	if E.IsMulti(err, io.ErrUnexpectedEOF) {
-		return io.EOF
-	}
-	return err
-}