github.com/metacubex/mihomo@v1.18.5/listener/http/upgrade.go (about)

     1  package http
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"net"
     7  	"net/http"
     8  	"strings"
     9  
    10  	"github.com/metacubex/mihomo/adapter/inbound"
    11  	N "github.com/metacubex/mihomo/common/net"
    12  	C "github.com/metacubex/mihomo/constant"
    13  	"github.com/metacubex/mihomo/transport/socks5"
    14  )
    15  
    16  func isUpgradeRequest(req *http.Request) bool {
    17  	for _, header := range req.Header["Connection"] {
    18  		for _, elm := range strings.Split(header, ",") {
    19  			if strings.EqualFold(strings.TrimSpace(elm), "Upgrade") {
    20  				return true
    21  			}
    22  		}
    23  	}
    24  
    25  	return false
    26  }
    27  
    28  func handleUpgrade(conn net.Conn, request *http.Request, tunnel C.Tunnel, additions ...inbound.Addition) {
    29  	defer conn.Close()
    30  
    31  	removeProxyHeaders(request.Header)
    32  	removeExtraHTTPHostPort(request)
    33  
    34  	address := request.Host
    35  	if _, _, err := net.SplitHostPort(address); err != nil {
    36  		address = net.JoinHostPort(address, "80")
    37  	}
    38  
    39  	dstAddr := socks5.ParseAddr(address)
    40  	if dstAddr == nil {
    41  		return
    42  	}
    43  
    44  	left, right := N.Pipe()
    45  
    46  	go tunnel.HandleTCPConn(inbound.NewHTTP(dstAddr, conn, right, additions...))
    47  
    48  	var bufferedLeft *N.BufferedConn
    49  	if request.TLS != nil {
    50  		tlsConn := tls.Client(left, &tls.Config{
    51  			ServerName: request.URL.Hostname(),
    52  		})
    53  
    54  		ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
    55  		defer cancel()
    56  		if tlsConn.HandshakeContext(ctx) != nil {
    57  			_ = left.Close()
    58  			return
    59  		}
    60  
    61  		bufferedLeft = N.NewBufferedConn(tlsConn)
    62  	} else {
    63  		bufferedLeft = N.NewBufferedConn(left)
    64  	}
    65  	defer func() {
    66  		_ = bufferedLeft.Close()
    67  	}()
    68  
    69  	err := request.Write(bufferedLeft)
    70  	if err != nil {
    71  		return
    72  	}
    73  
    74  	resp, err := http.ReadResponse(bufferedLeft.Reader(), request)
    75  	if err != nil {
    76  		return
    77  	}
    78  
    79  	removeProxyHeaders(resp.Header)
    80  
    81  	err = resp.Write(conn)
    82  	if err != nil {
    83  		return
    84  	}
    85  
    86  	if resp.StatusCode == http.StatusSwitchingProtocols {
    87  		N.Relay(bufferedLeft, conn)
    88  	}
    89  }