tracing_round_tripper.go raw

   1  // Copyright 2021-2023 The sacloud/go-http authors
   2  //
   3  // Licensed under the Apache License, Version 2.0 (the "License");
   4  // you may not use this file except in compliance with the License.
   5  // You may obtain a copy of the License at
   6  //
   7  //      http://www.apache.org/licenses/LICENSE-2.0
   8  //
   9  // Unless required by applicable law or agreed to in writing, software
  10  // distributed under the License is distributed on an "AS IS" BASIS,
  11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12  // See the License for the specific language governing permissions and
  13  // limitations under the License.
  14  
  15  package http
  16  
  17  import (
  18  	"bytes"
  19  	"io"
  20  	"log"
  21  	"net/http"
  22  	"net/http/httputil"
  23  )
  24  
  25  // TracingRoundTripper リクエスト/レスポンスのトレースログを出力するためのhttp.RoundTripper実装
  26  //
  27  // Client.Gzipがtrueの場合でも関知しないため利用者側で制御する必要がある
  28  type TracingRoundTripper struct {
  29  	// Transport 親となるhttp.RoundTripper、nilの場合http.DefaultTransportが利用される
  30  	Transport http.RoundTripper
  31  	// OutputOnlyError trueの場合レスポンスのステータスコードが200番台の時はリクエスト/レスポンスのトレースを出力しない
  32  	OutputOnlyError bool
  33  }
  34  
  35  // RoundTrip http.RoundTripperの実装
  36  func (r *TracingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
  37  	if r.Transport == nil {
  38  		r.Transport = http.DefaultTransport
  39  	}
  40  
  41  	var bodyBytes []byte
  42  	if req.Body != nil {
  43  		bb, err := io.ReadAll(req.Body)
  44  		if err != nil {
  45  			return nil, err
  46  		}
  47  		bodyBytes = bb
  48  		req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
  49  	}
  50  
  51  	res, err := r.Transport.RoundTrip(req)
  52  	if err != nil {
  53  		return nil, err
  54  	}
  55  
  56  	if r.OutputOnlyError && res.StatusCode < 300 {
  57  		return res, err
  58  	}
  59  
  60  	if req.Body != nil {
  61  		req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
  62  	}
  63  	data, err := httputil.DumpRequest(req, true)
  64  	if err != nil {
  65  		return nil, err
  66  	}
  67  	log.Printf("[TRACE] \trequest: %s %s\n==============================\n%s\n============================\n", req.Method, req.URL.String(), string(data))
  68  
  69  	data, err = httputil.DumpResponse(res, true)
  70  	if err != nil {
  71  		return nil, err
  72  	}
  73  	log.Printf("[TRACE] \tresponse: %s %s\n==============================\n%s\n============================\n", req.Method, req.URL.String(), string(data))
  74  
  75  	return res, err
  76  }
  77