github.com/qiniu/go-sdk/v7@v7.27.0/internal/clientv2/interceptor_debug.go (about)

     1  package clientv2
     2  
     3  import (
     4  	"crypto/tls"
     5  	"fmt"
     6  	"net/http"
     7  	"net/http/httptrace"
     8  	"net/http/httputil"
     9  
    10  	clientV1 "github.com/qiniu/go-sdk/v7/client"
    11  	"github.com/qiniu/go-sdk/v7/conf"
    12  	"github.com/qiniu/go-sdk/v7/internal/log"
    13  )
    14  
    15  type DebugLevel int
    16  
    17  const (
    18  	DebugLevelPrintNone   DebugLevel = 0
    19  	DebugLevelPrintNormal DebugLevel = 1
    20  	DebugLevelPrintDetail DebugLevel = 2
    21  )
    22  
    23  var (
    24  	printRequestTrace              = false
    25  	printRequestLevel  *DebugLevel = nil
    26  	printResponseLevel *DebugLevel = nil
    27  )
    28  
    29  func PrintRequestTrace(isPrint bool) {
    30  	printRequestTrace = isPrint
    31  }
    32  
    33  func IsPrintRequestTrace() bool {
    34  	return printRequestTrace
    35  }
    36  
    37  func PrintRequest(level DebugLevel) {
    38  	printRequestLevel = &level
    39  }
    40  
    41  func IsPrintRequest() bool {
    42  	if printRequestLevel != nil {
    43  		return *printRequestLevel == DebugLevelPrintNormal || *printRequestLevel == DebugLevelPrintDetail
    44  	}
    45  	return clientV1.DebugMode
    46  }
    47  
    48  func IsPrintRequestBody() bool {
    49  	if printRequestLevel != nil {
    50  		return *printRequestLevel == DebugLevelPrintDetail
    51  	}
    52  	return clientV1.DeepDebugInfo
    53  }
    54  
    55  func PrintResponse(level DebugLevel) {
    56  	printResponseLevel = &level
    57  }
    58  
    59  func IsPrintResponse() bool {
    60  	if printResponseLevel != nil {
    61  		return *printResponseLevel == DebugLevelPrintNormal || *printResponseLevel == DebugLevelPrintDetail
    62  	}
    63  	return clientV1.DebugMode
    64  }
    65  
    66  func IsPrintResponseBody() bool {
    67  	if printResponseLevel != nil {
    68  		return *printResponseLevel == DebugLevelPrintDetail
    69  	}
    70  	return clientV1.DeepDebugInfo
    71  }
    72  
    73  type debugInterceptor struct{}
    74  
    75  func newDebugInterceptor() Interceptor {
    76  	return &debugInterceptor{}
    77  }
    78  
    79  func (interceptor *debugInterceptor) Priority() InterceptorPriority {
    80  	return InterceptorPriorityDebug
    81  }
    82  
    83  func (interceptor *debugInterceptor) Intercept(req *http.Request, handler Handler) (*http.Response, error) {
    84  	if interceptor == nil {
    85  		return handler(req)
    86  	}
    87  
    88  	label := interceptor.requestLabel(req)
    89  
    90  	if e := interceptor.printRequest(label, req); e != nil {
    91  		return nil, e
    92  	}
    93  
    94  	req = interceptor.printRequestTrace(label, req)
    95  
    96  	resp, err := handler(req)
    97  
    98  	if e := interceptor.printResponse(label, resp); e != nil {
    99  		return nil, e
   100  	}
   101  
   102  	return resp, err
   103  }
   104  
   105  func (interceptor *debugInterceptor) requestLabel(req *http.Request) string {
   106  	if req == nil || req.URL == nil {
   107  		return ""
   108  	}
   109  	return fmt.Sprintf("Url:%s", req.URL.String())
   110  }
   111  
   112  func (interceptor *debugInterceptor) printRequest(label string, req *http.Request) error {
   113  	if req == nil {
   114  		return nil
   115  	}
   116  
   117  	printReq := IsPrintRequest()
   118  	if !printReq {
   119  		return nil
   120  	}
   121  
   122  	info := label + " request:\n"
   123  
   124  	var (
   125  		hasBody     = IsPrintRequestBody()
   126  		contentType = req.Header.Get("Content-Type")
   127  	)
   128  	if contentType != conf.CONTENT_TYPE_JSON && contentType != conf.CONTENT_TYPE_FORM {
   129  		hasBody = false
   130  	}
   131  
   132  	d, dErr := httputil.DumpRequest(req, hasBody)
   133  	if dErr != nil {
   134  		return dErr
   135  	}
   136  	info += string(d) + "\n"
   137  
   138  	log.Debug(info)
   139  	return nil
   140  }
   141  
   142  func (interceptor *debugInterceptor) printRequestTrace(label string, req *http.Request) *http.Request {
   143  	if !IsPrintRequestTrace() || req == nil {
   144  		return req
   145  	}
   146  
   147  	label += "\n"
   148  	trace := &httptrace.ClientTrace{
   149  		GetConn: func(hostPort string) {
   150  			log.Debug(label + fmt.Sprintf("GetConn, %s \n", hostPort))
   151  		},
   152  		GotConn: func(connInfo httptrace.GotConnInfo) {
   153  			remoteAddr := connInfo.Conn.RemoteAddr()
   154  			log.Debug(label + fmt.Sprintf("GotConn, Network:%s RemoteAddr:%s \n", remoteAddr.Network(), remoteAddr.String()))
   155  		},
   156  		PutIdleConn: func(err error) {
   157  			log.Debug(label + fmt.Sprintf("PutIdleConn, err:%v \n", err))
   158  		},
   159  		GotFirstResponseByte: func() {
   160  			log.Debug(label + "GotFirstResponseByte \n")
   161  		},
   162  		Got100Continue: func() {
   163  			log.Debug(label + "Got100Continue \n")
   164  		},
   165  		DNSStart: func(info httptrace.DNSStartInfo) {
   166  			log.Debug(label + fmt.Sprintf("DNSStart, host:%s \n", info.Host))
   167  		},
   168  		DNSDone: func(info httptrace.DNSDoneInfo) {
   169  			log.Debug(label + fmt.Sprintf("DNSDone, addr:%+v \n", info.Addrs))
   170  		},
   171  		ConnectStart: func(network, addr string) {
   172  			log.Debug(label + fmt.Sprintf("ConnectStart, network:%+v ip:%s \n", network, addr))
   173  		},
   174  		ConnectDone: func(network, addr string, err error) {
   175  			log.Debug(label + fmt.Sprintf("ConnectDone, network:%s ip:%s err:%v \n", network, addr, err))
   176  		},
   177  		TLSHandshakeStart: func() {
   178  			log.Debug(label + "TLSHandshakeStart \n")
   179  		},
   180  		TLSHandshakeDone: func(state tls.ConnectionState, err error) {
   181  			log.Debug(label + fmt.Sprintf("TLSHandshakeDone, state:%+v err:%s \n", state, err))
   182  		},
   183  		// go1.10 不支持
   184  		//WroteHeaderField: func(key string, value []string) {
   185  		//	log.Debug(label + fmt.Sprintf("WroteHeaderField, key:%s value:%s \n", key, value))
   186  		//},
   187  		WroteHeaders: func() {
   188  			log.Debug(label + "WroteHeaders \n")
   189  		},
   190  		Wait100Continue: func() {
   191  			log.Debug(label + "Wait100Continue \n")
   192  		},
   193  		WroteRequest: func(info httptrace.WroteRequestInfo) {
   194  			log.Debug(label + fmt.Sprintf("WroteRequest, err:%v \n", info.Err))
   195  		},
   196  	}
   197  	return req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
   198  }
   199  
   200  func (interceptor *debugInterceptor) printResponse(label string, resp *http.Response) error {
   201  	if resp == nil {
   202  		return nil
   203  	}
   204  
   205  	printResp := IsPrintResponse()
   206  	if !printResp {
   207  		return nil
   208  	}
   209  
   210  	info := label + " response:\n"
   211  
   212  	hasBody := IsPrintResponseBody()
   213  	switch {
   214  	case
   215  		resp.Header.Get("Content-Type") == "application/octet-stream",
   216  		resp.ContentLength <= 0 || resp.ContentLength > 1024*1024:
   217  		hasBody = false
   218  	}
   219  
   220  	d, dErr := httputil.DumpResponse(resp, hasBody)
   221  	if dErr != nil {
   222  		return dErr
   223  	}
   224  	info += string(d) + "\n"
   225  
   226  	log.Debug(info)
   227  	return nil
   228  }