[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