http2.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. package l7
  2. import (
  3. "encoding/binary"
  4. "net/http"
  5. "strconv"
  6. "strings"
  7. "time"
  8. "golang.org/x/net/http2"
  9. "golang.org/x/net/http2/hpack"
  10. )
  11. const (
  12. http2FrameHeaderLength = 9
  13. http2DecoderGcInterval = uint64(10 * time.Minute)
  14. )
  15. type Http2FrameHeader struct {
  16. Type http2.FrameType
  17. Flags http2.Flags
  18. Length int
  19. StreamId uint32
  20. }
  21. type Http2Request struct {
  22. Method string
  23. Path string
  24. Scheme string
  25. Status Status
  26. Duration time.Duration
  27. kernelTime uint64
  28. }
  29. type Http2Parser struct {
  30. clientDecoder *hpack.Decoder
  31. serverDecoder *hpack.Decoder
  32. activeRequests map[uint32]*Http2Request
  33. lastGcTime uint64
  34. }
  35. func NewHttp2Parser() *Http2Parser {
  36. return &Http2Parser{
  37. clientDecoder: hpack.NewDecoder(4096, nil),
  38. serverDecoder: hpack.NewDecoder(4096, nil),
  39. activeRequests: map[uint32]*Http2Request{},
  40. }
  41. }
  42. func (p *Http2Parser) Parse(method Method, payload []byte, kernelTime uint64) []Http2Request {
  43. if method == MethodHttp2ClientFrames {
  44. l := len(http2.ClientPreface)
  45. if len(payload) >= l && string(payload[:l]) == http2.ClientPreface {
  46. payload = payload[l:]
  47. }
  48. }
  49. if len(payload) == 0 {
  50. return nil
  51. }
  52. var decoder *hpack.Decoder
  53. statuses := map[uint32]Status{}
  54. offset := 0
  55. for {
  56. if len(payload)-offset < http2FrameHeaderLength {
  57. break
  58. }
  59. h := Http2FrameHeader{
  60. Length: int(binary.BigEndian.Uint32(payload[offset:]) >> 8),
  61. Type: http2.FrameType(payload[offset+3]),
  62. Flags: http2.Flags(payload[offset+4]),
  63. StreamId: binary.BigEndian.Uint32(payload[offset+5:]) & (1<<31 - 1),
  64. }
  65. offset += http2FrameHeaderLength
  66. if h.Type != http2.FrameHeaders {
  67. if len(payload)-offset < h.Length {
  68. break
  69. }
  70. offset += h.Length
  71. continue
  72. }
  73. switch method {
  74. case MethodHttp2ClientFrames:
  75. req := p.activeRequests[h.StreamId]
  76. if req == nil {
  77. req = &Http2Request{kernelTime: kernelTime}
  78. p.activeRequests[h.StreamId] = req
  79. }
  80. decoder = p.clientDecoder
  81. decoder.SetEmitFunc(func(hf hpack.HeaderField) {
  82. switch hf.Name {
  83. case ":method":
  84. if req.Method == "" && isHttpMethod(hf.Value) {
  85. req.Method = hf.Value
  86. }
  87. case ":path":
  88. if req.Path == "" && isHttpPath(hf.Value) {
  89. req.Path = hf.Value
  90. }
  91. case ":scheme":
  92. if req.Scheme == "" && isHttpScheme(hf.Value) {
  93. req.Scheme = hf.Value
  94. }
  95. }
  96. })
  97. case MethodHttp2ServerFrames:
  98. if _, ok := statuses[h.StreamId]; !ok {
  99. statuses[h.StreamId] = 0
  100. }
  101. decoder = p.serverDecoder
  102. decoder.SetEmitFunc(func(hf hpack.HeaderField) {
  103. if hf.Name == ":status" {
  104. s, _ := strconv.Atoi(hf.Value)
  105. statuses[h.StreamId] = Status(s)
  106. }
  107. })
  108. }
  109. next := offset + h.Length
  110. if next > len(payload) {
  111. next = len(payload)
  112. }
  113. if _, err := decoder.Write(payload[offset:next]); err != nil {
  114. continue
  115. }
  116. offset = next
  117. }
  118. var res []Http2Request
  119. for streamId, status := range statuses {
  120. r := p.activeRequests[streamId]
  121. if r == nil {
  122. continue
  123. }
  124. r.Status = status
  125. r.Duration = time.Duration(kernelTime - r.kernelTime)
  126. res = append(res, *r)
  127. delete(p.activeRequests, streamId)
  128. }
  129. // GC
  130. if kernelTime-p.lastGcTime > http2DecoderGcInterval {
  131. if p.lastGcTime > 0 {
  132. for streamId, r := range p.activeRequests {
  133. if kernelTime-r.kernelTime > http2DecoderGcInterval {
  134. delete(p.activeRequests, streamId)
  135. }
  136. }
  137. }
  138. p.lastGcTime = kernelTime
  139. }
  140. return res
  141. }
  142. func isHttpMethod(s string) bool {
  143. switch s {
  144. case http.MethodGet,
  145. http.MethodHead,
  146. http.MethodPost,
  147. http.MethodPut,
  148. http.MethodPatch,
  149. http.MethodDelete,
  150. http.MethodConnect,
  151. http.MethodOptions,
  152. http.MethodTrace:
  153. return true
  154. }
  155. return false
  156. }
  157. func isHttpPath(s string) bool {
  158. return strings.HasPrefix(s, "/") || s == "*"
  159. }
  160. func isHttpScheme(s string) bool {
  161. return s == "http" || s == "https"
  162. }