use of com.github.ambry.rest.RestResponseChannel in project ambry by linkedin.
the class FrontendRestRequestService method handlePut.
@Override
public void handlePut(RestRequest restRequest, RestResponseChannel restResponseChannel) {
ThrowingConsumer<RequestPath> routingAction = requestPath -> {
if (requestPath.matchesOperation(Operations.UPDATE_TTL)) {
ttlUpdateHandler.handle(restRequest, restResponseChannel, (r, e) -> {
if (e instanceof RouterException && ((RouterException) e).getErrorCode() == RouterErrorCode.BlobUpdateNotAllowed) {
restResponseChannel.setHeader(Headers.ALLOW, TTL_UPDATE_REJECTED_ALLOW_HEADER_VALUE);
}
submitResponse(restRequest, restResponseChannel, null, e);
});
} else if (requestPath.matchesOperation(Operations.UNDELETE) && frontendConfig.enableUndelete) {
// If the undelete is not enabled, then treat it as unrecognized operation.
// And always send failure reason back to client for undelete
restRequest.setArg(SEND_FAILURE_REASON, Boolean.TRUE);
undeleteHandler.handle(restRequest, restResponseChannel, (r, e) -> {
submitResponse(restRequest, restResponseChannel, null, e);
});
} else if (requestPath.matchesOperation(Operations.NAMED_BLOB)) {
restRequest.setArg(SEND_FAILURE_REASON, Boolean.TRUE);
namedBlobPutHandler.handle(restRequest, restResponseChannel, (r, e) -> submitResponse(restRequest, restResponseChannel, null, e));
} else {
throw new RestServiceException("Unrecognized operation: " + requestPath.getOperationOrBlobId(false), RestServiceErrorCode.BadRequest);
}
};
preProcessAndRouteRequest(restRequest, restResponseChannel, frontendMetrics.putPreProcessingMetrics, routingAction);
}
use of com.github.ambry.rest.RestResponseChannel in project ambry by linkedin.
the class PostAccountContainersHandlerTest method validRequestsTest.
/**
* Test valid request cases.
* @throws Exception
*/
@Test
public void validRequestsTest() throws Exception {
String accountName = theAccount.getName();
short accountId = theAccount.getId();
ThrowingConsumer<Collection<Container>> testAction = inputContainers -> {
String requestBody = new String(AccountCollectionSerde.serializeContainersInJson(inputContainers));
RestResponseChannel restResponseChannel = new MockRestResponseChannel();
RestRequest request = createRestRequest(requestBody, accountName, null);
ReadableStreamChannel responseChannel = sendRequestGetResponse(request, restResponseChannel);
assertNotNull("Date has not been set", restResponseChannel.getHeader(RestUtils.Headers.DATE));
assertEquals("Content-length is not as expected", responseChannel.getSize(), Integer.parseInt((String) restResponseChannel.getHeader(RestUtils.Headers.CONTENT_LENGTH)));
assertEquals("Account id in response header is not as expected", accountId, Short.parseShort((String) restResponseChannel.getHeader(RestUtils.Headers.TARGET_ACCOUNT_ID)));
RetainingAsyncWritableChannel asyncWritableChannel = new RetainingAsyncWritableChannel((int) responseChannel.getSize());
responseChannel.readInto(asyncWritableChannel, null).get();
Collection<Container> outputContainers = AccountCollectionSerde.containersFromInputStreamInJson(asyncWritableChannel.consumeContentAsInputStream(), accountId);
assertEquals("Unexpected count returned", inputContainers.size(), outputContainers.size());
for (Container container : outputContainers) {
assertEquals("Container in account service not as expected", container, accountService.getContainerByName(accountName, container.getName()));
}
};
// add new container
testAction.accept(Collections.singleton(accountService.getRandomContainer(accountId)));
// add multiple containers
List<Container> containerList = new ArrayList<>();
for (int j = 0; j < 10; j++) {
containerList.add(new ContainerBuilder(Container.UNKNOWN_CONTAINER_ID, "Test-" + j, Container.ContainerStatus.ACTIVE, "", accountId).build());
}
testAction.accept(containerList);
// TODO: update existing containers when support is added
}
use of com.github.ambry.rest.RestResponseChannel in project ambry by linkedin.
the class PostBlobHandlerTest method doChunkUploadTest.
// chunkUploadTest() helpers
/**
* Make a post request and verify behavior related to chunk uploads (for stitched blobs).
* @param contentLength the size of the blob to upload.
* @param chunkUpload {@code true} to send the "x-ambry-chunk-upload" header, or {@code false} to test full uploads.
* @param uploadSession the value for the "x-ambry-chunk-upload-session" request header, or null to not set it.
* @param maxUploadSize the value for the "x-ambry-max-upload-size" request header, or null to not set it.
* @param blobTtlSecs the blob TTL to use.
* @param errorChecker if non-null, expect an exception to be thrown by the post flow and verify it using this
* {@link ThrowingConsumer}.
* @throws Exception
*/
private void doChunkUploadTest(int contentLength, boolean chunkUpload, String uploadSession, Integer maxUploadSize, long blobTtlSecs, ThrowingConsumer<ExecutionException> errorChecker) throws Exception {
JSONObject headers = new JSONObject();
FrontendRestRequestServiceTest.setAmbryHeadersForPut(headers, blobTtlSecs, !REF_CONTAINER.isCacheable(), SERVICE_ID, CONTENT_TYPE, OWNER_ID, REF_ACCOUNT.getName(), REF_CONTAINER.getName(), null);
if (chunkUpload) {
headers.put(RestUtils.Headers.CHUNK_UPLOAD, true);
}
if (uploadSession != null) {
headers.put(RestUtils.Headers.SESSION, uploadSession);
}
if (maxUploadSize != null) {
headers.put(RestUtils.Headers.MAX_UPLOAD_SIZE, maxUploadSize);
}
byte[] content = TestUtils.getRandomBytes(contentLength);
RestRequest request = getRestRequest(headers, "/", content);
long creationTimeMs = System.currentTimeMillis();
time.setCurrentMilliseconds(creationTimeMs);
RestResponseChannel restResponseChannel = new MockRestResponseChannel();
FutureResult<Void> future = new FutureResult<>();
idConverterFactory.lastInput = null;
postBlobHandler.handle(request, restResponseChannel, future::done);
if (errorChecker == null) {
future.get(TIMEOUT_SECS, TimeUnit.SECONDS);
assertEquals("Unexpected converted ID", CONVERTED_ID, restResponseChannel.getHeader(RestUtils.Headers.LOCATION));
Object metadata = request.getArgs().get(RestUtils.InternalKeys.SIGNED_ID_METADATA_KEY);
if (chunkUpload) {
Map<String, String> expectedMetadata = new HashMap<>(3);
expectedMetadata.put(RestUtils.Headers.BLOB_SIZE, Integer.toString(contentLength));
expectedMetadata.put(RestUtils.Headers.SESSION, uploadSession);
expectedMetadata.put(PostBlobHandler.EXPIRATION_TIME_MS_KEY, Long.toString(Utils.addSecondsToEpochTime(creationTimeMs, blobTtlSecs)));
assertEquals("Unexpected signed ID metadata", expectedMetadata, metadata);
} else {
assertNull("Signed id metadata should not be set on non-chunk uploads", metadata);
}
InMemoryRouter.InMemoryBlob blob = router.getActiveBlobs().get(idConverterFactory.lastInput);
assertEquals("Unexpected blob content stored", ByteBuffer.wrap(content), blob.getBlob());
assertEquals("Unexpected ttl stored", blobTtlSecs, blob.getBlobProperties().getTimeToLiveInSeconds());
// check that blob size matches the actual upload size
assertEquals("Invalid blob size", Integer.toString(contentLength), restResponseChannel.getHeader(RestUtils.Headers.BLOB_SIZE));
} else {
TestUtils.assertException(ExecutionException.class, () -> future.get(TIMEOUT_SECS, TimeUnit.SECONDS), errorChecker);
}
}
use of com.github.ambry.rest.RestResponseChannel in project ambry by linkedin.
the class PostBlobHandlerTest method stitchBlobAndVerify.
/**
* Make a stitch blob call using {@link PostBlobHandler} and verify the result of the operation.
* @param requestBody the body of the stitch request to supply.
* @param expectedStitchedChunks the expected chunks stitched together.
* @param errorChecker if non-null, expect an exception to be thrown by the post flow and verify it using this
* {@link ThrowingConsumer}.
* @throws Exception
*/
private void stitchBlobAndVerify(byte[] requestBody, List<ChunkInfo> expectedStitchedChunks, ThrowingConsumer<ExecutionException> errorChecker) throws Exception {
JSONObject headers = new JSONObject();
FrontendRestRequestServiceTest.setAmbryHeadersForPut(headers, TestUtils.TTL_SECS, !REF_CONTAINER.isCacheable(), SERVICE_ID, CONTENT_TYPE, OWNER_ID, REF_ACCOUNT.getName(), REF_CONTAINER.getName(), null);
RestRequest request = getRestRequest(headers, "/" + Operations.STITCH, requestBody);
RestResponseChannel restResponseChannel = new MockRestResponseChannel();
FutureResult<Void> future = new FutureResult<>();
idConverterFactory.lastInput = null;
postBlobHandler.handle(request, restResponseChannel, future::done);
if (errorChecker == null) {
future.get(TIMEOUT_SECS, TimeUnit.SECONDS);
assertEquals("Unexpected converted ID", CONVERTED_ID, restResponseChannel.getHeader(RestUtils.Headers.LOCATION));
InMemoryRouter.InMemoryBlob blob = router.getActiveBlobs().get(idConverterFactory.lastInput);
assertEquals("List of chunks stitched does not match expected", expectedStitchedChunks, blob.getStitchedChunks());
ByteArrayOutputStream expectedContent = new ByteArrayOutputStream();
expectedStitchedChunks.stream().map(chunkInfo -> router.getActiveBlobs().get(chunkInfo.getBlobId()).getBlob().array()).forEach(buf -> expectedContent.write(buf, 0, buf.length));
assertEquals("Unexpected blob content stored", ByteBuffer.wrap(expectedContent.toByteArray()), blob.getBlob());
// check actual size of stitched blob
assertEquals("Unexpected blob size", Long.toString(getStitchedBlobSize(expectedStitchedChunks)), restResponseChannel.getHeader(RestUtils.Headers.BLOB_SIZE));
} else {
TestUtils.assertException(ExecutionException.class, () -> future.get(TIMEOUT_SECS, TimeUnit.SECONDS), errorChecker);
}
}
use of com.github.ambry.rest.RestResponseChannel in project ambry by linkedin.
the class FrontendTestUrlSigningServiceFactory method doRuntimeExceptionRouterTest.
// runtimeExceptionRouterTest() helpers
/**
* Tests reactions of various methods of {@link FrontendRestRequestService} to a {@link Router} that throws
* {@link RuntimeException}.
* @param restMethod used to determine the method to invoke in {@link FrontendRestRequestService}.
* @throws Exception
*/
private void doRuntimeExceptionRouterTest(RestMethod restMethod) throws Exception {
RestRequest restRequest = createRestRequest(restMethod, referenceBlobIdStr, null, null);
RestResponseChannel restResponseChannel = new MockRestResponseChannel();
try {
switch(restMethod) {
case GET:
case DELETE:
case HEAD:
doOperation(restRequest, restResponseChannel);
fail(restMethod + " should have detected a RestServiceException because of a bad router");
break;
case POST:
JSONObject headers = new JSONObject();
setAmbryHeadersForPut(headers, Utils.Infinite_Time, !refContainer.isCacheable(), "test-serviceID", "text/plain", "test-ownerId", refAccount.getName(), refContainer.getName(), null);
restRequest = createRestRequest(restMethod, "/", headers, null);
doOperation(restRequest, restResponseChannel);
fail("POST should have detected a RestServiceException because of a bad router");
break;
default:
throw new IllegalArgumentException("Unrecognized RestMethod: " + restMethod);
}
} catch (RuntimeException e) {
assertEquals("Unexpected error message", InMemoryRouter.OPERATION_THROW_EARLY_RUNTIME_EXCEPTION, e.getMessage());
}
}
Aggregations