github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/transport/internet/httpupgrade/dialer.go (about)

     1  package httpupgrade
     2  
     3  import (
     4  	"bufio"
     5  	"context"
     6  	"net/http"
     7  	"strings"
     8  
     9  	"github.com/v2fly/v2ray-core/v5/common"
    10  	"github.com/v2fly/v2ray-core/v5/common/net"
    11  	"github.com/v2fly/v2ray-core/v5/common/session"
    12  	"github.com/v2fly/v2ray-core/v5/transport/internet"
    13  	"github.com/v2fly/v2ray-core/v5/transport/internet/transportcommon"
    14  )
    15  
    16  func dialhttpUpgrade(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (net.Conn, error) {
    17  	transportConfiguration := streamSettings.ProtocolSettings.(*Config)
    18  
    19  	conn, err := transportcommon.DialWithSecuritySettings(ctx, dest, streamSettings)
    20  	if err != nil {
    21  		return nil, newError("failed to dial request to ", dest).Base(err)
    22  	}
    23  	req, err := http.NewRequest("GET", transportConfiguration.GetNormalizedPath(), nil)
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  
    28  	req.Header.Set("Connection", "upgrade")
    29  	req.Header.Set("Upgrade", "websocket")
    30  	req.Host = transportConfiguration.Host
    31  
    32  	err = req.Write(conn)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  
    37  	// TODO The bufio usage here is unreliable
    38  	resp, err := http.ReadResponse(bufio.NewReader(conn), req) // nolint:bodyclose
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	if resp.Status == "101 Switching Protocols" &&
    44  		strings.ToLower(resp.Header.Get("Upgrade")) == "websocket" &&
    45  		strings.ToLower(resp.Header.Get("Connection")) == "upgrade" {
    46  		return conn, nil
    47  	}
    48  	return nil, newError("unrecognized reply")
    49  }
    50  
    51  func dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) {
    52  	newError("creating connection to ", dest).WriteToLog(session.ExportIDToError(ctx))
    53  
    54  	conn, err := dialhttpUpgrade(ctx, dest, streamSettings)
    55  	if err != nil {
    56  		return nil, newError("failed to dial request to ", dest).Base(err)
    57  	}
    58  	return internet.Connection(conn), nil
    59  }
    60  
    61  func init() {
    62  	common.Must(internet.RegisterTransportDialer(protocolName, dial))
    63  }