Search in sources :

Example 1 with RequestPath

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;
}
Also used : RequestPath(com.github.ambry.rest.RequestPath) RestRequest(com.github.ambry.rest.RestRequest) HashMap(java.util.HashMap)

Example 2 with RequestPath

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);
}
Also used : RequestPath(com.github.ambry.rest.RequestPath) Histogram(com.codahale.metrics.Histogram) GetOption(com.github.ambry.protocol.GetOption) FrontendConfig(com.github.ambry.config.FrontendConfig) ResponseStatus(com.github.ambry.rest.ResponseStatus) LoggerFactory(org.slf4j.LoggerFactory) ByteBufferReadableStreamChannel(com.github.ambry.commons.ByteBufferReadableStreamChannel) AccountService(com.github.ambry.account.AccountService) QuotaManager(com.github.ambry.quota.QuotaManager) ByteBuffer(java.nio.ByteBuffer) ThrowingConsumer(com.github.ambry.utils.ThrowingConsumer) RequestPath(com.github.ambry.rest.RequestPath) NamedBlobDb(com.github.ambry.named.NamedBlobDb) RestRequestService(com.github.ambry.rest.RestRequestService) RestRequestMetrics(com.github.ambry.rest.RestRequestMetrics) SystemTime(com.github.ambry.utils.SystemTime) Router(com.github.ambry.router.Router) RouterErrorCode(com.github.ambry.router.RouterErrorCode) RestResponseHandler(com.github.ambry.rest.RestResponseHandler) ReadableStreamChannel(com.github.ambry.router.ReadableStreamChannel) Logger(org.slf4j.Logger) GregorianCalendar(java.util.GregorianCalendar) RestMethod(com.github.ambry.rest.RestMethod) RestResponseChannel(com.github.ambry.rest.RestResponseChannel) RestServiceErrorCode(com.github.ambry.rest.RestServiceErrorCode) InternalKeys(com.github.ambry.rest.RestUtils.InternalKeys) ClusterMap(com.github.ambry.clustermap.ClusterMap) Utils(com.github.ambry.utils.Utils) IOException(java.io.IOException) GetBlobOptions(com.github.ambry.router.GetBlobOptions) RouterException(com.github.ambry.router.RouterException) BlobInfo(com.github.ambry.messageformat.BlobInfo) QuotaUtils(com.github.ambry.quota.QuotaUtils) AccountStatsStore(com.github.ambry.accountstats.AccountStatsStore) RestServiceException(com.github.ambry.rest.RestServiceException) GetBlobResult(com.github.ambry.router.GetBlobResult) Callback(com.github.ambry.commons.Callback) RestUtils(com.github.ambry.rest.RestUtils) AsyncOperationTracker(com.github.ambry.utils.AsyncOperationTracker) RestRequest(com.github.ambry.rest.RestRequest) GetBlobOptionsBuilder(com.github.ambry.router.GetBlobOptionsBuilder) BlobId(com.github.ambry.commons.BlobId) RestServiceException(com.github.ambry.rest.RestServiceException) RouterException(com.github.ambry.router.RouterException)

Example 3 with RequestPath

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;
    }
}
Also used : RequestPath(com.github.ambry.rest.RequestPath) RestServiceException(com.github.ambry.rest.RestServiceException)

Example 4 with RequestPath

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);
}
Also used : RestServiceException(com.github.ambry.rest.RestServiceException) RequestPath(com.github.ambry.rest.RequestPath) GetBlobOptions(com.github.ambry.router.GetBlobOptions) GregorianCalendar(java.util.GregorianCalendar) RestUtils(com.github.ambry.rest.RestUtils) QuotaException(com.github.ambry.quota.QuotaException) RestServiceException(com.github.ambry.rest.RestServiceException) Date(java.util.Date) RestMethod(com.github.ambry.rest.RestMethod)

Example 5 with RequestPath

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);
    }
}
Also used : RestServiceException(com.github.ambry.rest.RestServiceException) RequestPath(com.github.ambry.rest.RequestPath) ThrottlingRecommendation(com.github.ambry.quota.ThrottlingRecommendation) Map(java.util.Map) QuotaException(com.github.ambry.quota.QuotaException) RestServiceException(com.github.ambry.rest.RestServiceException)

Aggregations

RequestPath (com.github.ambry.rest.RequestPath)6 RestServiceException (com.github.ambry.rest.RestServiceException)5 RestMethod (com.github.ambry.rest.RestMethod)3 RestRequest (com.github.ambry.rest.RestRequest)3 RestUtils (com.github.ambry.rest.RestUtils)3 GetBlobOptions (com.github.ambry.router.GetBlobOptions)3 GregorianCalendar (java.util.GregorianCalendar)3 Histogram (com.codahale.metrics.Histogram)2 AccountService (com.github.ambry.account.AccountService)2 AccountStatsStore (com.github.ambry.accountstats.AccountStatsStore)2 ClusterMap (com.github.ambry.clustermap.ClusterMap)2 BlobId (com.github.ambry.commons.BlobId)2 ByteBufferReadableStreamChannel (com.github.ambry.commons.ByteBufferReadableStreamChannel)2 Callback (com.github.ambry.commons.Callback)2 FrontendConfig (com.github.ambry.config.FrontendConfig)2 BlobInfo (com.github.ambry.messageformat.BlobInfo)2 NamedBlobDb (com.github.ambry.named.NamedBlobDb)2 GetOption (com.github.ambry.protocol.GetOption)2 QuotaException (com.github.ambry.quota.QuotaException)2 QuotaManager (com.github.ambry.quota.QuotaManager)2