1"use strict";(self.webpackChunkrspamd_docs=self.webpackChunkrspamd_docs||[]).push([[3267],{28453:(e,n,s)=>{s.d(n,{R:()=>o,x:()=>a});var r=s(96540);const l={},t=r.createContext(l);function o(e){const n=r.useContext(t);return r.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(l):e.components||l:o(e.components),r.createElement(t.Provider,{value:n},e.children)}},66911:(e,n,s)=>{s.r(n),s.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>u,frontMatter:()=>o,metadata:()=>r,toc:()=>i});const r=JSON.parse('{"id":"developers/sync_async","title":"Coroutines and async calls","description":"This guide describes how to make asynchronous calls from Rspamd plugins and rules.","source":"@site/docs/developers/sync_async.md","sourceDirName":"developers","slug":"/developers/sync_async","permalink":"/developers/sync_async","draft":false,"unlisted":false,"editUrl":"https://github.com/rspamd/docs.rspamd.com/edit/master/docs/developers/sync_async.md","tags":[],"version":"current","frontMatter":{"title":"Coroutines and async calls"},"sidebar":"docs","previous":{"title":"HTTPCrypt Encryption Protocol","permalink":"/developers/encryption"},"next":{"title":"Rule Writing Workshop","permalink":"/developers/writing_rules"}}');var l=s(74848),t=s(28453);const o={title:"Coroutines and async calls"},a="Coroutines and async calls",c={},i=[{value:"Overview",id:"overview",level:2},{value:"Introducing pseudo-synchronous API",id:"pseudo-synchronous-API",level:3},{value:"API example",id:"API-example",level:2},{value:"HTTP module",id:"API-example-http-module",level:3},{value:"Asynchronous HTTP request",id:"asynchronous-http-request",level:4},{value:"Synchronous HTTP request",id:"synchronous-http-request",level:4},{value:"DNS module",id:"API-example-DNS-module",level:3},{value:"Asynchronous DNS request",id:"asynchronous-dns-request",level:4},{value:"Synchronous DNS request",id:"synchronous-dns-request",level:4},{value:"TCP module",id:"API-example-TCP-module",level:3},{value:"Asynchronous TCP request",id:"asynchronous-tcp-request",level:4},{value:"Synchronous TCP request",id:"synchronous-tcp-request",level:4},{value:"Redis module",id:"API-example-Redis-module",level:3},{value:"Asynchronous Redis request",id:"asynchronous-redis-request",level:4},{value:"Synchronous Redis request",id:"synchronous-redis-request",level:4}];function d(e){const n={a:"a",code:"code",div:"div",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",i:"i",li:"li",p:"p",pre:"pre",small:"small",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(n.header,{children:(0,l.jsx)(n.h1,{id:"coroutines-and-async-calls",children:"Coroutines and async calls"})}),"\n",(0,l.jsx)(n.p,{children:"This guide describes how to make asynchronous calls from Rspamd plugins and rules."}),"\n",(0,l.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,l.jsx)(n.p,{children:"Prior to 1.8.0, if you needed to perform an action involving network request\n(i.e. Redis query, Anti-virus scan), you had to use callback-style approach.\nYou define callback and initiate an asynchronous request and stop the execution\nto allow other tasks proceed."}),"\n",(0,l.jsx)(n.p,{children:"As soon as request is completed, callback is called."}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{className:"language-lua",children:" -- define a callback\n local function request_done(err, code, body)\n if not err then\n task:insert_result('REQUEST_DONE', 1.0, body)\n end\n ...\n end\n\n -- initiate the request\n api.start_request({\n callback = request_done,\n ...\n })\n"})}),"\n",(0,l.jsx)(n.h3,{id:"pseudo-synchronous-API",children:"Introducing pseudo-synchronous API"}),"\n",(0,l.jsx)(n.p,{children:"Rspamd 1.8.0 introduces a new pseudo-synchronous API. Now you can write code in a typical imperative manner without blocking other tasks."}),"\n",(0,l.jsx)(n.p,{children:"Each operation that could potentially block creates a yielding point. Consequently, the code is paused until the operation is completed (similar to blocking), and it resumes only when there is a result. Meanwhile, other tasks are processed as usual."}),"\n",(0,l.jsx)(n.small,{children:'\n**Please note** that synchronous mode requires symbol to be registered with **`coro`** flag from the version 1.9 (see "full example").\n'}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{className:"language-lua",children:" local err, response = api.do_request(...)\n\n if not err then\n task:insert_result('REQUEST_DONE', 1.0, response)\n end\n ...\n"})}),"\n",(0,l.jsx)(n.h2,{id:"API-example",children:"API example"}),"\n",(0,l.jsx)(n.h3,{id:"API-example-http-module",children:"HTTP module"}),"\n",(0,l.jsxs)(n.p,{children:["To use Sync with HTTP API, just remove ",(0,l.jsx)(n.strong,{children:"callback"})," parameter from call parameters.\nIt returns two values:"]}),"\n",(0,l.jsxs)(n.ul,{children:["\n",(0,l.jsxs)(n.li,{children:["err ",(0,l.jsx)(n.code,{children:"nil"})," or ",(0,l.jsx)(n.code,{children:"string"})," containing error description if network or internal error happened"]}),"\n",(0,l.jsxs)(n.li,{children:["response ",(0,l.jsx)(n.code,{children:"nil"})," if error happened (",(0,l.jsx)(n.em,{children:"note"}),": HTTP-codes are returned with corresponding codes) or ",(0,l.jsx)(n.code,{children:"table"}),":\n",(0,l.jsxs)(n.ul,{children:["\n",(0,l.jsxs)(n.li,{children:["code ",(0,l.jsx)(n.code,{children:"int"})," HTTP response code"]}
1),"\n",(0,l.jsxs)(n.li,{children:["content ",(0,l.jsx)(n.code,{children:"string"})," Response body"]}),"\n",(0,l.jsxs)(n.li,{children:["headers ",(0,l.jsx)(n.code,{children:"table"})," (header -> value) list of response headers"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,l.jsx)(n.h4,{id:"asynchronous-http-request",children:"Asynchronous HTTP request"}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{className:"language-lua",children:" -- define a callback\n local function request_done(err, code, body)\n if not err then\n task:insert_result('HTTP_RESPONSE' .. code, 1.0, body)\n end\n ...\n end\n\n -- initiate the request\n rspamd_http.request({\n url = 'http://127.0.0.1:18080/abc',\n callback = request_done,\n ...\n })\n"})}),"\n",(0,l.jsxs)(n.div,{children:["\n ",(0,l.jsxs)(n.a,{className:"btn btn-info btn-code collapsed","data-bs-toggle":"collapse","data-bs-target":"#async_http",children:["\n ",(0,l.jsx)(n.i,{className:"fa-regular fa-square-caret-down fa-pull-right"}),"\n Full example\n "]}),"\n",(0,l.jsxs)(n.div,{id:"async_http",className:"collapse collapse-block",children:["\n",(0,l.jsx)(n.p,{children:'-- standard includes\nlocal rspamd_http = require "rspamd_http"\nlocal rspamd_logger = require "rspamd_logger"'}),"\n",(0,l.jsx)(n.p,{children:"local function http_symbol(task)"}),"\n",(0,l.jsx)(n.p,{children:"-- define a callback\nlocal function request_done(err, code, body)\nif err then\nrspamd_logger.errx('http_callback error: ' .. err)\ntask:insert_result('HTTP_ERROR', 1.0, err)\nelse\ntask:insert_result('HTTP_RESPONSE', 1.0, body)\nend\nend"}),"\n",(0,l.jsxs)(n.p,{children:["-- initiate the request\nrspamd_http.request({\nurl = '",(0,l.jsx)(n.a,{href:"http://127.0.0.1:18080/abc",children:"http://127.0.0.1:18080/abc"}),"',\ntask = task,\ncallback = request_done,\n})\nend"]}),"\n",(0,l.jsx)(n.p,{children:"rspamd_config:register_symbol({\nname = 'SIMPLE_HTTP',\nscore = 1.0,\ncallback = http_symbol,\n})"}),"\n"]})]}),"\n",(0,l.jsx)(n.h4,{id:"synchronous-http-request",children:"Synchronous HTTP request"}),"\n",(0,l.jsx)(n.small,{children:'\n**Please note** that synchronous mode requires symbol to be registered with **coro** flag (see "full example").\n'}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{className:"language-lua",children:" local err, response = rspamd_http.request({\n url = 'http://127.0.0.1:18080/abc',\n ...\n })\n\n if not err then\n task:insert_result('HTTP_SYNC', 1.0, response.content)\n end\n ...\n"})}),"\n",(0,l.jsxs)(n.div,{children:["\n ",(0,l.jsxs)(n.a,{className:"btn btn-info btn-code collapsed","data-bs-toggle":"collapse","data-bs-target":"#sync_http",children:["\n ",(0,l.jsx)(n.i,{className:"fa-regular fa-square-caret-down fa-pull-right"}),"\n Full example\n "]}),"\n",(0,l.jsxs)(n.div,{id:"sync_http",className:"collapse collapse-block",children:["\n",(0,l.jsx)(n.p,{children:'local rspamd_http = require "rspamd_http"\nlocal rspamd_logger = require "rspamd_logger"'}),"\n",(0,l.jsxs)(n.p,{children:["local function http_symbol(task)\n-- start the request\nlocal err, response = rspamd_http.request({\nurl = '",(0,l.jsx)(n.a,{href:"http://127.0.0.1:18080",children:"http://127.0.0.1:18080"}),"' .. url,\ntask = task,\nmethod = 'post',\ntimeout = 1,\n})"]}),"\n",(0,l.jsx)(n.p,{children:"rspamd_logger.errx(task, 'rspamd_http.request[done] err: %1 response:%2', err, response)"}),"\n",(0,l.jsx)(n.p,{children:"-- check response\nif err then\nrspamd_logger.errx('http error: ' .. err)\ntask:insert_result('HTTP_ERROR', 1.0, err)\nelse\ntask:insert_result('HTTP_RESPONSE', 1.0, response.content)\nend\nend"}),"\n",(0,l.jsx)(n.p,{children:"rspamd_config:register_symbol({\nname = 'SIMPLE_HTTP',\nscore = 1.0,\ncallback = http_symbol,\n-- Symbol using Synchronous API should have \"coro\" flag.\nflags = 'coro',\n})"}),"\n"]})]}),"\n",(0,l.jsx)(n.h3,{id:"API-example-DNS-module",children:"DNS module"}),"\n",(0,l.jsxs)(n.p,{children:["To work with DNS properly, a new module called ",(0,l.jsx)(n.code,{children:"rspamd_dns"})," has been introduced, which replaces the former ",(0,l.jsx)(n.code,{children:"task:get_resolver()"})," calls. The new API requires explicit specification of the type of request, rather than providing a set of ",(0,l.jsx)(n.code,{children:"resolve_*"})," methods."]}),"\n",(0,l.jsx)(n.h4,{id:"asynchronous-dns-request",children:"Asynchronous DNS request"}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{className:"language-lua",children:"local function dns_callback(_, to_resolve, results, err)\n if not err then\n ...\n end\nend\n\ntask:get_resolver():resolve_a({\n name = 'rspamd.com'\n callback = dns_callback,\n ...\n})\n"})}),"\n",(0,l.jsxs)(n.div,{children:["\n ",(0,l.jsxs)(n.a,{className:"btn btn-info btn-code collapsed","data-bs-toggle":"collapse","data-bs-target":"#async_dns",children:["\n ",(0,l.jsx)(n.i,{className:"fa-regular fa-square-caret-down fa-pull-right"}),"\n Full example\n "]}),"\n",(0,l.jsxs)(n.div,{id:"async_dns",className:"collapse collapse-block",children:["\n",(0,l.jsx)(n.p,{children:'local rspamd_dns = require "rspamd_dns"\nlocal logger = require "rspamd_logger"'}),"\n",(0,l.jsxs)(n.p,{children:["local function dns_symbol(task)\nlocal function dns_cb(",(0,l.jsx)(n.em,{children:', to_resolve, results, err)\nlogger.errx(task, "'}),"=%1, to_resolve=%2, results=%3, err%4\", _, to_resolve, results, err)\nif err then\ntask:insert_result('DNS_E
1RROR', 1.0, err)\nelse\ntask:insert_result('DNS', 1.0, tostring(results[1]))\nend\nend"]}),"\n",(0,l.jsx)(n.p,{children:"task:get_resolver():resolve_a({\ntask = task,\nname = 'rspamd.com',\ncallback = dns_cb\n})\nend"}),"\n",(0,l.jsx)(n.p,{children:"rspamd_config:register_symbol({\nname = 'SIMPLE_DNS',\nscore = 1.0,\ncallback = dns_symbol,\n})"}),"\n"]})]}),"\n",(0,l.jsx)(n.h4,{id:"synchronous-dns-request",children:"Synchronous DNS request"}),"\n",(0,l.jsx)(n.small,{children:'\n**Please note** that synchronous mode requires symbol to be registered with **coro** flag (see "full example").\n'}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{className:"language-lua",children:" local is_ok, results = rspamd_dns.request({\n type = 'a',\n name = to_resolve ,\n ...\n })\n if is_ok then\n task:insert_result('DNS_SYNC', 1.0, tostring(results[1]))\n end\n"})}),"\n",(0,l.jsxs)(n.div,{children:["\n ",(0,l.jsxs)(n.a,{className:"btn btn-info btn-code collapsed","data-bs-toggle":"collapse","data-bs-target":"#sync_dns",children:["\n ",(0,l.jsx)(n.i,{className:"fa-regular fa-square-caret-down fa-pull-right"}),"\n Full example\n "]}),"\n",(0,l.jsxs)(n.div,{id:"sync_dns",className:"collapse collapse-block",children:["\n",(0,l.jsx)(n.p,{children:'local rspamd_dns = require "rspamd_dns"\nlocal logger = require "rspamd_logger"'}),"\n",(0,l.jsx)(n.p,{children:"local function dns_sync_symbol(task)\nlocal to_resolve = tostring(task:get_request_header('to-resolve'))\nlocal is_ok, results = rspamd_dns.request({\ntask = task,\ntype = 'a',\nname = to_resolve ,\n})"}),"\n",(0,l.jsx)(n.p,{children:'logger.errx(task, "is_ok=%1, results=%2, results[1]=%3", is_ok, results, results[1])'}),"\n",(0,l.jsx)(n.p,{children:"if not is_ok then\ntask:insert_result('DNS_SYNC_ERROR', 1.0, results)\nelse\ntask:insert_result('DNS_SYNC', 1.0, tostring(results[1]))\nend\nend"}),"\n",(0,l.jsx)(n.p,{children:"rspamd_config:register_symbol({\nname = 'SIMPLE_DNS_SYNC',\nscore = 1.0,\ncallback = dns_sync_symbol,\n-- Symbol using Synchronous API should have \"coro\" flag.\nflags = 'coro',\n})"}),"\n"]})]}),"\n",(0,l.jsx)(n.h3,{id:"API-example-TCP-module",children:"TCP module"}),"\n",(0,l.jsxs)(n.p,{children:["It is recommended to use ",(0,l.jsx)(n.code,{children:"lua_tcp_sync"})," module to work TCP."]}),"\n",(0,l.jsx)(n.h4,{id:"asynchronous-tcp-request",children:"Asynchronous TCP request"}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{className:"language-lua",children:" local function http_read_cb(err, data, conn)\n task:insert_result('HTTP_ASYNC_RESPONSE', 1.0, data or err)\n ...\n end\n rspamd_tcp:request({\n callback = http_read_cb,\n host = '127.0.0.1',\n data = {'GET /request HTTP/1.1\\r\\nConnection: keep-alive\\r\\n\\r\\n'},\n ...\n })\n"})}),"\n",(0,l.jsxs)(n.div,{children:["\n ",(0,l.jsxs)(n.a,{className:"btn btn-info btn-code collapsed","data-bs-toggle":"collapse","data-bs-target":"#async_tcp",children:["\n ",(0,l.jsx)(n.i,{className:"fa-regular fa-square-caret-down fa-pull-right"}),"\n Full example\n "]}),"\n",(0,l.jsxs)(n.div,{id:"async_tcp",className:"collapse collapse-block",children:["\n",(0,l.jsx)(n.p,{children:'local rspamd_tcp = require "rspamd_tcp"\nlocal logger = require "rspamd_logger"'}),"\n",(0,l.jsx)(n.p,{children:"local function http_simple_tcp_async_symbol(task)\nlogger.errx(task, 'http_tcp_symbol: begin')\nlocal function http_read_cb(err, data, conn)\nlogger.errx(task, 'http_read_cb: got reply: %s, error: %s, conn: %s', data, err, conn)\ntask:insert_result('HTTP_ASYNC_RESPONSE', 1.0, data or err)\n-- if we want to send another request\n-- conn:add_write(http_read_post_cb, \"POST /request2 HTTP/1.1\\r\\n\\r\\n\")\nend\nrspamd_tcp:request({\ntask = task,\ncallback = http_read_cb,\nhost = '127.0.0.1',\ndata = {'GET /request HTTP/1.1\\r\\nConnection: keep-alive\\r\\n\\r\\n'},\nread = true,\nport = 18080,\n})\nend"}),"\n",(0,l.jsx)(n.p,{children:"rspamd_config:register_symbol({\nname = 'SIMPLE_TCP_ASYNC_TEST',\nscore = 1.0,\ncallback = http_simple_tcp_async_symbol,\n-- Symbol using Synchronous API should have \"coro\" flag.\nflags = 'coro',\n})"}),"\n"]})]}),"\n",(0,l.jsx)(n.h4,{id:"synchronous-tcp-request",children:"Synchronous TCP request"}),"\n",(0,l.jsx)(n.small,{children:'\n**Please note** that synchronous mode requires symbol to be registered with **coro** flag (see "full example").\n'}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{className:"language-lua",children:" local is_ok, connection = tcp_sync.connect {\n host = '127.0.0.1',\n ...\n }\n\n if not is_ok then\n logger.errx(task, 'write error: %1', connection)\n end\n\n logger.errx(task, 'connect_sync %1, %2', is_ok, tostring(connection))\n\n is_ok, err = connection:write('GET /request_sync HTTP/1.1\\r\\nConnection: keep-alive\\r\\n\\r\\n')\n if not is_ok then\n logger.errx(task, 'write error: %1', err)\n end\n \n is_ok, data = connection:read_once(); \n task:insert_result('HTTP_RESPONSE', 1.0, data or err)\n"})}),"\n",(0,l.jsxs)(n.div,{children:["\n ",(0,l.jsxs)(n.a,{className:"btn btn-info btn-code collapsed","data-bs-toggle":"collapse","data-bs-target":"#sync_tcp",children:["\n ",(0,l.jsx)(n.i,{className:"fa-regular fa-square-caret-down fa-pull-right"}),"\n Full example\n "]}),"\n",(0,l.jsxs)(n.div,{id:"sync_tcp",className:"collapse collapse-block",children:["\n",(0,l.jsx)(n.p,{children:'local logger = require "rspamd_logger"\nlocal tcp_sync = require "lua_tcp_sync"'}),"\n",(0,l.jsx)(n.p,{children:"local function http_tcp_symbol(task)"}),"\n",(0,l.jsx)(n.p,{children:"local err\nlocal is_ok, connection = tcp_sync.connect {\ntask = task,\nhost = '127.0.0.1',\ntimeout = 20,\nport = 18080,\n}"}),"\n",(0,l.jsx)(n.p,{children:"logger.errx(task, 'connect_sync %1, %2', is_ok, tostring(connection))\nif not is_ok then\nlogger.errx(task, 'connect error: %1', connection)\nreturn\nend"}),"\n",(0,l.jsx)(n.p,{children:"is_ok, err = connection:write(string.format('GET /request_sync HTTP/1.1\\r\\nConnection: close\\r\\n\\r\\n'))"}),"\n",(0,l.jsx)(n.p,{children:"if not is_ok then\nlogger.errx(task, 'write error: %1', err)\nreturn\nend"}),"\n",(0,l.jsx)(n.p,{children:"local content_length, content"}),"\n",(0,l.jsx)(n.p,{children:"while true do\nlocal header_line\nis_ok, header_line = connection:read_until(\"\\r\\n\")\nif not is_ok then\nlogger.errx(task, 'failed to get header: %1', header_line)\nreturn\nend"}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{children:'if header_line == "" then\n logger.errx(task, \'headers done\')\n break\nend\n\nlocal value\nlocal header = header_line:gsub("([%w-]+): (.*)", \n function (h, v) value = v; return h:lower() end)\n\nlogger.errx(task, \'parsed header: %1 -> "%2"\', header, value)\n\nif header == "content-length" then\n content_length = tonumber(value)\nend\n'})}),"\n",(0,l.jsx)(n.p,{children:"end"}),"\n",(0,l.jsx)(n.p,{children:"if content_length then\nis_ok, content = connection:read_bytes(content_length)\nif is_ok then\ntask:insert_result('HTTP_SYNC_CONTENT', 1.0, content)\nend\nelse\nis_ok, content = connection:read_until_eof()\nif is_ok then\ntask:insert_result('HTTP_SYNC_EOF', 1.0, content)\nend\nend\nlogger.errx(task, '(is_ok: %1) content [%2 bytes] %3', is_ok, content_length, content)\nend"}),"\n",(0,l.jsx)(n.p,{children:"rspamd_config:register_symbol({\nname = 'HTTP_TCP_TEST',\nscore = 1.0,\ncallback = http_tcp_symbol,\n-- Symbol using Synchronous API should have \"coro\" flag.\nflags = 'coro',\n})"}),"\n"]})]}),"\n",(0,l.jsx)(n.h3,{id:"API-example-Redis-module",children:"Redis module"}),"\n",(0,l.jsx)(n.h4,{id:"asynchronous-redis-request",children:"Asynchronous Redis request"}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{className:"language-lua",children:" local function redis_cb(err, data)\n if not err then\n task:insert_result('REDIS_ASYNC201809_ERROR', 1.0, err)\n end\n ...\n end\n\n local attrs = {\n callback = redis_cb\n ...\n }\n local request = {...}\n redis_lua.request(redis_params, attrs, request)\n"})}),"\n",(0,l.jsxs)(n.div,{children:["\n ",(0,l.jsxs)(n.a,{className:"btn btn-info btn-code collapsed","data-bs-toggle":"collapse","data-bs-target":"#async_redis",children:["\n ",(0,l.jsx)(n.i,{className:"fa-regular fa-square-caret-down fa-pull-right"}),"\n Full example\n "]}),"\n",(0,l.jsxs)(n.div,{id:"async_redis",className:"collapse collapse-block",children:["\n",(0,l.jsx)(n.p,{children:'local logger = require "rspamd_logger"\nlocal redis_lua = require "lua_redis"\nlocal lua_util = require "lua_util"\nlocal redis_params\nlocal N = \'redis_test\''}),"\n",(0,l.jsx)(n.p,{children:"local function redis_simple_async_api(task)\nlocal function redis_cb(err, data)\nif err then\ntask:insert_result('REDIS_ASYNC_ERROR', 1.0, err)\nelse\ntask:insert_result('REDIS_ASYNC', 1.0, data)\nend\nend"}),"\n",(0,l.jsx)(n.p,{children:"local attrs = {\ntask = task,\ncallback = redis_cb\n}\nlocal request = {\n'GET',\n'test_key'\n}\nredis_lua.request(redis_params, attrs, request)\nend"}),"\n",(0,l.jsx)(n.p,{children:"redis_params = rspamd_parse_redis_server(N)"}),"\n",(0,l.jsx)(n.p,{children:"rspamd_config:register_symbol({\nname = 'SIMPLE_REDIS_ASYNC_TEST',\nscore = 1.0,\ncallback = redis_simple_async_api,\n-- Symbol using Synchronous API should have \"coro\" flag.\nflags = 'coro',\n})"}),"\n"]})]}),"\n",(0,l.jsx)(n.h4,{id:"synchronous-redis-request",children:"Synchronous Redis request"}),"\n",(0,l.jsx)(n.small,{children:'\n**Please note** that synchronous mode requires symbol to be registered with **coro** flag (see "full example").\n'}),"\n",(0,l.jsx)(n.pre,{children:(0,l.jsx)(n.code,{className:"language-lua",children:" local is_ok, connection = redis_lua.connect(...)\n if not is_ok then\n return\n end\n\n is_ok, err = connection:add_cmd('EVAL', {[[return \"hello from lua on redis\"]], 0})\n\n if not is_ok then\n return\n end\n\n is_ok,data = connection:exec()\n if is_ok then\n task:insert_result('REDIS', 1.0, data)\n end\n ...\n"})}),"\n",(0,l.jsxs)(n.div,{children:["\n ",(0,l.jsxs)(n.a,{className:"btn btn-info btn-code collapsed","data-bs-toggle":"collapse","data-bs-target":"#sync_redis",children:["\n ",(0,l.jsx)(n.i,{className:"fa-regular fa-square-caret-down fa-pull-right"}),"\n Full example\n "]}),"\n",(0,l.jsxs)(n.div,{id:"sync_redis",className:"collapse collapse-block",children:["\n",(0,l.jsx)(n.p,{children:'local logger = require "rspamd_logger"\nlocal redis_lua = require "lua_redis"'}),"\n",(0,l.jsx)(n.p,{children:"local redis_params\nlocal N = 'redis_test'"}),"\n",(0,l.jsx)(n.p,{children:"local function redis_symbol(task)"}),"\n",(0,l.jsx)(n.p,{children:"local attrs = {task = task}\nlocal is_ok, connection = redis_lua.connect(redis_params, attrs)"}),"\n",(0,l.jsx)(n.p,{children:'logger.infox(task, "connect: %1, %2", is_ok, connection)'}),"\n",(0,l.jsx)(n.p,{children:"if not is_ok then\ntask:insert_result('REDIS_ERROR', 1.0, connection)\nreturn\nend"}),"\n",(0,l.jsx)(n.p,{children:"local err, data"}),"\n",(0,l.jsx)(n.p,{children:'local lua_script = [[return "hello from lua on redis"]]'}),"\n",(0,l.jsx)(n.p,{children:"is_ok, err = connection:add_cmd('EVAL', {lua_script, 0})\nlogger.infox(task, \"add_cmd: %1, %2\", is_ok, err)"}),"\n",(0,l.jsx)(n.p,{children:"if not is_ok then\ntask:insert_result('REDIS_ERROR_2', 1.0, err)\nreturn\nend"}),"\n",(0,l.jsx)(n.p,{children:"is_ok,data = connection:exec()"}),"\n",(0,l.jsx)(n.p,{children:'logger.infox(task, "exec: %1, %2", is_ok, data)'}),"\n",(0,l.jsx)(n.p,{children:"if not is_ok then\ntask:insert_result('REDIS_ERROR_3', 1.0, data)\nreturn\nend"}),"\n",(0,l.jsx)(n.p,{children:"task:insert_result('REDIS', 1.0, data)"}),"\n",(0,l.jsx)(n.p,{children:"end"}),"\n",(0,l.jsx)(n.p,{children:"redis_params = rspamd_parse_redis_server(N)"}),"\n",(0,l.jsx)(n.p,{children:"rspamd_config:register_symbol({\nname = 'REDIS_TEST',\nscore = 1.0,\ncallback = redis_symbol,\n-- Symbol using Synchronous API should have \"coro\" flag.\nflags = 'coro',\n})"}),"\n"]})]})]})}function u(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,l.jsx)(n,{...e,children:(0,l.jsx)(d,{...e})}):d(e)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.