[Freeswitch-svn] [commit] r10339 - freeswitch/trunk/scripts/contrib/mrene/mod_limit_hash
FreeSWITCH SVN
mrene at freeswitch.org
Tue Nov 11 19:36:41 PST 2008
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 */
}
More information about the Freeswitch-svn
mailing list