UNPKG

27.8 kBJavaScriptView Raw
1var AWS = require('./core');
2var AcceptorStateMachine = require('./state_machine');
3var inherit = AWS.util.inherit;
4var domain = AWS.util.domain;
5var jmespath = require('jmespath');
6
7/**
8 * @api private
9 */
10var hardErrorStates = {success: 1, error: 1, complete: 1};
11
12function isTerminalState(machine) {
13 return Object.prototype.hasOwnProperty.call(hardErrorStates, machine._asm.currentState);
14}
15
16var fsm = new AcceptorStateMachine();
17fsm.setupStates = function() {
18 var transition = function(_, done) {
19 var self = this;
20 self._haltHandlersOnError = false;
21
22 self.emit(self._asm.currentState, function(err) {
23 if (err) {
24 if (isTerminalState(self)) {
25 if (domain && self.domain instanceof domain.Domain) {
26 err.domainEmitter = self;
27 err.domain = self.domain;
28 err.domainThrown = false;
29 self.domain.emit('error', err);
30 } else {
31 throw err;
32 }
33 } else {
34 self.response.error = err;
35 done(err);
36 }
37 } else {
38 done(self.response.error);
39 }
40 });
41
42 };
43
44 this.addState('validate', 'build', 'error', transition);
45 this.addState('build', 'afterBuild', 'restart', transition);
46 this.addState('afterBuild', 'sign', 'restart', transition);
47 this.addState('sign', 'send', 'retry', transition);
48 this.addState('retry', 'afterRetry', 'afterRetry', transition);
49 this.addState('afterRetry', 'sign', 'error', transition);
50 this.addState('send', 'validateResponse', 'retry', transition);
51 this.addState('validateResponse', 'extractData', 'extractError', transition);
52 this.addState('extractError', 'extractData', 'retry', transition);
53 this.addState('extractData', 'success', 'retry', transition);
54 this.addState('restart', 'build', 'error', transition);
55 this.addState('success', 'complete', 'complete', transition);
56 this.addState('error', 'complete', 'complete', transition);
57 this.addState('complete', null, null, transition);
58};
59fsm.setupStates();
60
61/**
62 * ## Asynchronous Requests
63 *
64 * All requests made through the SDK are asynchronous and use a
65 * callback interface. Each service method that kicks off a request
66 * returns an `AWS.Request` object that you can use to register
67 * callbacks.
68 *
69 * For example, the following service method returns the request
70 * object as "request", which can be used to register callbacks:
71 *
72 * ```javascript
73 * // request is an AWS.Request object
74 * var request = ec2.describeInstances();
75 *
76 * // register callbacks on request to retrieve response data
77 * request.on('success', function(response) {
78 * console.log(response.data);
79 * });
80 * ```
81 *
82 * When a request is ready to be sent, the {send} method should
83 * be called:
84 *
85 * ```javascript
86 * request.send();
87 * ```
88 *
89 * Since registered callbacks may or may not be idempotent, requests should only
90 * be sent once. To perform the same operation multiple times, you will need to
91 * create multiple request objects, each with its own registered callbacks.
92 *
93 * ## Removing Default Listeners for Events
94 *
95 * Request objects are built with default listeners for the various events,
96 * depending on the service type. In some cases, you may want to remove
97 * some built-in listeners to customize behaviour. Doing this requires
98 * access to the built-in listener functions, which are exposed through
99 * the {AWS.EventListeners.Core} namespace. For instance, you may
100 * want to customize the HTTP handler used when sending a request. In this
101 * case, you can remove the built-in listener associated with the 'send'
102 * event, the {AWS.EventListeners.Core.SEND} listener and add your own.
103 *
104 * ## Multiple Callbacks and Chaining
105 *
106 * You can register multiple callbacks on any request object. The
107 * callbacks can be registered for different events, or all for the
108 * same event. In addition, you can chain callback registration, for
109 * example:
110 *
111 * ```javascript
112 * request.
113 * on('success', function(response) {
114 * console.log("Success!");
115 * }).
116 * on('error', function(error, response) {
117 * console.log("Error!");
118 * }).
119 * on('complete', function(response) {
120 * console.log("Always!");
121 * }).
122 * send();
123 * ```
124 *
125 * The above example will print either "Success! Always!", or "Error! Always!",
126 * depending on whether the request succeeded or not.
127 *
128 * @!attribute httpRequest
129 * @readonly
130 * @!group HTTP Properties
131 * @return [AWS.HttpRequest] the raw HTTP request object
132 * containing request headers and body information
133 * sent by the service.
134 *
135 * @!attribute startTime
136 * @readonly
137 * @!group Operation Properties
138 * @return [Date] the time that the request started
139 *
140 * @!group Request Building Events
141 *
142 * @!event validate(request)
143 * Triggered when a request is being validated. Listeners
144 * should throw an error if the request should not be sent.
145 * @param request [Request] the request object being sent
146 * @see AWS.EventListeners.Core.VALIDATE_CREDENTIALS
147 * @see AWS.EventListeners.Core.VALIDATE_REGION
148 * @example Ensuring that a certain parameter is set before sending a request
149 * var req = s3.putObject(params);
150 * req.on('validate', function() {
151 * if (!req.params.Body.match(/^Hello\s/)) {
152 * throw new Error('Body must start with "Hello "');
153 * }
154 * });
155 * req.send(function(err, data) { ... });
156 *
157 * @!event build(request)
158 * Triggered when the request payload is being built. Listeners
159 * should fill the necessary information to send the request
160 * over HTTP.
161 * @param (see AWS.Request~validate)
162 * @example Add a custom HTTP header to a request
163 * var req = s3.putObject(params);
164 * req.on('build', function() {
165 * req.httpRequest.headers['Custom-Header'] = 'value';
166 * });
167 * req.send(function(err, data) { ... });
168 *
169 * @!event sign(request)
170 * Triggered when the request is being signed. Listeners should
171 * add the correct authentication headers and/or adjust the body,
172 * depending on the authentication mechanism being used.
173 * @param (see AWS.Request~validate)
174 *
175 * @!group Request Sending Events
176 *
177 * @!event send(response)
178 * Triggered when the request is ready to be sent. Listeners
179 * should call the underlying transport layer to initiate
180 * the sending of the request.
181 * @param response [Response] the response object
182 * @context [Request] the request object that was sent
183 * @see AWS.EventListeners.Core.SEND
184 *
185 * @!event retry(response)
186 * Triggered when a request failed and might need to be retried or redirected.
187 * If the response is retryable, the listener should set the
188 * `response.error.retryable` property to `true`, and optionally set
189 * `response.error.retryDelay` to the millisecond delay for the next attempt.
190 * In the case of a redirect, `response.error.redirect` should be set to
191 * `true` with `retryDelay` set to an optional delay on the next request.
192 *
193 * If a listener decides that a request should not be retried,
194 * it should set both `retryable` and `redirect` to false.
195 *
196 * Note that a retryable error will be retried at most
197 * {AWS.Config.maxRetries} times (based on the service object's config).
198 * Similarly, a request that is redirected will only redirect at most
199 * {AWS.Config.maxRedirects} times.
200 *
201 * @param (see AWS.Request~send)
202 * @context (see AWS.Request~send)
203 * @example Adding a custom retry for a 404 response
204 * request.on('retry', function(response) {
205 * // this resource is not yet available, wait 10 seconds to get it again
206 * if (response.httpResponse.statusCode === 404 && response.error) {
207 * response.error.retryable = true; // retry this error
208 * response.error.retryDelay = 10000; // wait 10 seconds
209 * }
210 * });
211 *
212 * @!group Data Parsing Events
213 *
214 * @!event extractError(response)
215 * Triggered on all non-2xx requests so that listeners can extract
216 * error details from the response body. Listeners to this event
217 * should set the `response.error` property.
218 * @param (see AWS.Request~send)
219 * @context (see AWS.Request~send)
220 *
221 * @!event extractData(response)
222 * Triggered in successful requests to allow listeners to
223 * de-serialize the response body into `response.data`.
224 * @param (see AWS.Request~send)
225 * @context (see AWS.Request~send)
226 *
227 * @!group Completion Events
228 *
229 * @!event success(response)
230 * Triggered when the request completed successfully.
231 * `response.data` will contain the response data and
232 * `response.error` will be null.
233 * @param (see AWS.Request~send)
234 * @context (see AWS.Request~send)
235 *
236 * @!event error(error, response)
237 * Triggered when an error occurs at any point during the
238 * request. `response.error` will contain details about the error
239 * that occurred. `response.data` will be null.
240 * @param error [Error] the error object containing details about
241 * the error that occurred.
242 * @param (see AWS.Request~send)
243 * @context (see AWS.Request~send)
244 *
245 * @!event complete(response)
246 * Triggered whenever a request cycle completes. `response.error`
247 * should be checked, since the request may have failed.
248 * @param (see AWS.Request~send)
249 * @context (see AWS.Request~send)
250 *
251 * @!group HTTP Events
252 *
253 * @!event httpHeaders(statusCode, headers, response, statusMessage)
254 * Triggered when headers are sent by the remote server
255 * @param statusCode [Integer] the HTTP response code
256 * @param headers [map<String,String>] the response headers
257 * @param (see AWS.Request~send)
258 * @param statusMessage [String] A status message corresponding to the HTTP
259 * response code
260 * @context (see AWS.Request~send)
261 *
262 * @!event httpData(chunk, response)
263 * Triggered when data is sent by the remote server
264 * @param chunk [Buffer] the buffer data containing the next data chunk
265 * from the server
266 * @param (see AWS.Request~send)
267 * @context (see AWS.Request~send)
268 * @see AWS.EventListeners.Core.HTTP_DATA
269 *
270 * @!event httpUploadProgress(progress, response)
271 * Triggered when the HTTP request has uploaded more data
272 * @param progress [map] An object containing the `loaded` and `total` bytes
273 * of the request.
274 * @param (see AWS.Request~send)
275 * @context (see AWS.Request~send)
276 * @note This event will not be emitted in Node.js 0.8.x.
277 *
278 * @!event httpDownloadProgress(progress, response)
279 * Triggered when the HTTP request has downloaded more data
280 * @param progress [map] An object containing the `loaded` and `total` bytes
281 * of the request.
282 * @param (see AWS.Request~send)
283 * @context (see AWS.Request~send)
284 * @note This event will not be emitted in Node.js 0.8.x.
285 *
286 * @!event httpError(error, response)
287 * Triggered when the HTTP request failed
288 * @param error [Error] the error object that was thrown
289 * @param (see AWS.Request~send)
290 * @context (see AWS.Request~send)
291 *
292 * @!event httpDone(response)
293 * Triggered when the server is finished sending data
294 * @param (see AWS.Request~send)
295 * @context (see AWS.Request~send)
296 *
297 * @see AWS.Response
298 */
299AWS.Request = inherit({
300
301 /**
302 * Creates a request for an operation on a given service with
303 * a set of input parameters.
304 *
305 * @param service [AWS.Service] the service to perform the operation on
306 * @param operation [String] the operation to perform on the service
307 * @param params [Object] parameters to send to the operation.
308 * See the operation's documentation for the format of the
309 * parameters.
310 */
311 constructor: function Request(service, operation, params) {
312 var endpoint = service.endpoint;
313 var region = service.config.region;
314 var customUserAgent = service.config.customUserAgent;
315
316 // global endpoints sign as us-east-1
317 if (service.isGlobalEndpoint) region = 'us-east-1';
318
319 this.domain = domain && domain.active;
320 this.service = service;
321 this.operation = operation;
322 this.params = params || {};
323 this.httpRequest = new AWS.HttpRequest(endpoint, region);
324 this.httpRequest.appendToUserAgent(customUserAgent);
325 this.startTime = service.getSkewCorrectedDate();
326
327 this.response = new AWS.Response(this);
328 this._asm = new AcceptorStateMachine(fsm.states, 'validate');
329 this._haltHandlersOnError = false;
330
331 AWS.SequentialExecutor.call(this);
332 this.emit = this.emitEvent;
333 },
334
335 /**
336 * @!group Sending a Request
337 */
338
339 /**
340 * @overload send(callback = null)
341 * Sends the request object.
342 *
343 * @callback callback function(err, data)
344 * If a callback is supplied, it is called when a response is returned
345 * from the service.
346 * @context [AWS.Request] the request object being sent.
347 * @param err [Error] the error object returned from the request.
348 * Set to `null` if the request is successful.
349 * @param data [Object] the de-serialized data returned from
350 * the request. Set to `null` if a request error occurs.
351 * @example Sending a request with a callback
352 * request = s3.putObject({Bucket: 'bucket', Key: 'key'});
353 * request.send(function(err, data) { console.log(err, data); });
354 * @example Sending a request with no callback (using event handlers)
355 * request = s3.putObject({Bucket: 'bucket', Key: 'key'});
356 * request.on('complete', function(response) { ... }); // register a callback
357 * request.send();
358 */
359 send: function send(callback) {
360 if (callback) {
361 // append to user agent
362 this.httpRequest.appendToUserAgent('callback');
363 this.on('complete', function (resp) {
364 callback.call(resp, resp.error, resp.data);
365 });
366 }
367 this.runTo();
368
369 return this.response;
370 },
371
372 /**
373 * @!method promise()
374 * Sends the request and returns a 'thenable' promise.
375 *
376 * Two callbacks can be provided to the `then` method on the returned promise.
377 * The first callback will be called if the promise is fulfilled, and the second
378 * callback will be called if the promise is rejected.
379 * @callback fulfilledCallback function(data)
380 * Called if the promise is fulfilled.
381 * @param data [Object] the de-serialized data returned from the request.
382 * @callback rejectedCallback function(error)
383 * Called if the promise is rejected.
384 * @param error [Error] the error object returned from the request.
385 * @return [Promise] A promise that represents the state of the request.
386 * @example Sending a request using promises.
387 * var request = s3.putObject({Bucket: 'bucket', Key: 'key'});
388 * var result = request.promise();
389 * result.then(function(data) { ... }, function(error) { ... });
390 */
391
392 /**
393 * @api private
394 */
395 build: function build(callback) {
396 return this.runTo('send', callback);
397 },
398
399 /**
400 * @api private
401 */
402 runTo: function runTo(state, done) {
403 this._asm.runTo(state, done, this);
404 return this;
405 },
406
407 /**
408 * Aborts a request, emitting the error and complete events.
409 *
410 * @!macro nobrowser
411 * @example Aborting a request after sending
412 * var params = {
413 * Bucket: 'bucket', Key: 'key',
414 * Body: Buffer.alloc(1024 * 1024 * 5) // 5MB payload
415 * };
416 * var request = s3.putObject(params);
417 * request.send(function (err, data) {
418 * if (err) console.log("Error:", err.code, err.message);
419 * else console.log(data);
420 * });
421 *
422 * // abort request in 1 second
423 * setTimeout(request.abort.bind(request), 1000);
424 *
425 * // prints "Error: RequestAbortedError Request aborted by user"
426 * @return [AWS.Request] the same request object, for chaining.
427 * @since v1.4.0
428 */
429 abort: function abort() {
430 this.removeAllListeners('validateResponse');
431 this.removeAllListeners('extractError');
432 this.on('validateResponse', function addAbortedError(resp) {
433 resp.error = AWS.util.error(new Error('Request aborted by user'), {
434 code: 'RequestAbortedError', retryable: false
435 });
436 });
437
438 if (this.httpRequest.stream && !this.httpRequest.stream.didCallback) { // abort HTTP stream
439 this.httpRequest.stream.abort();
440 if (this.httpRequest._abortCallback) {
441 this.httpRequest._abortCallback();
442 } else {
443 this.removeAllListeners('send'); // haven't sent yet, so let's not
444 }
445 }
446
447 return this;
448 },
449
450 /**
451 * Iterates over each page of results given a pageable request, calling
452 * the provided callback with each page of data. After all pages have been
453 * retrieved, the callback is called with `null` data.
454 *
455 * @note This operation can generate multiple requests to a service.
456 * @example Iterating over multiple pages of objects in an S3 bucket
457 * var pages = 1;
458 * s3.listObjects().eachPage(function(err, data) {
459 * if (err) return;
460 * console.log("Page", pages++);
461 * console.log(data);
462 * });
463 * @example Iterating over multiple pages with an asynchronous callback
464 * s3.listObjects(params).eachPage(function(err, data, done) {
465 * doSomethingAsyncAndOrExpensive(function() {
466 * // The next page of results isn't fetched until done is called
467 * done();
468 * });
469 * });
470 * @callback callback function(err, data, [doneCallback])
471 * Called with each page of resulting data from the request. If the
472 * optional `doneCallback` is provided in the function, it must be called
473 * when the callback is complete.
474 *
475 * @param err [Error] an error object, if an error occurred.
476 * @param data [Object] a single page of response data. If there is no
477 * more data, this object will be `null`.
478 * @param doneCallback [Function] an optional done callback. If this
479 * argument is defined in the function declaration, it should be called
480 * when the next page is ready to be retrieved. This is useful for
481 * controlling serial pagination across asynchronous operations.
482 * @return [Boolean] if the callback returns `false`, pagination will
483 * stop.
484 *
485 * @see AWS.Request.eachItem
486 * @see AWS.Response.nextPage
487 * @since v1.4.0
488 */
489 eachPage: function eachPage(callback) {
490 // Make all callbacks async-ish
491 callback = AWS.util.fn.makeAsync(callback, 3);
492
493 function wrappedCallback(response) {
494 callback.call(response, response.error, response.data, function (result) {
495 if (result === false) return;
496
497 if (response.hasNextPage()) {
498 response.nextPage().on('complete', wrappedCallback).send();
499 } else {
500 callback.call(response, null, null, AWS.util.fn.noop);
501 }
502 });
503 }
504
505 this.on('complete', wrappedCallback).send();
506 },
507
508 /**
509 * Enumerates over individual items of a request, paging the responses if
510 * necessary.
511 *
512 * @api experimental
513 * @since v1.4.0
514 */
515 eachItem: function eachItem(callback) {
516 var self = this;
517 function wrappedCallback(err, data) {
518 if (err) return callback(err, null);
519 if (data === null) return callback(null, null);
520
521 var config = self.service.paginationConfig(self.operation);
522 var resultKey = config.resultKey;
523 if (Array.isArray(resultKey)) resultKey = resultKey[0];
524 var items = jmespath.search(data, resultKey);
525 var continueIteration = true;
526 AWS.util.arrayEach(items, function(item) {
527 continueIteration = callback(null, item);
528 if (continueIteration === false) {
529 return AWS.util.abort;
530 }
531 });
532 return continueIteration;
533 }
534
535 this.eachPage(wrappedCallback);
536 },
537
538 /**
539 * @return [Boolean] whether the operation can return multiple pages of
540 * response data.
541 * @see AWS.Response.eachPage
542 * @since v1.4.0
543 */
544 isPageable: function isPageable() {
545 return this.service.paginationConfig(this.operation) ? true : false;
546 },
547
548 /**
549 * Sends the request and converts the request object into a readable stream
550 * that can be read from or piped into a writable stream.
551 *
552 * @note The data read from a readable stream contains only
553 * the raw HTTP body contents.
554 * @example Manually reading from a stream
555 * request.createReadStream().on('data', function(data) {
556 * console.log("Got data:", data.toString());
557 * });
558 * @example Piping a request body into a file
559 * var out = fs.createWriteStream('/path/to/outfile.jpg');
560 * s3.service.getObject(params).createReadStream().pipe(out);
561 * @return [Stream] the readable stream object that can be piped
562 * or read from (by registering 'data' event listeners).
563 * @!macro nobrowser
564 */
565 createReadStream: function createReadStream() {
566 var streams = AWS.util.stream;
567 var req = this;
568 var stream = null;
569
570 if (AWS.HttpClient.streamsApiVersion === 2) {
571 stream = new streams.PassThrough();
572 process.nextTick(function() { req.send(); });
573 } else {
574 stream = new streams.Stream();
575 stream.readable = true;
576
577 stream.sent = false;
578 stream.on('newListener', function(event) {
579 if (!stream.sent && event === 'data') {
580 stream.sent = true;
581 process.nextTick(function() { req.send(); });
582 }
583 });
584 }
585
586 this.on('error', function(err) {
587 stream.emit('error', err);
588 });
589
590 this.on('httpHeaders', function streamHeaders(statusCode, headers, resp) {
591 if (statusCode < 300) {
592 req.removeListener('httpData', AWS.EventListeners.Core.HTTP_DATA);
593 req.removeListener('httpError', AWS.EventListeners.Core.HTTP_ERROR);
594 req.on('httpError', function streamHttpError(error) {
595 resp.error = error;
596 resp.error.retryable = false;
597 });
598
599 var shouldCheckContentLength = false;
600 var expectedLen;
601 if (req.httpRequest.method !== 'HEAD') {
602 expectedLen = parseInt(headers['content-length'], 10);
603 }
604 if (expectedLen !== undefined && !isNaN(expectedLen) && expectedLen >= 0) {
605 shouldCheckContentLength = true;
606 var receivedLen = 0;
607 }
608
609 var checkContentLengthAndEmit = function checkContentLengthAndEmit() {
610 if (shouldCheckContentLength && receivedLen !== expectedLen) {
611 stream.emit('error', AWS.util.error(
612 new Error('Stream content length mismatch. Received ' +
613 receivedLen + ' of ' + expectedLen + ' bytes.'),
614 { code: 'StreamContentLengthMismatch' }
615 ));
616 } else if (AWS.HttpClient.streamsApiVersion === 2) {
617 stream.end();
618 } else {
619 stream.emit('end');
620 }
621 };
622
623 var httpStream = resp.httpResponse.createUnbufferedStream();
624
625 if (AWS.HttpClient.streamsApiVersion === 2) {
626 if (shouldCheckContentLength) {
627 var lengthAccumulator = new streams.PassThrough();
628 lengthAccumulator._write = function(chunk) {
629 if (chunk && chunk.length) {
630 receivedLen += chunk.length;
631 }
632 return streams.PassThrough.prototype._write.apply(this, arguments);
633 };
634
635 lengthAccumulator.on('end', checkContentLengthAndEmit);
636 stream.on('error', function(err) {
637 shouldCheckContentLength = false;
638 httpStream.unpipe(lengthAccumulator);
639 lengthAccumulator.emit('end');
640 lengthAccumulator.end();
641 });
642 httpStream.pipe(lengthAccumulator).pipe(stream, { end: false });
643 } else {
644 httpStream.pipe(stream);
645 }
646 } else {
647
648 if (shouldCheckContentLength) {
649 httpStream.on('data', function(arg) {
650 if (arg && arg.length) {
651 receivedLen += arg.length;
652 }
653 });
654 }
655
656 httpStream.on('data', function(arg) {
657 stream.emit('data', arg);
658 });
659 httpStream.on('end', checkContentLengthAndEmit);
660 }
661
662 httpStream.on('error', function(err) {
663 shouldCheckContentLength = false;
664 stream.emit('error', err);
665 });
666 }
667 });
668
669 return stream;
670 },
671
672 /**
673 * @param [Array,Response] args This should be the response object,
674 * or an array of args to send to the event.
675 * @api private
676 */
677 emitEvent: function emit(eventName, args, done) {
678 if (typeof args === 'function') { done = args; args = null; }
679 if (!done) done = function() { };
680 if (!args) args = this.eventParameters(eventName, this.response);
681
682 var origEmit = AWS.SequentialExecutor.prototype.emit;
683 origEmit.call(this, eventName, args, function (err) {
684 if (err) this.response.error = err;
685 done.call(this, err);
686 });
687 },
688
689 /**
690 * @api private
691 */
692 eventParameters: function eventParameters(eventName) {
693 switch (eventName) {
694 case 'restart':
695 case 'validate':
696 case 'sign':
697 case 'build':
698 case 'afterValidate':
699 case 'afterBuild':
700 return [this];
701 case 'error':
702 return [this.response.error, this.response];
703 default:
704 return [this.response];
705 }
706 },
707
708 /**
709 * @api private
710 */
711 presign: function presign(expires, callback) {
712 if (!callback && typeof expires === 'function') {
713 callback = expires;
714 expires = null;
715 }
716 return new AWS.Signers.Presign().sign(this.toGet(), expires, callback);
717 },
718
719 /**
720 * @api private
721 */
722 isPresigned: function isPresigned() {
723 return Object.prototype.hasOwnProperty.call(this.httpRequest.headers, 'presigned-expires');
724 },
725
726 /**
727 * @api private
728 */
729 toUnauthenticated: function toUnauthenticated() {
730 this._unAuthenticated = true;
731 this.removeListener('validate', AWS.EventListeners.Core.VALIDATE_CREDENTIALS);
732 this.removeListener('sign', AWS.EventListeners.Core.SIGN);
733 return this;
734 },
735
736 /**
737 * @api private
738 */
739 toGet: function toGet() {
740 if (this.service.api.protocol === 'query' ||
741 this.service.api.protocol === 'ec2') {
742 this.removeListener('build', this.buildAsGet);
743 this.addListener('build', this.buildAsGet);
744 }
745 return this;
746 },
747
748 /**
749 * @api private
750 */
751 buildAsGet: function buildAsGet(request) {
752 request.httpRequest.method = 'GET';
753 request.httpRequest.path = request.service.endpoint.path +
754 '?' + request.httpRequest.body;
755 request.httpRequest.body = '';
756
757 // don't need these headers on a GET request
758 delete request.httpRequest.headers['Content-Length'];
759 delete request.httpRequest.headers['Content-Type'];
760 },
761
762 /**
763 * @api private
764 */
765 haltHandlersOnError: function haltHandlersOnError() {
766 this._haltHandlersOnError = true;
767 }
768});
769
770/**
771 * @api private
772 */
773AWS.Request.addPromisesToClass = function addPromisesToClass(PromiseDependency) {
774 this.prototype.promise = function promise() {
775 var self = this;
776 // append to user agent
777 this.httpRequest.appendToUserAgent('promise');
778 return new PromiseDependency(function(resolve, reject) {
779 self.on('complete', function(resp) {
780 if (resp.error) {
781 reject(resp.error);
782 } else {
783 // define $response property so that it is not enumberable
784 // this prevents circular reference errors when stringifying the JSON object
785 resolve(Object.defineProperty(
786 resp.data || {},
787 '$response',
788 {value: resp}
789 ));
790 }
791 });
792 self.runTo();
793 });
794 };
795};
796
797/**
798 * @api private
799 */
800AWS.Request.deletePromisesFromClass = function deletePromisesFromClass() {
801 delete this.prototype.promise;
802};
803
804AWS.util.addPromises(AWS.Request);
805
806AWS.util.mixin(AWS.Request, AWS.SequentialExecutor);