diff --git a/gateway/src/apicast/policy/custom_metrics/custom_metrics.lua b/gateway/src/apicast/policy/custom_metrics/custom_metrics.lua index 783d52fc7..7ad350b50 100644 --- a/gateway/src/apicast/policy/custom_metrics/custom_metrics.lua +++ b/gateway/src/apicast/policy/custom_metrics/custom_metrics.lua @@ -90,7 +90,11 @@ end -- report: Report the given usage information to the backend function _M.report(context, usage) - local backend = backend_client:new(context.service, http_ng_resty) + local backend, err = backend_client:new(context.service, http_ng_resty) + if not backend then + ngx.log(ngx.ERR, "failed to create backend_client, err: ", err) + return + end local reports = {} for key, value in pairs(usage.deltas) do local result = deepcopy(context.credentials) diff --git a/gateway/src/apicast/policy/on_failed/on_failed.lua b/gateway/src/apicast/policy/on_failed/on_failed.lua index 24c1f88be..a6f1c1e2f 100644 --- a/gateway/src/apicast/policy/on_failed/on_failed.lua +++ b/gateway/src/apicast/policy/on_failed/on_failed.lua @@ -1,3 +1,5 @@ +local get_phase = ngx.get_phase + local _M = require('apicast.policy').new('On failed', 'builtin') local new = _M.new @@ -11,6 +13,12 @@ end function _M:export() return { policy_error_callback = function(policy_name, error_message) + local phase = get_phase() + -- skip calling plugins on body_filter/log phase as ngx.exit does not + -- exist in these phases + if phase == "body_filter" or phase == "log" then + return + end ngx.log(ngx.DEBUG, "Stop request because policy: '", policy_name, "' failed, error='", error_message, "'") ngx.exit(self.error_status_code) end diff --git a/gateway/src/apicast/policy_chain.lua b/gateway/src/apicast/policy_chain.lua index f8b6d07a2..42794a5a3 100644 --- a/gateway/src/apicast/policy_chain.lua +++ b/gateway/src/apicast/policy_chain.lua @@ -216,6 +216,8 @@ local function call_chain(phase_name) if not status then if context.policy_error_callback then context.policy_error_callback(self[i]._NAME, return_val) + else + error(return_val) end -- This is important because Openresty just died on error on init -- phase, and we should keep in this way. diff --git a/gateway/src/apicast/proxy.lua b/gateway/src/apicast/proxy.lua index a2197b4ec..2d2aff3f8 100644 --- a/gateway/src/apicast/proxy.lua +++ b/gateway/src/apicast/proxy.lua @@ -103,7 +103,7 @@ local function matched_patterns(matched_rules) end local function build_backend_client(self, service) - return assert(backend_client:new(service, self.http_ng_backend), 'missing backend') + return assert(backend_client:new(service, self.http_ng_backend)) end function _M:authorize(context, service, usage, credentials, ttl) diff --git a/t/apicast-policy-chains-crash.t b/t/apicast-policy-chains-crash.t index 3bad7c918..3d2863264 100644 --- a/t/apicast-policy-chains-crash.t +++ b/t/apicast-policy-chains-crash.t @@ -32,3 +32,34 @@ GET /test HTTP/1.1 --- error_code: 200 --- error_log Policy error_policy crashed in .new() + + + +=== TEST 2: policy chain with a policy that crashes on rewrite/access() +Policies that crash during request phase should ternimate the request +--- configuration +{ + "services": [ + { + "id": 42, + "proxy": { + "policy_chain" : [ + { "name" : "error_policy", "version" : "2.0.0" }, + { "name" : "apicast.policy.echo" } + ] + } + } + ] +} +--- request +GET /test +--- ignore_response +--- error_code: 500 +--- error_log eval +[ +'lua entry thread aborted: runtime error:', +'terminated in rewrite phase', +'stack traceback:', +" in function 'error'", +": in function 'rewrite'", +] diff --git a/t/apicast.t b/t/apicast.t index 213a17201..f74276f96 100644 --- a/t/apicast.t +++ b/t/apicast.t @@ -1310,3 +1310,89 @@ Authentication parameters missing +=== TEST 36: works with BACKEND_ENDPOINT_OVERRIDE +--- env eval +( + 'BACKEND_ENDPOINT_OVERRIDE' => "http://test_backend:$ENV{TEST_NGINX_SERVER_PORT}" +) +--- configuration +{ + "services" : [ + { + "backend_version": 1, + "proxy": { + "api_backend": "http://test:$TEST_NGINX_SERVER_PORT/api-backend/", + "proxy_rules": [ + { "pattern" : "/", "http_method" : "GET", "metric_system_name" : "hits", "delta" : 1} + ], + "policy_chain" : [ + { "name" : "apicast.policy.apicast" } + ] + } + } + ] +} +--- upstream +location /api-backend/ { + echo "yay, api backend!"; +} +--- backend + location /transactions/authrep.xml { + content_by_lua_block { + content_by_lua_block { ngx.exit(200) } + } + } +--- request +GET /?user_key=value +--- error_code: 200 +--- response_body +yay, api backend! +--- no_error_log +[error] + + + +=== TEST 37: with invalid BACKEND_ENDPOINT_OVERRIDE +Print the stacktrace, and terminate the request with 500 response code +--- env eval +( + 'BACKEND_ENDPOINT_OVERRIDE' => 'invalid.backend.com' +) +--- configuration +{ + "services" : [ + { + "backend_version": 1, + "proxy": { + "api_backend": "http://test:$TEST_NGINX_SERVER_PORT/api-backend/", + "proxy_rules": [ + { "pattern" : "/", "http_method" : "GET", "metric_system_name" : "hits", "delta" : 1} + ], + "policy_chain" : [ + { "name" : "apicast.policy.apicast" } + ] + } + } + ] +} +--- upstream +location /api-backend/ { + echo "yay, api backend!"; +} +--- backend + location /transactions/authrep.xml { + content_by_lua_block { + error('APIcast called authrep, but it should not have') + } + } +--- request +GET /?user_key=value +--- error_code: 500 +--- error_log eval +[ +'lua entry thread aborted: runtime error:', +'missing scheme', +'stack traceback:', +" in function 'error'", +": in function 'access'", +]