github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/api/cloudcontroller/wrapper/request_logger.go (about) 1 package wrapper 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "net/http" 7 "sort" 8 "strings" 9 "time" 10 11 "code.cloudfoundry.org/cli/api/cloudcontroller" 12 ) 13 14 //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . RequestLoggerOutput 15 16 // RequestLoggerOutput is the interface for displaying logs 17 type RequestLoggerOutput interface { 18 DisplayHeader(name string, value string) error 19 DisplayHost(name string) error 20 DisplayJSONBody(body []byte) error 21 DisplayMessage(msg 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 // Cloud Controller server 32 type RequestLogger struct { 33 connection cloudcontroller.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 *cloudcontroller.Request, passedResponse *cloudcontroller.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 cloudcontroller.Connection) cloudcontroller.Connection { 65 logger.connection = innerconnection 66 return logger 67 } 68 69 func (logger *RequestLogger) displayRequest(request *cloudcontroller.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 contentType := request.Header.Get("Content-Type") 94 if request.Body != nil { 95 if strings.Contains(contentType, "json") { 96 rawRequestBody, err := ioutil.ReadAll(request.Body) 97 if err != nil { 98 return err 99 } 100 101 defer request.ResetBody() 102 103 return logger.output.DisplayJSONBody(rawRequestBody) 104 } else if strings.Contains(contentType, "x-www-form-urlencoded") { 105 rawRequestBody, err := ioutil.ReadAll(request.Body) 106 if err != nil { 107 return err 108 } 109 110 defer request.ResetBody() 111 112 return logger.output.DisplayMessage(fmt.Sprintf("[application/x-www-form-urlencoded %s]", rawRequestBody)) 113 } 114 } 115 if contentType != "" { 116 return logger.output.DisplayMessage(fmt.Sprintf("[%s Content Hidden]", strings.Split(contentType, ";")[0])) 117 } 118 return nil 119 } 120 121 func (logger *RequestLogger) displayResponse(passedResponse *cloudcontroller.Response) error { 122 err := logger.output.Start() 123 if err != nil { 124 return err 125 } 126 defer logger.output.Stop() 127 128 err = logger.output.DisplayType("RESPONSE", time.Now()) 129 if err != nil { 130 return err 131 } 132 err = logger.output.DisplayResponseHeader(passedResponse.HTTPResponse.Proto, passedResponse.HTTPResponse.Status) 133 if err != nil { 134 return err 135 } 136 err = logger.displaySortedHeaders(passedResponse.HTTPResponse.Header) 137 if err != nil { 138 return err 139 } 140 contentType := passedResponse.HTTPResponse.Header["Content-Type"] 141 if len(contentType) > 0 && strings.Contains(contentType[0], "application/x-yaml") { 142 return logger.output.DisplayMessage("[application/x-yaml Content Hidden]") 143 } 144 return logger.output.DisplayJSONBody(passedResponse.RawResponse) 145 } 146 147 func (logger *RequestLogger) displaySortedHeaders(headers http.Header) error { 148 keys := []string{} 149 for key := range headers { 150 keys = append(keys, key) 151 } 152 sort.Strings(keys) 153 154 for _, key := range keys { 155 for _, value := range headers[key] { 156 err := logger.output.DisplayHeader(key, redactHeaders(key, value)) 157 if err != nil { 158 return err 159 } 160 } 161 } 162 return nil 163 } 164 165 func redactHeaders(key string, value string) string { 166 redactedKeys := []string{"Authorization", "Set-Cookie"} 167 for _, redactedKey := range redactedKeys { 168 if key == redactedKey { 169 return "[PRIVATE DATA HIDDEN]" 170 } 171 } 172 173 return value 174 }