public static NamedBlobPath parseS3(String path, Map<String, Object> args) throws RestServiceException {
    path = path.startsWith("/") ? path.substring(1) : path;
    String[] splitPath = path.split("/", 4);
    String blobNamePrefix = RestUtils.getHeader(args, PREFIX_PARAM, false);
    boolean isBatchDelete = args.containsKey(BATCH_DELETE_QUERY_PARAM);
    boolean isGetObjectLockRequest = args.containsKey(OBJECT_LOCK_PARAM);
    // For s3 request, if splitPath.length = 3 && Method = GET, the expected segments = 3
    // All other requests require segments = 4
    boolean isListObjectRequest = args.containsKey(LIST_REQUEST);
    int expectedSegments = (isListObjectRequest || isGetObjectLockRequest) ? 3 : 4;
    if (splitPath.length != expectedSegments || !Operations.NAMED_BLOB.equalsIgnoreCase(splitPath[0])) {
      throw new RestServiceException(String.format(
          "Path must have format '/named/<account_name>/<container_name>%s.  Received path='%s', arg='%s'",
          isListObjectRequest || isGetObjectLockRequest ? "" : "/<blob_name>'", path, args), RestServiceErrorCode.BadRequest);
    }
    String accountName = splitPath[1];
    String containerName = splitPath[2];
    String pageToken;
    //S3 listObject use Marker as page token.
    //S3 listObjectV2 use ContinuationToken as page token.
    if (isListObjectRequest) {
      String tokenKey = isListObjectV2Request(args) ? CONTINUATION_TOKEN : MARKER;
      pageToken = RestUtils.getHeader(args, tokenKey, false);
      return new NamedBlobPath(accountName, containerName, null, blobNamePrefix, pageToken);
    }
    if (isGetObjectLockRequest || isBatchDelete) {
      return new NamedBlobPath(accountName, containerName, null, null, null);
    } else {
      String blobName = splitPath[3];
      if (blobName.length() > MAX_BLOB_NAME_LENGTH) {
        throw new RestServiceException(
            String.format("Blob name maximum length should be less than %s", MAX_BLOB_NAME_LENGTH),
            RestServiceErrorCode.BadRequest);
      }
      return new NamedBlobPath(accountName, containerName, blobName, null, null);
    }
  }
Parse the input path for S3 request.
@param path the URI path that needs to be parsed. This path should already be URL decoded and have query paramaters
            removed. In the request handling path, this would be done by the {@link RestRequest} implementation.
@param args a map containing any query parameters from the request.
@return the {@link NamedBlobPath} that indicates the parsing result from blobUrl.
@throws RestServiceException on parsing errors.
ambry-api/src/main/java/com/github/ambry/frontend/NamedBlobPath.java