diff --git a/tests/gold_tests/origin_connection/per_server_connection_max.test.py b/tests/gold_tests/origin_connection/per_server_connection_max.test.py new file mode 100644 index 00000000000..d1d4a4da614 --- /dev/null +++ b/tests/gold_tests/origin_connection/per_server_connection_max.test.py @@ -0,0 +1,74 @@ +''' +Verify the behavior of proxy.config.http.per_server.connection.max. +''' +# 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 = __doc__ + + +class PerServerConnectionMaxTest: + """Define an object to test our max origin connection behavior.""" + + _replay_file: str = 'slow_servers.replay.yaml' + _origin_max_connections: int = 3 + + def __init__(self) -> None: + """Configure the test processes in preparation for the TestRun.""" + self._configure_dns() + self._configure_server() + self._configure_trafficserver() + + def _configure_dns(self): + """Configure a nameserver for the test.""" + self._nameserver = Test.MakeDNServer("dns", default='127.0.0.1') + + def _configure_server(self) -> None: + """Configure the server to be used in the test.""" + self._server = Test.MakeVerifierServerProcess('server', self._replay_file) + + def _configure_trafficserver(self) -> None: + """Configure Traffic Server to be used in the test.""" + self._ts = Test.MakeATSProcess("ts") + self._ts.Disk.remap_config.AddLine( + f'map / http://127.0.0.1:{self._server.Variables.http_port}' + ) + self._ts.Disk.records_config.update({ + 'proxy.config.dns.nameservers': f"127.0.0.1:{self._nameserver.Variables.Port}", + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'http', + 'proxy.config.http.per_server.connection.max': self._origin_max_connections, + }) + self._ts.Disk.diags_log.Content += Testers.ContainsExpression( + f'WARNING:.*too many connections:.*limit={self._origin_max_connections}', + 'Verify the user is warned about the connection limit being hit.') + + def run(self) -> None: + """Configure the TestRun.""" + tr = Test.AddTestRun( + 'Verify we enforce proxy.config.http.per_server.connection.max') + tr.Processes.Default.StartBefore(self._nameserver) + tr.Processes.Default.StartBefore(self._server) + tr.Processes.Default.StartBefore(self._ts) + + tr.AddVerifierClientProcess( + 'client', + self._replay_file, + http_ports=[self._ts.Variables.port]) + + +PerServerConnectionMaxTest().run() diff --git a/tests/gold_tests/origin_connection/slow_servers.replay.yaml b/tests/gold_tests/origin_connection/slow_servers.replay.yaml new file mode 100644 index 00000000000..9b42e82477e --- /dev/null +++ b/tests/gold_tests/origin_connection/slow_servers.replay.yaml @@ -0,0 +1,166 @@ +# 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 Notes: +# This test makes the server reply slowly over multiple origin connections, +# allowing origin-side connections to build up. This allows us to test our +# proxy.config.http.per_server.connection.max feature. This test assumes that +# proxy.config.http.per_server.connection.max is configured to 3 and verifies +# that we respond with a 503 on the fourth origin connection. +# +# Be aware that Proxy Verifier will perform each of these sessions in parallel, +# not in serial. Thus, while there are multiple delays across the sessions, the +# delays are not additive. The replay should take about 2 seconds to complete. + +meta: + version: "1.0" +sessions: + +- transactions: + + - client-request: + method: GET + url: /some/path/first + version: '1.1' + headers: + fields: + - [ Host, www.example.com ] + - [ Content-Length, 0 ] + - [ X-Request, first-request ] + - [ uuid, first-request ] + + proxy-request: + headers: + fields: + - [ X-Request, {value: 'first-request', as: equal } ] + + server-response: + # Delay for 2 seconds so that this origin connection will exist for 2 seconds. + delay: 2s + + status: 200 + reason: OK + headers: + fields: + - [ Content-Length, 36 ] + - [ X-Response, first-response ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Response, {value: 'first-response', as: equal } ] + +- transactions: + - client-request: + method: GET + url: /some/path/second + version: '1.1' + headers: + fields: + - [ Host, www.example.com ] + - [ Content-Length, 0 ] + - [ X-Request, second-request ] + - [ uuid, second-request ] + + proxy-request: + headers: + fields: + - [ X-Request, {value: 'second-request', as: equal } ] + + server-response: + # Delay for 2 seconds so that this origin connection will exist for 2 seconds. + delay: 2s + + status: 200 + reason: OK + headers: + fields: + - [ Content-Length, 36 ] + - [ X-Response, second-response ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Response, {value: 'second-response', as: equal } ] + +- transactions: + - client-request: + method: GET + url: /some/path/third + version: '1.1' + headers: + fields: + - [ Host, www.example.com ] + - [ Content-Length, 0 ] + - [ X-Request, third-request ] + - [ uuid, third-request ] + + proxy-request: + headers: + fields: + - [ X-Request, {value: 'third-request', as: equal } ] + + server-response: + # Delay for 2 seconds so that this origin connection will exist for 2 seconds. + delay: 2s + + status: 200 + reason: OK + headers: + fields: + - [ Content-Length, 36 ] + - [ X-Response, third-response ] + + proxy-response: + status: 200 + headers: + fields: + - [ X-Response, {value: 'third-response', as: equal } ] + + +# Make sure this is the last attempted transaction by giving the above +# three transactions, all executed in parallel, a one second head start. +- delay: 1s + + transactions: + + - client-request: + method: GET + url: /some/path/fourth + version: '1.1' + headers: + fields: + - [ Host, www.example.com ] + - [ Content-Length, 0 ] + - [ X-Request, fourth-request ] + - [ uuid, fourth-request ] + + # The server should never see this request since ATS will serve a 503 + # response due to the per_server.connection.max being hit. + server-response: + status: 200 + reason: OK + headers: + fields: + - [ Content-Length, 36 ] + - [ X-Response, fourth-response ] + + # Expect ATS to reply with a 503 because we hit the + # proxy.config.http.per_server.connection.max value of 3. + proxy-response: + status: 503