| package httpsnoop |
| |
| import ( |
| "io" |
| "net/http" |
| "time" |
| ) |
| |
| // Metrics holds metrics captured from CaptureMetrics. |
| type Metrics struct { |
| // Code is the first http response code passed to the WriteHeader func of |
| // the ResponseWriter. If no such call is made, a default code of 200 is |
| // assumed instead. |
| Code int |
| // Duration is the time it took to execute the handler. |
| Duration time.Duration |
| // Written is the number of bytes successfully written by the Write or |
| // ReadFrom function of the ResponseWriter. ResponseWriters may also write |
| // data to their underlaying connection directly (e.g. headers), but those |
| // are not tracked. Therefor the number of Written bytes will usually match |
| // the size of the response body. |
| Written int64 |
| } |
| |
| // CaptureMetrics wraps the given hnd, executes it with the given w and r, and |
| // returns the metrics it captured from it. |
| func CaptureMetrics(hnd http.Handler, w http.ResponseWriter, r *http.Request) Metrics { |
| return CaptureMetricsFn(w, func(ww http.ResponseWriter) { |
| hnd.ServeHTTP(ww, r) |
| }) |
| } |
| |
| // CaptureMetricsFn wraps w and calls fn with the wrapped w and returns the |
| // resulting metrics. This is very similar to CaptureMetrics (which is just |
| // sugar on top of this func), but is a more usable interface if your |
| // application doesn't use the Go http.Handler interface. |
| func CaptureMetricsFn(w http.ResponseWriter, fn func(http.ResponseWriter)) Metrics { |
| m := Metrics{Code: http.StatusOK} |
| m.CaptureMetrics(w, fn) |
| return m |
| } |
| |
| // CaptureMetrics wraps w and calls fn with the wrapped w and updates |
| // Metrics m with the resulting metrics. This is similar to CaptureMetricsFn, |
| // but allows one to customize starting Metrics object. |
| func (m *Metrics) CaptureMetrics(w http.ResponseWriter, fn func(http.ResponseWriter)) { |
| var ( |
| start = time.Now() |
| headerWritten bool |
| hooks = Hooks{ |
| WriteHeader: func(next WriteHeaderFunc) WriteHeaderFunc { |
| return func(code int) { |
| next(code) |
| |
| if !(code >= 100 && code <= 199) && !headerWritten { |
| m.Code = code |
| headerWritten = true |
| } |
| } |
| }, |
| |
| Write: func(next WriteFunc) WriteFunc { |
| return func(p []byte) (int, error) { |
| n, err := next(p) |
| |
| m.Written += int64(n) |
| headerWritten = true |
| return n, err |
| } |
| }, |
| |
| ReadFrom: func(next ReadFromFunc) ReadFromFunc { |
| return func(src io.Reader) (int64, error) { |
| n, err := next(src) |
| |
| headerWritten = true |
| m.Written += n |
| return n, err |
| } |
| }, |
| } |
| ) |
| |
| fn(Wrap(w, hooks)) |
| m.Duration += time.Since(start) |
| } |