From a52bcda0158e605b85c1f08792862179f96427d8 Mon Sep 17 00:00:00 2001 From: bneradt Date: Wed, 24 Jun 2026 18:01:05 -0500 Subject: [PATCH] Test Lua server request guards Add an AuTest that calls ts.server_request accessors and setters before ATS has built a server request. This covers the guarded failure path and verifies the calls return nil instead of crashing. --- .../lua/lua_server_request_guard.test.py | 83 +++++++++++++++++++ .../pluginTest/lua/server_request_guard.lua | 60 ++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 tests/gold_tests/pluginTest/lua/lua_server_request_guard.test.py create mode 100644 tests/gold_tests/pluginTest/lua/server_request_guard.lua diff --git a/tests/gold_tests/pluginTest/lua/lua_server_request_guard.test.py b/tests/gold_tests/pluginTest/lua/lua_server_request_guard.test.py new file mode 100644 index 00000000000..9b7ceac1ada --- /dev/null +++ b/tests/gold_tests/pluginTest/lua/lua_server_request_guard.test.py @@ -0,0 +1,83 @@ +''' +Verify ts.server_request APIs fail safely before a server request exists. +''' +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Test.Summary = ''' +The ts.server_request APIs are only backed by a server request after ATS +constructs one. Verify calls made from do_remap return nil and do not crash. +''' + +Test.SkipUnless(Condition.PluginExists('tslua.so'),) + +Test.ContinueOnFail = True + + +class LuaServerRequestGuardTest: + """ + Verify ts.server_request guards before ATS creates a server request. + """ + + _headers = ( + "Early-Server-Header", + "Early-Server-Header-Table", + "Early-Server-Headers", + "Early-Server-Uri", + "Early-Server-Uri-Args", + "Early-Server-Method", + "Early-Server-Url-Host", + "Early-Server-Url-Scheme", + "Early-Server-Version", + ) + + def __init__(self): + self._ts = Test.MakeATSProcess("ts") + self._server = Test.MakeOriginServer("server") + self._configure_server() + self._configure_ats() + + def _configure_server(self): + req = {"headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""} + resp = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": "ok"} + self._server.addResponse("sessionfile.log", req, resp) + + def _configure_ats(self): + self._ts.Disk.remap_config.AddLine( + f'map http://www.example.com/ http://127.0.0.1:{self._server.Variables.Port}/' + ' @plugin=tslua.so @pparam=server_request_guard.lua') + + self._ts.Setup.Copy("server_request_guard.lua", self._ts.Variables.CONFIGDIR) + + self._ts.Disk.records_config.update({'proxy.config.diags.debug.enabled': 1, 'proxy.config.diags.debug.tags': 'ts_lua'}) + + def run(self): + tr = Test.AddTestRun("ts.server_request guards before server request creation") + tr.MakeCurlCommand(f"-s -D - -H 'Host: www.example.com' http://127.0.0.1:{self._ts.Variables.port}/", ts=self._ts) + tr.Processes.Default.StartBefore(self._server, ready=When.PortOpen(self._server.Variables.Port)) + tr.Processes.Default.StartBefore(self._ts) + tr.Processes.Default.ReturnCode = 0 + + tr.Processes.Default.Streams.stdout.Content = Testers.ContainsExpression( + f"{self._headers[0]}: ", f"{self._headers[0]} should be nil before ATS creates the server request") + for header in self._headers[1:]: + tr.Processes.Default.Streams.stdout.Content += Testers.ContainsExpression( + f"{header}: ", f"{header} should be nil before ATS creates the server request") + + tr.StillRunningAfter = self._server + + +LuaServerRequestGuardTest().run() diff --git a/tests/gold_tests/pluginTest/lua/server_request_guard.lua b/tests/gold_tests/pluginTest/lua/server_request_guard.lua new file mode 100644 index 00000000000..b5355f3ab07 --- /dev/null +++ b/tests/gold_tests/pluginTest/lua/server_request_guard.lua @@ -0,0 +1,60 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +local function as_text(value) + if value == nil then return "" end + if type(value) == "table" then return "" end + return tostring(value) +end + +local function record_early_server_request_values() + ts.ctx["early_header"] = as_text(ts.server_request.header["Host"]) + ts.ctx["early_header_table"] = as_text(ts.server_request.header_table["Host"]) + ts.ctx["early_headers"] = as_text(ts.server_request.get_headers()) + ts.ctx["early_uri"] = as_text(ts.server_request.get_uri()) + ts.ctx["early_uri_args"] = as_text(ts.server_request.get_uri_args()) + ts.ctx["early_method"] = as_text(ts.server_request.get_method()) + ts.ctx["early_url_host"] = as_text(ts.server_request.get_url_host()) + ts.ctx["early_url_scheme"] = as_text(ts.server_request.get_url_scheme()) + ts.ctx["early_version"] = as_text(ts.server_request.get_version()) + + ts.server_request.header["X-Early-Server-Request"] = "ignored" + ts.server_request.set_uri("/ignored") + ts.server_request.set_uri_args("ignored=true") + ts.server_request.set_method("POST") + ts.server_request.set_url_host("ignored.example") + ts.server_request.set_url_scheme("https") + ts.server_request.set_version("1.0") +end + +function send_response() + ts.client_response.header["Early-Server-Header"] = ts.ctx["early_header"] + ts.client_response.header["Early-Server-Header-Table"] = ts.ctx["early_header_table"] + ts.client_response.header["Early-Server-Headers"] = ts.ctx["early_headers"] + ts.client_response.header["Early-Server-Uri"] = ts.ctx["early_uri"] + ts.client_response.header["Early-Server-Uri-Args"] = ts.ctx["early_uri_args"] + ts.client_response.header["Early-Server-Method"] = ts.ctx["early_method"] + ts.client_response.header["Early-Server-Url-Host"] = ts.ctx["early_url_host"] + ts.client_response.header["Early-Server-Url-Scheme"] = ts.ctx["early_url_scheme"] + ts.client_response.header["Early-Server-Version"] = ts.ctx["early_version"] +end + +function do_remap() + record_early_server_request_values() + ts.hook(TS_LUA_HOOK_SEND_RESPONSE_HDR, send_response) + + return 0 +end