use of com.github.ambry.rest.RequestPath in project ambry by linkedin.
the class CostPolicyTestUtils method createMockRequestWithMethod.
/**
* Creates a mock {@link RestRequest} object for test.
* @param restMethod {@link RestMethod} of the RestRequest.
* @param bytesReceived number of bytes received in the request.
* @return RestRequest object.
*/
public static RestRequest createMockRequestWithMethod(RestMethod restMethod, String uri, long bytesReceived) throws RestServiceException {
RestRequest restRequest = mock(RestRequest.class);
when(restRequest.getRestMethod()).thenReturn(restMethod);
when(restRequest.getBlobBytesReceived()).thenReturn(bytesReceived);
when(restRequest.getBytesReceived()).thenReturn(bytesReceived);
if (restMethod == RestMethod.POST) {
when(restRequest.getUri()).thenReturn("/");
when(restRequest.getPath()).thenReturn("/");
} else {
when(restRequest.getUri()).thenReturn(uri);
when(restRequest.getPath()).thenReturn(uri);
}
RequestPath requestPath = RequestPath.parse(restRequest, null, "ambry-test");
if (restMethod == RestMethod.PUT && bytesReceived != -1) {
requestPath = RequestPath.parse("/" + Operations.NAMED_BLOB, Collections.emptyMap(), Collections.emptyList(), "ambry-test");
}
Map<String, Object> args = new HashMap<>();
args.put(RestUtils.InternalKeys.REQUEST_PATH, requestPath);
when(restRequest.getArgs()).thenReturn(args);
return restRequest;
}
use of com.github.ambry.rest.RequestPath 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.RequestPath in project ambry by linkedin.
the class SimpleAmbryCostModelPolicy method calculateCapacityUnitCost.
/**
* Calculate the cost incurred in terms of capacity unit to serve a request.
* For post and get requests it is the number of CU_COST_UNITs determined by blob size.
* For all other requests, the default cost is DEFAULT_COST_FOR_HEAD_DELETE_TTL.
* @param restRequest {@link RestRequest} to find type of request.
* @param restResponseChannel {@link RestResponseChannel} object.
* @param blobInfo {@link BlobInfo} object representing the blob being served.
* @return cost in terms of capacity units.
*/
private double calculateCapacityUnitCost(RestRequest restRequest, RestResponseChannel restResponseChannel, BlobInfo blobInfo) {
RequestPath requestPath = getRequestPath(restRequest);
switch(restRequest.getRestMethod()) {
case POST:
long contentSize;
if (requestPath.matchesOperation(Operations.STITCH)) {
contentSize = Long.parseLong((String) restResponseChannel.getHeader(RestUtils.Headers.BLOB_SIZE));
} else {
contentSize = restRequest.getBytesReceived();
}
double cost = Math.ceil(contentSize / CU_COST_UNIT);
return Math.max(cost, MIN_CU_COST);
case GET:
SubResource subResource = requestPath.getSubResource();
if (requestPath.matchesOperation(Operations.GET_SIGNED_URL)) {
return INDEX_ONLY_COST;
} else if (subResource == SubResource.BlobInfo || subResource == SubResource.UserMetadata) {
return INDEX_ONLY_COST;
} else {
long size;
if (blobInfo == null) {
// Maybe its a GET for a deleted blob
return MIN_CU_COST;
}
if (restRequest.getArgs().containsKey(RestUtils.Headers.RANGE)) {
// Range request case
try {
size = RestUtils.buildByteRange(restRequest.getArgs().get(RestUtils.Headers.RANGE).toString()).toResolvedByteRange(blobInfo.getBlobProperties().getBlobSize(), true).getRangeSize();
} catch (RestServiceException rsEx) {
// this should never happen.
logger.error("Exception while calculation CU cost for range request", rsEx);
// this will default to one chunk
size = 0;
}
} else {
// regular GET blob
size = blobInfo.getBlobProperties().getBlobSize();
}
cost = Math.ceil(size / CU_COST_UNIT);
return Math.max(cost, MIN_CU_COST);
}
default:
// Assuming 1 unit of capacity unit cost for all requests that don't fetch or store a blob's data.
return INDEX_ONLY_COST;
}
}
use of com.github.ambry.rest.RequestPath in project ambry by linkedin.
the class AmbrySecurityService method processResponse.
@Override
public void processResponse(RestRequest restRequest, RestResponseChannel responseChannel, BlobInfo blobInfo, Callback<Void> callback) {
Exception exception = null;
frontendMetrics.securityServiceProcessResponseRate.mark();
long startTimeMs = System.currentTimeMillis();
if (!isOpen) {
exception = new RestServiceException("SecurityService is closed", RestServiceErrorCode.ServiceUnavailable);
} else {
if (restRequest == null || responseChannel == null) {
throw new IllegalArgumentException("One of the required params is null");
}
RequestPath requestPath = RestUtils.getRequestPath(restRequest);
RestMethod restMethod = restRequest.getRestMethod();
if (blobInfo == null && !restMethod.equals(RestMethod.OPTIONS) && !restMethod.equals(RestMethod.PUT)) {
if (!requestPath.matchesOperation(Operations.GET_SIGNED_URL)) {
throw new IllegalArgumentException("BlobInfo is null");
}
}
try {
GetBlobOptions options;
responseChannel.setHeader(RestUtils.Headers.DATE, new GregorianCalendar().getTime());
switch(restMethod) {
case HEAD:
options = RestUtils.buildGetBlobOptions(restRequest.getArgs(), null, GetOption.None, restRequest, NO_BLOB_SEGMENT_IDX_SPECIFIED);
responseChannel.setStatus(options.getRange() == null ? ResponseStatus.Ok : ResponseStatus.PartialContent);
responseChannel.setHeader(RestUtils.Headers.LAST_MODIFIED, new Date(blobInfo.getBlobProperties().getCreationTimeInMs()));
setHeadResponseHeaders(blobInfo, options, restRequest, responseChannel);
break;
case GET:
if (!requestPath.matchesOperation(Operations.GET_SIGNED_URL)) {
options = RestUtils.buildGetBlobOptions(restRequest.getArgs(), null, GetOption.None, restRequest, NO_BLOB_SEGMENT_IDX_SPECIFIED);
responseChannel.setStatus(ResponseStatus.Ok);
RestUtils.SubResource subResource = RestUtils.getRequestPath(restRequest).getSubResource();
responseChannel.setHeader(RestUtils.Headers.LAST_MODIFIED, new Date(blobInfo.getBlobProperties().getCreationTimeInMs()));
if (subResource == null) {
Long ifModifiedSinceMs = getIfModifiedSinceMs(restRequest);
if (ifModifiedSinceMs != null && RestUtils.toSecondsPrecisionInMs(blobInfo.getBlobProperties().getCreationTimeInMs()) <= ifModifiedSinceMs) {
responseChannel.setStatus(ResponseStatus.NotModified);
} else {
if (options.getRange() != null) {
responseChannel.setStatus(ResponseStatus.PartialContent);
}
setGetBlobResponseHeaders(blobInfo, options, responseChannel);
setBlobPropertiesHeaders(blobInfo.getBlobProperties(), responseChannel);
setAccountAndContainerHeaders(restRequest, responseChannel);
setUserMetadataHeaders(blobInfo.getUserMetadata(), responseChannel);
responseChannel.setHeader(RestUtils.Headers.LIFE_VERSION, blobInfo.getLifeVersion());
}
setCacheHeaders(restRequest, responseChannel);
} else {
if (subResource.equals(RestUtils.SubResource.BlobInfo)) {
setBlobInfoHeaders(blobInfo.getBlobProperties(), responseChannel);
setBlobPropertiesHeaders(blobInfo.getBlobProperties(), responseChannel);
setAccountAndContainerHeaders(restRequest, responseChannel);
responseChannel.setHeader(RestUtils.Headers.LIFE_VERSION, blobInfo.getLifeVersion());
} else if (subResource.equals(SubResource.Segment)) {
setGetBlobResponseHeaders(blobInfo, options, responseChannel);
setBlobPropertiesHeaders(blobInfo.getBlobProperties(), responseChannel);
setAccountAndContainerHeaders(restRequest, responseChannel);
}
if (!setUserMetadataHeaders(blobInfo.getUserMetadata(), responseChannel)) {
restRequest.setArg(InternalKeys.SEND_USER_METADATA_AS_RESPONSE_BODY, true);
}
}
}
break;
case POST:
responseChannel.setStatus(ResponseStatus.Created);
responseChannel.setHeader(RestUtils.Headers.CONTENT_LENGTH, 0);
responseChannel.setHeader(RestUtils.Headers.CREATION_TIME, new Date(blobInfo.getBlobProperties().getCreationTimeInMs()));
break;
case OPTIONS:
case PUT:
if (requestPath.matchesOperation(Operations.NAMED_BLOB)) {
responseChannel.setStatus(ResponseStatus.Created);
responseChannel.setHeader(RestUtils.Headers.CONTENT_LENGTH, 0);
responseChannel.setHeader(RestUtils.Headers.CREATION_TIME, new Date(blobInfo.getBlobProperties().getCreationTimeInMs()));
}
break;
default:
exception = new RestServiceException("Cannot process response for request with method " + restMethod, RestServiceErrorCode.InternalServerError);
}
} catch (RestServiceException e) {
exception = e;
}
}
processRequestCharges(restRequest, responseChannel, blobInfo);
frontendMetrics.securityServiceProcessResponseTimeInMs.update(System.currentTimeMillis() - startTimeMs);
callback.onCompletion(null, exception);
}
use of com.github.ambry.rest.RequestPath in project ambry by linkedin.
the class AmbrySecurityService method postProcessRequest.
@Override
public void postProcessRequest(RestRequest restRequest, Callback<Void> callback) {
if (restRequest == null || callback == null) {
throw new IllegalArgumentException("RestRequest or Callback is null");
}
Exception exception = null;
frontendMetrics.securityServicePostProcessRequestRate.mark();
long startTimeMs = System.currentTimeMillis();
try {
if (!isOpen) {
exception = new RestServiceException("SecurityService is closed", RestServiceErrorCode.ServiceUnavailable);
} else if (hostLevelThrottler.shouldThrottle(restRequest)) {
exception = new RestServiceException("Too many requests", RestServiceErrorCode.TooManyRequests);
} else {
if (QuotaUtils.isRequestResourceQuotaManaged(restRequest) && quotaManager != null) {
ThrottlingRecommendation throttlingRecommendation = quotaManager.recommend(restRequest);
if (throttlingRecommendation != null && throttlingRecommendation.shouldThrottle() && quotaManager.getQuotaMode() == QuotaMode.THROTTLING) {
Map<String, String> quotaHeaderMap = RestUtils.buildUserQuotaHeadersMap(throttlingRecommendation);
throw new RestServiceException("User Quota Exceeded", RestServiceErrorCode.TooManyRequests, true, true, quotaHeaderMap);
}
}
if (restRequest.getRestMethod() == RestMethod.DELETE || restRequest.getRestMethod() == RestMethod.PUT) {
accountAndContainerNamePreconditionCheck(restRequest);
} else if (restRequest.getRestMethod() == RestMethod.GET) {
RequestPath requestPath = getRequestPath(restRequest);
String operationOrBlobId = requestPath.getOperationOrBlobId(true);
// ensure that secure path validation is only performed when getting blobs rather than other operations.
if (!operationOrBlobId.isEmpty() && !OPERATIONS.contains(operationOrBlobId)) {
validateSecurePathIfRequired(restRequest, requestPath.getPrefix(), frontendConfig.securePathPrefix);
}
}
}
} catch (Exception e) {
exception = e;
} finally {
frontendMetrics.securityServicePostProcessRequestTimeInMs.update(System.currentTimeMillis() - startTimeMs);
callback.onCompletion(null, exception);
}
}
Aggregations