[commit] r10339 - freeswitch/trunk/scripts/contrib/mrene/mod_limit_hash
Author: mrene Date: Tue Nov 11 22:36:41 2008 New Revision: 10339 Log: Make it work with multiple limit checks on the same channel (same and different realm+id) It is also possible to rate-limit and value-limit the same realm+id. For example: <action application="limit_hash" data="test test 3 no_more_channels XML default" /> <action application="limit_hash" data="test test 3/10 too_fast XML default" /> Modified: freeswitch/trunk/scripts/contrib/mrene/mod_limit_hash/mod_limit_hash.c Modified: freeswitch/trunk/scripts/contrib/mrene/mod_limit_hash/mod_limit_hash.c ============================================================================== --- freeswitch/trunk/scripts/contrib/mrene/mod_limit_hash/mod_limit_hash.c (original) +++ freeswitch/trunk/scripts/contrib/mrene/mod_limit_hash/mod_limit_hash.c Tue Nov 11 22:36:41 2008 @@ -30,7 +30,7 @@ #include <switch.h> -#define LIMIT_SYNTAX "<realm> <id> <max>[/interval] [transfer_destination_number [transfer_destination_context]]" +#define LIMIT_SYNTAX "<realm> <id> <max>[/interval] [transfer_destination_number [transfer_destination_dialplan [transfer_destination_context]]]" SWITCH_MODULE_LOAD_FUNCTION(mod_limit_hash_load); SWITCH_MODULE_DEFINITION(mod_limit_hash, mod_limit_hash_load, NULL , NULL); @@ -48,27 +48,39 @@ uint32_t rate_usage; time_t last_check; }; +typedef struct limit_hash_item limit_hash_item_t; static char *limit_def_xfer_exten = "limit_exceeded"; -typedef struct limit_hash_item limit_hash_item_t; - +/* Callback procedure so we know when to decrement counters */ static switch_status_t state_handler(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); switch_channel_state_t state = switch_channel_get_state(channel); - char *hashkey = switch_channel_get_private(channel, "limit_hashkey"); - limit_hash_item_t *item = NULL; + switch_hash_t *channel_hash = switch_channel_get_private(channel, "limit_hash"); + /* The call is either hung up, or is going back into the dialplan, decrement appropriate couters */ if (state == CS_HANGUP || state == CS_ROUTING) { + switch_hash_index_t *hi; switch_mutex_lock(globals.mutex); - item = (limit_hash_item_t*)switch_core_hash_find(globals.hash, hashkey); - - /* We keep the structure even though the count is 0 so we do not allocate too often */ - item->total_usage--; - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Usage for %s is now %d\n", hashkey, item->total_usage); - + + /* Loop through the channel's hashtable which contains mapping to all the limit_hash_item_t referenced by that channel */ + for(hi = switch_hash_first(NULL, channel_hash); hi; hi = switch_hash_next(hi)) + { + void *val = NULL; + const char *key; + switch_ssize_t keylen; + limit_hash_item_t *item = NULL; + + switch_hash_this(hi, (const void**)&key, &keylen, &val); + + item = (limit_hash_item_t*)val; + + /* We keep the structure even though the count is 0 so we do not allocate too often */ + item->total_usage--; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Usage for %s is now %d\n", key, item->total_usage); + } + switch_core_event_hook_remove_state_change(session, state_handler); switch_mutex_unlock(globals.mutex); } @@ -90,7 +102,11 @@ limit_hash_item_t *item = NULL; switch_channel_t *channel = switch_core_session_get_channel(session); time_t now = switch_timestamp(NULL); + switch_hash_t *channel_hash = NULL; + uint8_t increment = 1; + uint8_t new_channel = 0; + /* Parse application data */ if (!switch_strlen_zero(data)) { mydata = switch_core_session_strdup(session, data); argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); @@ -101,8 +117,6 @@ return; } - switch_mutex_lock(globals.mutex); - realm = argv[0]; id = argv[1]; if ((szinterval = strchr(argv[2], '/'))) @@ -123,45 +137,72 @@ max = 0; } - hashkey = switch_core_session_sprintf(session, "limit_%s_%s", realm, id); + hashkey = switch_core_session_sprintf(session, "%s_%s", realm, id); + switch_mutex_lock(globals.mutex); + /* Check if that realm+id has ever been checked */ if (!(item = (limit_hash_item_t*)switch_core_hash_find(globals.hash, hashkey))) { + /* No, create an empty structure and add it, then continue like as if it existed */ item = (limit_hash_item_t*)switch_core_alloc(globals.pool, sizeof(limit_hash_item_t)); memset(item, 0, sizeof(limit_hash_item_t)); switch_core_hash_insert(globals.hash, hashkey, item); } - if (item->last_check <= (now - interval)) { - item->rate_usage = 1; - item->last_check = now; - } else { - if (interval > 0 && item->rate_usage + 1 > max) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Usage for %s exceeds maximum rate of %d/%ds\n", - hashkey, max, interval); - switch_ivr_session_transfer(session, xfer_exten, argv[4], argv[5]); - goto end; + /* Did we already run on this channel before? */ + if ((channel_hash = switch_channel_get_private(channel, "limit_hash"))) + { + // Yes, but check if we did that realm+id + if (!switch_core_hash_find(channel_hash, hashkey)) { + // No, add it to our table so the state handler can take care of it + switch_core_hash_insert(channel_hash, hashkey, item); + } else { + /* Yes, dont touch total counter */ + increment = 0; } - item->rate_usage++; + } else { + /* This is the first limit check on this channel, create a hashtable, set our prviate data and add a state handler */ + new_channel = 1; } - if (interval == 0 && item->total_usage + 1 > max) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Usage for %s is already at max value\n", hashkey); + if (interval > 0) { + if (item->last_check <= (now - interval)) { + item->rate_usage = 1; + item->last_check = now; + } else { + // Always increment rate when its checked as it doesnt depend on the channel + item->rate_usage++; + + if (item->rate_usage > max) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Usage for %s exceeds maximum rate of %d/%ds, now at %d\n", hashkey, max, interval, item->rate_usage); + switch_ivr_session_transfer(session, xfer_exten, argv[4], argv[5]); + goto end; + } + } + } else if (/* interval == 0 && */ item->total_usage + increment > max) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Usage for %s is already at max value (%d)\n", hashkey, item->total_usage); switch_ivr_session_transfer(session, xfer_exten, argv[4], argv[5]); goto end; } - - item->total_usage++; - - if (interval == 0) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Usage for %s is now %d/%d\n", hashkey, item->total_usage, max); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Usage for %s is now %d/%d for the last %d seconds\n", hashkey, item->rate_usage, max, interval); + + if (increment) { + item->total_usage++; + + if (interval == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Usage for %s is now %d/%d\n", hashkey, item->total_usage, max); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Usage for %s is now %d/%d for the last %d seconds\n", hashkey, item->rate_usage, max, interval); + } } + - switch_channel_set_private(channel, "limit_hashkey", hashkey); - switch_core_event_hook_add_state_change(session, state_handler); + if (new_channel) { + switch_core_hash_init(&channel_hash, switch_core_session_get_pool(session)); + switch_core_hash_insert(channel_hash, hashkey, item); + switch_channel_set_private(channel, "limit_hash", channel_hash); + switch_core_event_hook_add_state_change(session, state_handler); + } -end: +end: switch_mutex_unlock(globals.mutex); } @@ -180,8 +221,7 @@ switch_core_hash_init(&globals.hash, pool); - SWITCH_ADD_APP(app_interface, "limit_hash", "Limit", - "Limits various resources usage", limit_hash_function, LIMIT_SYNTAX, SAF_SUPPORT_NOMEDIA); + SWITCH_ADD_APP(app_interface, "limit_hash", "Limit (hashtable backend)", "Limits various resources usage", limit_hash_function, LIMIT_SYNTAX, SAF_SUPPORT_NOMEDIA); return SWITCH_STATUS_NOUNLOAD; /* Potential Kaboom(tm) if we have active calls */ }
participants (1)
-
FreeSWITCH SVN