@@ -3,9 +3,33 @@ import * as _ from "lodash";
33
44import axios from 'axios' ;
55import queryString from 'querystring' ;
6+ import http from 'http' ;
7+ import https from 'https' ;
68
79var HttpsProxyAgent = require ( 'https-proxy-agent' ) ;
810
11+ const httpAgent = new http . Agent ( {
12+ keepAlive : true ,
13+ keepAliveMsecs : 1000 ,
14+ maxSockets : 100 , // Maximum sockets per host - requests queue when limit reached
15+ maxFreeSockets : 20 , // Maximum free sockets per host (increased proportionally)
16+ timeout : 60000 , // Socket timeout - requests fail if this timeout is reached
17+ freeSocketTimeout : 30000 // Free socket timeout
18+ } ) ;
19+
20+ const httpsAgent = new https . Agent ( {
21+ keepAlive : true ,
22+ keepAliveMsecs : 1000 ,
23+ maxSockets : 100 , // Requests queue when limit reached, don't fail immediately
24+ maxFreeSockets : 20 , // Increased proportionally with maxSockets
25+ timeout : 60000 , // Only fail if timeout reached while waiting for socket
26+ freeSocketTimeout : 30000
27+ } ) ;
28+
29+ // Set default agents for axios
30+ axios . defaults . httpAgent = httpAgent ;
31+ axios . defaults . httpsAgent = httpsAgent ;
32+
933export function Axios ( config ) {
1034 let auth = 'Basic ' + new Buffer ( config . authId + ':' + config . authToken )
1135 . toString ( 'base64' ) ;
@@ -24,20 +48,37 @@ export function Axios(config) {
2448 return code == 403 && method . toLowerCase ( ) == "post" && GeoPermissionEndpoints . some ( endpoint => url . endsWith ( endpoint ) )
2549 }
2650
27- const retryWrapper = ( axios , options ) => {
51+ const retryWrapper = ( axiosInstance , options ) => {
2852 const max_time = options . retryTime ;
2953 let counter = 0 ;
30- axios . interceptors . response . use ( null , ( error ) => {
54+
55+ // Create a dedicated axios instance for this request to avoid global interceptor pollution
56+ const requestAxios = axios . create ( {
57+ httpAgent : axiosInstance . defaults . httpAgent ,
58+ httpsAgent : axiosInstance . defaults . httpsAgent ,
59+ timeout : axiosInstance . defaults . timeout
60+ } ) ;
61+
62+ // Add interceptor with proper cleanup
63+ const interceptorId = requestAxios . interceptors . response . use ( null , ( error ) => {
3164 const config = error . config ;
3265 if ( counter < max_time && error . response && error . response . status >= 500 ) {
3366 counter ++ ;
3467 config . url = options . urls [ counter ] + options . authId + '/' + options . action ;
3568 return new Promise ( ( resolve ) => {
36- resolve ( axios ( config ) ) ;
37- } )
69+ resolve ( requestAxios ( config ) ) ;
70+ } ) ;
71+ }
72+ return Promise . reject ( error ) ;
73+ } ) ;
74+
75+ return {
76+ axios : requestAxios ,
77+ cleanup : ( ) => {
78+ // Clean up the interceptor when done
79+ requestAxios . interceptors . response . eject ( interceptorId ) ;
3880 }
39- return Promise . reject ( error )
40- } )
81+ } ;
4182 }
4283
4384 return ( method , action , params ) => {
@@ -189,7 +230,15 @@ export function Axios(config) {
189230 }
190231
191232 if ( typeof config . proxy !== 'undefined' ) {
192- options . httpsAgent = new HttpsProxyAgent ( config . proxy ) ;
233+ // Create proxy agent with connection pooling settings
234+ options . httpsAgent = new HttpsProxyAgent ( config . proxy , {
235+ keepAlive : true ,
236+ keepAliveMsecs : 1000 ,
237+ maxSockets : 100 , // Match the main configuration
238+ maxFreeSockets : 20 , // Match the main configuration
239+ timeout : 60000 ,
240+ freeSocketTimeout : 30000
241+ } ) ;
193242 }
194243
195244 if ( typeof config . timeout !== 'undefined' ) {
@@ -201,77 +250,85 @@ export function Axios(config) {
201250
202251 return new Promise ( ( resolve , reject ) => {
203252 if ( isVoiceReq ) {
204- retryWrapper ( axios , { retryTime : 2 , urls : apiVoiceUris , authId : config . authId , action : action } ) ;
205- options . url = apiVoiceUris [ 0 ] + config . authId + '/' + action ;
206- if ( method === 'GET' && options . data !== '' ) {
207- let query = '?' + queryString . stringify ( params ) ;
208- options . url += query ;
209- delete options . data
253+ // Set up retry wrapper with proper cleanup
254+ const retrySetup = retryWrapper ( axios , { retryTime : 2 , urls : apiVoiceUris , authId : config . authId , action : action } ) ;
255+ const retryAxios = retrySetup . axios ;
256+
257+ options . url = apiVoiceUris [ 0 ] + config . authId + '/' + action ;
258+ if ( method === 'GET' && options . data !== '' ) {
259+ let query = '?' + queryString . stringify ( params ) ;
260+ options . url += query ;
261+ delete options . data
262+ }
263+
264+ retryAxios ( options ) . then ( response => {
265+ var exceptionClass = {
266+ 400 : Exceptions . InvalidRequestError ,
267+ 401 : Exceptions . AuthenticationError ,
268+ 404 : Exceptions . ResourceNotFoundError ,
269+ 405 : Exceptions . InvalidRequestError ,
270+ 406 : Exceptions . NotAcceptableError ,
271+ 500 : Exceptions . ServerError ,
272+ 409 : Exceptions . InvalidRequestError ,
273+ 422 : Exceptions . InvalidRequestError ,
274+ 207 : Exceptions . InvalidRequestError ,
275+ } [ response . status ] || Error ;
276+
277+ if ( isGeoPermissionError ( response ) ) {
278+ exceptionClass = Exceptions . GeoPermissionError
210279 }
211- axios ( options ) . then ( response => {
212- var exceptionClass = {
213- 400 : Exceptions . InvalidRequestError ,
214- 401 : Exceptions . AuthenticationError ,
215- 404 : Exceptions . ResourceNotFoundError ,
216- 405 : Exceptions . InvalidRequestError ,
217- 406 : Exceptions . NotAcceptableError ,
218- 500 : Exceptions . ServerError ,
219- 409 : Exceptions . InvalidRequestError ,
220- 422 : Exceptions . InvalidRequestError ,
221- 207 : Exceptions . InvalidRequestError ,
222- } [ response . status ] || Error ;
223-
224- if ( isGeoPermissionError ( response ) ) {
225- exceptionClass = Exceptions . GeoPermissionError
226- }
227-
228- if ( ! _ . inRange ( response . status , 200 , 300 ) ) {
229- let body = response . data ;
230- if ( typeof body === 'object' ) {
231- reject ( new exceptionClass ( JSON . stringify ( body ) ) ) ;
232- }
233- else {
234- reject ( new exceptionClass ( body ) ) ;
235- }
236- }
237- resolve ( {
238- response : response ,
239- body : response . data
240- } ) ;
241- } )
242- . catch ( function ( error ) {
243- if ( error . response == undefined ) {
244- return reject ( error . stack ) ;
280+
281+ if ( ! _ . inRange ( response . status , 200 , 300 ) ) {
282+ let body = response . data ;
283+ if ( typeof body === 'object' ) {
284+ reject ( new exceptionClass ( JSON . stringify ( body ) ) ) ;
245285 }
246- var exceptionClass = {
247- 400 : Exceptions . InvalidRequestError ,
248- 401 : Exceptions . AuthenticationError ,
249- 404 : Exceptions . ResourceNotFoundError ,
250- 405 : Exceptions . InvalidRequestError ,
251- 406 : Exceptions . NotAcceptableError ,
252- 500 : Exceptions . ServerError ,
253- 409 : Exceptions . InvalidRequestError ,
254- 422 : Exceptions . InvalidRequestError ,
255- 207 : Exceptions . InvalidRequestError ,
256- } [ error . response . status ] || Error ;
257-
258- if ( isGeoPermissionError ( error . response ) ) {
259- exceptionClass = Exceptions . GeoPermissionError
286+ else {
287+ reject ( new exceptionClass ( body ) ) ;
260288 }
261-
262- if ( ! _ . inRange ( error . response . status , 200 , 300 ) ) {
263- let body = error . response . data ;
264- if ( typeof body === 'object' ) {
265- reject ( new exceptionClass ( error ) ) ;
266- } else {
267- if ( error . response . status >= 500 ) {
268- reject ( new Exceptions . ServerError ( error ) ) ;
269- }
270- reject ( new exceptionClass ( error ) ) ;
289+ }
290+ resolve ( {
291+ response : response ,
292+ body : response . data
293+ } ) ;
294+ } )
295+ . catch ( function ( error ) {
296+ if ( error . response == undefined ) {
297+ return reject ( error . stack ) ;
298+ }
299+ var exceptionClass = {
300+ 400 : Exceptions . InvalidRequestError ,
301+ 401 : Exceptions . AuthenticationError ,
302+ 404 : Exceptions . ResourceNotFoundError ,
303+ 405 : Exceptions . InvalidRequestError ,
304+ 406 : Exceptions . NotAcceptableError ,
305+ 500 : Exceptions . ServerError ,
306+ 409 : Exceptions . InvalidRequestError ,
307+ 422 : Exceptions . InvalidRequestError ,
308+ 207 : Exceptions . InvalidRequestError ,
309+ } [ error . response . status ] || Error ;
310+
311+ if ( isGeoPermissionError ( error . response ) ) {
312+ exceptionClass = Exceptions . GeoPermissionError
313+ }
314+
315+ if ( ! _ . inRange ( error . response . status , 200 , 300 ) ) {
316+ let body = error . response . data ;
317+ if ( typeof body === 'object' ) {
318+ reject ( new exceptionClass ( error ) ) ;
319+ } else {
320+ if ( error . response . status >= 500 ) {
321+ reject ( new Exceptions . ServerError ( error ) ) ;
271322 }
323+ reject ( new exceptionClass ( error ) ) ;
272324 }
273- reject ( error . stack + '\n' + JSON . stringify ( error . response . data ) ) ;
274- } )
325+ }
326+ reject ( error . stack + '\n' + JSON . stringify ( error . response . data ) ) ;
327+ } )
328+ . finally ( ( ) => {
329+ // Clean up interceptors in all cases - success, error, or unexpected exception
330+ retrySetup . cleanup ( ) ;
331+ } )
275332 }
276333 else {
277334 axios ( options ) . then ( response => {
0 commit comments