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 }