@@ -7,18 +7,26 @@ import { HttpStatusCodes } from "./constants";
77import * as request from "request" ;
88
99export class HttpClient implements Server . IHttpClient {
10- private defaultUserAgent : string ;
1110 private static STATUS_CODE_REGEX = / s t a t u s c o d e = ( \d + ) / i;
1211 private static STUCK_REQUEST_ERROR_MESSAGE = "The request can't receive any response." ;
1312 private static STUCK_RESPONSE_ERROR_MESSAGE = "Can't receive all parts of the response." ;
1413 private static STUCK_REQUEST_TIMEOUT = 60000 ;
1514 // We receive multiple response packets every ms but we don't need to be very aggressive here.
1615 private static STUCK_RESPONSE_CHECK_INTERVAL = 10000 ;
1716
17+ private defaultUserAgent : string ;
18+ private cleanupData : ICleanupRequestData [ ] ;
19+
1820 constructor ( private $config : Config . IConfig ,
1921 private $logger : ILogger ,
22+ private $processService : IProcessService ,
2023 private $proxyService : IProxyService ,
21- private $staticConfig : Config . IStaticConfig ) { }
24+ private $staticConfig : Config . IStaticConfig ) {
25+ this . cleanupData = [ ] ;
26+ this . $processService . attachToProcessExitSignals ( this , ( ) => {
27+ this . cleanupData . forEach ( d => this . cleanupAfterRequest ( d ) ) ;
28+ } ) ;
29+ }
2230
2331 public async httpRequest ( options : any , proxySettings ?: IProxySettings ) : Promise < Server . IResponse > {
2432 try {
@@ -97,9 +105,9 @@ export class HttpClient implements Server.IHttpClient {
97105 const result = new Promise < Server . IResponse > ( ( resolve , reject ) => {
98106 let timerId : number ;
99107 let stuckRequestTimerId : number ;
100- let stuckResponseIntervalId : NodeJS . Timer ;
101108 let hasResponse = false ;
102- const timers : number [ ] = [ ] ;
109+ const cleanupRequestData : ICleanupRequestData = { timers : [ ] , stuckResponseIntervalId : null } ;
110+ this . cleanupData . push ( cleanupRequestData ) ;
103111
104112 const promiseActions : IPromiseActions < Server . IResponse > = {
105113 resolve,
@@ -109,9 +117,9 @@ export class HttpClient implements Server.IHttpClient {
109117
110118 if ( options . timeout ) {
111119 timerId = setTimeout ( ( ) => {
112- this . setResponseResult ( promiseActions , timers , stuckResponseIntervalId , { err : new Error ( `Request to ${ unmodifiedOptions . url } timed out.` ) } ) ;
120+ this . setResponseResult ( promiseActions , cleanupRequestData , { err : new Error ( `Request to ${ unmodifiedOptions . url } timed out.` ) } ) ;
113121 } , options . timeout ) ;
114- timers . push ( timerId ) ;
122+ cleanupRequestData . timers . push ( timerId ) ;
115123
116124 delete options . timeout ;
117125 }
@@ -128,10 +136,10 @@ export class HttpClient implements Server.IHttpClient {
128136 stuckRequestTimerId = null ;
129137 if ( ! hasResponse ) {
130138 requestObj . abort ( ) ;
131- this . setResponseResult ( promiseActions , timers , stuckResponseIntervalId , { err : new Error ( HttpClient . STUCK_REQUEST_ERROR_MESSAGE ) } ) ;
139+ this . setResponseResult ( promiseActions , cleanupRequestData , { err : new Error ( HttpClient . STUCK_REQUEST_ERROR_MESSAGE ) } ) ;
132140 }
133141 } , options . timeout || HttpClient . STUCK_REQUEST_TIMEOUT ) ;
134- timers . push ( stuckRequestTimerId ) ;
142+ cleanupRequestData . timers . push ( stuckRequestTimerId ) ;
135143
136144 requestObj
137145 . on ( "error" , ( err : IHttpRequestError ) => {
@@ -145,18 +153,18 @@ export class HttpClient implements Server.IHttpClient {
145153 const errorMessage = this . getErrorMessage ( errorMessageStatusCode , null ) ;
146154 err . proxyAuthenticationRequired = errorMessageStatusCode === HttpStatusCodes . PROXY_AUTHENTICATION_REQUIRED ;
147155 err . message = errorMessage || err . message ;
148- this . setResponseResult ( promiseActions , timers , stuckResponseIntervalId , { err } ) ;
156+ this . setResponseResult ( promiseActions , cleanupRequestData , { err } ) ;
149157 } )
150158 . on ( "response" , ( response : Server . IRequestResponseData ) => {
151159 hasResponse = true ;
152160 let lastChunkTimestamp = Date . now ( ) ;
153- stuckResponseIntervalId = setInterval ( ( ) => {
161+ cleanupRequestData . stuckResponseIntervalId = setInterval ( ( ) => {
154162 if ( Date . now ( ) - lastChunkTimestamp > HttpClient . STUCK_RESPONSE_CHECK_INTERVAL ) {
155163 if ( ( < any > response ) . destroy ) {
156164 ( < any > response ) . destroy ( ) ;
157165 }
158166
159- this . setResponseResult ( promiseActions , timers , stuckResponseIntervalId , { err : new Error ( HttpClient . STUCK_RESPONSE_ERROR_MESSAGE ) } ) ;
167+ this . setResponseResult ( promiseActions , cleanupRequestData , { err : new Error ( HttpClient . STUCK_RESPONSE_ERROR_MESSAGE ) } ) ;
160168 }
161169 } , HttpClient . STUCK_RESPONSE_CHECK_INTERVAL ) ;
162170 const successful = helpers . isRequestSuccessful ( response ) ;
@@ -180,7 +188,7 @@ export class HttpClient implements Server.IHttpClient {
180188 if ( pipeTo ) {
181189 pipeTo . on ( "finish" , ( ) => {
182190 this . $logger . trace ( "httpRequest: Piping done. code = %d" , response . statusCode . toString ( ) ) ;
183- this . setResponseResult ( promiseActions , timers , stuckResponseIntervalId , { response } ) ;
191+ this . setResponseResult ( promiseActions , cleanupRequestData , { response } ) ;
184192 } ) ;
185193
186194 responseStream . pipe ( pipeTo ) ;
@@ -196,13 +204,13 @@ export class HttpClient implements Server.IHttpClient {
196204 const responseBody = data . join ( "" ) ;
197205
198206 if ( successful ) {
199- this . setResponseResult ( promiseActions , timers , stuckResponseIntervalId , { body : responseBody , response } ) ;
207+ this . setResponseResult ( promiseActions , cleanupRequestData , { body : responseBody , response } ) ;
200208 } else {
201209 const errorMessage = this . getErrorMessage ( response . statusCode , responseBody ) ;
202210 const err : any = new Error ( errorMessage ) ;
203211 err . response = response ;
204212 err . body = responseBody ;
205- this . setResponseResult ( promiseActions , timers , stuckResponseIntervalId , { err } ) ;
213+ this . setResponseResult ( promiseActions , cleanupRequestData , { err } ) ;
206214 }
207215 } ) ;
208216 }
@@ -233,19 +241,8 @@ export class HttpClient implements Server.IHttpClient {
233241 return response ;
234242 }
235243
236- private setResponseResult ( result : IPromiseActions < Server . IResponse > , timers : number [ ] , stuckResponseIntervalId : NodeJS . Timer , resultData : { response ?: Server . IRequestResponseData , body ?: string , err ?: Error } ) : void {
237- timers . forEach ( t => {
238- if ( t ) {
239- clearTimeout ( t ) ;
240- t = null ;
241- }
242- } ) ;
243-
244- if ( stuckResponseIntervalId ) {
245- clearInterval ( stuckResponseIntervalId ) ;
246- stuckResponseIntervalId = null ;
247- }
248-
244+ private setResponseResult ( result : IPromiseActions < Server . IResponse > , cleanupRequestData : ICleanupRequestData , resultData : { response ?: Server . IRequestResponseData , body ?: string , err ?: Error } ) : void {
245+ this . cleanupAfterRequest ( cleanupRequestData ) ;
249246 if ( ! result . isResolved ( ) ) {
250247 result . isResolved = ( ) => true ;
251248 if ( resultData . err ) {
@@ -317,5 +314,25 @@ export class HttpClient implements Server.IHttpClient {
317314 this . $logger . trace ( "Using proxy: %s" , options . proxy ) ;
318315 }
319316 }
317+
318+ private cleanupAfterRequest ( data : ICleanupRequestData ) : void {
319+ data . timers . forEach ( t => {
320+ if ( t ) {
321+ clearTimeout ( t ) ;
322+ t = null ;
323+ }
324+ } ) ;
325+
326+ if ( data . stuckResponseIntervalId ) {
327+ clearInterval ( data . stuckResponseIntervalId ) ;
328+ data . stuckResponseIntervalId = null ;
329+ }
330+ }
320331}
332+
333+ interface ICleanupRequestData {
334+ timers : number [ ] ;
335+ stuckResponseIntervalId : NodeJS . Timer ;
336+ }
337+
321338$injector . register ( "httpClient" , HttpClient ) ;
0 commit comments