use of org.wso2.carbon.metrics.manager.Timer in project carbon-apimgt by wso2.
the class ThrottleHandlerTest method testHandleRequestForGraphQLSubscriptions.
/**
* This method will test request flow when "isGraphqlSubscriptionRequest" property is set in axis2 message context
* when incoming transport is websocket. This occurs during Graphql Subscription request flow.
*/
@Test
public void testHandleRequestForGraphQLSubscriptions() {
ThrottleHandler throttleHandler = new ThrottlingHandlerWrapper(timer, new ThrottleDataHolder(), throttleEvaluator, accessInformation);
Axis2MessageContext messageContext = Mockito.mock(Axis2MessageContext.class);
org.apache.axis2.context.MessageContext axis2MessageContext = Mockito.mock(org.apache.axis2.context.MessageContext.class);
Mockito.when(messageContext.getAxis2MessageContext()).thenReturn(axis2MessageContext);
Mockito.when(axis2MessageContext.getIncomingTransportName()).thenReturn("ws");
Mockito.when(messageContext.getProperty(APIConstants.GRAPHQL_SUBSCRIPTION_REQUEST)).thenReturn(true);
Assert.assertTrue(throttleHandler.handleRequest(messageContext));
Mockito.when(axis2MessageContext.getIncomingTransportName()).thenReturn("wss");
Assert.assertTrue(throttleHandler.handleRequest(messageContext));
// clean up message context
Mockito.when(messageContext.getProperty(APIConstants.GRAPHQL_SUBSCRIPTION_REQUEST)).thenReturn(false);
Mockito.when(axis2MessageContext.getIncomingTransportName()).thenReturn("http");
}
use of org.wso2.carbon.metrics.manager.Timer in project carbon-apimgt by wso2.
the class ThrottleHandlerTest method testMsgThrottleOutWhenProductionHardThrottlingLimitsThrottled.
@Test
public void testMsgThrottleOutWhenProductionHardThrottlingLimitsThrottled() {
ThrottleDataHolder throttleDataHolder = new ThrottleDataHolder();
ThrottleHandler throttleHandler = new ThrottlingHandlerWrapper(timer, throttleDataHolder, throttleEvaluator, accessInformation);
throttleHandler.setProductionMaxCount("100");
SynapseEnvironment synapseEnvironment = Mockito.mock(SynapseEnvironment.class);
throttleHandler.init(synapseEnvironment);
MessageContext messageContext = TestUtils.getMessageContextWithAuthContext(apiContext, apiVersion);
messageContext.setProperty(VERB_INFO_DTO, verbInfoDTO);
((Axis2MessageContext) messageContext).getAxis2MessageContext().getProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS);
AuthenticationContext authenticationContext = (AuthenticationContext) messageContext.getProperty(API_AUTH_CONTEXT);
authenticationContext.setApiTier(throttlingTier);
authenticationContext.setStopOnQuotaReach(false);
authenticationContext.setKeyType("PRODUCTION");
authenticationContext.setSpikeArrestLimit(0);
messageContext.setProperty(API_AUTH_CONTEXT, authenticationContext);
verbInfo.setConditionGroups(conditionGroupDTOs);
ArrayList<ConditionGroupDTO> matchingConditions = new ArrayList<>();
matchingConditions.add(conditionGroupDTO);
Mockito.when(accessInformation.isAccessAllowed()).thenReturn(false);
// Should discontinue message flow if PRODUCTION hard throttling limits are exceeded
Assert.assertFalse(throttleHandler.handleRequest(messageContext));
}
use of org.wso2.carbon.metrics.manager.Timer in project carbon-apimgt by wso2.
the class ThrottleHandlerTest method testMsgThrottleContinueWhenAPITierIsNotAvailable.
@Test
public void testMsgThrottleContinueWhenAPITierIsNotAvailable() {
ThrottleDataHolder throttleDataHolder = new ThrottleDataHolder();
ThrottleHandler throttleHandler = new ThrottlingHandlerWrapper(timer, throttleDataHolder, throttleEvaluator);
MessageContext messageContext = TestUtils.getMessageContextWithAuthContext(apiContext, apiVersion);
((Axis2MessageContext) messageContext).getAxis2MessageContext().getProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS);
// Make sure that the tier info is not available in the message context
Assert.assertNull((VerbInfoDTO) messageContext.getProperty(VERB_INFO_DTO));
// Should continue the message flow if the message context does not have throttling tier information
Assert.assertTrue(throttleHandler.handleRequest(messageContext));
}
use of org.wso2.carbon.metrics.manager.Timer in project carbon-apimgt by wso2.
the class ThrottleHandler method doRoleBasedAccessThrottlingWithCEP.
/**
* This method is responsible for throttle incoming messages. This method will perform Application, Subscription
* and Resource level throttling.
*
* @param synCtx Synapse message context that contains message details.
* @param cc Configuration context which holds current configuration context.
* @return
*/
private boolean doRoleBasedAccessThrottlingWithCEP(MessageContext synCtx, ConfigurationContext cc, AuthenticationContext authenticationContext) {
// Throttle Keys
// applicationLevelThrottleKey key is combination of {applicationId}:{authorizedUser}
String applicationLevelThrottleKey;
// subscriptionLevelThrottleKey key for an api subscription is combination of {applicationId}:{apiContext}:{apiVersion}
// subscriptionLevelThrottleKey key for an api subscription is combination of {applicationId}:{productName}:{productProvider}
// Todo: add product version to key when versioning is supported
String subscriptionLevelThrottleKey;
// The key is combination of {apiContext}/ {apiVersion}{resourceUri}:{httpMethod} if policy is user level then authorized user will append at end
String resourceLevelThrottleKey = "";
// apiLevelThrottleKey key is combination of {apiContext}:{apiVersion}
String apiLevelThrottleKey;
// Throttle Tiers
String applicationLevelTier;
String subscriptionLevelTier;
String resourceLevelTier = "";
String apiLevelTier;
// Other Relevant parameters
AuthenticationContext authContext = authenticationContext;
String authorizedUser;
// Throttled decisions
boolean isThrottled = false;
boolean isResourceLevelThrottled = false;
boolean isOperationLevelThrottled = false;
boolean isApplicationLevelThrottled;
boolean isSubscriptionLevelThrottled;
boolean isSubscriptionLevelSpikeThrottled = false;
boolean isApiLevelThrottled = false;
boolean isBlockedRequest = false;
boolean apiLevelThrottledTriggered = false;
boolean policyLevelUserTriggered = false;
String ipLevelBlockingKey;
String appLevelBlockingKey = "";
String subscriptionLevelBlockingKey = "";
boolean stopOnQuotaReach = true;
String apiContext = (String) synCtx.getProperty(RESTConstants.REST_API_CONTEXT);
String apiVersion = (String) synCtx.getProperty(RESTConstants.SYNAPSE_REST_API_VERSION);
apiContext = apiContext != null ? apiContext : "";
apiVersion = apiVersion != null ? apiVersion : "";
String clientIp = GatewayUtils.getClientIp(synCtx);
String subscriberTenantDomain = "";
String apiTenantDomain = getTenantDomain();
ConditionGroupDTO[] conditionGroupDTOs;
String applicationId = authContext.getApplicationId();
// If Authz context is not null only we can proceed with throttling
if (authContext != null) {
authorizedUser = authContext.getUsername();
// Check if the tenant domain is appended with authorizedUser and append if it is not there
if (!StringUtils.contains(authorizedUser, apiTenantDomain)) {
authorizedUser = authContext.getUsername() + "@" + apiTenantDomain;
}
// Do blocking if there are blocking conditions present
if (getThrottleDataHolder().isBlockingConditionsPresent()) {
appLevelBlockingKey = authContext.getSubscriber() + ":" + authContext.getApplicationName();
subscriptionLevelBlockingKey = apiContext + ":" + apiVersion + ":" + authContext.getSubscriber() + "-" + authContext.getApplicationName() + ":" + authContext.getKeyType();
Timer timer = getTimer(MetricManager.name(APIConstants.METRICS_PREFIX, this.getClass().getSimpleName(), BLOCKED_TEST));
Timer.Context context = timer.start();
isBlockedRequest = getThrottleDataHolder().isRequestBlocked(apiContext, appLevelBlockingKey, authorizedUser, clientIp, apiTenantDomain, subscriptionLevelBlockingKey);
context.stop();
}
if (isBlockedRequest) {
String msg = "Request blocked as it violates defined blocking conditions, for API: " + apiContext + " ,application:" + appLevelBlockingKey + " ,user:" + authorizedUser;
if (log.isDebugEnabled()) {
log.debug(msg);
}
synCtx.setProperty(APIThrottleConstants.BLOCKED_REASON, msg);
synCtx.setProperty(APIThrottleConstants.THROTTLED_OUT_REASON, APIThrottleConstants.REQUEST_BLOCKED);
isThrottled = true;
} else {
subscriberTenantDomain = authContext.getSubscriberTenantDomain();
applicationLevelThrottleKey = applicationId + ":" + authorizedUser;
apiLevelThrottleKey = apiContext + ":" + apiVersion;
stopOnQuotaReach = authContext.isStopOnQuotaReach();
applicationLevelTier = authContext.getApplicationTier();
subscriptionLevelTier = authContext.getTier();
apiLevelTier = authContext.getApiTier();
VerbInfoDTO throttledResource = new VerbInfoDTO();
// If request is not blocked then only we perform throttling.
List<VerbInfoDTO> verbInfoDTOList = (List<VerbInfoDTO>) synCtx.getProperty(APIConstants.VERB_INFO_DTO);
// If API level tier is not present only we should move to resource level tiers.
if (verbInfoDTOList == null) {
log.warn("Error while getting throttling information for resource and http verb");
return false;
}
for (VerbInfoDTO verbInfoDTO : verbInfoDTOList) {
boolean isUnlimittedTier = false;
resourceLevelThrottleKey = verbInfoDTO.getRequestKey();
resourceLevelTier = verbInfoDTO.getThrottling();
if (APIConstants.UNLIMITED_TIER.equalsIgnoreCase(resourceLevelTier)) {
isUnlimittedTier = true;
}
// If API level throttle policy is present then it will apply and no resource level policy will apply for it
if (!StringUtils.isEmpty(apiLevelTier) && !APIConstants.UNLIMITED_TIER.equalsIgnoreCase(apiLevelTier)) {
resourceLevelThrottleKey = apiLevelThrottleKey;
apiLevelThrottledTriggered = true;
}
// If verbInfo is present then only we will do resource level throttling
if (isUnlimittedTier && !apiLevelThrottledTriggered) {
// If unlimited tier throttling will not apply at resource level and pass it
if (log.isDebugEnabled()) {
log.debug("Resource level throttling set as unlimited and request will pass " + "resource level");
}
} else {
if (APIConstants.API_POLICY_USER_LEVEL.equalsIgnoreCase(verbInfoDTO.getApplicableLevel())) {
resourceLevelThrottleKey = resourceLevelThrottleKey + "_" + authorizedUser;
policyLevelUserTriggered = true;
}
// If tier is not unlimited only throttling will apply.
List<String> resourceLevelThrottleConditions = new ArrayList<>();
resourceLevelThrottleConditions = verbInfoDTO.getThrottlingConditions();
conditionGroupDTOs = verbInfoDTO.getConditionGroups();
Timer timer1 = getTimer(MetricManager.name(APIConstants.METRICS_PREFIX, this.getClass().getSimpleName(), RESOURCE_THROTTLE));
Timer.Context context1 = timer1.start();
if (getThrottleDataHolder().isAPIThrottled(resourceLevelThrottleKey)) {
if (getThrottleDataHolder().isConditionsAvailable(resourceLevelThrottleKey)) {
Map<String, List<ConditionDto>> conditionDtoMap = getThrottleDataHolder().getConditionDtoMap(resourceLevelThrottleKey);
if (log.isDebugEnabled()) {
log.debug("Conditions available" + conditionDtoMap.size());
}
String throttledCondition = getThrottleConditionEvaluator().getThrottledInCondition(synCtx, authContext, conditionDtoMap);
if (StringUtils.isNotEmpty(throttledCondition)) {
if (log.isDebugEnabled()) {
log.debug("Throttled with Condition :" + throttledCondition);
}
String combinedResourceLevelThrottleKey = resourceLevelThrottleKey + "_" + throttledCondition;
if (log.isDebugEnabled()) {
log.debug("Checking condition : " + combinedResourceLevelThrottleKey);
}
if (getThrottleDataHolder().isThrottled(combinedResourceLevelThrottleKey)) {
if (!apiLevelThrottledTriggered) {
isResourceLevelThrottled = isThrottled = true;
} else {
isApiLevelThrottled = isThrottled = true;
}
long timestamp = getThrottleDataHolder().getThrottleNextAccessTimestamp(combinedResourceLevelThrottleKey);
synCtx.setProperty(APIThrottleConstants.THROTTLED_NEXT_ACCESS_TIMESTAMP, timestamp);
}
}
} else {
if (conditionGroupDTOs != null && conditionGroupDTOs.length > 0) {
if (log.isDebugEnabled()) {
log.debug("Evaluating Conditional Groups for " + apiLevelThrottleKey);
}
// Then we will apply resource level throttling
List<ConditionGroupDTO> applicableConditions = getThrottleConditionEvaluator().getApplicableConditions(synCtx, authContext, conditionGroupDTOs);
for (ConditionGroupDTO conditionGroup : applicableConditions) {
String combinedResourceLevelThrottleKey = resourceLevelThrottleKey + conditionGroup.getConditionGroupId();
if (log.isDebugEnabled()) {
log.debug("Checking condition : " + combinedResourceLevelThrottleKey);
}
if (getThrottleDataHolder().isThrottled(combinedResourceLevelThrottleKey)) {
if (!apiLevelThrottledTriggered) {
isResourceLevelThrottled = isThrottled = true;
} else {
isApiLevelThrottled = isThrottled = true;
}
long timestamp = getThrottleDataHolder().getThrottleNextAccessTimestamp(combinedResourceLevelThrottleKey);
synCtx.setProperty(APIThrottleConstants.THROTTLED_NEXT_ACCESS_TIMESTAMP, timestamp);
break;
}
}
} else {
log.warn("Unable to find throttling information for resource and http verb. Throttling " + "will not be applied");
}
}
}
context1.stop();
if (isThrottled) {
throttledResource = verbInfoDTO;
break;
}
}
}
if (!isApiLevelThrottled) {
Timer timer2 = getTimer(MetricManager.name(APIConstants.METRICS_PREFIX, this.getClass().getSimpleName(), RESOURCE_THROTTLE));
Timer.Context context2 = timer2.start();
// if resource level not throttled then move to subscription level
if (!isResourceLevelThrottled) {
// Subscription Level Throttling
subscriptionLevelThrottleKey = getSubscriptionLevelThrottleKey(subscriptionLevelTier, authContext, apiContext, apiVersion);
isSubscriptionLevelThrottled = getThrottleDataHolder().isThrottled(subscriptionLevelThrottleKey);
if (!isSubscriptionLevelThrottled && authContext.getSpikeArrestLimit() > 0) {
isSubscriptionLevelSpikeThrottled = isSubscriptionLevelSpike(synCtx, subscriptionLevelThrottleKey);
}
// Stop on quata reach
if (!isSubscriptionLevelThrottled && !isSubscriptionLevelSpikeThrottled) {
// Application Level Throttling
isApplicationLevelThrottled = getThrottleDataHolder().isThrottled(applicationLevelThrottleKey);
// if application level not throttled means it does not throttled at any level.
if (!isApplicationLevelThrottled) {
for (VerbInfoDTO verbInfo : verbInfoDTOList) {
resourceLevelThrottleKey = verbInfo.getRequestKey();
resourceLevelTier = verbInfo.getThrottling();
boolean keyTemplatesAvailable = getThrottleDataHolder().isKeyTemplatesPresent();
if (!keyTemplatesAvailable || !validateCustomPolicy(authorizedUser, applicationLevelThrottleKey, resourceLevelThrottleKey, apiLevelThrottleKey, subscriptionLevelThrottleKey, apiContext, apiVersion, subscriberTenantDomain, apiTenantDomain, applicationId, clientIp, getThrottleDataHolder().getKeyTemplateMap(), synCtx)) {
// publish event to Global Policy Server
if (isHardLimitThrottled(synCtx, authContext, apiContext, apiVersion)) {
isThrottled = true;
} else {
ServiceReferenceHolder.getInstance().getThrottleDataPublisher().publishNonThrottledEvent(applicationLevelThrottleKey, applicationLevelTier, apiLevelThrottleKey, apiLevelTier, subscriptionLevelThrottleKey, subscriptionLevelTier, resourceLevelThrottleKey, resourceLevelTier, authorizedUser, apiContext, apiVersion, subscriberTenantDomain, apiTenantDomain, applicationId, synCtx, authContext);
}
} else {
log.debug("Request throttled at custom throttling");
synCtx.setProperty(APIThrottleConstants.THROTTLED_OUT_REASON, APIThrottleConstants.CUSTOM_POLICY_LIMIT_EXCEED);
isThrottled = true;
}
}
} else {
if (log.isDebugEnabled()) {
log.debug("Request throttled at application level for throttle key" + applicationLevelThrottleKey);
}
synCtx.setProperty(APIThrottleConstants.THROTTLED_OUT_REASON, APIThrottleConstants.APPLICATION_LIMIT_EXCEEDED);
long timestamp = getThrottleDataHolder().getThrottleNextAccessTimestamp(applicationLevelThrottleKey);
synCtx.setProperty(APIThrottleConstants.THROTTLED_NEXT_ACCESS_TIMESTAMP, timestamp);
isThrottled = isApplicationLevelThrottled = true;
}
} else {
if (!stopOnQuotaReach) {
// limit has reached.
if (synCtx.getProperty(APIConstants.API_USAGE_THROTTLE_OUT_PROPERTY_KEY) == null) {
synCtx.setProperty(APIConstants.API_USAGE_THROTTLE_OUT_PROPERTY_KEY, Boolean.TRUE);
}
isThrottled = false;
if (log.isDebugEnabled()) {
log.debug("Request throttled at subscription level for throttle key" + subscriptionLevelThrottleKey + ". But subscription policy " + subscriptionLevelTier + " allows to continue to serve requests");
}
} else {
if (log.isDebugEnabled()) {
log.debug("Request throttled at subscription level for throttle key" + subscriptionLevelThrottleKey);
}
if (!isSubscriptionLevelSpikeThrottled) {
long timestamp = getThrottleDataHolder().getThrottleNextAccessTimestamp(subscriptionLevelThrottleKey);
synCtx.setProperty(APIThrottleConstants.THROTTLED_NEXT_ACCESS_TIMESTAMP, timestamp);
synCtx.setProperty(APIThrottleConstants.THROTTLED_OUT_REASON, APIThrottleConstants.API_LIMIT_EXCEEDED);
synCtx.setProperty(APIThrottleConstants.THROTTLED_OUT_REASON, APIThrottleConstants.SUBSCRIPTION_LIMIT_EXCEEDED);
}
isThrottled = true;
}
}
} else {
if (log.isDebugEnabled()) {
log.debug("Request throttled at resource level for throttle key" + throttledResource.getRequestKey());
}
// is throttled and resource level throttling
synCtx.setProperty(APIThrottleConstants.THROTTLED_OUT_REASON, APIThrottleConstants.RESOURCE_LIMIT_EXCEEDED);
}
context2.stop();
} else {
if (log.isDebugEnabled()) {
log.debug("Request throttled at api level for throttle key" + apiLevelThrottleKey);
if (policyLevelUserTriggered) {
log.debug("Request has throttled out in the user level for the throttle key" + apiLevelThrottleKey);
}
}
synCtx.setProperty(APIThrottleConstants.THROTTLED_OUT_REASON, APIThrottleConstants.API_LIMIT_EXCEEDED);
}
}
}
// if we need to publish throttled level or some other information we can do it here. Just before return.
return isThrottled;
}
use of org.wso2.carbon.metrics.manager.Timer in project carbon-apimgt by wso2.
the class ThrottleHandler method handleRequest.
/**
* Handle incoming requests and call throttling method to perform throttling.
*
* @param messageContext message context object which contains message details.
* @return return true if message flow need to continue and pass requests to next handler in chain. Else return
* false to notify error with handler
*/
public boolean handleRequest(MessageContext messageContext) {
if (Utils.isGraphQLSubscriptionRequest(messageContext)) {
if (log.isDebugEnabled()) {
log.debug("Skipping GraphQL subscription handshake request.");
}
return true;
}
if (ServiceReferenceHolder.getInstance().getThrottleDataPublisher() == null) {
log.error("Cannot publish events to traffic manager because ThrottleDataPublisher " + "has not been initialised");
return true;
}
Timer timer3 = getTimer(MetricManager.name(APIConstants.METRICS_PREFIX, this.getClass().getSimpleName(), THROTTLE_MAIN));
Timer.Context context3 = timer3.start();
TracingSpan throttleLatencySpan = null;
if (Util.tracingEnabled()) {
TracingSpan responseLatencySpan = (TracingSpan) messageContext.getProperty(APIMgtGatewayConstants.RESOURCE_SPAN);
TracingTracer tracer = Util.getGlobalTracer();
throttleLatencySpan = Util.startSpan(APIMgtGatewayConstants.THROTTLE_LATENCY, responseLatencySpan, tracer);
}
long executionStartTime = System.currentTimeMillis();
if (!ExtensionListenerUtil.preProcessRequest(messageContext, type)) {
return false;
}
try {
boolean throttleResponse = doThrottle(messageContext);
if (!ExtensionListenerUtil.postProcessRequest(messageContext, type)) {
return false;
}
return throttleResponse;
} catch (Exception e) {
if (Util.tracingEnabled() && throttleLatencySpan != null) {
Util.setTag(throttleLatencySpan, APIMgtGatewayConstants.ERROR, APIMgtGatewayConstants.THROTTLE_HANDLER_ERROR);
}
throw e;
} finally {
messageContext.setProperty(APIMgtGatewayConstants.THROTTLING_LATENCY, System.currentTimeMillis() - executionStartTime);
context3.stop();
if (Util.tracingEnabled()) {
Util.finishSpan(throttleLatencySpan);
}
}
}
Aggregations