@@ -3,7 +3,15 @@ import { IHttpClient } from './client-interface';
33import { HttpResponse } from './http-response' ;
44import configStore from '../config-handler' ;
55import authHandler from '../auth-handler' ;
6- import { hasProxy , getProxyUrl , getProxyConfig , getProxyConfigForHost } from '../proxy-helper' ;
6+ import {
7+ hasProxy ,
8+ getProxyUrl ,
9+ getProxyConfigForHost ,
10+ resolveRequestHost ,
11+ shouldBypassProxy ,
12+ } from '../proxy-helper' ;
13+
14+ type AxiosRequestConfigWithRetry = AxiosRequestConfig & { __httpClientRetryCount ?: number } ;
715
816/**
917 * Derive request host from baseURL or url for NO_PROXY checks.
@@ -52,6 +60,62 @@ export class HttpClient implements IHttpClient {
5260
5361 // Sets payload format as json by default
5462 this . asJson ( ) ;
63+ this . attachResponseInterceptor ( ) ;
64+ }
65+
66+ /** Single interceptor per instance — avoids stacking handlers on every request (major perf win). */
67+ private attachResponseInterceptor ( ) : void {
68+ this . axiosInstance . interceptors . response . use ( null , async ( error ) => {
69+ const cfg = error . config as AxiosRequestConfigWithRetry | undefined ;
70+ if ( ! cfg ) {
71+ return Promise . reject ( error ) ;
72+ }
73+
74+ const { message, response, code } = error ;
75+ const proxyFromCfg = cfg . proxy ;
76+ const isProxyConfigured = ! ! proxyFromCfg || hasProxy ( ) ;
77+
78+ const proxyErrorCodes = [ 'ECONNREFUSED' , 'ETIMEDOUT' , 'ENOTFOUND' , 'ERR_BAD_RESPONSE' ] ;
79+ if ( isProxyConfigured && ( proxyErrorCodes . includes ( code ) || message ?. includes ( 'ERR_BAD_RESPONSE' ) ) ) {
80+ const p = proxyFromCfg as { protocol ?: string ; host ?: string ; port ?: number } | undefined ;
81+ const proxyUrl =
82+ p && typeof p === 'object' && p . host
83+ ? `${ p . protocol ?? 'http' } ://${ p . host } :${ p . port ?? 80 } `
84+ : getProxyUrl ( ) ;
85+
86+ return Promise . reject (
87+ new Error (
88+ `Proxy error: Unable to connect to proxy server at ${ proxyUrl } . Please verify your proxy configuration.` ,
89+ ) ,
90+ ) ;
91+ }
92+
93+ if ( response ?. data ?. error_message ?. includes ( 'access token is invalid or expired' ) ) {
94+ const token = await this . refreshToken ( ) ;
95+ this . headers ( { ...this . request . headers , authorization : token . authorization } ) ;
96+ return this . axiosInstance ( {
97+ ...cfg ,
98+ headers : { ...cfg . headers , authorization : token . authorization } ,
99+ } ) ;
100+ }
101+
102+ if (
103+ ! (
104+ message ?. includes ( 'timeout' ) ||
105+ message ?. includes ( 'Network Error' ) ||
106+ message ?. includes ( 'getaddrinfo ENOTFOUND' )
107+ )
108+ ) {
109+ return Promise . reject ( error ) ;
110+ }
111+
112+ const retries = cfg . __httpClientRetryCount ?? 0 ;
113+ if ( retries < 1 ) {
114+ cfg . __httpClientRetryCount = retries + 1 ;
115+ return this . axiosInstance ( cfg ) ;
116+ }
117+ return Promise . reject ( error ) ;
118+ } ) ;
55119 }
56120
57121 /**
@@ -373,52 +437,6 @@ export class HttpClient implements IHttpClient {
373437 * @returns {Request }
374438 */
375439 async createAndSendRequest ( method : HttpMethod , url : string ) : Promise < AxiosResponse > {
376- let counter = 0 ;
377- this . axiosInstance . interceptors . response . use ( null , async ( error ) => {
378- const { message, response, code } = error ;
379-
380- // Don't retry proxy connection errors - fail fast
381- const proxyErrorCodes = [ 'ECONNREFUSED' , 'ETIMEDOUT' , 'ENOTFOUND' , 'ERR_BAD_RESPONSE' ] ;
382- const isProxyConfigured = this . request . proxy || hasProxy ( ) ;
383-
384- if ( isProxyConfigured && ( proxyErrorCodes . includes ( code ) || message ?. includes ( 'ERR_BAD_RESPONSE' ) ) ) {
385- const proxyUrl = this . request . proxy && typeof this . request . proxy === 'object'
386- ? `${ this . request . proxy . protocol } ://${ this . request . proxy . host } :${ this . request . proxy . port } `
387- : getProxyUrl ( ) ;
388-
389- return Promise . reject ( new Error ( `Proxy error: Unable to connect to proxy server at ${ proxyUrl } . Please verify your proxy configuration.` ) ) ;
390- }
391-
392- if ( response ?. data ?. error_message ?. includes ( 'access token is invalid or expired' ) ) {
393- const token = await this . refreshToken ( ) ;
394- this . headers ( { ...this . request . headers , authorization : token . authorization } ) ;
395- return await this . axiosInstance ( {
396- url,
397- method,
398- withCredentials : true ,
399- ...this . request ,
400- data : this . prepareRequestPayload ( ) ,
401- } ) ;
402- }
403-
404- if (
405- ! ( message . includes ( 'timeout' ) || message . includes ( 'Network Error' ) || message . includes ( 'getaddrinfo ENOTFOUND' ) )
406- ) {
407- return Promise . reject ( error ) ;
408- }
409- if ( counter < 1 ) {
410- counter ++ ;
411- return await this . axiosInstance ( {
412- url,
413- method,
414- withCredentials : true ,
415- ...this . request ,
416- data : this . prepareRequestPayload ( ) ,
417- } ) ;
418- }
419- return Promise . reject ( error ) ;
420- } ) ;
421-
422440 if ( ! this . disableEarlyAccessHeaders ) {
423441 // Add early access header by default
424442 const earlyAccessHeaders = configStore . get ( `earlyAccessHeaders` ) ;
@@ -427,12 +445,14 @@ export class HttpClient implements IHttpClient {
427445 }
428446 }
429447
430- // Configure proxy if available. NO_PROXY has priority: hosts in NO_PROXY never use proxy .
448+ // Configure proxy if available. NO_PROXY has priority; fall back to region CMA for host resolution .
431449 if ( ! this . request . proxy ) {
432- const host = getRequestHost ( this . request . baseURL , url ) ;
433- const proxyConfig = host ? getProxyConfigForHost ( host ) : getProxyConfig ( ) ;
450+ const host = getRequestHost ( this . request . baseURL , url ) || resolveRequestHost ( { } ) ;
451+ const proxyConfig = getProxyConfigForHost ( host ) ;
434452 if ( proxyConfig ) {
435453 this . request . proxy = proxyConfig ;
454+ } else if ( host && shouldBypassProxy ( host ) ) {
455+ this . request . proxy = false ;
436456 }
437457 }
438458
0 commit comments