blob: 3468e6bc69c8a333401289019b42e255b4c67a8b [file]
'''
'''
# 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()