diff --git a/service/ccm/credential_external.go b/service/ccm/credential_external.go index e8fe31799..97da6bd9e 100644 --- a/service/ccm/credential_external.go +++ b/service/ccm/credential_external.go @@ -192,6 +192,7 @@ func newExternalCredential(ctx context.Context, tag string, options option.CCMEx Time: ntp.TimeFuncFromContext(ctx), } } + cred.httpClient = &http.Client{Transport: transport} } else { // Normal mode: standard HTTP client for proxying cred.httpClient = &http.Client{Transport: transport} @@ -498,6 +499,47 @@ func (c *externalCredential) interruptConnections() { } } +func (c *externalCredential) doPollUsageRequest(ctx context.Context) (*http.Response, error) { + buildRequest := func(baseURL string) func() (*http.Request, error) { + return func() (*http.Request, error) { + request, err := http.NewRequestWithContext(ctx, http.MethodGet, baseURL+"/ccm/v1/status", nil) + if err != nil { + return nil, err + } + request.Header.Set("Authorization", "Bearer "+c.token) + return request, nil + } + } + // Try reverse transport first (single attempt, no retry) + if c.reverseHttpClient != nil { + session := c.getReverseSession() + if session != nil && !session.IsClosed() { + request, err := buildRequest(reverseProxyBaseURL)() + if err != nil { + return nil, err + } + reverseClient := &http.Client{ + Transport: c.reverseHttpClient.Transport, + Timeout: 5 * time.Second, + } + response, err := reverseClient.Do(request) + if err == nil { + return response, nil + } + // Reverse failed, fall through to forward if available + } + } + // Forward transport with retries + if c.httpClient != nil { + forwardClient := &http.Client{ + Transport: c.httpClient.Transport, + Timeout: 5 * time.Second, + } + return doHTTPWithRetry(ctx, forwardClient, buildRequest(c.baseURL)) + } + return nil, E.New("no transport available") +} + func (c *externalCredential) pollUsage(ctx context.Context) { if !c.pollAccess.TryLock() { return @@ -505,29 +547,7 @@ func (c *externalCredential) pollUsage(ctx context.Context) { defer c.pollAccess.Unlock() defer c.markUsagePollAttempted() - activeBaseURL := c.baseURL - activeTransport := c.httpClient.Transport - if c.reverseHttpClient != nil { - session := c.getReverseSession() - if session != nil && !session.IsClosed() { - activeBaseURL = reverseProxyBaseURL - activeTransport = c.reverseHttpClient.Transport - } - } - statusURL := activeBaseURL + "/ccm/v1/status" - httpClient := &http.Client{ - Transport: activeTransport, - Timeout: 5 * time.Second, - } - - response, err := doHTTPWithRetry(ctx, httpClient, func() (*http.Request, error) { - request, err := http.NewRequestWithContext(ctx, http.MethodGet, statusURL, nil) - if err != nil { - return nil, err - } - request.Header.Set("Authorization", "Bearer "+c.token) - return request, nil - }) + response, err := c.doPollUsageRequest(ctx) if err != nil { if !c.isPollBackoffAtCap() { c.logger.Error("poll usage for ", c.tag, ": ", err) diff --git a/service/ocm/credential_external.go b/service/ocm/credential_external.go index 0b60cff1e..fbe8f11c6 100644 --- a/service/ocm/credential_external.go +++ b/service/ocm/credential_external.go @@ -211,6 +211,7 @@ func newExternalCredential(ctx context.Context, tag string, options option.OCMEx Time: ntp.TimeFuncFromContext(ctx), } } + cred.httpClient = &http.Client{Transport: transport} } else { // Normal mode: standard HTTP client for proxying cred.credDialer = credentialDialer @@ -537,6 +538,47 @@ func (c *externalCredential) interruptConnections() { } } +func (c *externalCredential) doPollUsageRequest(ctx context.Context) (*http.Response, error) { + buildRequest := func(baseURL string) func() (*http.Request, error) { + return func() (*http.Request, error) { + request, err := http.NewRequestWithContext(ctx, http.MethodGet, baseURL+"/ocm/v1/status", nil) + if err != nil { + return nil, err + } + request.Header.Set("Authorization", "Bearer "+c.token) + return request, nil + } + } + // Try reverse transport first (single attempt, no retry) + if c.reverseHttpClient != nil { + session := c.getReverseSession() + if session != nil && !session.IsClosed() { + request, err := buildRequest(reverseProxyBaseURL)() + if err != nil { + return nil, err + } + reverseClient := &http.Client{ + Transport: c.reverseHttpClient.Transport, + Timeout: 5 * time.Second, + } + response, err := reverseClient.Do(request) + if err == nil { + return response, nil + } + // Reverse failed, fall through to forward if available + } + } + // Forward transport with retries + if c.httpClient != nil { + forwardClient := &http.Client{ + Transport: c.httpClient.Transport, + Timeout: 5 * time.Second, + } + return doHTTPWithRetry(ctx, forwardClient, buildRequest(c.baseURL)) + } + return nil, E.New("no transport available") +} + func (c *externalCredential) pollUsage(ctx context.Context) { if !c.pollAccess.TryLock() { return @@ -544,29 +586,7 @@ func (c *externalCredential) pollUsage(ctx context.Context) { defer c.pollAccess.Unlock() defer c.markUsagePollAttempted() - activeBaseURL := c.baseURL - activeTransport := c.httpClient.Transport - if c.reverseHttpClient != nil { - session := c.getReverseSession() - if session != nil && !session.IsClosed() { - activeBaseURL = reverseProxyBaseURL - activeTransport = c.reverseHttpClient.Transport - } - } - statusURL := activeBaseURL + "/ocm/v1/status" - httpClient := &http.Client{ - Transport: activeTransport, - Timeout: 5 * time.Second, - } - - response, err := doHTTPWithRetry(ctx, httpClient, func() (*http.Request, error) { - request, err := http.NewRequestWithContext(ctx, http.MethodGet, statusURL, nil) - if err != nil { - return nil, err - } - request.Header.Set("Authorization", "Bearer "+c.token) - return request, nil - }) + response, err := c.doPollUsageRequest(ctx) if err != nil { if !c.isPollBackoffAtCap() { c.logger.Error("poll usage for ", c.tag, ": ", err)