request.js 27 KB

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