@@ -29,6 +29,9 @@ var request = require('request').defaults({
2929 maxSockets : Infinity
3030 }
3131} ) ;
32+ var retryRequest = require ( 'retry-request' ) ;
33+ var streamForward = require ( 'stream-forward' ) ;
34+ var through = require ( 'through2' ) ;
3235var uuid = require ( 'node-uuid' ) ;
3336
3437/** @const {object} gcloud-node's package.json file. */
@@ -178,34 +181,68 @@ nodeutil.inherits(ApiError, Error);
178181 */
179182function handleResp ( err , resp , body , callback ) {
180183 callback = callback || noop ;
184+
181185 if ( err ) {
182186 callback ( err ) ;
183187 return ;
184188 }
185- if ( typeof body === 'string' ) {
186- try {
187- body = JSON . parse ( body ) ;
188- } catch ( err ) { }
189- }
190- if ( body && body . error ) {
191- // Error from JSON api.
192- callback ( new ApiError ( body . error ) ) ;
189+
190+ var parsedApiResponse = util . parseApiResp ( resp , body ) ;
191+
192+ if ( parsedApiResponse . err ) {
193+ callback ( parsedApiResponse . err ) ;
193194 return ;
194195 }
195- if ( resp && ( resp . statusCode < 200 || resp . statusCode > 299 ) ) {
196+
197+ callback ( null , parsedApiResponse . body , parsedApiResponse . resp ) ;
198+ }
199+
200+ util . handleResp = handleResp ;
201+
202+ /**
203+ * From an HTTP response, generate an error if one occurred.
204+ *
205+ * @param {* } resp - Response value.
206+ * @param {*= } body - Body value.
207+ * @return {object } parsedResponse - The parsed response.
208+ * @param {?error } parsedResponse.err - An error detected.
209+ * @param {object } parsedResponse.resp - The original response object.
210+ * @param {* } parsedREsponse.body - The original body value provided will try to
211+ * be JSON.parse'd. If it's successful, the parsed value will be returned
212+ * here, otherwise the original value.
213+ */
214+ function parseApiResp ( resp , body ) {
215+ var parsedResponse = {
216+ err : null ,
217+ resp : resp ,
218+ body : body
219+ } ;
220+
221+ if ( resp . statusCode < 200 || resp . statusCode > 299 ) {
196222 // Unknown error. Format according to ApiError standard.
197- callback ( new ApiError ( {
223+ parsedResponse . err = new ApiError ( {
198224 errors : [ ] ,
199225 code : resp . statusCode ,
200- message : body || 'Error during request.' ,
226+ message : 'Error during request.' ,
201227 response : resp
202- } ) ) ;
203- return ;
228+ } ) ;
229+ }
230+
231+ if ( util . is ( body , 'string' ) ) {
232+ try {
233+ parsedResponse . body = JSON . parse ( body ) ;
234+ } catch ( err ) { }
204235 }
205- callback ( null , body , resp ) ;
236+
237+ if ( parsedResponse . body && parsedResponse . body . error ) {
238+ // Error from JSON API.
239+ parsedResponse . err = new ApiError ( parsedResponse . body . error ) ;
240+ }
241+
242+ return parsedResponse ;
206243}
207244
208- util . handleResp = handleResp ;
245+ util . parseApiResp = parseApiResp ;
209246
210247/**
211248 * Get the type of a value.
@@ -418,20 +455,6 @@ function makeWritableStream(dup, options, onComplete) {
418455
419456util . makeWritableStream = makeWritableStream ;
420457
421- /**
422- * Returns an exponential distributed time to wait given the number of retries
423- * that have been previously been attempted on the request.
424- *
425- * @param {number } retryNumber - The number of retries previously attempted.
426- * @return {number } An exponentially distributed time to wait E.g. for use with
427- * exponential backoff.
428- */
429- function getNextRetryWait ( retryNumber ) {
430- return ( Math . pow ( 2 , retryNumber ) * 1000 ) + Math . floor ( Math . random ( ) * 1000 ) ;
431- }
432-
433- util . getNextRetryWait = getNextRetryWait ;
434-
435458/**
436459 * Returns true if the API request should be retried, given the error that was
437460 * given the first time the request was attempted. This is used for rate limit
@@ -600,30 +623,47 @@ function makeAuthorizedRequestFactory(config) {
600623 * request options.
601624 */
602625 function makeAuthorizedRequest ( reqOpts , callback ) {
603- if ( config . customEndpoint ) {
604- // Using a custom API override. Do not use `google-auth-library` for
605- // authentication. (ex: connecting to a local Datastore server)
606- if ( callback . onAuthorized ) {
607- callback . onAuthorized ( null , reqOpts ) ;
608- } else {
609- util . makeRequest ( reqOpts , config , callback ) ;
610- }
626+ var streamMode = ! callback ;
627+ var stream ;
628+ var reqConfig = extend ( { } , config ) ;
611629
612- return ;
630+ callback = callback || util . noop ;
631+
632+ if ( streamMode ) {
633+ stream = through ( ) ;
634+ reqConfig . stream = stream ;
613635 }
614636
615- util . authorizeRequest ( config , reqOpts , function ( err , authorizedReqOpts ) {
637+ function onAuthorized ( err , authorizedReqOpts ) {
616638 if ( err ) {
617- ( callback . onAuthorized || callback ) ( err ) ;
639+ if ( streamMode ) {
640+ stream . emit ( 'error' , err ) ;
641+ stream . end ( ) ;
642+ } else {
643+ ( callback . onAuthorized || callback ) ( err ) ;
644+ }
645+
618646 return ;
619647 }
620648
621649 if ( callback . onAuthorized ) {
622650 callback . onAuthorized ( null , authorizedReqOpts ) ;
623651 } else {
624- util . makeRequest ( authorizedReqOpts , config , callback ) ;
652+ util . makeRequest ( authorizedReqOpts , reqConfig , callback ) ;
625653 }
626- } ) ;
654+ }
655+
656+ if ( reqConfig . customEndpoint ) {
657+ // Using a custom API override. Do not use `google-auth-library` for
658+ // authentication. (ex: connecting to a local Datastore server)
659+ onAuthorized ( null , reqOpts ) ;
660+ } else {
661+ util . authorizeRequest ( reqConfig , reqOpts , onAuthorized ) ;
662+ }
663+
664+ if ( streamMode ) {
665+ return stream ;
666+ }
627667 }
628668
629669 makeAuthorizedRequest . getCredentials = function ( callback ) {
@@ -653,8 +693,8 @@ function makeAuthorizedRequestFactory(config) {
653693util . makeAuthorizedRequestFactory = makeAuthorizedRequestFactory ;
654694
655695/**
656- * Make a request through the `request ` module with built-in error handling and
657- * exponential back off.
696+ * Make a request through the `retryRequest ` module with built-in error handling
697+ * and exponential back off.
658698 *
659699 * @param {object } reqOpts - Request options in the format `request` expects.
660700 * @param {object= } config - Configuration object.
@@ -676,30 +716,26 @@ function makeRequest(reqOpts, config, callback) {
676716
677717 reqOpts = util . decorateRequest ( reqOpts ) ;
678718
679- var MAX_RETRIES = config . maxRetries || 3 ;
680- var autoRetry = config . autoRetry !== false ? true : false ;
681- var attemptedRetries = 0 ;
719+ var options = {
720+ request : request ,
682721
683- function shouldRetry ( err ) {
684- return autoRetry &&
685- MAX_RETRIES > attemptedRetries &&
686- util . shouldRetryRequest ( err ) ;
687- }
722+ retries : config . autoRetry !== false ? config . maxRetries || 3 : 0 ,
688723
689- function makeRateLimitedRequest ( ) {
690- request ( reqOpts , function ( err , resp , body ) {
691- util . handleResp ( err , resp , body , function ( err , body , resp ) {
692- if ( shouldRetry ( err ) ) {
693- var delay = util . getNextRetryWait ( attemptedRetries ++ ) ;
694- setTimeout ( makeRateLimitedRequest , delay ) ;
695- } else {
696- callback ( err || null , body , resp ) ;
697- }
698- } ) ;
724+ shouldRetryFn : function ( resp ) {
725+ var err = util . parseApiResp ( resp ) . err ;
726+ return err && util . shouldRetryRequest ( err ) ;
727+ }
728+ } ;
729+
730+ if ( config . stream ) {
731+ // `streamForward` is used to re-emit the events the request stream receives
732+ // on to the stream the user is holding (config.stream).
733+ streamForward ( retryRequest ( reqOpts , options ) ) . pipe ( config . stream ) ;
734+ } else {
735+ retryRequest ( reqOpts , options , function ( err , response , body ) {
736+ util . handleResp ( err , response , body , callback ) ;
699737 } ) ;
700738 }
701-
702- makeRateLimitedRequest ( ) ;
703739}
704740
705741util . makeRequest = makeRequest ;
0 commit comments