From 46c318c6fec2aa48c2ea08e1cd3ccc8da00e958e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 7 Apr 2023 18:20:07 +0800 Subject: [PATCH] Fix v2ray HTTP/1.1 transport compatibility --- option/v2ray_transport.go | 20 ++--- test/box_test.go | 5 +- test/go.mod | 26 +++--- test/go.sum | 54 +++++------ transport/sip003/v2ray.go | 4 +- transport/v2rayhttp/client.go | 44 +++------ transport/v2rayhttp/conn.go | 139 ++++++++++++++++++++++++++--- transport/v2rayhttp/server.go | 15 +++- transport/v2raywebsocket/client.go | 2 +- 9 files changed, 208 insertions(+), 101 deletions(-) diff --git a/option/v2ray_transport.go b/option/v2ray_transport.go index aa07dba9..b01cafa7 100644 --- a/option/v2ray_transport.go +++ b/option/v2ray_transport.go @@ -61,19 +61,19 @@ 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"` - IdleTimeout Duration `json:"idle_timeout,omitempty"` - PingTimeout Duration `json:"ping_timeout,omitempty"` + Host Listable[string] `json:"host,omitempty"` + Path string `json:"path,omitempty"` + Method string `json:"method,omitempty"` + Headers map[string]Listable[string] `json:"headers,omitempty"` + IdleTimeout Duration `json:"idle_timeout,omitempty"` + PingTimeout Duration `json:"ping_timeout,omitempty"` } type V2RayWebsocketOptions struct { - Path string `json:"path,omitempty"` - Headers map[string]string `json:"headers,omitempty"` - MaxEarlyData uint32 `json:"max_early_data,omitempty"` - EarlyDataHeaderName string `json:"early_data_header_name,omitempty"` + Path string `json:"path,omitempty"` + Headers map[string]Listable[string] `json:"headers,omitempty"` + MaxEarlyData uint32 `json:"max_early_data,omitempty"` + EarlyDataHeaderName string `json:"early_data_header_name,omitempty"` } type V2RayQUICOptions struct{} diff --git a/test/box_test.go b/test/box_test.go index db6075b9..f94b1680 100644 --- a/test/box_test.go +++ b/test/box_test.go @@ -37,7 +37,10 @@ func startInstance(t *testing.T, options option.Options) *box.Box { var instance *box.Box var err error for retry := 0; retry < 3; retry++ { - instance, err = box.New(ctx, options, nil) + instance, err = box.New(box.Options{ + Context: ctx, + Options: options, + }) require.NoError(t, err) err = instance.Start() if err != nil { diff --git a/test/go.mod b/test/go.mod index 006dfcf9..559c2ef7 100644 --- a/test/go.mod +++ b/test/go.mod @@ -10,17 +10,17 @@ require ( github.com/docker/docker v20.10.18+incompatible github.com/docker/go-connections v0.4.0 github.com/gofrs/uuid v4.4.0+incompatible - github.com/sagernet/sing v0.2.1-0.20230318094614-4bbf5f2c3046 - github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9 + github.com/sagernet/sing v0.2.2-0.20230407053809-308e421e33c2 + github.com/sagernet/sing-shadowsocks v0.2.0 github.com/spyzhov/ajson v0.7.1 github.com/stretchr/testify v1.8.2 go.uber.org/goleak v1.2.0 - golang.org/x/net v0.8.0 + golang.org/x/net v0.9.0 ) require ( berty.tech/go-libtor v1.0.385 // indirect - github.com/Dreamacro/clash v1.13.0 // indirect + github.com/Dreamacro/clash v1.14.0 // indirect github.com/Microsoft/go-winio v0.5.1 // indirect github.com/ajg/form v1.5.1 // indirect github.com/andybalholm/brotli v1.0.5 // indirect @@ -42,14 +42,14 @@ require ( github.com/google/btree v1.0.1 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/hashicorp/yamux v0.1.1 // indirect - github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961 // indirect + github.com/insomniacslk/dhcp v0.0.0-20230327135226-74ae03f2425e // indirect github.com/josharian/native v1.1.0 // indirect github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/cpuid/v2 v2.1.1 // indirect github.com/libdns/libdns v0.2.1 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/mholt/acmez v1.1.0 // indirect - github.com/miekg/dns v1.1.52 // indirect + github.com/miekg/dns v1.1.53 // indirect github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/onsi/ginkgo/v2 v2.2.0 // indirect @@ -69,10 +69,10 @@ require ( github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect - github.com/sagernet/reality v0.0.0-20230312150606-35ea9af0e0b8 // indirect - github.com/sagernet/sing-dns v0.1.4 // indirect + github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect + github.com/sagernet/sing-dns v0.1.5-0.20230407055526-2a27418e7855 // indirect github.com/sagernet/sing-shadowtls v0.1.0 // indirect - github.com/sagernet/sing-tun v0.1.3-0.20230315134716-fe89bbded22d // indirect + github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab // indirect github.com/sagernet/sing-vmess v0.1.3 // indirect github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect @@ -88,14 +88,14 @@ require ( go.uber.org/zap v1.24.0 // indirect go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35 // indirect golang.org/x/crypto v0.7.0 // indirect - golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 // indirect + golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect golang.org/x/mod v0.8.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/tools v0.6.0 // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect - google.golang.org/grpc v1.53.0 // indirect + google.golang.org/grpc v1.54.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.4.0 // indirect diff --git a/test/go.sum b/test/go.sum index a110ef8e..6d40ad7e 100644 --- a/test/go.sum +++ b/test/go.sum @@ -1,8 +1,8 @@ berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw= berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Dreamacro/clash v1.13.0 h1:gF0E0TluE1LCmuhhg0/bjqABYDmSnXkUjXjRhZxyrm8= -github.com/Dreamacro/clash v1.13.0/go.mod h1:hf0RkWPsQ0e8oS8WVJBIRocY/1ILYzQQg9zeMwd8LsM= +github.com/Dreamacro/clash v1.14.0 h1:ehJ/C/1m9LEjmME72WSE/Y2YqbR3Q54AbjqiRCvtyW4= +github.com/Dreamacro/clash v1.14.0/go.mod h1:ia2CU7V713H1QdCqMwOLK9U9V5Ay8X0voj3yQr2tk+I= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= @@ -61,8 +61,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961 h1:x/YtdDlmypenG1te/FfH6LVM+3krhXk5CFV8VYNNX5M= -github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI= +github.com/insomniacslk/dhcp v0.0.0-20230327135226-74ae03f2425e h1:8ChxkWKTVYg7LKBvYNLNRnlobgbPrzzossZUoST2T7o= +github.com/insomniacslk/dhcp v0.0.0-20230327135226-74ae03f2425e/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= @@ -80,8 +80,8 @@ github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczG github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY= github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs= -github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c= -github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= +github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c h1:RC8WMpjonrBfyAh6VN/POIPtYD5tRAq0qMqCRjQNK+g= github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c/go.mod h1:9OcmHNQQUTbk4XCffrLgN1NEKc2mh5u++biHVrvHsSU= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -122,20 +122,20 @@ github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6E github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY= github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8= -github.com/sagernet/reality v0.0.0-20230312150606-35ea9af0e0b8 h1:4M3+0/kqvJuTsimEORH0/vUYTpMDUETBH2zNUU38SUw= -github.com/sagernet/reality v0.0.0-20230312150606-35ea9af0e0b8/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= +github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= +github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= -github.com/sagernet/sing v0.2.1-0.20230318094614-4bbf5f2c3046 h1:/+ZWbxRvQmco9ES2qT5Eh/x/IiQRjAcUyRG/vQ4dpxc= -github.com/sagernet/sing v0.2.1-0.20230318094614-4bbf5f2c3046/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= -github.com/sagernet/sing-dns v0.1.4 h1:7VxgeoSCiiazDSaXXQVcvrTBxFpOePPq/4XdgnUDN+0= -github.com/sagernet/sing-dns v0.1.4/go.mod h1:1+6pCa48B1AI78lD+/i/dLgpw4MwfnsSpZo0Ds8wzzk= -github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9 h1:qS39eA4C7x+zhEkySbASrtmb6ebdy5v0y2M6mgkmSO0= -github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9/go.mod h1:f3mHTy5shnVM9l8UocMlJgC/1G/zdj5FuEuVXhDinGU= +github.com/sagernet/sing v0.2.2-0.20230407053809-308e421e33c2 h1:VjeHDxEgpB2fqK5G16yBvtLacibvg3h2MsIjal0UXH0= +github.com/sagernet/sing v0.2.2-0.20230407053809-308e421e33c2/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= +github.com/sagernet/sing-dns v0.1.5-0.20230407055526-2a27418e7855 h1:a3W2X1n5C/oYGp/Dd26eoymME3iXN8TJq7LZtO2MSUY= +github.com/sagernet/sing-dns v0.1.5-0.20230407055526-2a27418e7855/go.mod h1:69PNSHyEmXdjf6C+bXBOdr2GQnPeEyWjIzo/MV8gmz8= +github.com/sagernet/sing-shadowsocks v0.2.0 h1:ILDWL7pwWfkPLEbviE/MyCgfjaBmJY/JVVY+5jhSb58= +github.com/sagernet/sing-shadowsocks v0.2.0/go.mod h1:ysYzszRLpNzJSorvlWRMuzU6Vchsp7sd52q+JNY4axw= github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ= github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc= -github.com/sagernet/sing-tun v0.1.3-0.20230315134716-fe89bbded22d h1:1gt4Hu2fHCrmL2NZYCNJ3nCgeczuhK09oCMni9mZmZk= -github.com/sagernet/sing-tun v0.1.3-0.20230315134716-fe89bbded22d/go.mod h1:KnRkwaDHbb06zgeNPu0LQ8A+vA9myMxKEgHN1brCPHg= +github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab h1:a9oeWuPBuIZ70qMhIIH6RrYhp886xN9jJIwsuu4ZFUo= +github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab/go.mod h1:4YxIDEkkCjGXDOTMPw1SXpLmCQUFAWuaQN250oo+928= github.com/sagernet/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM= github.com/sagernet/sing-vmess v0.1.3/go.mod h1:GVXqAHwe9U21uS+Voh4YBIrADQyE4F9v0ayGSixSQAE= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= @@ -191,8 +191,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo= -golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -206,8 +206,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 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.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -232,15 +232,15 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -257,8 +257,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= diff --git a/transport/sip003/v2ray.go b/transport/sip003/v2ray.go index 19b17baa..b32335d5 100644 --- a/transport/sip003/v2ray.go +++ b/transport/sip003/v2ray.go @@ -66,8 +66,8 @@ func newV2RayPlugin(pluginOpts Args, router adapter.Router, dialer N.Dialer, ser transportOptions = option.V2RayTransportOptions{ Type: C.V2RayTransportTypeWebsocket, WebsocketOptions: option.V2RayWebsocketOptions{ - Headers: map[string]string{ - "Host": host, + Headers: map[string]option.Listable[string]{ + "Host": []string{host}, }, Path: path, }, diff --git a/transport/v2rayhttp/client.go b/transport/v2rayhttp/client.go index ddaff872..2131160f 100644 --- a/transport/v2rayhttp/client.go +++ b/transport/v2rayhttp/client.go @@ -1,7 +1,6 @@ package v2rayhttp import ( - "bufio" "context" "io" "math/rand" @@ -81,8 +80,8 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt if !strings.HasPrefix(uri.Path, "/") { uri.Path = "/" + uri.Path } - for key, value := range options.Headers { - client.headers.Set(key, value) + for key, valueList := range options.Headers { + client.headers[key] = valueList } client.url = &uri return client @@ -97,18 +96,16 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) { } func (c *Client) dialHTTP(ctx context.Context) (net.Conn, error) { - conn, err := c.dialer.DialContext(c.ctx, N.NetworkTCP, c.serverAddr) + conn, err := c.dialer.DialContext(ctx, N.NetworkTCP, c.serverAddr) if err != nil { return nil, err } + request := &http.Request{ - Method: c.method, - URL: c.url, - ProtoMajor: 1, - Proto: "HTTP/1.1", - Header: c.headers.Clone(), + Method: c.method, + URL: c.url, + Header: c.headers.Clone(), } - request = request.WithContext(ctx) switch hostLen := len(c.host); hostLen { case 0: request.Host = c.serverAddr.AddrString() @@ -117,30 +114,17 @@ func (c *Client) dialHTTP(ctx context.Context) (net.Conn, error) { default: request.Host = c.host[rand.Intn(hostLen)] } - err = request.Write(conn) - if err != nil { - return nil, err - } - reader := bufio.NewReader(conn) - response, err := http.ReadResponse(reader, request) - if err != nil { - return nil, err - } - if response.StatusCode != 200 { - return nil, E.New("unexpected status: ", response.Status) - } - return conn, nil + + return NewHTTP1Conn(conn, request), nil } func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) { pipeInReader, pipeInWriter := io.Pipe() request := &http.Request{ - Method: c.method, - Body: pipeInReader, - URL: c.url, - ProtoMajor: 2, - Proto: "HTTP/2", - Header: c.headers.Clone(), + Method: c.method, + Body: pipeInReader, + URL: c.url, + Header: c.headers.Clone(), } request = request.WithContext(ctx) switch hostLen := len(c.host); hostLen { @@ -152,8 +136,6 @@ func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) { default: request.Host = c.host[rand.Intn(hostLen)] } - // Disable any compression method from server. - request.Header.Set("Accept-Encoding", "identity") conn := newLateHTTPConn(pipeInWriter) go func() { response, err := c.transport.RoundTrip(request) diff --git a/transport/v2rayhttp/conn.go b/transport/v2rayhttp/conn.go index 1a22331b..ca97a520 100644 --- a/transport/v2rayhttp/conn.go +++ b/transport/v2rayhttp/conn.go @@ -1,10 +1,12 @@ package v2rayhttp import ( + std_bufio "bufio" "io" "net" "net/http" "os" + "strings" "sync" "time" @@ -12,37 +14,146 @@ import ( "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/bufio" + E "github.com/sagernet/sing/common/exceptions" + F "github.com/sagernet/sing/common/format" N "github.com/sagernet/sing/common/network" ) type HTTPConn struct { + net.Conn + request *http.Request + requestWritten bool + responseRead bool + responseCache *buf.Buffer +} + +func NewHTTP1Conn(conn net.Conn, request *http.Request) *HTTPConn { + return &HTTPConn{ + Conn: conn, + request: request, + } +} + +func (c *HTTPConn) Read(b []byte) (n int, err error) { + if !c.responseRead { + reader := std_bufio.NewReader(c.Conn) + response, err := http.ReadResponse(reader, c.request) + if err != nil { + return 0, E.Cause(err, "read response") + } + if response.StatusCode != 200 { + return 0, E.New("unexpected status: ", response.Status) + } + if cacheLen := reader.Buffered(); cacheLen > 0 { + c.responseCache = buf.NewSize(cacheLen) + _, err = c.responseCache.ReadFullFrom(reader, cacheLen) + if err != nil { + c.responseCache.Release() + return 0, E.Cause(err, "read cache") + } + } + c.responseRead = true + } + if c.responseCache != nil { + n, err = c.responseCache.Read(b) + if err == io.EOF { + c.responseCache.Release() + c.responseCache = nil + } + if n > 0 { + return n, nil + } + } + return c.Conn.Read(b) +} + +func (c *HTTPConn) Write(b []byte) (int, error) { + if !c.requestWritten { + err := c.writeRequest(b) + if err != nil { + return 0, E.Cause(err, "write request") + } + c.requestWritten = true + return len(b), nil + } + return c.Conn.Write(b) +} + +func (c *HTTPConn) writeRequest(payload []byte) error { + writer := bufio.NewBufferedWriter(c.Conn, buf.New()) + const CRLF = "\r\n" + _, err := writer.Write([]byte(F.ToString(c.request.Method, " ", c.request.URL.RequestURI(), " HTTP/1.1", CRLF))) + if err != nil { + return err + } + if c.request.Header.Get("Host") == "" { + c.request.Header.Set("Host", c.request.Host) + } + for key, value := range c.request.Header { + _, err = writer.Write([]byte(F.ToString(key, ": ", strings.Join(value, ", "), CRLF))) + if err != nil { + return err + } + } + _, err = writer.Write([]byte(CRLF)) + if err != nil { + return err + } + _, err = writer.Write(payload) + if err != nil { + return err + } + err = writer.Fallthrough() + if err != nil { + return err + } + return nil +} + +func (c *HTTPConn) ReaderReplaceable() bool { + return c.responseRead +} + +func (c *HTTPConn) WriterReplaceable() bool { + return c.requestWritten +} + +func (c *HTTPConn) NeedHandshake() bool { + return !c.requestWritten +} + +func (c *HTTPConn) Upstream() any { + return c.Conn +} + +type HTTP2Conn struct { reader io.Reader writer io.Writer create chan struct{} err error } -func NewHTTPConn(reader io.Reader, writer io.Writer) HTTPConn { - return HTTPConn{ +func NewHTTPConn(reader io.Reader, writer io.Writer) HTTP2Conn { + return HTTP2Conn{ reader: reader, writer: writer, } } -func newLateHTTPConn(writer io.Writer) *HTTPConn { - return &HTTPConn{ +func newLateHTTPConn(writer io.Writer) *HTTP2Conn { + return &HTTP2Conn{ create: make(chan struct{}), writer: writer, } } -func (c *HTTPConn) setup(reader io.Reader, err error) { +func (c *HTTP2Conn) setup(reader io.Reader, err error) { c.reader = reader c.err = err close(c.create) } -func (c *HTTPConn) Read(b []byte) (n int, err error) { +func (c *HTTP2Conn) Read(b []byte) (n int, err error) { if c.reader == nil { <-c.create if c.err != nil { @@ -53,24 +164,24 @@ func (c *HTTPConn) Read(b []byte) (n int, err error) { return n, baderror.WrapH2(err) } -func (c *HTTPConn) Write(b []byte) (n int, err error) { +func (c *HTTP2Conn) Write(b []byte) (n int, err error) { n, err = c.writer.Write(b) return n, baderror.WrapH2(err) } -func (c *HTTPConn) Close() error { +func (c *HTTP2Conn) Close() error { return common.Close(c.reader, c.writer) } -func (c *HTTPConn) LocalAddr() net.Addr { +func (c *HTTP2Conn) LocalAddr() net.Addr { return nil } -func (c *HTTPConn) RemoteAddr() net.Addr { +func (c *HTTP2Conn) RemoteAddr() net.Addr { return nil } -func (c *HTTPConn) SetDeadline(t time.Time) error { +func (c *HTTP2Conn) SetDeadline(t time.Time) error { if responseWriter, loaded := c.writer.(interface { SetWriteDeadline(time.Time) error }); loaded { @@ -79,7 +190,7 @@ func (c *HTTPConn) SetDeadline(t time.Time) error { return os.ErrInvalid } -func (c *HTTPConn) SetReadDeadline(t time.Time) error { +func (c *HTTP2Conn) SetReadDeadline(t time.Time) error { if responseWriter, loaded := c.writer.(interface { SetReadDeadline(time.Time) error }); loaded { @@ -88,7 +199,7 @@ func (c *HTTPConn) SetReadDeadline(t time.Time) error { return os.ErrInvalid } -func (c *HTTPConn) SetWriteDeadline(t time.Time) error { +func (c *HTTP2Conn) SetWriteDeadline(t time.Time) error { if responseWriter, loaded := c.writer.(interface { SetWriteDeadline(time.Time) error }); loaded { @@ -98,7 +209,7 @@ func (c *HTTPConn) SetWriteDeadline(t time.Time) error { } type ServerHTTPConn struct { - HTTPConn + HTTP2Conn flusher http.Flusher } diff --git a/transport/v2rayhttp/server.go b/transport/v2rayhttp/server.go index 358d7429..95288631 100644 --- a/transport/v2rayhttp/server.go +++ b/transport/v2rayhttp/server.go @@ -13,6 +13,8 @@ import ( C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/buf" + "github.com/sagernet/sing/common/bufio" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" @@ -62,7 +64,7 @@ func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig t server.path = "/" + server.path } for key, value := range options.Headers { - server.headers.Set(key, value) + server.headers[key] = value } server.httpServer = &http.Server{ Handler: server, @@ -106,11 +108,20 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) { var metadata M.Metadata metadata.Source = sHttp.SourceAddress(request) if h, ok := writer.(http.Hijacker); ok { - conn, _, err := h.Hijack() + conn, reader, err := h.Hijack() if err != nil { s.fallbackRequest(request.Context(), writer, request, http.StatusInternalServerError, E.Cause(err, "hijack conn")) return } + if cacheLen := reader.Reader.Buffered(); cacheLen > 0 { + cache := buf.NewSize(cacheLen) + _, err = cache.ReadFullFrom(reader.Reader, cacheLen) + if err != nil { + s.fallbackRequest(request.Context(), writer, request, http.StatusInternalServerError, E.Cause(err, "read cache")) + return + } + conn = bufio.NewCachedConn(conn, cache) + } s.handler.NewConnection(request.Context(), conn, metadata) } else { conn := NewHTTP2Wrapper(&ServerHTTPConn{ diff --git a/transport/v2raywebsocket/client.go b/transport/v2raywebsocket/client.go index c4b8faca..49d7555a 100644 --- a/transport/v2raywebsocket/client.go +++ b/transport/v2raywebsocket/client.go @@ -62,7 +62,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt } headers := make(http.Header) for key, value := range options.Headers { - headers.Set(key, value) + headers[key] = value } return &Client{ wsDialer,