You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

пре 8 година
пре 8 година
пре 8 година
пре 9 година
пре 9 година
пре 9 година
пре 10 година
пре 10 година
пре 10 година
пре 5 година
пре 10 година
пре 5 година
пре 10 година
пре 9 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 9 година
пре 5 година
пре 5 година
пре 5 година
пре 5 година
пре 5 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 5 година
пре 11 година
пре 10 година
пре 5 година
пре 10 година
пре 10 година
пре 10 година
пре 5 година
пре 5 година
пре 5 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 5 година
пре 10 година
пре 5 година
пре 5 година
пре 10 година
пре 10 година
пре 10 година
пре 9 година
пре 10 година
пре 9 година
пре 9 година
пре 9 година
пре 9 година
пре 9 година
пре 10 година
пре 10 година
пре 9 година
пре 9 година
пре 10 година
пре 9 година
пре 10 година
пре 6 година
пре 10 година
пре 6 година
пре 10 година
пре 6 година
пре 7 година
пре 10 година
пре 7 година
пре 10 година
пре 7 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 9 година
пре 9 година
пре 10 година
пре 9 година
пре 7 година
пре 9 година
пре 9 година
пре 7 година
пре 9 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 9 година
пре 9 година
пре 10 година
пре 10 година
пре 8 година
пре 9 година
пре 8 година
пре 9 година
пре 9 година
пре 9 година
пре 9 година
пре 10 година
пре 10 година
пре 9 година
пре 10 година
пре 5 година
пре 5 година
пре 5 година
пре 10 година
пре 9 година
пре 9 година
пре 10 година
пре 5 година
пре 5 година
пре 5 година
пре 10 година
пре 9 година
пре 10 година
пре 10 година
пре 9 година
пре 10 година
пре 9 година
пре 9 година
пре 9 година
пре 9 година
пре 9 година
пре 5 година
пре 5 година
пре 5 година
пре 5 година
пре 5 година
пре 9 година
пре 9 година
пре 9 година
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082
  1. /*-
  2. * Copyright 2016 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "lua_common.h"
  17. #include "lua_thread_pool.h"
  18. #include "http_private.h"
  19. #include "ref.h"
  20. #include "unix-std.h"
  21. #include "zlib.h"
  22. /***
  23. * @module rspamd_http
  24. * Rspamd HTTP module represents HTTP asynchronous client available from LUA code.
  25. * This module hides all complexity: DNS resolving, sessions management, zero-copy
  26. * text transfers and so on under the hood.
  27. * @example
  28. local rspamd_http = require "rspamd_http"
  29. local function symbol_callback(task)
  30. local function http_callback(err_message, code, body, headers)
  31. task:insert_result('SYMBOL', 1) -- task is available via closure
  32. end
  33. rspamd_http.request({
  34. task=task,
  35. url='http://example.com/data',
  36. body=task:get_content(),
  37. callback=http_callback,
  38. headers={Header='Value', OtherHeader='Value'},
  39. mime_type='text/plain',
  40. })
  41. end
  42. */
  43. #define MAX_HEADERS_SIZE 8192
  44. static const gchar *M = "rspamd lua http";
  45. LUA_FUNCTION_DEF (http, request);
  46. static const struct luaL_reg httplib_m[] = {
  47. LUA_INTERFACE_DEF (http, request),
  48. {"__tostring", rspamd_lua_class_tostring},
  49. {NULL, NULL}
  50. };
  51. #define RSPAMD_LUA_HTTP_FLAG_TEXT (1 << 0)
  52. #define RSPAMD_LUA_HTTP_FLAG_NOVERIFY (1 << 1)
  53. #define RSPAMD_LUA_HTTP_FLAG_RESOLVED (1 << 2)
  54. #define RSPAMD_LUA_HTTP_FLAG_KEEP_ALIVE (1 << 3)
  55. #define RSPAMD_LUA_HTTP_FLAG_YIELDED (1 << 4)
  56. struct lua_http_cbdata {
  57. struct rspamd_http_connection *conn;
  58. struct rspamd_async_session *session;
  59. struct rspamd_symcache_item *item;
  60. struct rspamd_http_message *msg;
  61. struct event_base *ev_base;
  62. struct rspamd_config *cfg;
  63. struct rspamd_task *task;
  64. struct timeval tv;
  65. struct rspamd_cryptobox_keypair *local_kp;
  66. struct rspamd_cryptobox_pubkey *peer_pk;
  67. rspamd_inet_addr_t *addr;
  68. gchar *mime_type;
  69. gchar *host;
  70. gchar *auth;
  71. const gchar *url;
  72. gsize max_size;
  73. gint flags;
  74. gint fd;
  75. gint cbref;
  76. struct thread_entry *thread;
  77. ref_entry_t ref;
  78. };
  79. static const int default_http_timeout = 5000;
  80. static struct rspamd_dns_resolver *
  81. lua_http_global_resolver (struct event_base *ev_base)
  82. {
  83. static struct rspamd_dns_resolver *global_resolver;
  84. if (global_resolver == NULL) {
  85. global_resolver = rspamd_dns_resolver_init (NULL, ev_base, NULL);
  86. }
  87. return global_resolver;
  88. }
  89. static void
  90. lua_http_fin (gpointer arg)
  91. {
  92. struct lua_http_cbdata *cbd = (struct lua_http_cbdata *)arg;
  93. if (cbd->cbref != -1) {
  94. luaL_unref (cbd->cfg->lua_state, LUA_REGISTRYINDEX, cbd->cbref);
  95. }
  96. if (cbd->conn) {
  97. /* Here we already have a connection, so we need to unref it */
  98. rspamd_http_connection_unref (cbd->conn);
  99. }
  100. else if (cbd->msg != NULL) {
  101. /* We need to free message */
  102. rspamd_http_message_unref (cbd->msg);
  103. }
  104. if (cbd->fd != -1) {
  105. close (cbd->fd);
  106. }
  107. if (cbd->addr) {
  108. rspamd_inet_address_free (cbd->addr);
  109. }
  110. if (cbd->mime_type) {
  111. g_free (cbd->mime_type);
  112. }
  113. if (cbd->auth) {
  114. g_free (cbd->auth);
  115. }
  116. if (cbd->local_kp) {
  117. rspamd_keypair_unref (cbd->local_kp);
  118. }
  119. if (cbd->peer_pk) {
  120. rspamd_pubkey_unref (cbd->peer_pk);
  121. }
  122. g_free (cbd);
  123. }
  124. static void
  125. lua_http_cbd_dtor (struct lua_http_cbdata *cbd)
  126. {
  127. if (cbd->session) {
  128. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_RESOLVED) {
  129. /* Event is added merely for resolved events */
  130. if (cbd->item) {
  131. rspamd_symcache_item_async_dec_check (cbd->task, cbd->item, M);
  132. }
  133. rspamd_session_remove_event (cbd->session, lua_http_fin, cbd);
  134. }
  135. }
  136. else {
  137. lua_http_fin (cbd);
  138. }
  139. }
  140. static void
  141. lua_http_push_error (struct lua_http_cbdata *cbd, const char *err)
  142. {
  143. struct lua_callback_state lcbd;
  144. lua_State *L;
  145. lua_thread_pool_prepare_callback (cbd->cfg->lua_thread_pool, &lcbd);
  146. L = lcbd.L;
  147. lua_rawgeti (L, LUA_REGISTRYINDEX, cbd->cbref);
  148. lua_pushstring (L, err);
  149. if (cbd->item) {
  150. rspamd_symcache_set_cur_item (cbd->task, cbd->item);
  151. }
  152. if (lua_pcall (L, 1, 0, 0) != 0) {
  153. msg_info ("callback call failed: %s", lua_tostring (L, -1));
  154. lua_pop (L, 1);
  155. }
  156. lua_thread_pool_restore_callback (&lcbd);
  157. }
  158. static void lua_http_resume_handler (struct rspamd_http_connection *conn,
  159. struct rspamd_http_message *msg, const char *err);
  160. static void
  161. lua_http_error_handler (struct rspamd_http_connection *conn, GError *err)
  162. {
  163. struct lua_http_cbdata *cbd = (struct lua_http_cbdata *)conn->ud;
  164. if (cbd->cbref == -1) {
  165. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_YIELDED) {
  166. cbd->flags &= ~RSPAMD_LUA_HTTP_FLAG_YIELDED;
  167. lua_http_resume_handler (conn, NULL, err->message);
  168. }
  169. else {
  170. /* TODO: kill me please */
  171. msg_info ("lost HTTP error from %s in coroutines mess: %s",
  172. rspamd_inet_address_to_string_pretty (cbd->addr),
  173. err->message);
  174. }
  175. }
  176. else {
  177. lua_http_push_error (cbd, err->message);
  178. }
  179. REF_RELEASE (cbd);
  180. }
  181. static int
  182. lua_http_finish_handler (struct rspamd_http_connection *conn,
  183. struct rspamd_http_message *msg)
  184. {
  185. struct lua_http_cbdata *cbd = (struct lua_http_cbdata *)conn->ud;
  186. struct rspamd_http_header *h, *htmp;
  187. const gchar *body;
  188. gsize body_len;
  189. struct lua_callback_state lcbd;
  190. lua_State *L;
  191. if (cbd->cbref == -1) {
  192. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_YIELDED) {
  193. cbd->flags &= ~RSPAMD_LUA_HTTP_FLAG_YIELDED;
  194. lua_http_resume_handler (conn, msg, NULL);
  195. }
  196. else {
  197. /* TODO: kill me please */
  198. msg_err ("lost HTTP data from %s in coroutines mess",
  199. rspamd_inet_address_to_string_pretty (cbd->addr));
  200. }
  201. REF_RELEASE (cbd);
  202. return 0;
  203. }
  204. lua_thread_pool_prepare_callback (cbd->cfg->lua_thread_pool, &lcbd);
  205. L = lcbd.L;
  206. lua_rawgeti (L, LUA_REGISTRYINDEX, cbd->cbref);
  207. /* Error */
  208. lua_pushnil (L);
  209. /* Reply code */
  210. lua_pushinteger (L, msg->code);
  211. /* Body */
  212. body = rspamd_http_message_get_body (msg, &body_len);
  213. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_TEXT) {
  214. struct rspamd_lua_text *t;
  215. t = lua_newuserdata (L, sizeof (*t));
  216. rspamd_lua_setclass (L, "rspamd{text}", -1);
  217. t->start = body;
  218. t->len = body_len;
  219. t->flags = 0;
  220. }
  221. else {
  222. if (body_len > 0) {
  223. lua_pushlstring (L, body, body_len);
  224. }
  225. else {
  226. lua_pushnil (L);
  227. }
  228. }
  229. /* Headers */
  230. lua_newtable (L);
  231. HASH_ITER (hh, msg->headers, h, htmp) {
  232. /*
  233. * Lowercase header name, as Lua cannot search in caseless matter
  234. */
  235. rspamd_str_lc (h->combined->str, h->name.len);
  236. lua_pushlstring (L, h->name.begin, h->name.len);
  237. lua_pushlstring (L, h->value.begin, h->value.len);
  238. lua_settable (L, -3);
  239. }
  240. if (cbd->item) {
  241. /* Replace watcher to deal with nested calls */
  242. rspamd_symcache_set_cur_item (cbd->task, cbd->item);
  243. }
  244. if (lua_pcall (L, 4, 0, 0) != 0) {
  245. msg_info ("callback call failed: %s", lua_tostring (L, -1));
  246. lua_pop (L, 1);
  247. }
  248. REF_RELEASE (cbd);
  249. lua_thread_pool_restore_callback (&lcbd);
  250. return 0;
  251. }
  252. /*
  253. * resumes yielded thread
  254. */
  255. static void
  256. lua_http_resume_handler (struct rspamd_http_connection *conn,
  257. struct rspamd_http_message *msg, const char *err)
  258. {
  259. struct lua_http_cbdata *cbd = (struct lua_http_cbdata *)conn->ud;
  260. lua_State *L = cbd->thread->lua_state;
  261. const gchar *body;
  262. gsize body_len;
  263. struct rspamd_http_header *h, *htmp;
  264. if (err) {
  265. lua_pushstring (L, err);
  266. lua_pushnil (L);
  267. }
  268. else {
  269. /*
  270. * 1 - nil (error)
  271. * 2 - table:
  272. * code (int)
  273. * content (string)
  274. * headers (table: header -> value)
  275. */
  276. lua_pushnil (L); // error code
  277. lua_createtable (L, 0, 3);
  278. /* code */
  279. lua_pushliteral (L, "code");
  280. lua_pushinteger (L, msg->code);
  281. lua_settable (L, -3);
  282. /* content */
  283. lua_pushliteral (L, "content");
  284. body = rspamd_http_message_get_body (msg, &body_len);
  285. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_TEXT) {
  286. struct rspamd_lua_text *t;
  287. t = lua_newuserdata (L, sizeof (*t));
  288. rspamd_lua_setclass (L, "rspamd{text}", -1);
  289. t->start = body;
  290. t->len = body_len;
  291. t->flags = 0;
  292. }
  293. else {
  294. if (body_len > 0) {
  295. lua_pushlstring (L, body, body_len);
  296. }
  297. else {
  298. lua_pushnil (L);
  299. }
  300. }
  301. lua_settable (L, -3);
  302. /* headers */
  303. lua_pushliteral (L, "headers");
  304. lua_newtable (L);
  305. HASH_ITER (hh, msg->headers, h, htmp) {
  306. /*
  307. * Lowercase header name, as Lua cannot search in caseless matter
  308. */
  309. rspamd_str_lc (h->combined->str, h->name.len);
  310. lua_pushlstring (L, h->name.begin, h->name.len);
  311. lua_pushlstring (L, h->value.begin, h->value.len);
  312. lua_settable (L, -3);
  313. }
  314. lua_settable (L, -3);
  315. }
  316. if (cbd->item) {
  317. /* Replace watcher to deal with nested calls */
  318. rspamd_symcache_set_cur_item (cbd->task, cbd->item);
  319. }
  320. lua_thread_resume (cbd->thread, 2);
  321. }
  322. static gboolean
  323. lua_http_make_connection (struct lua_http_cbdata *cbd)
  324. {
  325. rspamd_inet_address_set_port (cbd->addr, cbd->msg->port);
  326. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_KEEP_ALIVE) {
  327. cbd->fd = -1; /* FD is owned by keepalive connection */
  328. cbd->conn = rspamd_http_connection_new_keepalive (
  329. NULL, /* Default context */
  330. NULL,
  331. lua_http_error_handler,
  332. lua_http_finish_handler,
  333. cbd->addr,
  334. cbd->host);
  335. }
  336. else {
  337. cbd->fd = -1;
  338. cbd->conn = rspamd_http_connection_new_client (
  339. NULL, /* Default context */
  340. NULL,
  341. lua_http_error_handler,
  342. lua_http_finish_handler,
  343. RSPAMD_HTTP_CLIENT_SIMPLE,
  344. cbd->addr);
  345. }
  346. if (cbd->conn) {
  347. if (cbd->local_kp) {
  348. rspamd_http_connection_set_key (cbd->conn, cbd->local_kp);
  349. }
  350. if (cbd->peer_pk) {
  351. rspamd_http_message_set_peer_key (cbd->msg, cbd->peer_pk);
  352. }
  353. if (cbd->flags & RSPAMD_LUA_HTTP_FLAG_NOVERIFY) {
  354. cbd->msg->flags |= RSPAMD_HTTP_FLAG_SSL_NOVERIFY;
  355. }
  356. if (cbd->max_size) {
  357. rspamd_http_connection_set_max_size (cbd->conn, cbd->max_size);
  358. }
  359. if (cbd->auth) {
  360. rspamd_http_message_add_header (cbd->msg, "Authorization",
  361. cbd->auth);
  362. }
  363. if (cbd->session) {
  364. rspamd_session_add_event (cbd->session,
  365. (event_finalizer_t) lua_http_fin, cbd,
  366. M);
  367. cbd->flags |= RSPAMD_LUA_HTTP_FLAG_RESOLVED;
  368. }
  369. if (cbd->item) {
  370. rspamd_symcache_item_async_inc (cbd->task, cbd->item, M);
  371. }
  372. struct rspamd_http_message *msg = cbd->msg;
  373. /* Message is now owned by a connection object */
  374. cbd->msg = NULL;
  375. rspamd_http_connection_write_message (cbd->conn, msg,
  376. cbd->host, cbd->mime_type, cbd,
  377. &cbd->tv);
  378. return TRUE;
  379. }
  380. return FALSE;
  381. }
  382. static void
  383. lua_http_dns_handler (struct rdns_reply *reply, gpointer ud)
  384. {
  385. struct lua_http_cbdata *cbd = (struct lua_http_cbdata *)ud;
  386. if (reply->code != RDNS_RC_NOERROR) {
  387. lua_http_push_error (cbd, "unable to resolve host");
  388. REF_RELEASE (cbd);
  389. }
  390. else {
  391. if (reply->entries->type == RDNS_REQUEST_A) {
  392. cbd->addr = rspamd_inet_address_new (AF_INET,
  393. &reply->entries->content.a.addr);
  394. }
  395. else if (reply->entries->type == RDNS_REQUEST_AAAA) {
  396. cbd->addr = rspamd_inet_address_new (AF_INET6,
  397. &reply->entries->content.aaa.addr);
  398. }
  399. REF_RETAIN (cbd);
  400. if (!lua_http_make_connection (cbd)) {
  401. lua_http_push_error (cbd, "unable to make connection to the host");
  402. if (cbd->ref.refcount > 1) {
  403. REF_RELEASE (cbd);
  404. }
  405. REF_RELEASE (cbd);
  406. return;
  407. }
  408. REF_RELEASE (cbd);
  409. }
  410. if (cbd->item) {
  411. rspamd_symcache_item_async_dec_check (cbd->task, cbd->item, M);
  412. }
  413. }
  414. static void
  415. lua_http_push_headers (lua_State *L, struct rspamd_http_message *msg)
  416. {
  417. const char *name, *value;
  418. gint i, sz;
  419. lua_pushnil (L);
  420. while (lua_next (L, -2) != 0) {
  421. lua_pushvalue (L, -2);
  422. name = lua_tostring (L, -1);
  423. sz = rspamd_lua_table_size (L, -2);
  424. if (sz != 0 && name != NULL) {
  425. for (i = 1; i <= sz ; i++) {
  426. lua_rawgeti (L, -2, i);
  427. value = lua_tostring (L, -1);
  428. if (value != NULL) {
  429. rspamd_http_message_add_header (msg, name, value);
  430. }
  431. lua_pop (L, 1);
  432. }
  433. } else {
  434. value = lua_tostring (L, -2);
  435. if (name != NULL && value != NULL) {
  436. rspamd_http_message_add_header (msg, name, value);
  437. }
  438. }
  439. lua_pop (L, 2);
  440. }
  441. }
  442. /***
  443. * @function rspamd_http.request({params...})
  444. * This function creates HTTP request and accepts several parameters as a table using key=value syntax.
  445. * Required params are:
  446. *
  447. * - `url`
  448. * - `task`
  449. *
  450. * In taskless mode, instead of `task` required are:
  451. *
  452. * - `ev_base`
  453. * - `config`
  454. *
  455. * @param {string} url specifies URL for a request in the standard URI form (e.g. 'http://example.com/path')
  456. * @param {function} callback specifies callback function in format `function (err_message, code, body, headers)` that is called on HTTP request completion. if this parameter is missing, the function performs "pseudo-synchronous" call (see [Synchronous and Asynchronous API overview](/doc/lua/sync_async.html#API-example-http-module)
  457. * @param {task} task if called from symbol handler it is generally a good idea to use the common task objects: event base, DNS resolver and events session
  458. * @param {table} headers optional headers in form `[name='value', name='value']`
  459. * @param {string} mime_type MIME type of the HTTP content (for example, `text/html`)
  460. * @param {string/text} body full body content, can be opaque `rspamd{text}` to avoid data copying
  461. * @param {number} timeout floating point request timeout value in seconds (default is 5.0 seconds)
  462. * @param {resolver} resolver to perform DNS-requests. Usually got from either `task` or `config`
  463. * @param {boolean} gzip if true, body of the requests will be compressed
  464. * @param {boolean} no_ssl_verify disable SSL peer checks
  465. * @param {boolean} keepalive enable keep-alive pool
  466. * @param {string} user for HTTP authentication
  467. * @param {string} password for HTTP authentication, only if "user" present
  468. * @return {boolean} `true`, in **async** mode, if a request has been successfully scheduled. If this value is `false` then some error occurred, the callback thus will not be called.
  469. * @return In **sync** mode `string|nil, nil|table` In sync mode error message if any and response as table: `int` _code_, `string` _content_ and `table` _headers_ (header -> value)
  470. */
  471. static gint
  472. lua_http_request (lua_State *L)
  473. {
  474. LUA_TRACE_POINT;
  475. struct event_base *ev_base;
  476. struct rspamd_http_message *msg;
  477. struct lua_http_cbdata *cbd;
  478. struct rspamd_dns_resolver *resolver;
  479. struct rspamd_async_session *session = NULL;
  480. struct rspamd_lua_text *t;
  481. struct rspamd_task *task = NULL;
  482. struct rspamd_config *cfg = NULL;
  483. struct rspamd_cryptobox_pubkey *peer_key = NULL;
  484. struct rspamd_cryptobox_keypair *local_kp = NULL;
  485. const gchar *url, *lua_body;
  486. rspamd_fstring_t *body = NULL;
  487. gint cbref = -1;
  488. gsize bodylen;
  489. gdouble timeout = default_http_timeout;
  490. gint flags = 0;
  491. gchar *mime_type = NULL;
  492. gchar *auth = NULL;
  493. gsize max_size = 0;
  494. gboolean gzip = FALSE;
  495. if (lua_gettop (L) >= 2) {
  496. /* url, callback and event_base format */
  497. url = luaL_checkstring (L, 1);
  498. if (url == NULL || lua_type (L, 2) != LUA_TFUNCTION) {
  499. msg_err ("http request has bad params");
  500. lua_pushboolean (L, FALSE);
  501. return 1;
  502. }
  503. lua_pushvalue (L, 2);
  504. cbref = luaL_ref (L, LUA_REGISTRYINDEX);
  505. if (lua_gettop (L) >= 3 && rspamd_lua_check_udata_maybe (L, 3, "rspamd{ev_base}")) {
  506. ev_base = *(struct event_base **)lua_touserdata (L, 3);
  507. }
  508. else {
  509. ev_base = NULL;
  510. }
  511. if (lua_gettop (L) >= 4 && rspamd_lua_check_udata_maybe (L, 4, "rspamd{resolver}")) {
  512. resolver = *(struct rspamd_dns_resolver **)lua_touserdata (L, 4);
  513. }
  514. else {
  515. resolver = lua_http_global_resolver (ev_base);
  516. }
  517. if (lua_gettop (L) >= 5 && rspamd_lua_check_udata_maybe (L, 5, "rspamd{session}")) {
  518. session = *(struct rspamd_async_session **)lua_touserdata (L, 5);
  519. }
  520. else {
  521. session = NULL;
  522. }
  523. msg = rspamd_http_message_from_url (url);
  524. if (msg == NULL) {
  525. lua_pushboolean (L, FALSE);
  526. return 1;
  527. }
  528. }
  529. else if (lua_type (L, 1) == LUA_TTABLE) {
  530. lua_pushstring (L, "url");
  531. lua_gettable (L, 1);
  532. url = luaL_checkstring (L, -1);
  533. lua_pop (L, 1);
  534. lua_pushstring (L, "callback");
  535. lua_gettable (L, 1);
  536. if (url == NULL || lua_type (L, -1) != LUA_TFUNCTION) {
  537. lua_pop (L, 1);
  538. } else {
  539. cbref = luaL_ref (L, LUA_REGISTRYINDEX);
  540. }
  541. lua_pushstring (L, "task");
  542. lua_gettable (L, 1);
  543. if (lua_type (L, -1) == LUA_TUSERDATA) {
  544. task = lua_check_task (L, -1);
  545. ev_base = task->ev_base;
  546. resolver = task->resolver;
  547. session = task->s;
  548. cfg = task->cfg;
  549. }
  550. lua_pop (L, 1);
  551. if (task == NULL) {
  552. lua_pushstring (L, "ev_base");
  553. lua_gettable (L, 1);
  554. if (rspamd_lua_check_udata_maybe (L, -1, "rspamd{ev_base}")) {
  555. ev_base = *(struct event_base **)lua_touserdata (L, -1);
  556. }
  557. else {
  558. ev_base = NULL;
  559. }
  560. lua_pop (L, 1);
  561. lua_pushstring (L, "session");
  562. lua_gettable (L, 1);
  563. if (rspamd_lua_check_udata_maybe (L, -1, "rspamd{session}")) {
  564. session = *(struct rspamd_async_session **)lua_touserdata (L, -1);
  565. }
  566. else {
  567. session = NULL;
  568. }
  569. lua_pop (L, 1);
  570. lua_pushstring (L, "config");
  571. lua_gettable (L, 1);
  572. if (rspamd_lua_check_udata_maybe (L, -1, "rspamd{config}")) {
  573. cfg = *(struct rspamd_config **)lua_touserdata (L, -1);
  574. }
  575. else {
  576. cfg = NULL;
  577. }
  578. lua_pop (L, 1);
  579. lua_pushstring (L, "resolver");
  580. lua_gettable (L, 1);
  581. if (rspamd_lua_check_udata_maybe (L, -1, "rspamd{resolver}")) {
  582. resolver = *(struct rspamd_dns_resolver **)lua_touserdata (L, -1);
  583. }
  584. else {
  585. if (cfg && cfg->dns_resolver) {
  586. resolver = cfg->dns_resolver;
  587. }
  588. else {
  589. resolver = lua_http_global_resolver (ev_base);
  590. }
  591. }
  592. lua_pop (L, 1);
  593. }
  594. msg = rspamd_http_message_from_url (url);
  595. if (msg == NULL) {
  596. msg_err ("cannot create HTTP message from url %s", url);
  597. lua_pushboolean (L, FALSE);
  598. return 1;
  599. }
  600. lua_pushstring (L, "headers");
  601. lua_gettable (L, 1);
  602. if (lua_type (L, -1) == LUA_TTABLE) {
  603. lua_http_push_headers (L, msg);
  604. }
  605. lua_pop (L, 1);
  606. lua_pushstring (L, "timeout");
  607. lua_gettable (L, 1);
  608. if (lua_type (L, -1) == LUA_TNUMBER) {
  609. timeout = lua_tonumber (L, -1) * 1000.;
  610. }
  611. lua_pop (L, 1);
  612. lua_pushstring (L, "mime_type");
  613. lua_gettable (L, 1);
  614. if (lua_type (L, -1) == LUA_TSTRING) {
  615. mime_type = g_strdup (lua_tostring (L, -1));
  616. }
  617. lua_pop (L, 1);
  618. lua_pushstring (L, "body");
  619. lua_gettable (L, 1);
  620. if (lua_type (L, -1) == LUA_TSTRING) {
  621. lua_body = lua_tolstring (L, -1, &bodylen);
  622. body = rspamd_fstring_new_init (lua_body, bodylen);
  623. }
  624. else if (lua_type (L, -1) == LUA_TUSERDATA) {
  625. t = lua_check_text (L, -1);
  626. /* TODO: think about zero-copy possibilities */
  627. if (t) {
  628. body = rspamd_fstring_new_init (t->start, t->len);
  629. }
  630. else {
  631. rspamd_http_message_unref (msg);
  632. g_free (mime_type);
  633. return luaL_error (L, "invalid body argument type: %s",
  634. lua_typename (L, lua_type (L, -1)));
  635. }
  636. }
  637. else if (lua_type (L, -1) == LUA_TTABLE) {
  638. body = rspamd_fstring_new ();
  639. for (lua_pushnil (L); lua_next (L, -2); lua_pop (L, 1)) {
  640. if (lua_type (L, -1) == LUA_TSTRING) {
  641. lua_body = lua_tolstring (L, -1, &bodylen);
  642. body = rspamd_fstring_append (body, lua_body, bodylen);
  643. }
  644. else if (lua_type (L, -1) == LUA_TUSERDATA) {
  645. t = lua_check_text (L, -1);
  646. if (t) {
  647. body = rspamd_fstring_append (body, t->start, t->len);
  648. }
  649. else {
  650. rspamd_http_message_unref (msg);
  651. if (body) {
  652. rspamd_fstring_free (body);
  653. }
  654. if (mime_type) {
  655. g_free (mime_type);
  656. }
  657. return luaL_error (L, "invalid body argument: %s",
  658. lua_typename (L, lua_type (L, -1)));
  659. }
  660. }
  661. else {
  662. rspamd_http_message_unref (msg);
  663. if (body) {
  664. rspamd_fstring_free (body);
  665. }
  666. return luaL_error (L, "invalid body argument type: %s",
  667. lua_typename (L, lua_type (L, -1)));
  668. }
  669. }
  670. }
  671. else if (lua_type (L, -1) != LUA_TNONE && lua_type (L, -1) != LUA_TNIL) {
  672. rspamd_http_message_unref (msg);
  673. return luaL_error (L, "invalid body argument type: %s",
  674. lua_typename (L, lua_type (L, -1)));
  675. }
  676. lua_pop (L, 1);
  677. lua_pushstring (L, "peer_key");
  678. lua_gettable (L, 1);
  679. if (lua_type (L, -1) == LUA_TSTRING) {
  680. const gchar *in;
  681. gsize inlen;
  682. in = lua_tolstring (L, -1, &inlen);
  683. peer_key = rspamd_pubkey_from_base32 (in, inlen,
  684. RSPAMD_KEYPAIR_KEX, RSPAMD_CRYPTOBOX_MODE_25519);
  685. }
  686. lua_pop (L, 1);
  687. lua_pushstring (L, "keypair");
  688. lua_gettable (L, 1);
  689. if (lua_type (L, -1) == LUA_TTABLE) {
  690. ucl_object_t *kp_ucl = ucl_object_lua_import (L, -1);
  691. local_kp = rspamd_keypair_from_ucl (kp_ucl);
  692. ucl_object_unref (kp_ucl);
  693. }
  694. lua_pop (L, 1);
  695. lua_pushstring (L, "opaque_body");
  696. lua_gettable (L, 1);
  697. if (!!lua_toboolean (L, -1)) {
  698. flags |= RSPAMD_LUA_HTTP_FLAG_TEXT;
  699. }
  700. lua_pop (L, 1);
  701. lua_pushstring (L, "gzip");
  702. lua_gettable (L, 1);
  703. if (!!lua_toboolean (L, -1)) {
  704. gzip = TRUE;
  705. }
  706. lua_pop (L, 1);
  707. lua_pushstring (L, "no_ssl_verify");
  708. lua_gettable (L, 1);
  709. if (!!lua_toboolean (L, -1)) {
  710. flags |= RSPAMD_LUA_HTTP_FLAG_NOVERIFY;
  711. }
  712. lua_pop (L, 1);
  713. lua_pushstring (L, "keepalive");
  714. lua_gettable (L, 1);
  715. if (!!lua_toboolean (L, -1)) {
  716. flags |= RSPAMD_LUA_HTTP_FLAG_KEEP_ALIVE;
  717. }
  718. lua_pop (L, 1);
  719. lua_pushstring (L, "max_size");
  720. lua_gettable (L, 1);
  721. if (lua_type (L, -1) == LUA_TNUMBER) {
  722. max_size = lua_tonumber (L, -1);
  723. }
  724. lua_pop (L, 1);
  725. lua_pushstring (L, "method");
  726. lua_gettable (L, 1);
  727. if (lua_type (L, -1) == LUA_TSTRING) {
  728. rspamd_http_message_set_method (msg, lua_tostring (L, -1));
  729. }
  730. lua_pop (L, 1);
  731. lua_pushstring (L, "user");
  732. lua_gettable (L, 1);
  733. if (lua_type (L, -1) == LUA_TSTRING) {
  734. const gchar *user = lua_tostring (L, -1);
  735. lua_pushstring (L, "password");
  736. lua_gettable (L, 1);
  737. if (lua_type (L, -1) == LUA_TSTRING) {
  738. const gchar *password = lua_tostring (L, -1);
  739. gchar *tmpbuf;
  740. gsize tlen;
  741. tlen = strlen (user) + strlen (password) + 1;
  742. tmpbuf = g_malloc (tlen + 1);
  743. rspamd_snprintf (tmpbuf, tlen + 1, "%s:%s", user, password);
  744. tlen *= 2;
  745. tlen += sizeof ("Basic ") - 1;
  746. auth = g_malloc (tlen + 1);
  747. rspamd_snprintf (auth, tlen + 1, "Basic %Bs", tmpbuf);
  748. g_free (tmpbuf);
  749. }
  750. else {
  751. msg_warn ("HTTP user must have password, disabling auth");
  752. }
  753. lua_pop (L, 1); /* password */
  754. }
  755. lua_pop (L, 1); /* username */
  756. }
  757. else {
  758. msg_err ("http request has bad params");
  759. lua_pushboolean (L, FALSE);
  760. return 1;
  761. }
  762. if (session && rspamd_session_blocked (session)) {
  763. lua_pushboolean (L, FALSE);
  764. g_free (auth);
  765. rspamd_http_message_unref (msg);
  766. if (body) {
  767. rspamd_fstring_free (body);
  768. }
  769. return 1;
  770. }
  771. if (task == NULL && cfg == NULL) {
  772. g_free (auth);
  773. rspamd_http_message_unref (msg);
  774. if (body) {
  775. rspamd_fstring_free (body);
  776. }
  777. return luaL_error (L,
  778. "Bad params to rspamd_http:request(): either task or config should be set");
  779. }
  780. if (ev_base == NULL) {
  781. return luaL_error (L,
  782. "Bad params to rspamd_http:request(): ev_base isn't passed");
  783. }
  784. cbd = g_malloc0 (sizeof (*cbd));
  785. cbd->cbref = cbref;
  786. cbd->msg = msg;
  787. cbd->ev_base = ev_base;
  788. cbd->mime_type = mime_type;
  789. msec_to_tv (timeout, &cbd->tv);
  790. cbd->fd = -1;
  791. cbd->cfg = cfg;
  792. cbd->peer_pk = peer_key;
  793. cbd->local_kp = local_kp;
  794. cbd->flags = flags;
  795. cbd->max_size = max_size;
  796. cbd->url = url;
  797. cbd->auth = auth;
  798. cbd->task = task;
  799. if (cbd->cbref == -1) {
  800. cbd->thread = lua_thread_pool_get_running_entry (cfg->lua_thread_pool);
  801. }
  802. REF_INIT_RETAIN (cbd, lua_http_cbd_dtor);
  803. if (task) {
  804. cbd->item = rspamd_symcache_get_cur_item (task);
  805. }
  806. if (msg->host) {
  807. cbd->host = msg->host->str;
  808. }
  809. if (body) {
  810. if (gzip) {
  811. if (rspamd_fstring_gzip (&body)) {
  812. rspamd_http_message_add_header (msg, "Content-Encoding", "gzip");
  813. }
  814. }
  815. rspamd_http_message_set_body_from_fstring_steal (msg, body);
  816. }
  817. if (session) {
  818. cbd->session = session;
  819. }
  820. if (rspamd_parse_inet_address (&cbd->addr, msg->host->str, msg->host->len)) {
  821. /* Host is numeric IP, no need to resolve */
  822. gboolean ret;
  823. REF_RETAIN (cbd);
  824. ret = lua_http_make_connection (cbd);
  825. if (!ret) {
  826. if (cbd->ref.refcount > 1) {
  827. /* Not released by make_connection */
  828. REF_RELEASE (cbd);
  829. }
  830. REF_RELEASE (cbd);
  831. lua_pushboolean (L, FALSE);
  832. return 1;
  833. }
  834. REF_RELEASE (cbd);
  835. }
  836. else {
  837. if (!cbd->host) {
  838. REF_RELEASE (cbd);
  839. return luaL_error (L, "no host has been specified");
  840. }
  841. if (task == NULL) {
  842. REF_RETAIN (cbd);
  843. if (!rspamd_dns_resolver_request (resolver, session, NULL, lua_http_dns_handler, cbd,
  844. RDNS_REQUEST_A,
  845. cbd->host)) {
  846. if (cbd->ref.refcount > 1) {
  847. /* Not released by make_connection */
  848. REF_RELEASE (cbd);
  849. }
  850. REF_RELEASE (cbd);
  851. lua_pushboolean (L, FALSE);
  852. return 1;
  853. }
  854. REF_RELEASE (cbd);
  855. }
  856. else {
  857. REF_RETAIN (cbd);
  858. if (!rspamd_dns_resolver_request_task_forced (task, lua_http_dns_handler, cbd,
  859. RDNS_REQUEST_A, cbd->host)) {
  860. if (cbd->ref.refcount > 1) {
  861. /* Not released by make_connection */
  862. REF_RELEASE (cbd);
  863. }
  864. REF_RELEASE (cbd);
  865. lua_pushboolean (L, FALSE);
  866. return 1;
  867. }
  868. else if (cbd->item) {
  869. rspamd_symcache_item_async_inc (cbd->task, cbd->item, M);
  870. }
  871. REF_RELEASE (cbd);
  872. }
  873. }
  874. if (cbd->cbref == -1) {
  875. cbd->thread = lua_thread_pool_get_running_entry (cfg->lua_thread_pool);
  876. cbd->flags |= RSPAMD_LUA_HTTP_FLAG_YIELDED;
  877. return lua_thread_yield (cbd->thread, 0);
  878. }
  879. else {
  880. lua_pushboolean (L, TRUE);
  881. }
  882. return 1;
  883. }
  884. static gint
  885. lua_load_http (lua_State * L)
  886. {
  887. lua_newtable (L);
  888. luaL_register (L, NULL, httplib_m);
  889. return 1;
  890. }
  891. void
  892. luaopen_http (lua_State * L)
  893. {
  894. rspamd_lua_add_preload (L, "rspamd_http", lua_load_http);
  895. }