Revision
16997
Author
mrene
Date
2010-03-15 16:17:53 -0500 (Mon, 15 Mar 2010)

Log Message

XML-22

Modified Paths

Diff

Modified: freeswitch/trunk/src/mod/xml_int/mod_xml_curl/mod_xml_curl.c (16996 => 16997)


--- freeswitch/trunk/src/mod/xml_int/mod_xml_curl/mod_xml_curl.c	2010-03-15 21:00:19 UTC (rev 16996)
+++ freeswitch/trunk/src/mod/xml_int/mod_xml_curl/mod_xml_curl.c	2010-03-15 21:17:53 UTC (rev 16997)
@@ -57,6 +57,10 @@
 	int use_dynamic_url;
 	int auth_scheme;
 	int timeout;
+	int timeout_ms;
+	int weight;
+	struct xml_binding* next;
+	int chosen;
 };
 
 static int keep_files_around = 0;
@@ -132,9 +136,6 @@
 	return x;
 }
 
-
-
-
 static switch_xml_t xml_url_fetch(const char *section, const char *tag_name, const char *key_name, const char *key_value, switch_event_t *params,
 								  void *user_data)
 {
@@ -240,6 +241,11 @@
 			curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
 		}
 
+		if (binding->timeout_ms) {
+			curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT_MS, binding->timeout_ms);
+                        curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
+		}
+
 		if (binding->disable100continue) {
 			slist = curl_slist_append(slist, "Expect:");
 			curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, slist);
@@ -324,12 +330,128 @@
 	return xml;
 }
 
+static switch_xml_t xml_curl_fetch(const char *section, const char *tag_name, const char *key_name, const char *key_value, switch_event_t *params,
+								  void *user_data)
+{
+	xml_binding_t *root = (xml_binding_t*) user_data;
+	xml_binding_t *binding = root;
+	int running_weight = 0, total_weight = 0, rand_weight = 0, count = 0, pos = 0;
+	xml_binding_t *bindings_copy = NULL;
+	xml_binding_t *tmp = NULL;
+	xml_binding_t **first_order = NULL;
+	xml_binding_t **second_order = NULL;
+	switch_xml_t result = NULL;
+
+	// Count how many elements we have
+	for( ; binding != NULL; binding = binding->next) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Found [%s]\n", binding->url);
+		count++;
+	}
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "XML_CURL Fetch on %d bindings\n", count);
+
+	// Make a local copy of the bindings
+	bindings_copy = malloc(count*sizeof(xml_binding_t));
+	tmp = bindings_copy;
+	binding = root;
+	pos = 0;
+	for( ; binding != NULL; binding = binding->next) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Copying [%s] to 0x%0X\n", root->url, (unsigned)(intptr_t)&bindings_copy[pos]);
+		memcpy(&bindings_copy[pos], binding, sizeof(xml_binding_t));
+		pos++;
+	}
+	
+	// And two ordering arrays
+	first_order = malloc(count*sizeof(xml_binding_t*));
+	memset(first_order, 0, count*sizeof(xml_binding_t*));
+	second_order = malloc(count*sizeof(xml_binding_t*));
+	memset(second_order, 0, count*sizeof(xml_binding_t*));
+
+	// Update root to point to local copy of binding data
+	root = bindings_copy;
+
+	// Put all weight=0 at beginning of first ordering array
+	binding = root; pos = 0;
+	for(int i = 0; i < count; i++) {
+		binding = &bindings_copy[i];
+		if(binding->weight == 0) {
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Copying [%s] to first_order[%d]\n", binding->url, pos);
+			first_order[pos] = binding;
+			pos++;
+		}
+	}
+
+	// Then dump everything else into the first ordering array
+	binding = root;
+	for(int i = 0; i < count; i++) {
+		binding = &bindings_copy[i];
+		if(binding->weight != 0) {
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Copying [%s] to first_order[%d]\n", binding->url, pos);
+			first_order[pos] = binding;
+			pos++;
+		}
+	}
+
+	srand((unsigned)((unsigned)(intptr_t)switch_thread_self() + switch_micro_time_now()));
+
+	// We need to pick every element in the array, so loop once for every one of them
+	pos = 0;
+	for(int i = 0; i < count; i++) {
+		// Calculate the total remaining unchosen weight
+		total_weight = 0; running_weight = 0;
+		for(int b = 0; b < count; b++) {
+			binding = &bindings_copy[b];
+			if(!binding->chosen) total_weight += binding->weight;
+		}
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Remaining total weight [%d]\n", total_weight);
+
+		// Pick a random number between [0, total_weight]
+		// Note: Don't use rand() % M as it is not as random as this more complex 
+		// method which achieves the same intent with more random results
+		rand_weight = (int)( ((double)rand()) / (((double)(RAND_MAX) + (double)(1)) / ((double)total_weight + (double)(1))) );
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Random weight [%d]\n", rand_weight);
+
+		// Then grab the first unchosen element whose running_weight >= rand_weight 
+		// and move it into our 'picked' list
+		binding = root;
+		for(int b = 0; b < count; b++) {
+			binding = &bindings_copy[b];
+			if(binding->chosen) continue; // skip already picked elements
+
+			running_weight += binding->weight;
+			if(running_weight >= rand_weight) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Chose binding %d as [%s] with weight [%d] based on running weight [%d]\n", pos, binding->url, binding->weight, running_weight);
+				second_order[pos] = binding;
+				pos++;
+				binding->chosen = 1;
+				break;
+			}
+		}
+	}
+
+	/* Then call our XML url fetch function for each binding based on our 
+	 * chosen order until we get a valid result or run out of bindings */
+	pos = 0;
+	for( ; pos < count && result == NULL; pos++) {
+		binding = second_order[pos];
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Querying binding [%s]\n", binding->url);
+		result = xml_url_fetch(section, tag_name, key_name, key_value, params, (void*)binding);
+	}
+
+	free(first_order);
+	free(second_order);
+	free(bindings_copy);
+	return result;
+}
+
+
 #define ENABLE_PARAM_VALUE "enabled"
 static switch_status_t do_config(void)
 {
 	char *cf = "xml_curl.conf";
 	switch_xml_t cfg, xml, bindings_tag, binding_tag, param;
 	xml_binding_t *binding = NULL;
+	xml_binding_t *prev_binding = NULL;
+	xml_binding_t *root = NULL;
 	int x = 0;
 	int need_vars_map = 0;
 	switch_hash_t *vars_map = NULL;
@@ -344,14 +466,16 @@
 		goto done;
 	}
 
-	for (binding_tag = switch_xml_child(bindings_tag, "binding"); binding_tag; binding_tag = binding_tag->next) {
+	// Only 32768 bindings allowed or weight sum could exceed signed int max 
+	// value breaking load balancing algorithm
+	for (binding_tag = switch_xml_child(bindings_tag, "binding"); binding_tag && x < 32768; binding_tag = binding_tag->next) {
 		char *bname = (char *) switch_xml_attr_soft(binding_tag, "name");
 		char *url = NULL;
 		char *bind_cred = NULL;
 		char *bind_mask = NULL;
 		char *method = NULL;
 		int disable100continue = 0;
-		int use_dynamic_url = 0, timeout = 0;
+		int use_dynamic_url = 0, timeout = 0, timeout_ms = 0, weight = 0;
 		uint32_t enable_cacert_check = 0;
 		char *ssl_cert_file = NULL;
 		char *ssl_key_file = NULL;
@@ -398,6 +522,17 @@
 				disable100continue = 1;
 			} else if (!strcasecmp(var, "method")) {
 				method = val;
+			} else if (!strcasecmp(var, "weight")) {
+				int tmp = atoi(val);
+				if (tmp >= 0) {
+					if(tmp > 65535) {
+						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set weight > 65535!\n");
+					} else {
+						weight = tmp;
+					}
+				} else {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set a negative weight!\n");
+				}
 			} else if (!strcasecmp(var, "timeout")) {
 				int tmp = atoi(val);
 				if (tmp >= 0) {
@@ -405,6 +540,13 @@
 				} else {
 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set a negative timeout!\n");
 				}
+			} else if (!strcasecmp(var, "timeout-ms")) {
+				int tmp = atoi(val);
+				if (tmp >= 0) {
+					timeout_ms = tmp;
+				} else {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set a negative timeout!\n");
+				}
 			} else if (!strcasecmp(var, "enable-cacert-check") && switch_true(val)) {
 				enable_cacert_check = 1;
 			} else if (!strcasecmp(var, "ssl-cert-path")) {
@@ -455,7 +597,9 @@
 		memset(binding, 0, sizeof(*binding));
 
 		binding->auth_scheme = auth_scheme;
+		binding->weight = weight;
 		binding->timeout = timeout;
+		binding->timeout_ms = timeout_ms;
 		binding->url = strdup(url);
 		switch_assert(binding->url);
 
@@ -522,12 +666,24 @@
 
 		}
 
+		if(root == NULL) {
+			root = binding;
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Root binding [%s] [%s] [%s]\n",
+						  zstr(bname) ? "N/A" : bname, binding->url, binding->bindings ? binding->bindings : "all");
+		}
+		if(prev_binding != NULL) {
+			prev_binding->next = binding;
+		}
+		prev_binding = binding;
+
 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Binding [%s] XML Fetch Function [%s] [%s]\n",
 						  zstr(bname) ? "N/A" : bname, binding->url, binding->bindings ? binding->bindings : "all");
-		switch_xml_bind_search_function(xml_url_fetch, switch_xml_parse_section_string(binding->bindings), binding);
 		x++;
 		binding = NULL;
 	}
+	if(root != NULL) {
+		switch_xml_bind_search_function(xml_curl_fetch, switch_xml_parse_section_string(root->bindings), root);
+	}
 
   done:
 	switch_xml_free(xml);