github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/httputil/transport.go (about) 1 /* 2 Copyright 2011 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package httputil 18 19 import ( 20 "io" 21 "log" 22 "net/http" 23 "sync" 24 "time" 25 ) 26 27 // StatsTransport wraps another RoundTripper (or uses the default one) and 28 // counts the number of HTTP requests performed. 29 type StatsTransport struct { 30 mu sync.Mutex 31 reqs int 32 33 // Transport optionally specifies the transport to use. 34 // If nil, http.DefaultTransport is used. 35 Transport http.RoundTripper 36 37 // If VerboseLog is true, HTTP request summaries are logged. 38 VerboseLog bool 39 } 40 41 func (t *StatsTransport) Requests() int { 42 t.mu.Lock() 43 defer t.mu.Unlock() 44 return t.reqs 45 } 46 47 func (t *StatsTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) { 48 t.mu.Lock() 49 t.reqs++ 50 n := t.reqs 51 t.mu.Unlock() 52 53 rt := t.Transport 54 if rt == nil { 55 rt = http.DefaultTransport 56 } 57 var t0 time.Time 58 if t.VerboseLog { 59 t0 = time.Now() 60 log.Printf("(%d) %s %s ...", n, req.Method, req.URL) 61 } 62 resp, err = rt.RoundTrip(req) 63 if t.VerboseLog { 64 t1 := time.Now() 65 td := t1.Sub(t1) 66 if err == nil { 67 log.Printf("(%d) %s %s = status %d (in %v)", n, req.Method, req.URL, resp.StatusCode, td) 68 resp.Body = &logBody{body: resp.Body, n: n, t0: t0, t1: t1} 69 } else { 70 log.Printf("(%d) %s %s = error: %v (in %v)", n, req.Method, req.URL, err, td) 71 } 72 } 73 return 74 } 75 76 type logBody struct { 77 body io.ReadCloser 78 n int 79 t0, t1 time.Time 80 readOnce sync.Once 81 closeOnce sync.Once 82 } 83 84 func (b *logBody) Read(p []byte) (n int, err error) { 85 b.readOnce.Do(func() { 86 log.Printf("(%d) Read body", b.n) 87 }) 88 return b.body.Read(p) 89 } 90 91 func (b *logBody) Close() error { 92 b.closeOnce.Do(func() { 93 t := time.Now() 94 log.Printf("(%d) Close body (%v tot, %v post-header)", b.n, t.Sub(b.t0), t.Sub(b.t1)) 95 }) 96 return b.body.Close() 97 }