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 }