operation = function () {
    // retry policies dont really do anything to the request options
    // so move on to next
    if (next) {
      next(retryRequestOptions, function (returnObject, finalCallback, nextPostCallback) {
        // Previous operation ended so update the retry data
        if (returnObject.error) {
          if (retryRequestOptions.retryContext.error) {
            returnObject.error.innerError = retryRequestOptions.retryContext.error;
          }

          retryRequestOptions.retryContext.error = returnObject.error;
        }

        // If a request sent to the secondary location fails with 404 (Not Found), it is possible
        // that the resource replication is not finished yet. So, in case of 404 only in the secondary
        // location, the failure should still be retryable.
        var secondaryNotFound = (retryRequestOptions.currentLocation === Constants.StorageLocation.SECONDARY) && ((returnObject.response && returnObject.response.statusCode === 404) || (returnObject.error && returnObject.error.code === 'ENOTFOUND'));
 
        var notExceedMaxRetryCount = retryRequestOptions.retryContext.retryCount ? retryRequestOptions.retryContext.retryCount <= self.retryCount : true;
        var retryInfo = self.shouldRetry(secondaryNotFound ? 500 : (azureutil.objectIsNull(returnObject.response) ? 306 : returnObject.response.statusCode), retryRequestOptions);
        retryRequestOptions.retryContext.retryCount++;

        if (retryInfo.ignore) {
          returnObject.error = null;
        }

        // If the custom retry logic(shouldRetry) does not return a targetLocation, calculate based on the previous location and locationMode.
        if(azureutil.objectIsNull(retryInfo.targetLocation)) {
          retryInfo.targetLocation = azureutil.getNextLocation(retryRequestOptions.currentLocation, retryRequestOptions.locationMode);
        }

        // If the custom retry logic(shouldRetry) does not return a retryInterval, try to set it to the value on the instance if it is available. Otherwise, the default(30000) will be used.
        if(azureutil.objectIsNull(retryInfo.retryInterval)) {
          retryInfo.retryInterval = self.retryInterval;
        }
        
        // Only in the case of success from server but client side failure like MD5 or length mismatch, returnObject.retryable has a value(we explicitly set it to false). 
        // In this case, we should not retry the request.
        // If the output stream already get sent to server and get error back, 
        // we should NOT retry within the SDK as the stream data is not valid anymore if we retry directly.
        if (
            !returnObject.outputStreamSent && returnObject.error && azureutil.objectIsNull(returnObject.retryable) && notExceedMaxRetryCount &&
            (
              (!azureutil.objectIsNull(returnObject.response) && retryInfo.retryable) || 
              (
                returnObject.error.code === 'ECONNREFUSED' ||
                returnObject.error.code === 'ETIMEDOUT' ||
                returnObject.error.code === 'ESOCKETTIMEDOUT' || 
                returnObject.error.code === 'ECONNRESET' || 
                returnObject.error.code === 'EAI_AGAIN' ||
                returnObject.error.message === 'XHR error' // stream-http XHR network error message in browsers
              )
            )
          ) {
          if (retryRequestOptions.currentLocation === Constants.StorageLocation.PRIMARY) {
            lastPrimaryAttempt = returnObject.operationEndTime;
          } else {
            lastSecondaryAttempt = returnObject.operationEndTime;
          }

          // Moreover, in case of 404 when trying the secondary location, instead of retrying on the
          // secondary, further requests should be sent only to the primary location, as it most
          // probably has a higher chance of succeeding there.
          if (secondaryNotFound && (retryRequestOptions.locationMode !== StorageUtilities.LocationMode.SECONDARY_ONLY))
          {
            retryInfo.locationMode = StorageUtilities.LocationMode.PRIMARY_ONLY;
            retryInfo.targetLocation = Constants.StorageLocation.PRIMARY;
          }

          // Now is the time to calculate the exact retry interval. ShouldRetry call above already
          // returned back how long two requests to the same location should be apart from each other.
          // However, for the reasons explained above, the time spent between the last attempt to
          // the target location and current time must be subtracted from the total retry interval
          // that ShouldRetry returned.
          var lastAttemptTime = retryInfo.targetLocation === Constants.StorageLocation.PRIMARY ? lastPrimaryAttempt : lastSecondaryAttempt;
          if (!azureutil.objectIsNull(lastAttemptTime)) {
            var sinceLastAttempt = new Date().getTime() - lastAttemptTime.getTime();
            if (sinceLastAttempt < 0) {
              sinceLastAttempt = 0;
            }

            retryRequestOptions.retryInterval = retryInfo.retryInterval - sinceLastAttempt;
          }
          else {
            retryRequestOptions.retryInterval = 0;
          }

          if(!azureutil.objectIsNull(retryInfo.locationMode)) {
            retryRequestOptions.locationMode = retryInfo.locationMode;
          }

          retryRequestOptions.currentLocation = retryInfo.targetLocation;
          operation();
        } else {
          if (nextPostCallback) {
            nextPostCallback(returnObject);
          } else if (finalCallback) {
            finalCallback(returnObject);
          }
        }
      });
    }
  }
Handles an operation with a retry policy.

@param {Object}   requestOptions The original request options.
@param {function} next           The next filter to be handled.