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  }