github.com/xmplusdev/xmcore@v1.8.11-0.20240412132628-5518b55526af/common/protocol/http/sniff.go (about) 1 package http 2 3 import ( 4 "bytes" 5 "errors" 6 "strings" 7 8 "github.com/xmplusdev/xmcore/common" 9 "github.com/xmplusdev/xmcore/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 methods = [...]string{"get", "post", "head", "put", "delete", "options", "connect"} 41 42 errNotHTTPMethod = errors.New("not an HTTP method") 43 ) 44 45 func beginWithHTTPMethod(b []byte) error { 46 for _, m := range &methods { 47 if len(b) >= len(m) && strings.EqualFold(string(b[:len(m)]), m) { 48 return nil 49 } 50 51 if len(b) < len(m) { 52 return common.ErrNoClue 53 } 54 } 55 56 return errNotHTTPMethod 57 } 58 59 func SniffHTTP(b []byte) (*SniffHeader, error) { 60 if err := beginWithHTTPMethod(b); err != nil { 61 return nil, err 62 } 63 64 sh := &SniffHeader{ 65 version: HTTP1, 66 } 67 68 headers := bytes.Split(b, []byte{'\n'}) 69 for i := 1; i < len(headers); i++ { 70 header := headers[i] 71 if len(header) == 0 { 72 break 73 } 74 parts := bytes.SplitN(header, []byte{':'}, 2) 75 if len(parts) != 2 { 76 continue 77 } 78 key := strings.ToLower(string(parts[0])) 79 if key == "host" { 80 rawHost := strings.ToLower(string(bytes.TrimSpace(parts[1]))) 81 dest, err := ParseHost(rawHost, net.Port(80)) 82 if err != nil { 83 return nil, err 84 } 85 sh.host = dest.Address.String() 86 } 87 } 88 89 if len(sh.host) > 0 { 90 return sh, nil 91 } 92 93 return nil, common.ErrNoClue 94 }