github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/api/logcache/log_cache_client.go (about)

     1  package logcache
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"net/http"
     7  	"runtime"
     8  	"strings"
     9  	"time"
    10  
    11  	logcache "code.cloudfoundry.org/go-log-cache/v2"
    12  
    13  	"code.cloudfoundry.org/cli/actor/v7action"
    14  	"code.cloudfoundry.org/cli/api/shared"
    15  	"code.cloudfoundry.org/cli/command"
    16  	"code.cloudfoundry.org/cli/util"
    17  )
    18  
    19  type RequestLoggerOutput interface {
    20  	Start() error
    21  	Stop() error
    22  
    23  	DisplayType(name string, requestDate time.Time) error
    24  	DisplayDump(dump string) error
    25  
    26  	DisplayHost(name string) error
    27  	DisplayRequestHeader(method string, uri string, httpProtocol string) error
    28  	DisplayResponseHeader(httpProtocol string, status string) error
    29  }
    30  
    31  type DebugPrinter struct {
    32  	outputs []RequestLoggerOutput
    33  }
    34  
    35  func (p DebugPrinter) PrintError(err error) {
    36  	for _, output := range p.outputs {
    37  		_ = output.Start()                          //nolint
    38  		_ = output.DisplayType("ERROR", time.Now()) //nolint
    39  		_ = output.DisplayDump(err.Error())         //nolint
    40  		_ = output.Stop()                           //nolint
    41  	}
    42  }
    43  
    44  func (p DebugPrinter) PrintRequest(req *http.Request) {
    45  	for _, output := range p.outputs {
    46  		_ = output.Start()                                                           //nolint
    47  		_ = output.DisplayType("REQUEST", time.Now())                                //nolint
    48  		_ = output.DisplayRequestHeader(req.Method, req.URL.RequestURI(), req.Proto) //nolint
    49  		_ = output.DisplayHost(req.URL.Host)                                         //nolint
    50  		_ = output.DisplayDump(headersString(req.Header))                            //nolint
    51  		_ = output.Stop()                                                            //nolint
    52  	}
    53  }
    54  
    55  func (p DebugPrinter) PrintResponse(resp *http.Response) {
    56  	for _, output := range p.outputs {
    57  		_ = output.Start()                                        //nolint
    58  		_ = output.DisplayType("RESPONSE", time.Now())            //nolint
    59  		_ = output.DisplayResponseHeader(resp.Proto, resp.Status) //nolint
    60  		_ = output.DisplayDump(headersString(resp.Header))        //nolint
    61  		_ = output.Stop()                                         //nolint
    62  	}
    63  }
    64  
    65  func (p *DebugPrinter) addOutput(output RequestLoggerOutput) {
    66  	p.outputs = append(p.outputs, output)
    67  }
    68  
    69  type userAgentHTTPClient struct {
    70  	c         logcache.HTTPClient
    71  	userAgent string
    72  }
    73  
    74  func (c *userAgentHTTPClient) Do(req *http.Request) (*http.Response, error) {
    75  	req.Header.Set("User-Agent", c.userAgent)
    76  	return c.c.Do(req)
    77  }
    78  
    79  type tokenHTTPClient struct {
    80  	c           logcache.HTTPClient
    81  	accessToken func() string
    82  }
    83  
    84  func (c *tokenHTTPClient) Do(req *http.Request) (*http.Response, error) {
    85  	req.Header.Set("Authorization", c.accessToken())
    86  	return c.c.Do(req)
    87  }
    88  
    89  type httpDebugClient struct {
    90  	printer DebugPrinter
    91  	c       logcache.HTTPClient
    92  }
    93  
    94  func (c *httpDebugClient) Do(req *http.Request) (*http.Response, error) {
    95  	c.printer.PrintRequest(req)
    96  
    97  	resp, err := c.c.Do(req)
    98  	if err != nil {
    99  		c.printer.PrintError(err)
   100  		return nil, err
   101  	}
   102  
   103  	c.printer.PrintResponse(resp)
   104  
   105  	return resp, err
   106  }
   107  
   108  // NewClient returns back a configured Log Cache Client.
   109  func NewClient(logCacheEndpoint string, config command.Config, ui command.UI, k8sConfigGetter v7action.KubernetesConfigGetter) (*logcache.Client, error) {
   110  	var tr http.RoundTripper = &http.Transport{
   111  		Proxy:           http.ProxyFromEnvironment,
   112  		TLSClientConfig: util.NewTLSConfig(nil, config.SkipSSLValidation()),
   113  		DialContext: (&net.Dialer{
   114  			KeepAlive: 30 * time.Second,
   115  			Timeout:   config.DialTimeout(),
   116  		}).DialContext,
   117  	}
   118  
   119  	if config.IsCFOnK8s() {
   120  		var err error
   121  		tr, err = shared.WrapForCFOnK8sAuth(config, k8sConfigGetter, tr)
   122  		if err != nil {
   123  			return nil, err
   124  		}
   125  	}
   126  
   127  	var client logcache.HTTPClient //nolint
   128  	client = &userAgentHTTPClient{
   129  		c:         &http.Client{Transport: tr},
   130  		userAgent: fmt.Sprintf("%s/%s (%s; %s %s)", config.BinaryName(), config.BinaryVersion(), runtime.Version(), runtime.GOARCH, runtime.GOOS),
   131  	}
   132  
   133  	verbose, location := config.Verbose()
   134  	if verbose && ui != nil {
   135  		printer := DebugPrinter{}
   136  		printer.addOutput(ui.RequestLoggerTerminalDisplay())
   137  		if location != nil {
   138  			printer.addOutput(ui.RequestLoggerFileWriter(location))
   139  		}
   140  
   141  		client = &httpDebugClient{printer: printer, c: client}
   142  	}
   143  
   144  	if !config.IsCFOnK8s() {
   145  		client = &tokenHTTPClient{
   146  			c:           client,
   147  			accessToken: config.AccessToken,
   148  		}
   149  	}
   150  
   151  	return logcache.NewClient(
   152  		logCacheEndpoint,
   153  		logcache.WithHTTPClient(client),
   154  	), nil
   155  }
   156  
   157  func headersString(header http.Header) string {
   158  	var result string
   159  	for name, values := range header {
   160  		result += name + ": " + strings.Join(values, ", ") + "\n"
   161  	}
   162  	return result
   163  }