Skip to content

Commit

Permalink
http2d: Add support for HTTP/2 responses in opensips.cfg
Browse files Browse the repository at this point in the history
... via the new http2_send_response(code, [hdrs], [body]) function.
  • Loading branch information
liviuchircu authored and bogdan-iancu committed Apr 18, 2024
1 parent 53a1264 commit b54a6d8
Show file tree
Hide file tree
Showing 9 changed files with 410 additions and 70 deletions.
4 changes: 3 additions & 1 deletion lib/cJSON.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@ extern cJSON_Hooks sys_mem_hooks;
extern cJSON_Hooks shm_mem_hooks;


/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
/* Supply a block of JSON, and this returns a cJSON object you can interrogate.
* The input @value can be safely freed immediately after the parsing.
* Call cJSON_Delete when finished. */
extern cJSON *cJSON_Parse(const char *value);
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
extern char *cJSON_Print(const cJSON *item);
Expand Down
2 changes: 1 addition & 1 deletion modules/aaa_diameter/aaa_diameter.c
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ static int dm_send_answer(struct sip_msg *msg, str *avp_json, int *is_error)
}

cJSON_Delete(avps);
return 0;
return 1;

error:
_dm_destroy_message(dmsg);
Expand Down
2 changes: 1 addition & 1 deletion modules/aaa_diameter/dm_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -2028,7 +2028,7 @@ int _dm_send_message(aaa_conn *_, aaa_message *msg, struct dm_cond **reply_cond)
gettimeofday(&now, NULL);
wait_time.tv_sec = dm_answer_timeout / 1000;
wait_time.tv_usec = dm_answer_timeout % 1000 * 1000UL;
LM_DBG("awaiting auth reply (%ld s, %ld us)...\n", wait_time.tv_sec, wait_time.tv_usec);
LM_DBG("awaiting reply (%ld s, %ld us)...\n", wait_time.tv_sec, wait_time.tv_usec);

timeradd(&now, &wait_time, &res);

Expand Down
27 changes: 10 additions & 17 deletions modules/http2d/h2_evi.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
*/

#include "server.h"
#include "h2_evi.h"

#include "../../dprint.h"
Expand All @@ -30,13 +31,11 @@ static evi_param_p h2ev_req_param_method;
static evi_param_p h2ev_req_param_path;
static evi_param_p h2ev_req_param_headers;
static evi_param_p h2ev_req_param_body;
static evi_param_p h2ev_req_param_msg;

str h2ev_req_pname_method = str_init("method");
str h2ev_req_pname_path = str_init("path");
str h2ev_req_pname_headers = str_init("headers");
str h2ev_req_pname_body = str_init("body");
str h2ev_req_pname_msg = str_init("_h2msg_");


int h2_init_evi(void)
Expand All @@ -55,14 +54,19 @@ int h2_init_evi(void)
}
memset(h2ev_req_params, 0, sizeof *h2ev_req_params);

h2_response = shm_malloc(sizeof *h2_response);
if (!h2_response) {
LM_ERR("oom SHM\n");
return -1;
}
*h2_response = NULL;

h2ev_req_param_method = evi_param_create(h2ev_req_params, &h2ev_req_pname_method);
h2ev_req_param_path = evi_param_create(h2ev_req_params, &h2ev_req_pname_path);
h2ev_req_param_headers = evi_param_create(h2ev_req_params, &h2ev_req_pname_headers);
h2ev_req_param_body = evi_param_create(h2ev_req_params, &h2ev_req_pname_body);
h2ev_req_param_msg = evi_param_create(h2ev_req_params, &h2ev_req_pname_msg);
if (!h2ev_req_param_method || !h2ev_req_param_path
|| !h2ev_req_param_headers || !h2ev_req_param_body
|| !h2ev_req_param_msg) {
|| !h2ev_req_param_headers || !h2ev_req_param_body) {
LM_ERR("failed to create EVI params\n");
return -1;
}
Expand All @@ -77,10 +81,8 @@ int h2_init_evi(void)
* @sroutes, causing a crash when attempting to raise a script event
*/
void h2_raise_event_request(const char *method, const char *path,
const char *headers_json, const str *body, void *msg)
const char *headers_json, const str *body)
{
char buf[sizeof(long)*2 + 1], *p = buf;
int sz = sizeof(buf);
str st;

init_str(&st, method);
Expand All @@ -106,15 +108,6 @@ void h2_raise_event_request(const char *method, const char *path,
return;
}

int64_2reverse_hex(&p, &sz, (unsigned long)msg);
*p = '\0';
init_str(&st, buf);

if (evi_param_set_str(h2ev_req_param_msg, &st) < 0) {
LM_ERR("failed to set '_h2msg_'\n");
return;
}

if (evi_raise_event(h2ev_req_id, h2ev_req_params) < 0)
LM_ERR("failed to raise '"H2EV_REQ_NAME"' event\n");
}
3 changes: 1 addition & 2 deletions modules/http2d/h2_evi.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@
#include "../../str.h"

#define H2EV_REQ_NAME "E_HTTP2_REQUEST"
extern str h2ev_req_pname_msg;

int h2_init_evi(void);
void h2_raise_event_request(const char *method, const char *path,
const char *headers_json, const str *body, void *msg);
const char *headers_json, const str *body);

#endif /* __H2_EVI__ */
192 changes: 191 additions & 1 deletion modules/http2d/http2d.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,24 @@ unsigned int h2_port = 9111;
char *h2_ip;
str h2_tls_cert = STR_NULL;
str h2_tls_key = STR_NULL;

int h2_response_timeout = 2000; /* ms */
unsigned int max_headers_size = 8192; /* B */

struct h2_response **h2_response, *ng_h2_response;

static int h2_send_response(struct sip_msg *msg, int *code,
str *headers_json, str *body);

static const cmd_export_t cmds[]= {
{"http2_send_response", (cmd_function)h2_send_response, {
{CMD_PARAM_INT,0,0},
{CMD_PARAM_STR|CMD_PARAM_OPT,0,0},
{CMD_PARAM_STR|CMD_PARAM_OPT,0,0}, {0,0,0}},
EVENT_ROUTE},

{0,0,{{0,0,0}},0}
};

static const proc_export_t procs[] = {
{"HTTP2D", 0, 0, http2_server, 1, PROC_FLAG_INITCHILD|PROC_FLAG_NEEDS_SCRIPT },
Expand Down Expand Up @@ -70,7 +86,7 @@ struct module_exports exports = {
DEFAULT_DLFLAGS, /* dlopen flags */
0, /* load function */
NULL, /* OpenSIPS module dependencies */
NULL, /* exported functions */
cmds, /* exported functions */
0, /* exported async functions */
params, /* exported parameters */
NULL, /* exported statistics */
Expand Down Expand Up @@ -114,6 +130,180 @@ static int mod_init(void)
}


static int h2_send_response(struct sip_msg *msg, int *code,
str *headers_json, str *body)
{
#define H_STATUS ":status"
cJSON *hdrs, *it;
struct h2_response *r;
int nh = 1;

if (!h2_response)
return -1;
r = *h2_response;
r->code = -1;

if (*code < 100 || *code > 599) {
LM_ERR("invalid HTTP/2 response code: %d, must be 100-599\n", *code);
goto error;
}

if (headers_json) {
char *hp;
str h;

/* safe to dereference outside buff (still within PKG block) */
if (headers_json->s[headers_json->len] != '\0') {
if (pkg_nt_str_dup(&h, headers_json) != 0) {
LM_ERR("oom\n");
goto error;
}
hp = h.s;
} else {
hp = headers_json->s;
}

hdrs = cJSON_Parse(hp);
if (hp != headers_json->s)
pkg_free(hp);

if (!hdrs) {
LM_ERR("failed to parse 'headers_json' (bad JSON syntax)\n");
LM_ERR("first %d characters: %.*s ...\n",
headers_json->len > 20 ? 20 : headers_json->len,
headers_json->len > 20 ? 20 : headers_json->len, headers_json->s);
cJSON_Delete(hdrs);
goto error;
}

if (hdrs->type != cJSON_Array) {
LM_ERR("bad 'headers_json' value (must be a List of name/value pairs)\n");
LM_ERR("first %d characters: %.*s ...\n",
headers_json->len > 20 ? 20 : headers_json->len,
headers_json->len > 20 ? 20 : headers_json->len, headers_json->s);
cJSON_Delete(hdrs);
goto error;
}

int pseudo_headers_done = 0;

for (it = hdrs->child; it; it = it->next, nh++) {
if (it->type != cJSON_Object) {
LM_ERR("bad 'headers_json' value (must be a List of Objects, but "
"detected cJSON type %d as element)\n", it->type);
LM_ERR("first %d characters: %.*s ...\n",
headers_json->len > 20 ? 20 : headers_json->len,
headers_json->len > 20 ? 20 : headers_json->len, headers_json->s);
cJSON_Delete(hdrs);
goto error;
}

if (it->child->type != cJSON_String) {
LM_ERR("bad 'headers_json' value (header values must be Strings, but "
"detected cJSON type %d as value)\n", it->child->type);
LM_ERR("first %d characters: %.*s ...\n",
headers_json->len > 20 ? 20 : headers_json->len,
headers_json->len > 20 ? 20 : headers_json->len, headers_json->s);
cJSON_Delete(hdrs);
goto error;
}

if (!strlen(it->child->string)) {
LM_ERR("bad 'headers_json' value (empty-string header found)\n");
LM_ERR("first %d characters: %.*s ...\n",
headers_json->len > 20 ? 20 : headers_json->len,
headers_json->len > 20 ? 20 : headers_json->len, headers_json->s);
cJSON_Delete(hdrs);
goto error;
}

if (!strcmp(it->child->string, H_STATUS)) {
LM_ERR("bad 'headers_json' value (':status' header/code "
"already given as 1st argument)\n");
LM_ERR("first %d characters: %.*s ...\n",
headers_json->len > 20 ? 20 : headers_json->len,
headers_json->len > 20 ? 20 : headers_json->len, headers_json->s);
cJSON_Delete(hdrs);
goto error;
}

if (it->child->string[0] != ':') {
pseudo_headers_done = 1;
} else if (pseudo_headers_done) {
LM_ERR("bad response headers ordering: pseudo-header '%s' follows a literal header\n",
it->child->string);
cJSON_Delete(hdrs);
goto error;
}
}
}

r->hdrs = shm_malloc(nh * sizeof *r->hdrs);
if (!r->hdrs) {
LM_ERR("oom\n");
cJSON_Delete(hdrs);
goto error;
}
r->hdrs_len = 1;
nh = 0;

r->hdrs[nh].name = (uint8_t *)shm_strdup(H_STATUS);
r->hdrs[nh].value = (uint8_t *)shm_malloc(4);
if (!r->hdrs[nh].name || !r->hdrs[nh].value) {
LM_ERR("oom (SHM)\n");
cJSON_Delete(hdrs);
h2_response_clean();
goto error;
}

r->hdrs[nh].namelen = strlen((const char *)r->hdrs[nh].name);
sprintf((char *)r->hdrs[nh].value, "%d", *code);
r->hdrs[nh].valuelen = 3;
r->hdrs[nh].flags = NGHTTP2_NV_FLAG_NONE;
nh++;

if (headers_json) {
for (it = hdrs->child; it; it = it->next, nh++, r->hdrs_len++) {
r->hdrs[nh].name = (uint8_t *)shm_strdup(it->child->string);
r->hdrs[nh].value = (uint8_t *)shm_strdup(it->child->valuestring);

if (!r->hdrs[nh].name || !r->hdrs[nh].value) {
LM_ERR("oom (SHM)\n");
cJSON_Delete(hdrs);
h2_response_clean();
goto error;
}

r->hdrs[nh].namelen = strlen((const char *)r->hdrs[nh].name);
r->hdrs[nh].valuelen = strlen((const char *)r->hdrs[nh].value);
r->hdrs[nh].flags = NGHTTP2_NV_FLAG_NONE;
}

cJSON_Delete(hdrs);
}

if (body) {
if (shm_str_dup(&r->body, body) != 0) {
LM_ERR("oom (SHM)\n");
h2_response_clean();
goto error;
}
}

r->code = *code;
pthread_mutex_lock(&r->mutex);
pthread_cond_signal(&r->cond);
pthread_mutex_unlock(&r->mutex);
return 1;

error:
pthread_mutex_lock(&r->mutex);
pthread_cond_signal(&r->cond);
pthread_mutex_unlock(&r->mutex);
return -1;
}


static void mod_destroy(void)
{
return;
Expand Down

0 comments on commit b54a6d8

Please sign in to comment.