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.statusCode !== 403 && // Invalid credentials
                ((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://slss.io/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;
  }