github.com/yaling888/clash@v1.53.0/listener/http/upgrade.go (about)

     1  package http
     2  
     3  import (
     4  	"net"
     5  	"net/http"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/yaling888/clash/adapter/inbound"
    10  	N "github.com/yaling888/clash/common/net"
    11  	C "github.com/yaling888/clash/constant"
    12  	"github.com/yaling888/clash/transport/socks5"
    13  )
    14  
    15  func isUpgradeRequest(req *http.Request) bool {
    16  	for _, header := range req.Header["Connection"] {
    17  		for _, elm := range strings.Split(header, ",") {
    18  			if strings.EqualFold(strings.TrimSpace(elm), "Upgrade") {
    19  				return true
    20  			}
    21  		}
    22  	}
    23  
    24  	return false
    25  }
    26  
    27  func HandleUpgrade(localConn net.Conn, serverConn *N.BufferedConn, request *http.Request, in chan<- C.ConnContext) (resp *http.Response) {
    28  	removeProxyHeaders(request.Header)
    29  	RemoveExtraHTTPHostPort(request)
    30  
    31  	if serverConn == nil {
    32  		address := request.Host
    33  		if _, _, err := net.SplitHostPort(address); err != nil {
    34  			port := "80"
    35  			if request.TLS != nil {
    36  				port = "443"
    37  			}
    38  			address = net.JoinHostPort(address, port)
    39  		}
    40  
    41  		dstAddr := socks5.ParseAddr(address)
    42  		if dstAddr == nil {
    43  			return
    44  		}
    45  
    46  		left, right := net.Pipe()
    47  
    48  		in <- inbound.NewHTTP(dstAddr, localConn.RemoteAddr(), localConn.LocalAddr(), right)
    49  
    50  		serverConn = N.NewBufferedConn(left)
    51  
    52  		defer func() {
    53  			_ = serverConn.Close()
    54  		}()
    55  	}
    56  
    57  	err := request.Write(serverConn)
    58  	if err != nil {
    59  		_ = localConn.Close()
    60  		return
    61  	}
    62  
    63  	resp, err = http.ReadResponse(serverConn.Reader(), request)
    64  	if err != nil {
    65  		_ = localConn.Close()
    66  		return
    67  	}
    68  
    69  	if resp.StatusCode == http.StatusSwitchingProtocols {
    70  		removeProxyHeaders(resp.Header)
    71  
    72  		err = localConn.SetReadDeadline(time.Time{}) // set to not time out
    73  		if err != nil {
    74  			return
    75  		}
    76  
    77  		err = resp.Write(localConn)
    78  		if err != nil {
    79  			return
    80  		}
    81  
    82  		N.Relay(serverConn, localConn) // blocking here
    83  		_ = localConn.Close()
    84  		resp = nil
    85  	}
    86  	return
    87  }