lib/plugins/aws/provider/awsProvider.js/AwsProvider/request
class AwsProvider:  request(service, method, params, options) {
    const that = this;
    const credentials = Object.assign({}, that.getCredentials());
    // Make sure options is an object (honors wrong calls of request)
    const requestOptions = _.isObject(options) ? options : {};
    const shouldCache = _.get(requestOptions, 'useCache', false);
    const paramsWithRegion = _.merge({}, params, {
      region: _.get(options, 'region'),
    });
    const paramsHash = objectHash.sha1(paramsWithRegion);
    const MAX_TRIES = 4;
    const persistentRequest = (f) => new BbPromise((resolve, reject) => {
      const doCall = (numTry) => {
        f()
          // We're resembling if/else logic, therefore single `then` instead of `then`/`catch` pair
          .then(resolve, e => {
            if (numTry < MAX_TRIES &&
              ((e.providerError && e.providerError.retryable) || e.statusCode === 429)) {
              that.serverless.cli.log(
                _.join([
                  `Recoverable error occurred (${e.message}), sleeping for 5 seconds.`,
                  `Try ${numTry + 1} of ${MAX_TRIES}`,
                ], ' ')
              );
              setTimeout(doCall, 5000, numTry + 1);
            } else {
              reject(e);
            }
          });
      };
      return doCall(0);
    });

    // Emit a warning for misuses of the old signature including stage and region
    // TODO: Determine calling module and log that
    if (process.env.SLS_DEBUG && !_.isNil(options) && !_.isObject(options)) {
      this.serverless.cli.log('WARNING: Inappropriate call of provider.request()');
    }

    // Support S3 Transfer Acceleration
    if (this.canUseS3TransferAcceleration(service, method)) {
      this.enableS3TransferAcceleration(credentials);
    }

    if (shouldCache) {
      const cachedRequest = _.get(this.requestCache, `${service}.${method}.${paramsHash}`);
      if (cachedRequest) {
        return BbPromise.resolve(cachedRequest);
      }
    }

    const request = this.requestQueue.add(() => persistentRequest(() => {
      if (options && !_.isUndefined(options.region)) {
        credentials.region = options.region;
      }
      const awsService = new that.sdk[service](credentials);
      const req = awsService[method](params);

      // TODO: Add listeners, put Debug statments here...
      // req.on('send', function (r) {console.log(r)});

      const promise = req.promise ? req.promise() : BbPromise.fromCallback(cb => {
        req.send(cb);
      });
      return promise.catch(err => {
        let message = err.message !== null ? err.message : err.code;
        if (err.message === 'Missing credentials in config') {
          const errorMessage = [
            'AWS provider credentials not found.',
            ' Learn how to set up AWS provider credentials',
            ` in our docs here: <${chalk.green('http://bit.ly/aws-creds-setup')}>.`,
          ].join('');
          message = errorMessage;
          userStats.track('user_awsCredentialsNotFound');
          // We do not want to trigger the retry mechanism for credential errors
          return BbPromise.reject(Object.assign(
            new this.serverless.classes.Error(message, err.statusCode),
            { providerError: _.assign({}, err, { retryable: false }) }
          ));
        }

        return BbPromise.reject(Object.assign(
          new this.serverless.classes.Error(message, err.statusCode),
          { providerError: err }
        ));
      });
    })
      .then(data => {
        const result = BbPromise.resolve(data);
        if (shouldCache) {
          _.set(this.requestCache, `${service}.${method}.${paramsHash}`, result);
        }
        return result;
      }));

    if (shouldCache) {
      _.set(this.requestCache, `${service}.${method}.${paramsHash}`, request);
    }

    return request;
  }