Просмотр исходного кода

Allow http1 in v2ray HTTP transport

世界 3 лет назад
Родитель
Сommit
a24a2b475a

+ 70 - 0
test/vmess_transport_test.go

@@ -211,3 +211,73 @@ func TestVMessQUICSelf(t *testing.T) {
 	})
 	testSuitQUIC(t, clientPort, testPort)
 }
+
+func TestVMessHTTPNoTLSSelf(t *testing.T) {
+	transport := &option.V2RayTransportOptions{
+		Type: C.V2RayTransportTypeHTTP,
+	}
+	user, err := uuid.DefaultGenerator.NewV4()
+	require.NoError(t, err)
+	startInstance(t, option.Options{
+		Log: &option.LogOptions{
+			Level: "trace",
+		},
+		Inbounds: []option.Inbound{
+			{
+				Type: C.TypeMixed,
+				Tag:  "mixed-in",
+				MixedOptions: option.HTTPMixedInboundOptions{
+					ListenOptions: option.ListenOptions{
+						Listen:     option.ListenAddress(netip.IPv4Unspecified()),
+						ListenPort: clientPort,
+					},
+				},
+			},
+			{
+				Type: C.TypeVMess,
+				VMessOptions: option.VMessInboundOptions{
+					ListenOptions: option.ListenOptions{
+						Listen:     option.ListenAddress(netip.IPv4Unspecified()),
+						ListenPort: serverPort,
+					},
+					Users: []option.VMessUser{
+						{
+							Name: "sekai",
+							UUID: user.String(),
+						},
+					},
+					Transport: transport,
+				},
+			},
+		},
+		Outbounds: []option.Outbound{
+			{
+				Type: C.TypeDirect,
+			},
+			{
+				Type: C.TypeVMess,
+				Tag:  "vmess-out",
+				VMessOptions: option.VMessOutboundOptions{
+					ServerOptions: option.ServerOptions{
+						Server:     "127.0.0.1",
+						ServerPort: serverPort,
+					},
+					UUID:      user.String(),
+					Security:  "zero",
+					Transport: transport,
+				},
+			},
+		},
+		Route: &option.RouteOptions{
+			Rules: []option.Rule{
+				{
+					DefaultOptions: option.DefaultRule{
+						Inbound:  []string{"mixed-in"},
+						Outbound: "vmess-out",
+					},
+				},
+			},
+		},
+	})
+	testSuitQUIC(t, clientPort, testPort)
+}

+ 0 - 3
transport/v2ray/transport.go

@@ -44,9 +44,6 @@ func NewClientTransport(ctx context.Context, dialer N.Dialer, serverAddr M.Socks
 	}
 	switch options.Type {
 	case C.V2RayTransportTypeHTTP:
-		if tlsConfig == nil {
-			return nil, C.ErrTLSRequired
-		}
 		return v2rayhttp.NewClient(ctx, dialer, serverAddr, options.HTTPOptions, tlsConfig), nil
 	case C.V2RayTransportTypeGRPC:
 		if tlsConfig == nil {

+ 59 - 11
transport/v2rayhttp/client.go

@@ -1,6 +1,7 @@
 package v2rayhttp
 
 import (
+	"bufio"
 	"context"
 	"crypto/tls"
 	"io"
@@ -20,20 +21,25 @@ import (
 var _ adapter.V2RayClientTransport = (*Client)(nil)
 
 type Client struct {
-	ctx     context.Context
-	client  *http.Client
-	url     *url.URL
-	host    []string
-	method  string
-	headers http.Header
+	ctx        context.Context
+	dialer     N.Dialer
+	serverAddr M.Socksaddr
+	client     *http.Client
+	http2      bool
+	url        *url.URL
+	host       []string
+	method     string
+	headers    http.Header
 }
 
 func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayHTTPOptions, tlsConfig *tls.Config) adapter.V2RayClientTransport {
 	client := &Client{
-		ctx:     ctx,
-		host:    options.Host,
-		method:  options.Method,
-		headers: make(http.Header),
+		ctx:        ctx,
+		dialer:     dialer,
+		serverAddr: serverAddr,
+		host:       options.Host,
+		method:     options.Method,
+		headers:    make(http.Header),
 		client: &http.Client{
 			Transport: &http.Transport{
 				DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
@@ -43,6 +49,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
 				TLSClientConfig:   tlsConfig,
 			},
 		},
+		http2: tlsConfig != nil,
 	}
 	if client.method == "" {
 		client.method = "PUT"
@@ -66,13 +73,54 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
 }
 
 func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
+	if !c.http2 {
+		return c.dialHTTP()
+	} else {
+		return c.dialHTTP2()
+	}
+}
+
+func (c *Client) dialHTTP() (net.Conn, error) {
+	conn, err := c.dialer.DialContext(c.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(),
+	}
+	switch hostLen := len(c.host); hostLen {
+	case 0:
+	case 1:
+		request.Host = c.host[0]
+	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
+}
+
+func (c *Client) dialHTTP2() (net.Conn, error) {
 	pipeInReader, pipeInWriter := io.Pipe()
 	request := &http.Request{
 		Method:     c.method,
 		Body:       pipeInReader,
 		URL:        c.url,
 		ProtoMajor: 2,
-		ProtoMinor: 0,
 		Proto:      "HTTP/2",
 		Header:     c.headers.Clone(),
 	}

+ 1 - 13
transport/v2rayhttp/server.go

@@ -12,8 +12,6 @@ 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"
@@ -96,22 +94,12 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
 	}
 
 	if h, ok := writer.(http.Hijacker); ok {
-		conn, reader, err := h.Hijack()
+		conn, _, err := h.Hijack()
 		if err != nil {
 			writer.WriteHeader(http.StatusInternalServerError)
 			s.badRequest(request, E.Cause(err, "hijack conn"))
 			return
 		}
-		if reader.Available() > 0 {
-			buffer := buf.NewSize(reader.Available())
-			_, err = buffer.ReadFullFrom(reader, buffer.FreeLen())
-			if err != nil {
-				writer.WriteHeader(http.StatusInternalServerError)
-				s.badRequest(request, E.Cause(err, "read cached data"))
-				return
-			}
-			conn = bufio.NewCachedConn(conn, buffer)
-		}
 		s.handler.NewConnection(request.Context(), conn, M.Metadata{})
 	} else {
 		conn := &ServerHTTPConn{