github.com/orange-cloudfoundry/cli@v7.1.0+incompatible/api/uaa/wrapper/request_logger.go (about)

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