history.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package client
  2. import (
  3. "container/list"
  4. "net/http"
  5. "time"
  6. )
  7. type RequestHistoryEntry struct {
  8. req *http.Request
  9. resp *http.Response
  10. start time.Time
  11. duration time.Duration
  12. }
  13. type RequestHistory struct {
  14. maxSize int
  15. reqToEntry map[*http.Request]*RequestHistoryEntry
  16. reqs chan *http.Request
  17. resps chan *http.Response
  18. history *list.List
  19. onChange func([]*RequestHistoryEntry)
  20. metrics *ClientMetrics
  21. }
  22. func NewRequestHistory(maxSize int, metrics *ClientMetrics, onChange func([]*RequestHistoryEntry)) *RequestHistory {
  23. rh := &RequestHistory{
  24. maxSize: maxSize,
  25. reqToEntry: make(map[*http.Request]*RequestHistoryEntry),
  26. reqs: make(chan *http.Request),
  27. resps: make(chan *http.Response),
  28. history: list.New(),
  29. onChange: onChange,
  30. metrics: metrics,
  31. }
  32. go func() {
  33. for {
  34. select {
  35. case req := <-rh.reqs:
  36. rh.addRequest(req)
  37. case resp := <-rh.resps:
  38. rh.addResponse(resp)
  39. }
  40. }
  41. }()
  42. return rh
  43. }
  44. func (rh *RequestHistory) addRequest(req *http.Request) {
  45. rh.metrics.reqMeter.Mark(1)
  46. if rh.history.Len() >= rh.maxSize {
  47. entry := rh.history.Remove(rh.history.Back()).(*RequestHistoryEntry)
  48. delete(rh.reqToEntry, entry.req)
  49. }
  50. entry := &RequestHistoryEntry{req: req, start: time.Now()}
  51. rh.reqToEntry[req] = entry
  52. rh.history.PushFront(entry)
  53. rh.onChange(rh.copy())
  54. }
  55. func (rh *RequestHistory) addResponse(resp *http.Response) {
  56. if entry, ok := rh.reqToEntry[resp.Request]; ok {
  57. entry.duration = time.Since(entry.start)
  58. rh.metrics.reqTimer.Update(entry.duration)
  59. entry.resp = resp
  60. rh.onChange(rh.copy())
  61. } else {
  62. // XXX: log warning instead of panic
  63. panic("no request for response!")
  64. }
  65. }
  66. func (rh *RequestHistory) copy() []*RequestHistoryEntry {
  67. entries := make([]*RequestHistoryEntry, rh.history.Len())
  68. i := 0
  69. for e := rh.history.Front(); e != nil; e = e.Next() {
  70. // force a copy
  71. entry := *(e.Value.(*RequestHistoryEntry))
  72. entries[i] = &entry
  73. i++
  74. }
  75. return entries
  76. }
  77. func (rhe *RequestHistoryEntry) GetRequest() *http.Request {
  78. return rhe.req
  79. }
  80. func (rhe *RequestHistoryEntry) GetResponse() *http.Response {
  81. return rhe.resp
  82. }