grthtrhthjhtyjytjytkergtrhtrjytjerhrfh4:24 29/09/2026-- vim: st=4 sts=4 sw=4 et: --- Network backend using the [lua-nginx-module](https://github.com/openresty/lua-nginx-module) cosocket API. -- This module can be used with the raw Lua module for nginx or the OpenResty -- bundle. -- -- As socket API is not available in all contexts, the messages are queued and -- processed in a timer when necessary. This can be forced for all messages too -- in order to not wait for the message to be sent (see the `async` filed). -- -- It will require the `lua_ssl_trusted_certificate` to be set correctly when -- reporting to a HTTPS endpoint. -- -- @module raven.senders.ngx -- @copyright 2014-2017 CloudFlare, Inc. -- @license BSD 3-clause (see LICENSE file) -- luacheck: globals ngx local util = require 'raven.util' local ngx_socket = ngx.socket local ngx_get_phase = ngx.get_phase local string_format = string.format local table_remove = table.remove local parse_dsn = util.parse_dsn local generate_auth_header = util.generate_auth_header local _VERSION = util._VERSION local _M = {} -- provide a more sensible implementation of the error log function function util.errlog(...) ngx.log(ngx.ERR, 'raven-lua failure: ', ...) end -- as we don't want to use an external HTTP library, just send the HTTP request -- directly using cosocket API local HTTP_REQUEST = string.gsub([[POST %s HTTP/1.0 Host: %s Connection: close Content-Type: application/json Content-Length: %d User-Agent: %s X-Sentry-Auth: %s %s ]], '\r?\n', '\r\n') local CALLBACK_DEFAULT_ERRMSG = "failed to configure socket (custom callback did not returned a value)" local function send_msg(self, msg) local ok local sock, err = ngx_socket.tcp() if not sock then return nil, err end if self.configure_socket then ok, err = self.configure_socket(self, sock) if not ok then return nil, err or CALLBACK_DEFAULT_ERRMSG end end ok, err = sock:connect(self.target, self.port) if not ok then return nil, err end if self.protocol == 'https' then -- TODO: session resumption? ok, err = sock:sslhandshake(false, self.host, self.verify_ssl) if not ok then sock:close() return nil, err end end ok, err = sock:send(msg) if not ok then sock:close() return nil, err end local resp, partial resp, err, partial = sock:receive('*a') if err then -- If the connection was forcibly reset by the server after sending the -- response, it will look like an error. To cover for this, take the -- partial data as normal response and try to parse it -- See: https://github.com/cloudflare/raven-lua/issues/30 if partial and partial ~= "" then resp = partial else return nil, err end end local status = resp:match("HTTP/%d%.%d (%d%d%d) %w+") if status ~= "200" then return nil, "Server response status not 200:" .. (status or "nil") end return resp:match("\r\n\r\n(.*)") or "" end -- async task pushing: sometimes the socket API is not available, in this case, -- messages are pushed into a queue and sent in a timer. Only one timer at most -- is running per instance (and per worker): timers are a scarce resource, we -- don't want to exhaust the timer pool during message storms. local function send_task(premature, self) if premature then return end local ok, err = xpcall(function() local send_queue = self.send_queue while #send_queue > 0 do local msg = send_queue[1] -- do not remove the message yet as an empty queue is the signal to -- re-start the task local ok, err = send_msg(self, msg) if not ok then ngx.log(ngx.ERR, 'raven: failed to send message asyncronously: ', err, '. Drop the message.') end table_remove(send_queue, 1) end end, debug.traceback) if not ok then ngx.log(ngx.ERR, 'raven: failed to run the async sender task: ', err) -- TODO: restart the task here? requires a extra counter as we don't want -- to fail in loop indefinitely. end self.task_running = false end local mt = {} mt.__index = mt function mt:send(json_str) local auth = generate_auth_header(self) local msg = string_format(HTTP_REQUEST, self.request_uri, self.host, #json_str, "raven-lua-ngx/" .. _VERSION, auth, json_str) local phase = ngx_get_phase() -- rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua* if (not self.async) and ( phase == 'rewrite' or phase == 'access' or phase == 'content' or phase == 'timer' or phase == 'ssl_cert' or phase == 'ssl_session_fetch' ) then -- socket is available return send_msg(self, msg) else -- cannot use socket: push the message in the async queue local send_queue = self.send_queue local queue_size = #send_queue if queue_size <= self.queue_limit then send_queue[queue_size + 1] = msg if not self.task_running then local ok, err = ngx.timer.at(0, send_task, self) if not ok then return nil, 'failed to schedule async sender task: ' .. err end -- assume the task is already running, as other messages might -- be reported before it is actually scheduled and run. self.task_running = true end else return nil, 'failed to send message asyncronously: queue is full' end return true end end --- Configuration table for the nginx sender. -- @field dsn DSN string -- @field verify_ssl Whether or not the SSL certificate is checked (boolean, -- defaults to false) -- @field configure_socket Callback used to configure the created socket (e.g. -- adjusting the timeout). Called with the sender object and socket as arguments. -- Must return `true` or `nil, error_message`. -- @field queue_limit Maximum number of message in the asynchronous sending queue -- (default: 50) -- @field async Always send message asynchronously, even when it can be sent -- right away. This is to prevent to slow down processing while contacting the -- Sentry server. (default: false) -- @table sender_conf --- Create a new sender object for the given DSN -- @param conf Configuration table, see @{sender_conf} -- @return A sender object function _M.new(conf) local obj, err = parse_dsn(conf.dsn) if not obj then return nil, err end obj.target = conf.target or obj.host obj.verify_ssl = conf.verify_ssl obj.configure_socket = conf.configure_socket obj.queue_limit = conf.queue_limit or 50 obj.async = conf.async or false obj.send_queue = {} obj.task_running = false return setmetatable(obj, mt) end --- Returns the value of the `server_name` variable if possible. -- Otherwise (wrong phase), this will return "undefined". -- -- It is intended to be used as a `get_server_name` override on the main raven -- instance. -- -- @usage -- local raven_ngx = require("raven.senders.ngx") -- local rvn = raven.new(...) -- rvn.get_server_name = raven_ngx.get_server_name function _M.get_server_name() local phase = ngx.get_phase() -- the ngx.var.* API is not available in all contexts if phase == "set" or phase == "rewrite" or phase == "access" or phase == "content" or phase == "header_filter" or phase == "body_filter" or phase == "log" then return ngx.var.server_name end return "undefined" end return _M