github.com/lukasheimann/cloudfoundrycli@v7.1.0+incompatible/api/plugin/wrapper/request_logger.go (about)

     1  package wrapper
     2  
     3  import (
     4  	"bytes"
     5  	"io/ioutil"
     6  	"net/http"
     7  	"sort"
     8  	"time"
     9  
    10  	"code.cloudfoundry.org/cli/api/plugin"
    11  )
    12  
    13  //go:generate counterfeiter . RequestLoggerOutput
    14  
    15  // RequestLoggerOutput is the interface for displaying logs
    16  type RequestLoggerOutput interface {
    17  	DisplayDump(dump string) error
    18  	DisplayHeader(name string, value string) error
    19  	DisplayHost(name string) error
    20  	DisplayJSONBody(body []byte) error
    21  	DisplayRequestHeader(method string, uri string, httpProtocol string) error
    22  	DisplayResponseHeader(httpProtocol string, status string) error
    23  	DisplayType(name string, requestDate time.Time) error
    24  	HandleInternalError(err error)
    25  	Start() error
    26  	Stop() error
    27  }
    28  
    29  // RequestLogger is the wrapper that logs requests to and responses from
    30  // a plugin repository
    31  type RequestLogger struct {
    32  	connection plugin.Connection
    33  	output     RequestLoggerOutput
    34  }
    35  
    36  // NewRequestLogger returns a pointer to a RequestLogger wrapper
    37  func NewRequestLogger(output RequestLoggerOutput) *RequestLogger {
    38  	return &RequestLogger{
    39  		output: output,
    40  	}
    41  }
    42  
    43  // Make records the request and the response to UI
    44  func (logger *RequestLogger) Make(request *http.Request, passedResponse *plugin.Response, proxyReader plugin.ProxyReader) error {
    45  	err := logger.displayRequest(request)
    46  	if err != nil {
    47  		logger.output.HandleInternalError(err)
    48  	}
    49  
    50  	err = logger.connection.Make(request, passedResponse, proxyReader)
    51  
    52  	if passedResponse.HTTPResponse != nil {
    53  		displayErr := logger.displayResponse(passedResponse)
    54  		if displayErr != nil {
    55  			logger.output.HandleInternalError(displayErr)
    56  		}
    57  	}
    58  
    59  	return err
    60  }
    61  
    62  // Wrap sets the connection on the RequestLogger and returns itself
    63  func (logger *RequestLogger) Wrap(innerconnection plugin.Connection) plugin.Connection {
    64  	logger.connection = innerconnection
    65  	return logger
    66  }
    67  
    68  func (logger *RequestLogger) displayRequest(request *http.Request) error {
    69  	err := logger.output.Start()
    70  	if err != nil {
    71  		return err
    72  	}
    73  	defer logger.output.Stop()
    74  
    75  	err = logger.output.DisplayType("REQUEST", time.Now())
    76  	if err != nil {
    77  		return err
    78  	}
    79  	err = logger.output.DisplayRequestHeader(request.Method, request.URL.RequestURI(), request.Proto)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	err = logger.output.DisplayHost(request.URL.Host)
    84  	if err != nil {
    85  		return err
    86  	}
    87  	err = logger.displaySortedHeaders(request.Header)
    88  	if err != nil {
    89  		return err
    90  	}
    91  
    92  	if request.Body != nil && request.Header.Get("Content-Type") == "application/json" {
    93  		rawRequestBody, err := ioutil.ReadAll(request.Body)
    94  		defer request.Body.Close()
    95  		if err != nil {
    96  			return err
    97  		}
    98  
    99  		request.Body = ioutil.NopCloser(bytes.NewBuffer(rawRequestBody))
   100  		err = logger.output.DisplayJSONBody(rawRequestBody)
   101  		if err != nil {
   102  			return err
   103  		}
   104  	}
   105  
   106  	return nil
   107  }
   108  
   109  func (logger *RequestLogger) displayResponse(passedResponse *plugin.Response) error {
   110  	err := logger.output.Start()
   111  	if err != nil {
   112  		return err
   113  	}
   114  	defer logger.output.Stop()
   115  
   116  	err = logger.output.DisplayType("RESPONSE", time.Now())
   117  	if err != nil {
   118  		return err
   119  	}
   120  	err = logger.output.DisplayResponseHeader(passedResponse.HTTPResponse.Proto, passedResponse.HTTPResponse.Status)
   121  	if err != nil {
   122  		return err
   123  	}
   124  	err = logger.displaySortedHeaders(passedResponse.HTTPResponse.Header)
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	if passedResponse.HTTPResponse.Body == nil {
   130  		return nil
   131  	}
   132  	if passedResponse.HTTPResponse.Header.Get("Content-Type") != "application/json" {
   133  		return logger.output.DisplayDump("[NON-JSON BODY CONTENT HIDDEN]")
   134  	}
   135  
   136  	return logger.output.DisplayJSONBody(passedResponse.RawResponse)
   137  }
   138  
   139  func (logger *RequestLogger) displaySortedHeaders(headers http.Header) error {
   140  	keys := []string{}
   141  	for key := range headers {
   142  		keys = append(keys, key)
   143  	}
   144  	sort.Strings(keys)
   145  
   146  	for _, key := range keys {
   147  		for _, value := range headers[key] {
   148  			err := logger.output.DisplayHeader(key, redactHeaders(key, value))
   149  			if err != nil {
   150  				return err
   151  			}
   152  		}
   153  	}
   154  	return nil
   155  }
   156  
   157  func redactHeaders(key string, value string) string {
   158  	redactedKeys := []string{"Authorization", "Set-Cookie"}
   159  	for _, redactedKey := range redactedKeys {
   160  		if key == redactedKey {
   161  			return "[PRIVATE DATA HIDDEN]"
   162  		}
   163  	}
   164  
   165  	return value
   166  }