/** * 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)); }
/** * Utility to test blob POST, GET, HEAD and DELETE operations for a specified size * * @param contentSize the size of the blob to be tested * @param multipartPost {@code true} if multipart POST is desired, {@code false} otherwise. * @throws Exception */ private void doPostGetHeadDeleteTest(int contentSize, boolean multipartPost) throws Exception { ByteBuffer content = ByteBuffer.wrap(RestTestUtils.getRandomBytes(contentSize)); String serviceId = "postGetHeadDeleteServiceID"; String contentType = "application/octet-stream"; String ownerId = "postGetHeadDeleteOwnerID"; HttpHeaders headers = new DefaultHttpHeaders(); setAmbryHeaders(headers, content.capacity(), 7200, false, serviceId, contentType, ownerId); headers.set(HttpHeaders.Names.CONTENT_LENGTH, content.capacity()); String blobId; byte[] usermetadata = null; if (multipartPost) { usermetadata = UtilsTest.getRandomString(32).getBytes(); blobId = multipartPostBlobAndVerify(headers, content, ByteBuffer.wrap(usermetadata)); } else { headers.add(RestUtils.Headers.USER_META_DATA_HEADER_PREFIX + "key1", "value1"); headers.add(RestUtils.Headers.USER_META_DATA_HEADER_PREFIX + "key2", "value2"); blobId = postBlobAndVerify(headers, content); } getBlobAndVerify(blobId, null, headers, content); getHeadAndVerify(blobId, null, headers); ByteRange range = ByteRange.fromLastNBytes(ThreadLocalRandom.current().nextLong(content.capacity() + 1)); getBlobAndVerify(blobId, range, headers, content); getHeadAndVerify(blobId, range, headers); if (contentSize > 0) { range = ByteRange.fromStartOffset(ThreadLocalRandom.current().nextLong(content.capacity())); getBlobAndVerify(blobId, range, headers, content); getHeadAndVerify(blobId, range, headers); long random1 = ThreadLocalRandom.current().nextLong(content.capacity()); long random2 = ThreadLocalRandom.current().nextLong(content.capacity()); range = ByteRange.fromOffsetRange(Math.min(random1, random2), Math.max(random1, random2)); getBlobAndVerify(blobId, range, headers, content); getHeadAndVerify(blobId, range, headers); } getNotModifiedBlobAndVerify(blobId); getUserMetadataAndVerify(blobId, headers, usermetadata); getBlobInfoAndVerify(blobId, headers, usermetadata); deleteBlobAndVerify(blobId); // check GET, HEAD and DELETE after delete. verifyOperationsAfterDelete(blobId); }