| ''' |
| ''' |
| # 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. |
| |
| import os |
| |
| Test.Summary = ''' |
| Test the Multiplexer plugin. |
| ''' |
| |
| Test.SkipUnless(Condition.PluginExists('multiplexer.so')) |
| |
| |
| class MultiplexerTestBase: |
| """ |
| Encapsulates the base configuration used by each test. |
| """ |
| |
| client_counter = 0 |
| dns_counter = 0 |
| server_counter = 0 |
| ts_counter = 0 |
| |
| def __init__(self, replay_file, multiplexed_host_replay_file, skip_post): |
| self.replay_file = replay_file |
| self.multiplexed_host_replay_file = multiplexed_host_replay_file |
| |
| self.setupServers() |
| self.setupDns() |
| self.setupTS(skip_post) |
| |
| def setupDns(self): |
| counter = MultiplexerTestBase.dns_counter |
| MultiplexerTestBase.dns_counter += 1 |
| self.dns = Test.MakeDNServer(f"dns_{counter}", default='127.0.0.1') |
| |
| def setupServers(self): |
| counter = MultiplexerTestBase.server_counter |
| MultiplexerTestBase.server_counter += 1 |
| self.server_origin = Test.MakeVerifierServerProcess(f"server_origin_{counter}", self.replay_file) |
| self.server_http = Test.MakeVerifierServerProcess(f"server_http_{counter}", self.multiplexed_host_replay_file) |
| self.server_https = Test.MakeVerifierServerProcess(f"server_https_{counter}", self.multiplexed_host_replay_file) |
| |
| # The origin should never receive "X-Multiplexer: copy" |
| self.server_origin.Streams.All += Testers.ExcludesExpression( |
| 'X-Multiplexer: copy', 'Verify the original server target never receives a "copy".') |
| |
| # Nor should the multiplexed hosts receive an "original" X-Multiplexer value. |
| self.server_http.Streams.All += Testers.ExcludesExpression( |
| 'X-Multiplexer: original', 'Verify the HTTP multiplexed host does not receive an "original".') |
| self.server_https.Streams.All += Testers.ExcludesExpression( |
| 'X-Multiplexer: original', 'Verify the HTTPS multiplexed host does not receive an "original".') |
| self.server_https.Streams.All += Testers.ExcludesExpression(r'\[ERROR\]', 'Verify there were no errors in the replay.') |
| |
| # In addition, the original server should always receive the POST and |
| # PUT requests. |
| self.server_origin.Streams.All += Testers.ContainsExpression( |
| 'uuid: POST', "Verify the client's original target received the POST transaction.") |
| self.server_origin.Streams.All += Testers.ContainsExpression( |
| 'uuid: PUT', "Verify the client's original target received the PUT transaction.") |
| self.server_origin.Streams.All += Testers.ExcludesExpression(r'\[ERROR\]', 'Verify there were no errors in the replay.') |
| # The chunked POST should go to the origin. |
| self.server_origin.Streams.All += Testers.ContainsExpression( |
| 'uuid: CHUNKED_POST', "Verify the client's original target received the chunked POST transaction.") |
| |
| # Under all configurations, the GET request should be multiplexed. |
| self.server_origin.Streams.All += Testers.ContainsExpression( |
| 'X-Multiplexer: original', 'Verify the client\'s original target received the "original" request.') |
| self.server_origin.Streams.All += Testers.ContainsExpression( |
| 'uuid: GET', "Verify the client's original target received the GET request.") |
| |
| self.server_http.Streams.All += Testers.ContainsExpression( |
| 'X-Multiplexer: copy', 'Verify the HTTP server received a "copy" of the request.') |
| self.server_http.Streams.All += Testers.ContainsExpression('uuid: GET', "Verify the HTTP server received the GET request.") |
| self.server_http.Streams.All += Testers.ExcludesExpression(r'\[ERROR\]', 'Verify there were no errors in the replay.') |
| # Chunked POST requests are not supported for multiplexing. |
| self.server_http.Streams.All += Testers.ExcludesExpression( |
| 'uuid: CHUNKED_POST', 'We do not expect a multiplexed chunked POST.') |
| |
| self.server_https.Streams.All += Testers.ContainsExpression( |
| 'X-Multiplexer: copy', 'Verify the HTTPS server received a "copy" of the request.') |
| self.server_https.Streams.All += Testers.ContainsExpression( |
| 'uuid: GET', "Verify the HTTPS server received the GET request.") |
| # Chunked POST requests are not supported for multiplexing. |
| self.server_https.Streams.All += Testers.ExcludesExpression( |
| 'uuid: CHUNKED_POST', 'We do not expect a multiplexed chunked POST.') |
| |
| # Verify that the CUSTOM_METHOD is sent to all servers. |
| self.server_origin.Streams.All += Testers.ContainsExpression( |
| 'uuid: MYCUSTOMMETHOD', "Verify the client's original target received the MYCUSTOMMETHOD transaction.") |
| self.server_http.Streams.All += Testers.ContainsExpression( |
| 'uuid: MYCUSTOMMETHOD', "Verify the HTTP server received the MYCUSTOMMETHOD request.") |
| self.server_http.Streams.All += Testers.ContainsExpression( |
| 'x-response: fifth', "Verify the HTTP server sent the MYCUSTOMMETHOD response.") |
| self.server_https.Streams.All += Testers.ContainsExpression( |
| 'uuid: MYCUSTOMMETHOD', "Verify the HTTPS server received the MYCUSTOMMETHOD request.") |
| self.server_https.Streams.All += Testers.ContainsExpression( |
| 'x-response: fifth', "Verify the HTTPS server sent the MYCUSTOMMETHOD response.") |
| |
| # Verify that the HTTPS server receives a TLS connection. |
| self.server_https.Streams.All += Testers.ContainsExpression( |
| 'Finished accept using TLSSession', "Verify the HTTPS was indeed used by the HTTPS server.") |
| |
| def setupTS(self, skip_post): |
| counter = MultiplexerTestBase.ts_counter |
| MultiplexerTestBase.ts_counter += 1 |
| self.ts = Test.MakeATSProcess(f"ts_{counter}", enable_tls=True, enable_cache=False) |
| self.ts.addDefaultSSLFiles() |
| self.ts.Disk.records_config.update( |
| { |
| "proxy.config.ssl.server.cert.path": f'{self.ts.Variables.SSLDir}', |
| "proxy.config.ssl.server.private_key.path": f'{self.ts.Variables.SSLDir}', |
| "proxy.config.ssl.client.verify.server.policy": 'PERMISSIVE', |
| 'proxy.config.diags.debug.enabled': 1, |
| 'proxy.config.diags.debug.tags': 'http|multiplexer', |
| 'proxy.config.dns.nameservers': f'127.0.0.1:{self.dns.Variables.Port}', |
| 'proxy.config.dns.resolv_conf': 'NULL', |
| }) |
| self.ts.Disk.ssl_multicert_yaml.AddLines( |
| """ |
| ssl_multicert: |
| - dest_ip: "*" |
| ssl_cert_name: server.pem |
| ssl_key_name: server.key |
| """.split("\n")) |
| skip_remap_param = '' |
| if skip_post: |
| skip_remap_param = ' @pparam=proxy.config.multiplexer.skip_post_put=1' |
| self.ts.Disk.remap_config.AddLines( |
| [ |
| f'map https://origin.server.com https://backend.origin.server.com:{self.server_origin.Variables.https_port} ' |
| f'@plugin=multiplexer.so @pparam=nontls.server.com @pparam=tls.server.com' |
| f'{skip_remap_param}', |
| |
| # Now create remap entries for the multiplexed hosts: one that |
| # verifies HTTP, and another that verifies HTTPS. |
| f'map http://nontls.server.com http://backend.nontls.server.com:{self.server_http.Variables.http_port}', |
| f'map http://tls.server.com https://backend.tls.server.com:{self.server_https.Variables.https_port}', |
| ]) |
| |
| def run(self): |
| tr = Test.AddTestRun() |
| self.ts.StartBefore(self.dns) |
| tr.Processes.Default.StartBefore(self.server_origin) |
| tr.Processes.Default.StartBefore(self.server_http) |
| tr.Processes.Default.StartBefore(self.server_https) |
| tr.Processes.Default.StartBefore(self.ts) |
| |
| counter = MultiplexerTestBase.client_counter |
| MultiplexerTestBase.client_counter += 1 |
| client = tr.AddVerifierClientProcess(f"client_{counter}", self.replay_file, https_ports=[self.ts.Variables.ssl_port]) |
| client.Streams.All += Testers.ExcludesExpression(r'\[ERROR\]', 'Verify there were no errors in the replay.') |
| |
| |
| class MultiplexerTest(MultiplexerTestBase): |
| """ |
| Exercise multiplexing without skip_post configuration. |
| """ |
| |
| replay_file = os.path.join("replays", "multiplexer_original.replay.yaml") |
| multiplexed_host_replay_file = os.path.join("replays", "multiplexer_copy.replay.yaml") |
| |
| def __init__(self): |
| super().__init__(MultiplexerTest.replay_file, MultiplexerTest.multiplexed_host_replay_file, skip_post=False) |
| |
| def setupServers(self): |
| super().setupServers() |
| |
| # Both of the multiplexed hosts should receive the POST because skip_post |
| # is disabled. |
| self.server_http.Streams.All += Testers.ContainsExpression( |
| 'uuid: POST', "Verify the HTTP server received the POST request.") |
| self.server_https.Streams.All += Testers.ContainsExpression( |
| 'uuid: POST', "Verify the HTTPS server received the POST request.") |
| self.server_http.Streams.All += Testers.ContainsExpression( |
| 'x-response: second', "Verify the HTTP server sent the POST response.") |
| self.server_https.Streams.All += Testers.ContainsExpression( |
| 'x-response: second', "Verify the HTTPS server sent the POST response.") |
| self.server_https.Streams.All += Testers.ContainsExpression( |
| 'x-response: fifth', "Verify the HTTPS server sent the custom method response.") |
| |
| # Same with PUT |
| self.server_http.Streams.All += Testers.ContainsExpression('uuid: PUT', "Verify the HTTP server received the PUT request.") |
| self.server_https.Streams.All += Testers.ContainsExpression( |
| 'uuid: PUT', "Verify the HTTPS server received the PUT request.") |
| |
| # Same with MYCUSTOMMETHOD |
| self.server_http.Streams.All += Testers.ContainsExpression( |
| 'uuid: MYCUSTOMMETHOD', "Verify the HTTP server received the custom method request.") |
| self.server_https.Streams.All += Testers.ContainsExpression( |
| 'uuid: MYCUSTOMMETHOD', "Verify the HTTPS server received the custom method request.") |
| |
| |
| class MultiplexerSkipPostTest(MultiplexerTestBase): |
| """ |
| Exercise multiplexing with skip_post configuration. |
| """ |
| |
| replay_file = os.path.join("replays", "multiplexer_original_skip_post.replay.yaml") |
| multiplexed_host_replay_file = os.path.join("replays", "multiplexer_copy_skip_post.replay.yaml") |
| |
| def __init__(self): |
| super().__init__(MultiplexerSkipPostTest.replay_file, MultiplexerSkipPostTest.multiplexed_host_replay_file, skip_post=True) |
| |
| def setupServers(self): |
| super().setupServers() |
| |
| # Neither of the multiplexed hosts should receive the POST because skip_post |
| # is enabled. |
| self.server_http.Streams.All += Testers.ExcludesExpression( |
| 'uuid: POST', "Verify the HTTP server did not receive the POST request.") |
| self.server_https.Streams.All += Testers.ExcludesExpression( |
| 'uuid: POST', "Verify the HTTPS server did not receive the POST request.") |
| |
| # Same with PUT. |
| self.server_http.Streams.All += Testers.ExcludesExpression( |
| 'uuid: PUT', "Verify the HTTP server did not receive the PUT request.") |
| self.server_https.Streams.All += Testers.ExcludesExpression( |
| 'uuid: PUT', "Verify the HTTPS server did not receive the PUT request.") |
| |
| |
| MultiplexerTest().run() |
| MultiplexerSkipPostTest().run() |