github.com/imannamdari/v2ray-core/v5@v5.0.5/common/protocol/http/sniff.go (about)

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