private void doMultipartUploadTest(Account account, Container container, int partCount, int partSize)
      throws Exception {
    // Prepare data
    byte[][] contents = new byte[partCount][partSize];
    for (int i = 0; i < partCount; i++) {
      contents[i] = TestUtils.getRandomBytes(partSize);
    }
    String[] eTags = new String[partCount];
    String[] locations = new String[partCount];

    // Prepare account etc.
    String endpoint = String.format("/s3/%s", account.getName());
    String bucket = container.getName();
    String key = "1/2/3/4/5";
    String uploadId = null;

    {
      // 1. Initiate multipart upload
      String uri =  String.format("%s/%s/%s?uploads", endpoint, bucket, key);
      HttpHeaders headers = new DefaultHttpHeaders();
      headers.add(CONTENT_TYPE, OCTET_STREAM_CONTENT_TYPE);
      headers.add(CONTENT_LENGTH, 0);
      FullHttpRequest httpRequest = buildRequest(HttpMethod.POST, uri, headers, null);
      NettyClient.ResponseParts responseParts = nettyClient.sendRequest(httpRequest, null, null).get();
      HttpResponse response = getHttpResponse(responseParts);
      assertEquals("Unexpected status", HttpResponseStatus.OK, response.status());
      assertEquals("Unexpected content type", XML_CONTENT_TYPE, response.headers().get(CONTENT_TYPE));
      InitiateMultipartUploadResult initUploadResult = xmlMapper.readValue(
          getContent(responseParts.queue, HttpUtil.getContentLength(response)).array(),
          S3MessagePayload.InitiateMultipartUploadResult.class);
      assertEquals("Unexpected bucket", bucket, initUploadResult.getBucket());
      assertEquals("Unexpected key", key, initUploadResult.getKey());
      uploadId = initUploadResult.getUploadId();
      assertNotNull("uploadid not found", uploadId);
    } {
      // 2. Upload parts
      HttpHeaders headers = new DefaultHttpHeaders();
      headers.add(CONTENT_TYPE, OCTET_STREAM_CONTENT_TYPE);
      headers.add(CONTENT_LENGTH, partSize);
      for (int i = 0; i < contents.length; i++) {
        String uri = String.format("%s/%s/%s?uploadId=%s&partNumber=%d", endpoint, bucket, key, uploadId, i + 1);
        FullHttpRequest httpRequest = buildRequest(HttpMethod.PUT, uri, headers, ByteBuffer.wrap(contents[i]));
        NettyClient.ResponseParts responseParts = nettyClient.sendRequest(httpRequest, null, null).get();
        HttpResponse response = getHttpResponse(responseParts);
        eTags[i] = response.headers().get(ETAG);
        locations[i] = response.headers().get(LOCATION);
        assertEquals("Unexpected status", HttpResponseStatus.OK, response.status());
        assertNotNull("Etag not found", eTags[i]);
        assertNotNull("Location not found", locations[i]);
      }
    } {
      // 3. Complete multipart upload
      String uri =  String.format("%s/%s/%s?uploadId=%s", endpoint, bucket, key, uploadId);
      Part[] parts = new Part[contents.length];
      for (int i = 0; i < contents.length; i++) {
        parts[i] = new Part(String.valueOf(i + 1), eTags[i]);
      }
      CompleteMultipartUpload completeMultipartUpload = new CompleteMultipartUpload(parts);
      ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
      xmlMapper.writeValue(byteArrayOutputStream, completeMultipartUpload);
      String completeMultipartStr = byteArrayOutputStream.toString();
      byte[] content = completeMultipartStr.getBytes(StandardCharsets.UTF_8);
      HttpHeaders headers = new DefaultHttpHeaders();
      headers.add(CONTENT_TYPE, OCTET_STREAM_CONTENT_TYPE);
      headers.add(CONTENT_LENGTH, content.length);
      FullHttpRequest httpRequest = buildRequest(HttpMethod.POST, uri, headers, ByteBuffer.wrap(content));
      NettyClient.ResponseParts responseParts = nettyClient.sendRequest(httpRequest, null, null).get();
      HttpResponse response = getHttpResponse(responseParts);
      CompleteMultipartUploadResult uploadResult = xmlMapper.readValue(
          getContent(responseParts.queue, HttpUtil.getContentLength(response)).array(),
          S3MessagePayload.CompleteMultipartUploadResult.class);
      assertEquals("Unexpected status", HttpResponseStatus.OK, response.status());
      assertEquals("Unexpected content type", XML_CONTENT_TYPE, response.headers().get(CONTENT_TYPE));
      assertEquals("Unexpected bucket", bucket, uploadResult.getBucket());
      assertEquals("Unexpected key", key, uploadResult.getKey());
      assertTrue("Unexpected location", uri.startsWith(uploadResult.getLocation()));
      assertNotNull("ETag not found", uploadResult.geteTag());
    } {
      // 4. Get the blob and verify content
      String uri = String.format("%s/%s/%s", endpoint, bucket, key);
      HttpHeaders headers = new DefaultHttpHeaders();
      FullHttpRequest httpRequest = buildRequest(HttpMethod.GET, uri, headers, null);
      NettyClient.ResponseParts responseParts = nettyClient.sendRequest(httpRequest, null, null).get();
      HttpResponse response = getHttpResponse(responseParts);
      assertEquals("Unexpected status", HttpResponseStatus.OK, response.status());
      assertEquals("Unexpected content type", OCTET_STREAM_CONTENT_TYPE, response.headers().get(CONTENT_TYPE));
      long expectedContentLength = partCount * partSize;
      if (expectedContentLength <= frontendConfig.chunkedGetResponseThresholdInBytes) {
        int contentLength = response.headers().getInt(CONTENT_LENGTH);
        assertEquals("Unexpected content length", expectedContentLength, contentLength);
      }
      byte[] content = getContent(responseParts.queue, expectedContentLength).array();
      ByteBuffer expectedContent = ByteBuffer.wrap(new byte[partCount * partSize]);
      Arrays.stream(contents).forEach(expectedContent::put);
      assertArrayEquals("Unexpected content", expectedContent.array(), content);
    }
  }
Shuts down the Ambry frontend server.
ambry-frontend/src/integration-test/java/com/github/ambry/frontend/S3IntegrationTest.java