How to trace http.Client with httptrace in Go

huangapple go评论91阅读模式
英文:

How to trace http.Client with httptrace in Go

问题

根据您提供的代码,您想要将httptrace应用于NewWithClienthttp.Client参数以进行HTTP跟踪。然而,您尝试的代码无法跟踪http.ClientGotConn没有输出。您希望有人帮助您找出上述代码的问题所在。

根据您提供的代码,问题可能出在以下几个地方:

  1. TraceTransport结构体的RoundTrip方法没有实现对http.DefaultTransport.RoundTrip的调用,这可能导致请求没有被正确处理。您可以尝试在RoundTrip方法中添加对http.DefaultTransport.RoundTrip的调用,以确保请求被正确处理。

  2. ClientWrapper结构体的Do方法中,您将clientWrapperTrace作为httptrace.WithClientTrace的参数传递给了req.Context(),但是在代码中并没有使用req进行实际的HTTP请求。您需要确保在Do方法中使用req进行实际的HTTP请求,例如使用c.defaultClient.Do(req)

请尝试对上述问题进行修复,并检查是否能够成功跟踪http.Client。如果问题仍然存在,请提供更多关于问题的详细信息,以便我们能够更好地帮助您解决问题。

英文:

Per this doc, we can trace http.Client with httptrace in this way

    t := &transport{}

    req, _ := http.NewRequest("GET", "https://google.com", nil)
    trace := &httptrace.ClientTrace{
        GotConn: t.GotConn,
    }
    req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))

    client := &http.Client{Transport: t}

For google API client, here are the one wrapper codes

func NewWithClient(jsonKey []byte, cli *http.Client) (*Client, error) {
	if cli == nil {
		return nil, fmt.Errorf("client is nil")
	}

	ctx := context.WithValue(context.Background(), oauth2.HTTPClient, cli)

	conf, err := google.JWTConfigFromJSON(jsonKey, androidpublisher.AndroidpublisherScope)
	if err != nil {
		return nil, err
	}

	service, err := androidpublisher.NewService(ctx, option.WithHTTPClient(conf.Client(ctx)))
	if err != nil {
		return nil, err
	}

	return &Client{service}, err
}

We want to apply httptrace to the http.Client argument of NewWithClient to do HTTP trace.

What we have tried

type TraceTransport struct {
}

var traceTransport = &TraceTransport{}

var trace = &httptrace.ClientTrace{
	GotConn: traceTransport.GotConn,
}

func (t *TraceTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	return http.DefaultTransport.RoundTrip(req)
}

func (t *TraceTransport) GotConn(info httptrace.GotConnInfo) {
	fmt.Printf("Connection reused for %v \n", info.Reused)
}

type ClientWrapper struct {
	defaultClient *http.Client
}

var clientWrapperTrace = &httptrace.ClientTrace{GotConn: traceTransport.GotConn}

func (c *ClientWrapper) Do(req *http.Request) (*http.Response, error) {
	req = req.WithContext(httptrace.WithClientTrace(req.Context(), clientWrapperTrace))
	return c.defaultClient.Do(req)
}

func NewClientTrace(jsonKey []byte) (*Client, error) {
	cli := &http.Client{
		Transport: traceTransport,
		Timeout:   time.Duration(10) * time.Second,
	}
	cliWrapper := &ClientWrapper{defaultClient: cli}
	ctx := context.WithValue(context.Background(), oauth2.HTTPClient, cliWrapper)

	conf, err := google.JWTConfigFromJSON(jsonKey, androidpublisher.AndroidpublisherScope)
	if err != nil {
		return nil, err
	}

	service, err := androidpublisher.NewService(ctx, option.WithHTTPClient(conf.Client(ctx)))
	if err != nil {
		return nil, err
	}

	return &Client{service}, err
}

type Client struct {
	service *androidpublisher.Service
}

func (c *Client) VerifyProduct(
	ctx context.Context,
	packageName string,
	productID string,
	token string,
) (*androidpublisher.ProductPurchase, error) {
	ps := androidpublisher.NewPurchasesProductsService(c.service)
	result, err := ps.Get(packageName, productID, token).Context(ctx).Do()

	return result, err
}

// test codes
  c, err := NewClientTrace([]byte(privateKey)) 
  if err != nil {
    return
  }

  packageName := "package.name"
  productID := "product_id"
  token := "xxxxx"
  r, err := c.VerifyProduct(context.Background(), packageName, productID, token)

However, it is failed to trace http.Client, There is no output of GotConn. Could someone help us to figure out the issue of the above codes?

答案1

得分: 2

  1. google/oauth2的请求无法通过httptrace进行跟踪。你在ClientWrapper中使用context.WithValue传递的内容将被忽略这里,oauth2有自己的http.Client,它只是使用了来自context.Value的*http.ClientTransport方法。

  2. 使用httptrace可以跟踪来自androidpublisher的请求,示例如下:

ctx := httptrace.WithClientTrace(context.Background(), clientWrapperTrace)
r, err := c.VerifyProduct(ctx, packageName, productID, token)
  1. 如果你只想计算请求的数量,我认为覆盖http.Client.Transport是一种简单的方法。
type TraceTransport struct {
}

func (t *TraceTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	fmt.Printf("RoundTrip hook %v\n", req.URL)
	return http.DefaultTransport.RoundTrip(req)
}

func NewClientTrace(jsonKey []byte) (*Client, error) {
	cli := &http.Client{Transport: &TraceTransport{}}
	ctx := context.WithValue(context.Background(), oauth2.HTTPClient, cli)

	// ...
	service, err := androidpublisher.NewService(ctx, option.WithHTTPClient(conf.Client(ctx)))
	// ....
}
英文:
  1. Requests from google/oauth2 are not traceable by httptrace. your ClientWrapper passed with context.WithValue will be ignored here, and oauth2 has it's own http.Client, it just use the Transport method of *http.Client from context.Value.

  2. Requests from androidpublisher can be traced by httptrace like this:

ctx := httptrace.WithClientTrace(context.Background(), clientWrapperTrace)
r, err := c.VerifyProduct(ctx, packageName, productID, token)
  1. If you just want to count the requests, i think overwrite the http.Client.Transport is a easy way.
type TraceTransport struct {
}

func (t *TraceTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	fmt.Printf("RoundTrip hook %v\n", req.URL)
	return http.DefaultTransport.RoundTrip(req)
}

func NewClientTrace(jsonKey []byte) (*Client, error) {
	cli := &http.Client{Transport: &TraceTransport{}}
	ctx := context.WithValue(context.Background(), oauth2.HTTPClient, cli)

	// ...
	service, err := androidpublisher.NewService(ctx, option.WithHTTPClient(conf.Client(ctx)))
	// ....
}

huangapple
  • 本文由 发表于 2021年10月21日 15:34:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/69657710.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定