Files
freeswitch/src/mod/applications/mod_http/mod_http.c
T
2009-01-21 19:31:45 +00:00

383 lines
9.4 KiB
C

/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2008, Eric des Courtis <eric.des.courtis@benbria.com>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Eric des Courtis <eric.des.courtis@benbria.com>
* Copyright (C) Benbria. All Rights Reserved.
*
* Contributor(s):
*
* Eric des Courtis <eric.des.courtis@benbria.com>
*
*
* mod_http.c -- HTTP client implementation for FreeSWITCH
*
* The purpose is to provide laguages like LUA with a _fast_ HTTP
* client implementation.
*
* Support for SSL will be provided in future versions.
* Initial release does not include win32 support.
*
*/
#include <switch.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/queue.h>
#include "json.h"
#include "http_req.h"
#include "url_encoding.h"
#define HTTP_SYNTAX "<http_method> <url> <urlejson_headers> [urlejson_body]"
#define HTTP_PARAMS 4
#define HTTP_BUFFER_SIZE (256 * 1024)
/* SWITCH_STANDARD_API(http_api_main); */
#define MAX_MEMLOCS 512
#define GARBAGE_TYPES_INIT() \
typedef struct memloc{\
void *p;\
LIST_ENTRY(memloc) memlocs;\
} memloc_t
#define GARBAGE_CLEANUP() \
do{\
for(memloc_p = head.lh_first; memloc_p != NULL; \
memloc_p = memloc_p->memlocs.le_next){\
free(memloc_p->p);\
}\
}while(0)
#define GARBAGE_ADD(a) \
do {\
if(memloc_i >= MAX_MEMLOCS){\
switch_safe_free(ccmd);\
GARBAGE_CLEANUP();\
stream->write_function(stream, "-ERR\n");\
return SWITCH_STATUS_SUCCESS;\
}\
memloc_a[memloc_i].p = (void *)a;\
LIST_INSERT_HEAD(&head, memloc_a + memloc_i, memlocs);\
memloc_i++;\
}while(0)
#define GARBAGE_INIT() \
LIST_HEAD(listhead, memloc) head;\
memloc_t memloc_a[MAX_MEMLOCS];\
memloc_t *memloc_p;\
size_t memloc_i;\
LIST_INIT(&head);\
memloc_i = 0
GARBAGE_TYPES_INIT();
SWITCH_STANDARD_API(http_api_main)
{
char *ccmd;
int argc;
char *argv[HTTP_PARAMS];
char *buf;
char *method;
char *url;
char *headers_dec;
char *headers_str;
char *value;
char *body;
char *body_dec;
char *t;
char *json_response;
struct json_object *json_http_headers;
char *key;
struct json_object *val;
enum json_type jsontype;
struct lh_entry *entry;
int i;
int j;
int f;
size_t l;
size_t m;
size_t a = 0;
int ret;
http_header_t *headers;
http_request_t request;
http_response_t response;
GARBAGE_INIT();
(void) memset(&response, 0, sizeof(response));
if (cmd == NULL) {
stream->write_function(stream, "-USAGE: %s\n", HTTP_SYNTAX);
return SWITCH_STATUS_SUCCESS;
}
ccmd = strdup(cmd);
argc = switch_separate_string(ccmd, ' ', argv, HTTP_PARAMS);
if (argc != HTTP_PARAMS && argc != (HTTP_PARAMS - 1)) {
switch_safe_free(ccmd);
stream->write_function(stream, "-ERR\n");
return SWITCH_STATUS_SUCCESS;
}
method = argv[0];
if (strcasecmp("GET", method) == 0)
request.method = GET;
else if (strcasecmp("POST", method) == 0)
request.method = POST;
else if (strcasecmp("HEAD", method) == 0)
request.method = HEAD;
else if (strcasecmp("DELETE", method) == 0)
request.method = DELETE;
else if (strcasecmp("PUT", method) == 0)
request.method = PUT;
url = argv[1];
headers_str = argv[2];
if (argc == HTTP_PARAMS) {
body = argv[3];
} else {
body = (char *) malloc(1 * sizeof(char));
if (body == NULL) {
switch_safe_free(ccmd);
stream->write_function(stream, "-ERR\n");
return SWITCH_STATUS_SUCCESS;
}
body[0] = '\0';
GARBAGE_ADD(body);
}
buf = (char *) malloc(HTTP_BUFFER_SIZE * sizeof(char));
if (buf == NULL) {
switch_safe_free(ccmd);
stream->write_function(stream, "-ERR\n");
GARBAGE_CLEANUP();
return SWITCH_STATUS_SUCCESS;
}
GARBAGE_ADD(buf);
request.version = DEFAULT_HTTP_VERSION;
l = strlen(url);
request.url = (char *) malloc((l + 1) * sizeof(char));
if (request.url == NULL) {
switch_safe_free(ccmd);
stream->write_function(stream, "-ERR\n");
GARBAGE_CLEANUP();
return SWITCH_STATUS_SUCCESS;
}
GARBAGE_ADD(request.url);
strcpy(request.url, url);
l = strlen(headers_str);
headers_dec = url_decode(headers_str, l);
GARBAGE_ADD(headers_dec);
json_http_headers = json_tokener_parse(headers_dec);
if (is_error(json_http_headers)) {
switch_safe_free(ccmd);
stream->write_function(stream, "-ERR\n");
GARBAGE_CLEANUP();
return SWITCH_STATUS_SUCCESS;
}
jsontype = json_object_get_type(json_http_headers);
if (jsontype != json_type_object) {
switch_safe_free(ccmd);
stream->write_function(stream, "-ERR\n");
GARBAGE_CLEANUP();
return SWITCH_STATUS_SUCCESS;
}
i = 0;
json_object_object_foreach(json_http_headers, key, val) {
i++;
}
request.header_len = i;
headers = (http_header_t *) malloc(i * sizeof(http_header_t));
request.headers = headers;
GARBAGE_ADD(headers);
i = 0;
json_object_object_foreach(json_http_headers, key, val) {
l = strlen(key);
request.headers[i].field_name = (char *) malloc((l + 1) * sizeof(char));
if (request.headers[i].field_name == NULL) {
switch_safe_free(ccmd);
stream->write_function(stream, "-ERR\n");
GARBAGE_CLEANUP();
return SWITCH_STATUS_SUCCESS;
}
GARBAGE_ADD(request.headers[i].field_name);
strcpy(request.headers[i].field_name, key);
a += strlen(key);
jsontype = json_object_get_type(val);
if (jsontype != json_type_string) {
switch_safe_free(ccmd);
stream->write_function(stream, "-ERR\n");
GARBAGE_CLEANUP();
return SWITCH_STATUS_SUCCESS;
}
value = json_object_get_string(val);
/* value = json_object_to_json_string(val); */
l = strlen(value);
request.headers[i].value = (char *) malloc((l + 1) * sizeof(char));
if (request.headers[i].value == NULL) {
switch_safe_free(ccmd);
stream->write_function(stream, "-ERR\n");
GARBAGE_CLEANUP();
return SWITCH_STATUS_SUCCESS;
}
GARBAGE_ADD(request.headers[i].value);
strcpy(request.headers[i].value, value);
a += strlen(value);
i++;
}
if (argc == HTTP_PARAMS) {
l = strlen(body);
body_dec = url_decode(body, l);
GARBAGE_ADD(body_dec);
l = strlen(body_dec);
request.body_len = l;
request.body = body_dec;
} else
request.body_len = 0;
ret = http_req(&request, &response);
if (response.version != NULL)
GARBAGE_ADD(response.version);
if (response.phrase != NULL)
GARBAGE_ADD(response.phrase);
if (response.headers != NULL)
GARBAGE_ADD(response.headers);
if (response.body != NULL)
GARBAGE_ADD(response.body);
for (i = 0; i < response.header_len; i++) {
GARBAGE_ADD(response.headers[i].field_name);
GARBAGE_ADD(response.headers[i].value);
}
if (ret == ERROR) {
switch_safe_free(ccmd);
stream->write_function(stream, "-ERR\n");
GARBAGE_CLEANUP();
return SWITCH_STATUS_SUCCESS;
}
/* This is evil and should be changed in the future */
l = 128 + (256 * response.header_len) + (a * 2)
+ strlen("version") + strlen(response.version)
+ strlen("status_code") + 3 + strlen("phrase") + strlen(response.phrase)
+ strlen("body") + (response.body_len * 3) + 1 + strlen("headers")
+ 1;
/* to be safe */
l <<= 2;
json_response = (char *) malloc(l * sizeof(char));
if (json_response == NULL) {
switch_safe_free(ccmd);
stream->write_function(stream, "-ERR\n");
GARBAGE_CLEANUP();
return SWITCH_STATUS_SUCCESS;
}
GARBAGE_ADD(json_response);
if (response.body_len != 0) {
t = (char *) malloc((response.body_len + 1) * sizeof(char));
if (t == NULL) {
switch_safe_free(ccmd);
stream->write_function(stream, "-ERR\n");
GARBAGE_CLEANUP();
return SWITCH_STATUS_SUCCESS;
}
GARBAGE_ADD(t);
(void) memcpy(t, response.body, response.body_len);
t[response.body_len] = '\0';
response.body = url_encode(t, response.body_len);
GARBAGE_ADD(response.body);
}
m = snprintf(json_response, l,
"{"
"\"version\": \"%s\","
"\"status_code\": \"%3d\","
"\"phrase\": \"%s\","
"\"body\": \"%s\","
"\"headers\": [", response.version, response.status_code, response.phrase, ((response.body_len <= 0) ? "" : response.body)
);
for (f = HTTP_FALSE, j = 0; j < response.header_len; j++) {
if (f != HTTP_FALSE) {
m += snprintf(json_response + m, l - m, ",");
} else
f = HTTP_TRUE;
m += snprintf(json_response + m, l - m, "{\"key\": \"%s\",\"value\": \"%s\"}", response.headers[j].field_name, response.headers[j].value);
}
m += snprintf(json_response + m, l - m, "]}");
json_response[m] = '\0';
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "RESERVED %ld BYTES, USED %ld BYTES, HTTP Response as JSON: %s\n", l, m, json_response);
stream->write_function(stream, "%s\n", json_response);
switch_safe_free(ccmd);
GARBAGE_CLEANUP();
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_LOAD_FUNCTION(mod_http_load);
SWITCH_MODULE_DEFINITION(mod_http, mod_http_load, NULL, NULL);
SWITCH_MODULE_LOAD_FUNCTION(mod_http_load)
{
switch_api_interface_t *api_interface;
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "HTTP request mod enabled\n");
SWITCH_ADD_API(api_interface, "http", "Make HTTP requests", http_api_main, HTTP_SYNTAX);
return SWITCH_STATUS_SUCCESS;
}