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) { switch_xml_t xml = NULL; char *data = NULL; xml_binding_t *binding = (xml_binding_t *) user_data; char hostname[256] = ""; char basic_data[512]; unsigned char buf[16336] = ""; ssize_t len = -1, bytes = 0; scgi_handle_t handle = { 0 }; switch_stream_handle_t stream = { 0 }; char *txt = NULL; strncpy(hostname, switch_core_get_switchname(), sizeof(hostname)); if (!binding) { return NULL; } switch_snprintf(basic_data, sizeof(basic_data), "hostname=%s§ion=%s&tag_name=%s&key_name=%s&key_value=%s", hostname, section, switch_str_nil(tag_name), switch_str_nil(key_name), switch_str_nil(key_value)); data = switch_event_build_param_string(params, basic_data, binding->vars_map); switch_assert(data); scgi_add_param(&handle, "REQUEST_METHOD", "POST"); scgi_add_param(&handle, "SERVER_PROTOCOL", "HTTP/1.0"); scgi_add_param(&handle, "REQUEST_URI", binding->uri); scgi_add_body(&handle, data); if (scgi_connect(&handle, binding->host, binding->port, binding->timeout * 1000) == SCGI_SUCCESS) { scgi_send_request(&handle); SWITCH_STANDARD_STREAM(stream); txt = (char *) stream.data; while((len = scgi_recv(&handle, buf, sizeof(buf))) > 0) { char *expanded = switch_event_expand_headers(params, (char *)buf); bytes += len; if (bytes > XML_SCGI_MAX_BYTES) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Data too big!\n"); len = -1; break; } stream.write_function(&stream, "%s", expanded); txt = (char *) stream.data; if (expanded != (char *)buf) { free(expanded); } memset(buf, 0, sizeof(buf)); } scgi_disconnect(&handle); if (len < 0 && (!txt || !strlen(txt))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DEBUG:\nURL: %s Connection Read Failed: [%s]\n", binding->url, handle.err); goto end; } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DEBUG:\nURL: %s Connection Failed: [%s]\n", binding->url, handle.err); goto end; } if (GLOBAL_DEBUG) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DEBUG:\nURL: %s\nPOST_DATA:\n%s\n\nRESPONSE:\n-----\n%s\n-----\n", binding->url, data, switch_str_nil(txt)); } if (bytes && txt) { if (!(xml = switch_xml_parse_str_dynamic(txt, FALSE))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Parsing Result! [%s]\ndata: [%s] RESPONSE[%s]\n", binding->url, data, switch_str_nil(txt)); } txt = NULL; } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received error trying to fetch %s\ndata: [%s] RESPONSE [%s]\n", binding->url, data, switch_str_nil(txt)); } end: switch_safe_free(data); switch_safe_free(txt); return xml; }
static switch_xml_t fetch_handler(const char *section, const char *tag_name, const char *key_name, const char *key_value, switch_event_t *params, void *user_data) { switch_xml_t xml = NULL; switch_uuid_t uuid; switch_time_t now = 0; ei_xml_agent_t *agent = (ei_xml_agent_t *) user_data; ei_xml_client_t *client; fetch_handler_t *fetch_handler; xml_fetch_reply_t reply, *pending, *prev = NULL; now = switch_micro_time_now(); if (!switch_test_flag(&globals, LFLAG_RUNNING)) { return xml; } /* read-lock the agent */ switch_thread_rwlock_rdlock(agent->lock); /* serialize access to current, used to round-robin requests */ /* TODO: check globals for round-robin boolean or loop all clients */ switch_mutex_lock(agent->current_client_mutex); if (!agent->current_client) { client = agent->clients; } else { client = agent->current_client; } if (client) { agent->current_client = client->next; } switch_mutex_unlock(agent->current_client_mutex); /* no client, no work required */ if (!client || !client->fetch_handlers) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "No %s XML erlang handler currently available\n" ,section); switch_thread_rwlock_unlock(agent->lock); return xml; } /* prepare the reply collector */ switch_uuid_get(&uuid); switch_uuid_format(reply.uuid_str, &uuid); reply.next = NULL; reply.xml_str = NULL; /* add our reply placeholder to the replies list */ switch_mutex_lock(agent->replies_mutex); if (!agent->replies) { agent->replies = &reply; } else { reply.next = agent->replies; agent->replies = &reply; } switch_mutex_unlock(agent->replies_mutex); fetch_handler = client->fetch_handlers; while (fetch_handler != NULL) { ei_send_msg_t *send_msg; switch_malloc(send_msg, sizeof(*send_msg)); memcpy(&send_msg->pid, &fetch_handler->pid, sizeof(erlang_pid)); ei_x_new_with_version(&send_msg->buf); ei_x_encode_tuple_header(&send_msg->buf, 7); ei_x_encode_atom(&send_msg->buf, "fetch"); ei_x_encode_atom(&send_msg->buf, section); _ei_x_encode_string(&send_msg->buf, tag_name ? tag_name : "undefined"); _ei_x_encode_string(&send_msg->buf, key_name ? key_name : "undefined"); _ei_x_encode_string(&send_msg->buf, key_value ? key_value : "undefined"); _ei_x_encode_string(&send_msg->buf, reply.uuid_str); if (params) { ei_encode_switch_event_headers(&send_msg->buf, params); } else { ei_x_encode_empty_list(&send_msg->buf); } if (switch_queue_trypush(client->ei_node->send_msgs, send_msg) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send %s XML request to %s <%d.%d.%d>\n" ,section ,fetch_handler->pid.node ,fetch_handler->pid.creation ,fetch_handler->pid.num ,fetch_handler->pid.serial); ei_x_free(&send_msg->buf); switch_safe_free(send_msg); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sending %s XML request (%s) to %s <%d.%d.%d>\n" ,section ,reply.uuid_str ,fetch_handler->pid.node ,fetch_handler->pid.creation ,fetch_handler->pid.num ,fetch_handler->pid.serial); } fetch_handler = fetch_handler->next; } /* wait for a reply (if there isnt already one...amazingly improbable but lets not take shortcuts */ switch_mutex_lock(agent->replies_mutex); switch_thread_rwlock_unlock(agent->lock); if (!reply.xml_str) { switch_time_t timeout; timeout = switch_micro_time_now() + 3000000; while (switch_micro_time_now() < timeout) { /* unlock the replies list and go to sleep, calculate a three second timeout before we started the loop * plus 100ms to add a little hysteresis between the timeout and the while loop */ switch_thread_cond_timedwait(agent->new_reply, agent->replies_mutex, (timeout - switch_micro_time_now() + 100000)); /* if we woke up (and therefore have locked replies again) check if we got our reply * otherwise we either timed-out (the while condition will fail) or one of * our sibling processes got a reply and we should go back to sleep */ if (reply.xml_str) { break; } } } /* find our reply placeholder in the linked list and remove it */ pending = agent->replies; while (pending != NULL) { if (pending->uuid_str == reply.uuid_str) { break; } prev = pending; pending = pending->next; } if (pending) { if (!prev) { agent->replies = reply.next; } else { prev->next = reply.next; } } /* we are done with the replies link-list */ switch_mutex_unlock(agent->replies_mutex); /* after all that did we get what we were after?! */ if (reply.xml_str) { /* HELL YA WE DID */ reply.xml_str = expand_vars(reply.xml_str); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Received %s XML (%s) after %dms: %s\n" ,section ,reply.uuid_str ,(unsigned int) (switch_micro_time_now() - now) / 1000 ,reply.xml_str); xml = switch_xml_parse_str_dynamic(reply.xml_str, SWITCH_FALSE); } else { /* facepalm */ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Request for %s XML (%s) timed-out after %dms\n" ,section ,reply.uuid_str ,(unsigned int) (switch_micro_time_now() - now) / 1000); } return xml; }