|
|
@@ -43,10 +43,15 @@ struct lua_http_header { |
|
|
|
}; |
|
|
|
|
|
|
|
struct lua_http_ud { |
|
|
|
gint parser_state; |
|
|
|
struct worker_task *task; |
|
|
|
gint parser_state; |
|
|
|
struct rspamd_async_session *s; |
|
|
|
memory_pool_t *pool; |
|
|
|
struct rspamd_dns_resolver *resolver; |
|
|
|
struct event_base *ev_base; |
|
|
|
lua_State *L; |
|
|
|
const gchar *callback; |
|
|
|
gint cbref; |
|
|
|
gchar *req_buf; |
|
|
|
gint req_len; |
|
|
|
gint port; |
|
|
@@ -58,19 +63,15 @@ struct lua_http_ud { |
|
|
|
GList *headers; |
|
|
|
}; |
|
|
|
|
|
|
|
static struct worker_task * |
|
|
|
lua_check_task (lua_State * L) |
|
|
|
{ |
|
|
|
void *ud = luaL_checkudata (L, 1, "rspamd{task}"); |
|
|
|
luaL_argcheck (L, ud != NULL, 1, "'task' expected"); |
|
|
|
return ud ? *((struct worker_task **)ud) : NULL; |
|
|
|
} |
|
|
|
|
|
|
|
static void |
|
|
|
lua_http_fin (void *arg) |
|
|
|
{ |
|
|
|
struct lua_http_ud *ud = arg; |
|
|
|
|
|
|
|
if (ud->callback == NULL) { |
|
|
|
/* Unref callback */ |
|
|
|
luaL_unref (ud->L, LUA_REGISTRYINDEX, ud->cbref); |
|
|
|
} |
|
|
|
rspamd_remove_dispatcher (ud->io_dispatcher); |
|
|
|
close (ud->fd); |
|
|
|
} |
|
|
@@ -79,21 +80,35 @@ static void |
|
|
|
lua_http_push_error (gint code, struct lua_http_ud *ud) |
|
|
|
{ |
|
|
|
struct worker_task **ptask; |
|
|
|
gint num; |
|
|
|
gboolean need_unlock = FALSE; |
|
|
|
|
|
|
|
/* Avoid LOR here as mutex can be acquired before in lua_call */ |
|
|
|
if (g_mutex_trylock (lua_mtx)) { |
|
|
|
need_unlock = TRUE; |
|
|
|
} |
|
|
|
|
|
|
|
/* Push error */ |
|
|
|
lua_getglobal (ud->L, ud->callback); |
|
|
|
ptask = lua_newuserdata (ud->L, sizeof (struct worker_task *)); |
|
|
|
lua_setclass (ud->L, "rspamd{task}", -1); |
|
|
|
if (ud->callback) { |
|
|
|
lua_getglobal (ud->L, ud->callback); |
|
|
|
ptask = lua_newuserdata (ud->L, sizeof (struct worker_task *)); |
|
|
|
lua_setclass (ud->L, "rspamd{task}", -1); |
|
|
|
*ptask = ud->task; |
|
|
|
num = 4; |
|
|
|
} |
|
|
|
else { |
|
|
|
lua_rawgeti (ud->L, LUA_REGISTRYINDEX, ud->cbref); |
|
|
|
num = 3; |
|
|
|
} |
|
|
|
|
|
|
|
*ptask = ud->task; |
|
|
|
/* Code */ |
|
|
|
lua_pushnumber (ud->L, code); |
|
|
|
/* Headers */ |
|
|
|
lua_pushnil (ud->L); |
|
|
|
/* Reply */ |
|
|
|
lua_pushnil (ud->L); |
|
|
|
if (lua_pcall (ud->L, 4, 0, 0) != 0) { |
|
|
|
msg_info ("call to %s failed: %s", ud->callback, lua_tostring (ud->L, -1)); |
|
|
|
if (lua_pcall (ud->L, num, 0, 0) != 0) { |
|
|
|
msg_info ("call to %s failed: %s", ud->callback ? ud->callback : "local function", lua_tostring (ud->L, -1)); |
|
|
|
} |
|
|
|
|
|
|
|
if (ud->headers != NULL) { |
|
|
@@ -102,7 +117,10 @@ lua_http_push_error (gint code, struct lua_http_ud *ud) |
|
|
|
} |
|
|
|
|
|
|
|
ud->parser_state = 3; |
|
|
|
remove_normal_event (ud->task->s, lua_http_fin, ud); |
|
|
|
remove_normal_event (ud->s, lua_http_fin, ud); |
|
|
|
if (need_unlock) { |
|
|
|
g_mutex_unlock (lua_mtx); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void |
|
|
@@ -111,13 +129,27 @@ lua_http_push_reply (f_str_t *in, struct lua_http_ud *ud) |
|
|
|
GList *cur; |
|
|
|
struct lua_http_header *header; |
|
|
|
struct worker_task **ptask; |
|
|
|
gint num; |
|
|
|
gboolean need_unlock = FALSE; |
|
|
|
|
|
|
|
/* Push error */ |
|
|
|
lua_getglobal (ud->L, ud->callback); |
|
|
|
ptask = lua_newuserdata (ud->L, sizeof (struct worker_task *)); |
|
|
|
lua_setclass (ud->L, "rspamd{task}", -1); |
|
|
|
/* Avoid LOR here as mutex can be acquired before in lua_call */ |
|
|
|
if (g_mutex_trylock (lua_mtx)) { |
|
|
|
need_unlock = TRUE; |
|
|
|
} |
|
|
|
|
|
|
|
if (ud->callback) { |
|
|
|
/* Push error */ |
|
|
|
lua_getglobal (ud->L, ud->callback); |
|
|
|
ptask = lua_newuserdata (ud->L, sizeof (struct worker_task *)); |
|
|
|
lua_setclass (ud->L, "rspamd{task}", -1); |
|
|
|
|
|
|
|
*ptask = ud->task; |
|
|
|
*ptask = ud->task; |
|
|
|
num = 4; |
|
|
|
} |
|
|
|
else { |
|
|
|
lua_rawgeti (ud->L, LUA_REGISTRYINDEX, ud->cbref); |
|
|
|
num = 3; |
|
|
|
} |
|
|
|
/* Code */ |
|
|
|
lua_pushnumber (ud->L, ud->code); |
|
|
|
/* Headers */ |
|
|
@@ -134,8 +166,8 @@ lua_http_push_reply (f_str_t *in, struct lua_http_ud *ud) |
|
|
|
/* Reply */ |
|
|
|
lua_pushlstring (ud->L, in->begin, in->len); |
|
|
|
|
|
|
|
if (lua_pcall (ud->L, 4, 0, 0) != 0) { |
|
|
|
msg_info ("call to %s failed: %s", ud->callback, lua_tostring (ud->L, -1)); |
|
|
|
if (lua_pcall (ud->L, num, 0, 0) != 0) { |
|
|
|
msg_info ("call to %s failed: %s", ud->callback ? ud->callback : "local function", lua_tostring (ud->L, -1)); |
|
|
|
} |
|
|
|
|
|
|
|
if (ud->headers != NULL) { |
|
|
@@ -143,7 +175,10 @@ lua_http_push_reply (f_str_t *in, struct lua_http_ud *ud) |
|
|
|
ud->headers = NULL; |
|
|
|
} |
|
|
|
|
|
|
|
remove_normal_event (ud->task->s, lua_http_fin, ud); |
|
|
|
remove_normal_event (ud->s, lua_http_fin, ud); |
|
|
|
if (need_unlock) { |
|
|
|
g_mutex_unlock (lua_mtx); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
@@ -184,8 +219,8 @@ lua_http_parse_header_line (struct lua_http_ud *ud, f_str_t *in) |
|
|
|
return FALSE; |
|
|
|
} |
|
|
|
/* Copy name */ |
|
|
|
new = memory_pool_alloc (ud->task->task_pool, sizeof (struct lua_http_header)); |
|
|
|
new->name = memory_pool_alloc (ud->task->task_pool, p - in->begin + 1); |
|
|
|
new = memory_pool_alloc (ud->pool, sizeof (struct lua_http_header)); |
|
|
|
new->name = memory_pool_alloc (ud->pool, p - in->begin + 1); |
|
|
|
rspamd_strlcpy (new->name, in->begin, p - in->begin + 1); |
|
|
|
|
|
|
|
p ++; |
|
|
@@ -193,7 +228,7 @@ lua_http_parse_header_line (struct lua_http_ud *ud, f_str_t *in) |
|
|
|
while (p < in->begin + in->len && g_ascii_isspace (*p)) { |
|
|
|
p ++; |
|
|
|
} |
|
|
|
new->value = memory_pool_alloc (ud->task->task_pool, in->begin + in->len - p + 1); |
|
|
|
new->value = memory_pool_alloc (ud->pool, in->begin + in->len - p + 1); |
|
|
|
rspamd_strlcpy (new->value, p, in->begin + in->len - p + 1); |
|
|
|
|
|
|
|
/* Check content-length */ |
|
|
@@ -260,7 +295,7 @@ lua_http_err_cb (GError * err, void *arg) |
|
|
|
lua_http_push_error (500, ud); |
|
|
|
} |
|
|
|
else { |
|
|
|
remove_normal_event (ud->task->s, lua_http_fin, ud); |
|
|
|
remove_normal_event (ud->s, lua_http_fin, ud); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@@ -292,10 +327,10 @@ lua_http_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) |
|
|
|
|
|
|
|
/* Create dispatcher for HTTP protocol */ |
|
|
|
msec_to_tv (ud->timeout, &tv); |
|
|
|
ud->io_dispatcher = rspamd_create_dispatcher (ud->task->ev_base, ud->fd, BUFFER_LINE, lua_http_read_cb, NULL, |
|
|
|
ud->io_dispatcher = rspamd_create_dispatcher (ud->ev_base, ud->fd, BUFFER_LINE, lua_http_read_cb, NULL, |
|
|
|
lua_http_err_cb, &tv, ud); |
|
|
|
/* Write request */ |
|
|
|
register_async_event (ud->task->s, lua_http_fin, ud, g_quark_from_static_string ("lua http")); |
|
|
|
register_async_event (ud->s, lua_http_fin, ud, g_quark_from_static_string ("lua http")); |
|
|
|
|
|
|
|
if (!rspamd_dispatcher_write (ud->io_dispatcher, ud->req_buf, ud->req_len, TRUE, TRUE)) { |
|
|
|
lua_http_push_error (450, ud); |
|
|
@@ -320,6 +355,9 @@ lua_http_make_request_common (lua_State *L, struct worker_task *task, const gcha |
|
|
|
|
|
|
|
ud = memory_pool_alloc0 (task->task_pool, sizeof (struct lua_http_ud)); |
|
|
|
ud->L = L; |
|
|
|
ud->s = task->s; |
|
|
|
ud->pool = task->task_pool; |
|
|
|
ud->ev_base = task->ev_base; |
|
|
|
ud->task = task; |
|
|
|
/* Preallocate buffer */ |
|
|
|
ud->req_buf = memory_pool_alloc (task->task_pool, s); |
|
|
@@ -376,6 +414,106 @@ lua_http_make_request_common (lua_State *L, struct worker_task *task, const gcha |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Common request function (new version) |
|
|
|
*/ |
|
|
|
static gint |
|
|
|
lua_http_make_request_common_new (lua_State *L, struct rspamd_async_session *session, memory_pool_t *pool, struct event_base *base, gint cbref, |
|
|
|
const gchar *hostname, const gchar *path, const gchar *data, gint top) |
|
|
|
{ |
|
|
|
gint r, s, datalen; |
|
|
|
struct lua_http_ud *ud; |
|
|
|
struct in_addr ina; |
|
|
|
struct timeval tv; |
|
|
|
|
|
|
|
/* Calculate buffer size */ |
|
|
|
datalen = (data != NULL) ? strlen (data) : 0; |
|
|
|
s = MAX_HEADERS_SIZE + sizeof (CRLF) * 3 + strlen (hostname) + strlen (path) + datalen |
|
|
|
+ sizeof ("POST HTTP/1.1"); |
|
|
|
|
|
|
|
ud = memory_pool_alloc0 (pool, sizeof (struct lua_http_ud)); |
|
|
|
ud->L = L; |
|
|
|
ud->pool = pool; |
|
|
|
ud->s = session; |
|
|
|
ud->ev_base = base; |
|
|
|
/* Preallocate buffer */ |
|
|
|
ud->req_buf = memory_pool_alloc (pool, s); |
|
|
|
ud->callback = NULL; |
|
|
|
ud->cbref = cbref; |
|
|
|
|
|
|
|
/* Print request */ |
|
|
|
r = rspamd_snprintf (ud->req_buf, s, "%s %s HTTP/1.1" CRLF |
|
|
|
"Connection: close" CRLF, |
|
|
|
(data != NULL) ? "POST" : "GET", path); |
|
|
|
if (datalen > 0) { |
|
|
|
r += rspamd_snprintf (ud->req_buf + r, s - r, "Content-Length: %d" CRLF, datalen); |
|
|
|
} |
|
|
|
/* Now assume that we have a table with headers at the top of the stack */ |
|
|
|
|
|
|
|
if (lua_gettop (L) > top && lua_istable (L, top + 1)) { |
|
|
|
/* Add headers */ |
|
|
|
lua_pushnil (L); /* first key */ |
|
|
|
while (lua_next (L, top + 1) != 0) { |
|
|
|
r += rspamd_snprintf (ud->req_buf + r, s - r, "%s: %s" CRLF, lua_tostring (L, -2), lua_tostring (L, -1)); |
|
|
|
lua_pop (L, 1); |
|
|
|
} |
|
|
|
} |
|
|
|
/* Now check port and timeout */ |
|
|
|
if (lua_gettop (L) > top + 1) { |
|
|
|
ud->port = lua_tonumber (L, top + 2); |
|
|
|
} |
|
|
|
else { |
|
|
|
ud->port = 80; |
|
|
|
} |
|
|
|
if (lua_gettop (L) > top + 2) { |
|
|
|
ud->timeout = lua_tonumber (L, top + 3); |
|
|
|
} |
|
|
|
else { |
|
|
|
/* Assume default timeout as 1000 msec */ |
|
|
|
ud->timeout = 1000; |
|
|
|
} |
|
|
|
|
|
|
|
if (datalen > 0) { |
|
|
|
r += rspamd_snprintf (ud->req_buf + r, s - r, CRLF "%s", data); |
|
|
|
} |
|
|
|
else { |
|
|
|
r += rspamd_snprintf (ud->req_buf + r, s - r, CRLF); |
|
|
|
} |
|
|
|
|
|
|
|
ud->req_len = r; |
|
|
|
|
|
|
|
if (inet_aton (hostname, &ina) == 0) { |
|
|
|
msg_err ("%s is not valid ip address", hostname); |
|
|
|
luaL_unref (L, LUA_REGISTRYINDEX, cbref); |
|
|
|
lua_pushnil (L); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
ud->fd = make_tcp_socket (&ina, ud->port, FALSE, TRUE); |
|
|
|
|
|
|
|
if (ud->fd == -1) { |
|
|
|
luaL_unref (L, LUA_REGISTRYINDEX, cbref); |
|
|
|
lua_pushnil (L); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
/* Create dispatcher for HTTP protocol */ |
|
|
|
msec_to_tv (ud->timeout, &tv); |
|
|
|
ud->io_dispatcher = rspamd_create_dispatcher (ud->ev_base, ud->fd, BUFFER_LINE, lua_http_read_cb, NULL, |
|
|
|
lua_http_err_cb, &tv, ud); |
|
|
|
/* Write request */ |
|
|
|
register_async_event (ud->s, lua_http_fin, ud, g_quark_from_static_string ("lua http")); |
|
|
|
|
|
|
|
if (!rspamd_dispatcher_write (ud->io_dispatcher, ud->req_buf, ud->req_len, TRUE, TRUE)) { |
|
|
|
luaL_unref (L, LUA_REGISTRYINDEX, cbref); |
|
|
|
lua_pushnil (L); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
* Typical usage: |
|
|
|
* rspamd_http.post_request(task, 'callback', 'hostname', 'path', 'data'[, headers -> { name = 'value' }]) |
|
|
@@ -383,21 +521,55 @@ lua_http_make_request_common (lua_State *L, struct worker_task *task, const gcha |
|
|
|
static gint |
|
|
|
lua_http_make_post_request (lua_State *L) |
|
|
|
{ |
|
|
|
struct worker_task *task = lua_check_task (L); |
|
|
|
struct worker_task *task, **ptask; |
|
|
|
memory_pool_t *pool, **ppool; |
|
|
|
struct rspamd_async_session *session, **psession; |
|
|
|
struct event_base *base, **pbase; |
|
|
|
const gchar *hostname, *path, *data, *callback; |
|
|
|
gint cbref; |
|
|
|
|
|
|
|
|
|
|
|
/* Check whether we have a task object */ |
|
|
|
ptask = lua_check_class (L, 1, "rspamd{task}"); |
|
|
|
task = ptask ? *(ptask) : NULL; |
|
|
|
|
|
|
|
if (!task) { |
|
|
|
psession = luaL_checkudata (L, 1, "rspamd{session}"); |
|
|
|
luaL_argcheck (L, psession != NULL, 1, "'session' expected"); |
|
|
|
session = psession ? *(psession) : NULL; |
|
|
|
ppool = luaL_checkudata (L, 2, "rspamd{mempool}"); |
|
|
|
luaL_argcheck (L, ppool != NULL, 2, "'mempool' expected"); |
|
|
|
pool = ppool ? *(ppool) : NULL; |
|
|
|
pbase = luaL_checkudata (L, 3, "rspamd{ev_base}"); |
|
|
|
luaL_argcheck (L, ppool != NULL, 3, "'ev_base' expected"); |
|
|
|
base = pbase ? *(pbase) : NULL; |
|
|
|
} |
|
|
|
|
|
|
|
/* Now extract hostname, path and data */ |
|
|
|
|
|
|
|
callback = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 2)); |
|
|
|
hostname = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 3)); |
|
|
|
path = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 4)); |
|
|
|
data = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 5)); |
|
|
|
if (task) { |
|
|
|
callback = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 2)); |
|
|
|
hostname = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 3)); |
|
|
|
path = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 4)); |
|
|
|
data = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 5)); |
|
|
|
|
|
|
|
if (callback != NULL && hostname != NULL && path != NULL && data != NULL) { |
|
|
|
return lua_http_make_request_common (L, task, callback, hostname, path, data, 5); |
|
|
|
if (callback != NULL && hostname != NULL && path != NULL && data != NULL) { |
|
|
|
return lua_http_make_request_common (L, task, callback, hostname, path, data, 5); |
|
|
|
} |
|
|
|
else { |
|
|
|
msg_info ("invalid arguments number"); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
msg_info ("invalid arguments number"); |
|
|
|
/* Common version */ |
|
|
|
hostname = memory_pool_strdup (pool, luaL_checkstring (L, 4)); |
|
|
|
path = memory_pool_strdup (pool, luaL_checkstring (L, 5)); |
|
|
|
data = memory_pool_strdup (pool, luaL_checkstring (L, 6)); |
|
|
|
if (session != NULL && pool != NULL && hostname != NULL && path != NULL && data != NULL && lua_isfunction (L, 7)) { |
|
|
|
lua_pushvalue (L, 7); |
|
|
|
cbref = luaL_ref (L, LUA_REGISTRYINDEX); |
|
|
|
return lua_http_make_request_common_new (L, session, pool, base, cbref, hostname, path, data, 7); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
@@ -410,20 +582,53 @@ lua_http_make_post_request (lua_State *L) |
|
|
|
static gint |
|
|
|
lua_http_make_get_request (lua_State *L) |
|
|
|
{ |
|
|
|
struct worker_task *task = lua_check_task (L); |
|
|
|
struct worker_task *task, **ptask; |
|
|
|
memory_pool_t *pool, **ppool; |
|
|
|
struct rspamd_async_session *session, **psession; |
|
|
|
struct event_base *base, **pbase; |
|
|
|
const gchar *hostname, *path, *callback; |
|
|
|
gint cbref; |
|
|
|
|
|
|
|
|
|
|
|
/* Check whether we have a task object */ |
|
|
|
ptask = lua_check_class (L, 1, "rspamd{task}"); |
|
|
|
task = ptask ? *(ptask) : NULL; |
|
|
|
|
|
|
|
if (!task) { |
|
|
|
psession = luaL_checkudata (L, 1, "rspamd{session}"); |
|
|
|
luaL_argcheck (L, psession != NULL, 1, "'session' expected"); |
|
|
|
session = psession ? *(psession) : NULL; |
|
|
|
ppool = luaL_checkudata (L, 2, "rspamd{mempool}"); |
|
|
|
luaL_argcheck (L, ppool != NULL, 2, "'mempool' expected"); |
|
|
|
pool = ppool ? *(ppool) : NULL; |
|
|
|
pbase = luaL_checkudata (L, 3, "rspamd{ev_base}"); |
|
|
|
luaL_argcheck (L, ppool != NULL, 3, "'ev_base' expected"); |
|
|
|
base = pbase ? *(pbase) : NULL; |
|
|
|
} |
|
|
|
|
|
|
|
/* Now extract hostname, path and data */ |
|
|
|
|
|
|
|
callback = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 2)); |
|
|
|
hostname = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 3)); |
|
|
|
path = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 4)); |
|
|
|
if (task) { |
|
|
|
callback = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 2)); |
|
|
|
hostname = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 3)); |
|
|
|
path = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 4)); |
|
|
|
|
|
|
|
if (callback != NULL && hostname != NULL && path != NULL) { |
|
|
|
return lua_http_make_request_common (L, task, callback, hostname, path, NULL, 4); |
|
|
|
if (callback != NULL && hostname != NULL && path != NULL) { |
|
|
|
return lua_http_make_request_common (L, task, callback, hostname, path, NULL, 4); |
|
|
|
} |
|
|
|
else { |
|
|
|
msg_info ("invalid arguments number"); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
msg_info ("invalid arguments number"); |
|
|
|
/* Common version */ |
|
|
|
hostname = memory_pool_strdup (pool, luaL_checkstring (L, 4)); |
|
|
|
path = memory_pool_strdup (pool, luaL_checkstring (L, 5)); |
|
|
|
if (session != NULL && pool != NULL && hostname != NULL && path != NULL && lua_isfunction (L, 6)) { |
|
|
|
lua_pushvalue (L, 6); |
|
|
|
cbref = luaL_ref (L, LUA_REGISTRYINDEX); |
|
|
|
return lua_http_make_request_common_new (L, session, pool, base, cbref, hostname, path, NULL, 6); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |