2016-12-21 18:22:22 +00:00
|
|
|
package lfsapi
|
|
|
|
|
|
|
|
import (
|
2017-04-27 16:07:12 +00:00
|
|
|
"context"
|
2016-12-22 18:20:39 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
2016-12-21 18:22:22 +00:00
|
|
|
"net/http"
|
|
|
|
"net/http/httputil"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type httpTransferStats struct {
|
|
|
|
HeaderSize int
|
|
|
|
BodySize int64
|
|
|
|
Start time.Time
|
|
|
|
Stop time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
type httpTransfer struct {
|
2017-04-27 16:47:10 +00:00
|
|
|
Key string
|
2017-04-27 17:35:53 +00:00
|
|
|
RequestStats httpTransferStats
|
|
|
|
ResponseStats httpTransferStats
|
2016-12-21 18:22:22 +00:00
|
|
|
}
|
|
|
|
|
2017-04-27 16:07:12 +00:00
|
|
|
type statsContextKey string
|
|
|
|
|
2017-04-27 17:35:53 +00:00
|
|
|
const statsKeytransferKey = statsContextKey("transfer")
|
2017-04-27 16:07:12 +00:00
|
|
|
|
2017-04-27 17:15:13 +00:00
|
|
|
func (c *Client) LogHTTPStats(w io.WriteCloser) {
|
|
|
|
fmt.Fprintf(w, "concurrent=%d time=%d version=%s\n", c.ConcurrentTransfers, time.Now().Unix(), UserAgent)
|
|
|
|
c.httpLogger = w
|
|
|
|
}
|
|
|
|
|
2016-12-22 18:20:39 +00:00
|
|
|
// LogStats is intended to be called after all HTTP operations for the
|
|
|
|
// commmand have finished. It dumps k/v logs, one line per httpTransfer into
|
|
|
|
// a log file with the current timestamp.
|
2017-04-27 17:15:13 +00:00
|
|
|
//
|
|
|
|
// DEPRECATED: Call LogHTTPStats() before the first HTTP request.
|
|
|
|
func (c *Client) LogStats(out io.Writer) {}
|
2016-12-22 18:20:39 +00:00
|
|
|
|
2017-04-27 16:07:12 +00:00
|
|
|
// LogRequest tells the client to log the request's stats to the http log
|
|
|
|
// after the response body has been read.
|
|
|
|
func (c *Client) LogRequest(r *http.Request, reqKey string) *http.Request {
|
2017-04-27 17:35:53 +00:00
|
|
|
ctx := context.WithValue(r.Context(), transferKey, httpTransfer{
|
|
|
|
Key: reqKey,
|
|
|
|
RequestStats: httpTransferStats{},
|
|
|
|
ResponseStats: httpTransferStats{},
|
|
|
|
})
|
2017-04-27 16:07:12 +00:00
|
|
|
return r.WithContext(ctx)
|
|
|
|
}
|
2016-12-21 18:22:22 +00:00
|
|
|
|
2017-04-27 16:07:12 +00:00
|
|
|
// LogResponse sends the current response stats to the http log.
|
|
|
|
//
|
|
|
|
// DEPRECATED: Use LogRequest() instead.
|
2017-04-27 16:47:10 +00:00
|
|
|
func (c *Client) LogResponse(key string, res *http.Response) {}
|
2016-12-21 18:22:22 +00:00
|
|
|
|
2016-12-21 18:49:40 +00:00
|
|
|
func (c *Client) startResponseStats(res *http.Response, start time.Time) {
|
2017-04-27 17:35:53 +00:00
|
|
|
v := res.Request.Context().Value(transferKey)
|
|
|
|
if v == nil {
|
2016-12-21 18:49:40 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-04-27 17:35:53 +00:00
|
|
|
t := v.(httpTransfer)
|
2016-12-21 18:22:22 +00:00
|
|
|
|
2017-04-27 17:35:53 +00:00
|
|
|
t.RequestStats.BodySize = res.Request.ContentLength
|
2016-12-21 18:22:22 +00:00
|
|
|
if dump, err := httputil.DumpRequest(res.Request, false); err == nil {
|
2017-04-27 17:35:53 +00:00
|
|
|
t.RequestStats.HeaderSize = len(dump)
|
2016-12-21 18:22:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Response body size cannot be figured until it is read. Do not rely on a Content-Length
|
|
|
|
// header because it may not exist or be -1 in the case of chunked responses.
|
2017-04-27 17:35:53 +00:00
|
|
|
t.ResponseStats.Start = start
|
|
|
|
if dump, err := httputil.DumpResponse(res, false); err == nil {
|
|
|
|
t.ResponseStats.HeaderSize = len(dump)
|
2016-12-21 18:22:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-21 18:49:40 +00:00
|
|
|
func (c *Client) finishResponseStats(res *http.Response, bodySize int64) {
|
2017-04-27 17:35:53 +00:00
|
|
|
v := res.Request.Context().Value(transferKey)
|
|
|
|
if v == nil {
|
2016-12-21 18:22:22 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-04-27 17:35:53 +00:00
|
|
|
t := v.(httpTransfer)
|
|
|
|
t.ResponseStats.BodySize = bodySize
|
|
|
|
t.ResponseStats.Stop = time.Now()
|
|
|
|
if c.httpLogger != nil {
|
|
|
|
writeHTTPStats(c.httpLogger, res, t)
|
2016-12-21 18:22:22 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-27 17:15:13 +00:00
|
|
|
|
2017-04-27 17:35:53 +00:00
|
|
|
func writeHTTPStats(w io.Writer, res *http.Response, t httpTransfer) {
|
2017-04-27 17:15:13 +00:00
|
|
|
fmt.Fprintf(w, "key=%s reqheader=%d reqbody=%d resheader=%d resbody=%d restime=%d status=%d url=%s\n",
|
|
|
|
t.Key,
|
2017-04-27 17:35:53 +00:00
|
|
|
t.RequestStats.HeaderSize,
|
|
|
|
t.RequestStats.BodySize,
|
|
|
|
t.ResponseStats.HeaderSize,
|
|
|
|
t.ResponseStats.BodySize,
|
|
|
|
t.ResponseStats.Stop.Sub(t.ResponseStats.Start).Nanoseconds(),
|
2017-04-27 17:15:13 +00:00
|
|
|
res.StatusCode,
|
|
|
|
res.Request.URL,
|
|
|
|
)
|
|
|
|
}
|