/**
  * Gets the blob with blob ID {@code blobId} and verifies that the headers and content match with
  * what is expected.
  *
  * @param blobId the blob ID of the blob to GET.
  * @param range the {@link ByteRange} for the request.
  * @param expectedHeaders the expected headers in the response.
  * @param expectedContent the expected content of the blob.
  * @throws ExecutionException
  * @throws InterruptedException
  */
 private void getBlobAndVerify(
     String blobId, ByteRange range, HttpHeaders expectedHeaders, ByteBuffer expectedContent)
     throws ExecutionException, InterruptedException, RestServiceException {
   HttpHeaders headers = null;
   if (range != null) {
     headers =
         new DefaultHttpHeaders()
             .add(RestUtils.Headers.RANGE, RestTestUtils.getRangeHeaderString(range));
   }
   FullHttpRequest httpRequest = buildRequest(HttpMethod.GET, blobId, headers, null);
   Queue<HttpObject> responseParts = nettyClient.sendRequest(httpRequest, null, null).get();
   HttpResponse response = (HttpResponse) responseParts.poll();
   assertEquals(
       "Unexpected response status",
       range == null ? HttpResponseStatus.OK : HttpResponseStatus.PARTIAL_CONTENT,
       response.getStatus());
   checkCommonGetHeadHeaders(response.headers());
   assertEquals(
       "Content-Type does not match",
       expectedHeaders.get(RestUtils.Headers.AMBRY_CONTENT_TYPE),
       response.headers().get(HttpHeaders.Names.CONTENT_TYPE));
   assertEquals(
       RestUtils.Headers.BLOB_SIZE + " does not match",
       expectedHeaders.get(RestUtils.Headers.BLOB_SIZE),
       response.headers().get(RestUtils.Headers.BLOB_SIZE));
   assertEquals(
       "Accept-Ranges not set correctly",
       "bytes",
       response.headers().get(RestUtils.Headers.ACCEPT_RANGES));
   byte[] expectedContentArray = expectedContent.array();
   if (range != null) {
     long blobSize = Long.parseLong(expectedHeaders.get(RestUtils.Headers.BLOB_SIZE));
     assertEquals(
         "Content-Range header not set correctly",
         RestUtils.buildContentRangeAndLength(range, blobSize).getFirst(),
         response.headers().get(RestUtils.Headers.CONTENT_RANGE));
     ByteRange resolvedRange = range.toResolvedByteRange(blobSize);
     expectedContentArray =
         Arrays.copyOfRange(
             expectedContentArray,
             (int) resolvedRange.getStartOffset(),
             (int) resolvedRange.getEndOffset() + 1);
   } else {
     assertNull(
         "Content-Range header should not be set",
         response.headers().get(RestUtils.Headers.CONTENT_RANGE));
   }
   if (expectedContentArray.length < FRONTEND_CONFIG.frontendChunkedGetResponseThresholdInBytes) {
     assertEquals(
         "Content-length not as expected",
         expectedContentArray.length,
         HttpHeaders.getContentLength(response));
   }
   byte[] responseContentArray = getContent(responseParts, expectedContentArray.length).array();
   assertArrayEquals(
       "GET content does not match original content", expectedContentArray, responseContentArray);
   assertTrue("Channel should be active", HttpHeaders.isKeepAlive(response));
 }
 /** Shuts down the Ambry frontend server. */
 @AfterClass
 public static void teardown() {
   if (nettyClient != null) {
     nettyClient.close();
   }
   if (ambryRestServer != null) {
     ambryRestServer.shutdown();
   }
 }
 /**
  * Verifies that a request returns the right response code once the blob has been deleted.
  *
  * @param httpRequest the {@link FullHttpRequest} to send to the server.
  * @param expectedStatusCode the expected {@link HttpResponseStatus}.
  * @throws ExecutionException
  * @throws InterruptedException
  */
 private void verifyDeleted(FullHttpRequest httpRequest, HttpResponseStatus expectedStatusCode)
     throws ExecutionException, InterruptedException {
   Queue<HttpObject> responseParts = nettyClient.sendRequest(httpRequest, null, null).get();
   HttpResponse response = (HttpResponse) responseParts.poll();
   assertEquals("Unexpected response status", expectedStatusCode, response.getStatus());
   assertTrue(
       "No Date header",
       HttpHeaders.getDateHeader(response, HttpHeaders.Names.DATE, null) != null);
   discardContent(responseParts, 1);
   assertTrue("Channel should be active", HttpHeaders.isKeepAlive(response));
 }
 /**
  * Gets the blob info of the blob with blob ID {@code blobId} and verifies them against what is
  * expected.
  *
  * @param blobId the blob ID of the blob to HEAD.
  * @param expectedHeaders the expected headers in the response.
  * @param usermetadata if non-null, this is expected to come as the body.
  * @throws ExecutionException
  * @throws InterruptedException
  */
 private void getBlobInfoAndVerify(String blobId, HttpHeaders expectedHeaders, byte[] usermetadata)
     throws ExecutionException, InterruptedException {
   FullHttpRequest httpRequest =
       buildRequest(HttpMethod.GET, blobId + "/" + RestUtils.SubResource.BlobInfo, null, null);
   Queue<HttpObject> responseParts = nettyClient.sendRequest(httpRequest, null, null).get();
   HttpResponse response = (HttpResponse) responseParts.poll();
   assertEquals("Unexpected response status", HttpResponseStatus.OK, response.getStatus());
   checkCommonGetHeadHeaders(response.headers());
   verifyBlobProperties(expectedHeaders, response);
   verifyUserMetadata(expectedHeaders, response, usermetadata, responseParts);
   assertTrue("Channel should be active", HttpHeaders.isKeepAlive(response));
 }
 /*
  * Tests health check request
  * @throws ExecutionException
  * @throws InterruptedException
  * @throws IOException
  */
 @Test
 public void healthCheckRequestTest()
     throws ExecutionException, InterruptedException, IOException {
   FullHttpRequest httpRequest =
       new DefaultFullHttpRequest(
           HttpVersion.HTTP_1_1, HttpMethod.GET, "/healthCheck", Unpooled.buffer(0));
   Queue<HttpObject> responseParts = nettyClient.sendRequest(httpRequest, null, null).get();
   HttpResponse response = (HttpResponse) responseParts.poll();
   assertEquals("Unexpected response status", HttpResponseStatus.OK, response.getStatus());
   final String expectedResponseBody = "GOOD";
   ByteBuffer content = getContent(responseParts, expectedResponseBody.length());
   assertEquals(
       "GET content does not match original content",
       expectedResponseBody,
       new String(content.array()));
 }
 /**
  * Gets the headers of the blob with blob ID {@code blobId} and verifies them against what is
  * expected.
  *
  * @param blobId the blob ID of the blob to HEAD.
  * @param range the {@link ByteRange} for the request.
  * @param expectedHeaders the expected headers in the response.
  * @throws ExecutionException
  * @throws InterruptedException
  */
 private void getHeadAndVerify(String blobId, ByteRange range, HttpHeaders expectedHeaders)
     throws ExecutionException, InterruptedException, RestServiceException {
   HttpHeaders headers = null;
   if (range != null) {
     headers =
         new DefaultHttpHeaders()
             .add(RestUtils.Headers.RANGE, RestTestUtils.getRangeHeaderString(range));
   }
   FullHttpRequest httpRequest = buildRequest(HttpMethod.HEAD, blobId, headers, null);
   Queue<HttpObject> responseParts = nettyClient.sendRequest(httpRequest, null, null).get();
   HttpResponse response = (HttpResponse) responseParts.poll();
   assertEquals(
       "Unexpected response status",
       range == null ? HttpResponseStatus.OK : HttpResponseStatus.PARTIAL_CONTENT,
       response.getStatus());
   checkCommonGetHeadHeaders(response.headers());
   long contentLength = Long.parseLong(expectedHeaders.get(RestUtils.Headers.BLOB_SIZE));
   if (range != null) {
     Pair<String, Long> rangeAndLength =
         RestUtils.buildContentRangeAndLength(range, contentLength);
     assertEquals(
         "Content-Range header not set correctly",
         rangeAndLength.getFirst(),
         response.headers().get(RestUtils.Headers.CONTENT_RANGE));
     contentLength = rangeAndLength.getSecond();
   } else {
     assertNull(
         "Content-Range header should not be set",
         response.headers().get(RestUtils.Headers.CONTENT_RANGE));
   }
   assertEquals(
       "Accept-Ranges not set correctly",
       "bytes",
       response.headers().get(RestUtils.Headers.ACCEPT_RANGES));
   assertEquals(
       RestUtils.Headers.CONTENT_LENGTH + " does not match expected",
       contentLength,
       HttpHeaders.getContentLength(response));
   assertEquals(
       RestUtils.Headers.CONTENT_TYPE + " does not match " + RestUtils.Headers.AMBRY_CONTENT_TYPE,
       expectedHeaders.get(RestUtils.Headers.AMBRY_CONTENT_TYPE),
       HttpHeaders.getHeader(response, HttpHeaders.Names.CONTENT_TYPE));
   verifyBlobProperties(expectedHeaders, response);
   discardContent(responseParts, 1);
   assertTrue("Channel should be active", HttpHeaders.isKeepAlive(response));
 }
  /**
   * Posts a blob with the given {@code headers} and {@code content}.
   *
   * @param headers the headers required.
   * @param content the content of the blob.
   * @return the blob ID of the blob.
   * @throws ExecutionException
   * @throws InterruptedException
   */
  private String postBlobAndVerify(HttpHeaders headers, ByteBuffer content)
      throws ExecutionException, InterruptedException {
    FullHttpRequest httpRequest = buildRequest(HttpMethod.POST, "/", headers, content);
    Queue<HttpObject> responseParts = nettyClient.sendRequest(httpRequest, null, null).get();
    HttpResponse response = (HttpResponse) responseParts.poll();
    assertEquals("Unexpected response status", HttpResponseStatus.CREATED, response.getStatus());
    assertTrue(
        "No Date header",
        HttpHeaders.getDateHeader(response, HttpHeaders.Names.DATE, null) != null);
    assertTrue(
        "No " + RestUtils.Headers.CREATION_TIME,
        HttpHeaders.getHeader(response, RestUtils.Headers.CREATION_TIME, null) != null);
    assertEquals("Content-Length is not 0", 0, HttpHeaders.getContentLength(response));
    String blobId = HttpHeaders.getHeader(response, HttpHeaders.Names.LOCATION, null);

    if (blobId == null) {
      fail("postBlobAndVerify did not return a blob ID");
    }
    discardContent(responseParts, 1);
    assertTrue("Channel should be active", HttpHeaders.isKeepAlive(response));
    return blobId;
  }
 /**
  * Gets the blob with blob ID {@code blobId} and verifies that the blob is not returned as blob is
  * not modified
  *
  * @param blobId the blob ID of the blob to GET.
  * @throws Exception
  */
 private void getNotModifiedBlobAndVerify(String blobId) throws Exception {
   HttpHeaders headers = new DefaultHttpHeaders();
   headers.add(RestUtils.Headers.IF_MODIFIED_SINCE, new Date());
   FullHttpRequest httpRequest = buildRequest(HttpMethod.GET, blobId, headers, null);
   Queue<HttpObject> responseParts = nettyClient.sendRequest(httpRequest, null, null).get();
   HttpResponse response = (HttpResponse) responseParts.poll();
   assertEquals(
       "Unexpected response status", HttpResponseStatus.NOT_MODIFIED, response.getStatus());
   assertTrue("No Date header", response.headers().get(RestUtils.Headers.DATE) != null);
   assertNull("No Last-Modified header expected", response.headers().get("Last-Modified"));
   assertNull(
       "Accept-Ranges should not be set", response.headers().get(RestUtils.Headers.ACCEPT_RANGES));
   assertNull(
       "Content-Range header should not be set",
       response.headers().get(RestUtils.Headers.CONTENT_RANGE));
   assertNull(
       RestUtils.Headers.BLOB_SIZE + " should have been null ",
       response.headers().get(RestUtils.Headers.BLOB_SIZE));
   assertNull(
       "Content-Type should have been null",
       response.headers().get(RestUtils.Headers.CONTENT_TYPE));
   assertNoContent(responseParts);
 }
  /**
   * Posts a blob with the given {@code headers} and {@code content}.
   *
   * @param headers the headers required.
   * @param content the content of the blob.
   * @param usermetadata the {@link ByteBuffer} that represents user metadata
   * @return the blob ID of the blob.
   * @throws Exception
   */
  private String multipartPostBlobAndVerify(
      HttpHeaders headers, ByteBuffer content, ByteBuffer usermetadata) throws Exception {
    HttpRequest httpRequest = RestTestUtils.createRequest(HttpMethod.POST, "/", headers);
    HttpPostRequestEncoder encoder = createEncoder(httpRequest, content, usermetadata);
    Queue<HttpObject> responseParts =
        nettyClient.sendRequest(encoder.finalizeRequest(), encoder, null).get();
    HttpResponse response = (HttpResponse) responseParts.poll();
    assertEquals("Unexpected response status", HttpResponseStatus.CREATED, response.getStatus());
    assertTrue(
        "No Date header",
        HttpHeaders.getDateHeader(response, HttpHeaders.Names.DATE, null) != null);
    assertTrue(
        "No " + RestUtils.Headers.CREATION_TIME,
        HttpHeaders.getHeader(response, RestUtils.Headers.CREATION_TIME, null) != null);
    assertEquals("Content-Length is not 0", 0, HttpHeaders.getContentLength(response));
    String blobId = HttpHeaders.getHeader(response, HttpHeaders.Names.LOCATION, null);

    if (blobId == null) {
      fail("postBlobAndVerify did not return a blob ID");
    }
    discardContent(responseParts, 1);
    assertTrue("Channel should be active", HttpHeaders.isKeepAlive(response));
    return blobId;
  }