github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/common/protocol/http/sniff.go (about)

     1  package http
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"strings"
     7  
     8  	"github.com/v2fly/v2ray-core/v5/common"
     9  	"github.com/v2fly/v2ray-core/v5/common/net"
    10  )
    11  
    12  type version byte
    13  
    14  type SniffHeader struct {
    15  	host string
    16  }
    17  
    18  func (h *SniffHeader) Protocol() string {
    19  	return "http1"
    20  }
    21  
    22  func (h *SniffHeader) Domain() string {
    23  	return h.host
    24  }
    25  
    26  var (
    27  	// refer to https://pkg.go.dev/net/http@master#pkg-constants
    28  	methods = [...]string{"get", "post", "head", "put", "delete", "options", "connect", "patch", "trace"}
    29  
    30  	errNotHTTPMethod = errors.New("not an HTTP method")
    31  )
    32  
    33  func beginWithHTTPMethod(b []byte) error {
    34  	for _, m := range &methods {
    35  		if len(b) >= len(m) && strings.EqualFold(string(b[:len(m)]), m) {
    36  			return nil
    37  		}
    38  
    39  		if len(b) < len(m) {
    40  			return common.ErrNoClue
    41  		}
    42  	}
    43  
    44  	return errNotHTTPMethod
    45  }
    46  
    47  func SniffHTTP(b []byte) (*SniffHeader, error) {
    48  	if err := beginWithHTTPMethod(b); err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	sh := &SniffHeader{}
    53  
    54  	headers := bytes.Split(b, []byte{'\n'})
    55  	for i := 1; i < len(headers); i++ {
    56  		header := headers[i]
    57  		if len(header) == 0 {
    58  			break
    59  		}
    60  		parts := bytes.SplitN(header, []byte{':'}, 2)
    61  		if len(parts) != 2 {
    62  			continue
    63  		}
    64  		key := strings.ToLower(string(parts[0]))
    65  		if key == "host" {
    66  			rawHost := strings.ToLower(string(bytes.TrimSpace(parts[1])))
    67  			dest, err := ParseHost(rawHost, net.Port(80))
    68  			if err != nil {
    69  				return nil, err
    70  			}
    71  			sh.host = dest.Address.String()
    72  		}
    73  	}
    74  
    75  	if len(sh.host) > 0 {
    76  		return sh, nil
    77  	}
    78  
    79  	return nil, common.ErrNoClue
    80  }