Skip to content

Commit df7ccfe

Browse files
authored
Added Autest for H2 CONNECT and fix a crash triggered by the test (#9781)
In #9616, @maskit wrote an H2 CONNECT Autest but couldn't include that in the final PR because of a Proxy Verifier issue. Now that the Proxy Verifier issue is resolved, the Autest is added in this PR(with a few tweaks). ATS crashes with the new test executing HTTP/2 tunneling traffic. This PR also includes a fix to resolve that.
1 parent 61277dc commit df7ccfe

File tree

3 files changed

+135
-1
lines changed

3 files changed

+135
-1
lines changed

proxy/http/HttpSM.cc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4119,6 +4119,16 @@ HttpSM::tunnel_handler_ssl_producer(int event, HttpTunnelProducer *p)
41194119
STATE_ENTER(&HttpSM::tunnel_handler_ssl_producer, event);
41204120

41214121
switch (event) {
4122+
case VC_EVENT_READ_READY:
4123+
// This event is triggered when receiving DATA frames without the END_STREAM
4124+
// flag set in a HTTP/2 CONNECT request. Breaking as there are more DATA
4125+
// frames to come.
4126+
break;
4127+
case VC_EVENT_READ_COMPLETE:
4128+
// This event is triggered during an HTTP/2 CONNECT request when a DATA
4129+
// frame with the END_STREAM flag set is received, indicating the end of the
4130+
// stream.
4131+
[[fallthrough]];
41224132
case VC_EVENT_EOS:
41234133
// The write side of this connection is still alive
41244134
// so half-close the read
@@ -4150,7 +4160,6 @@ HttpSM::tunnel_handler_ssl_producer(int event, HttpTunnelProducer *p)
41504160
}
41514161
}
41524162
break;
4153-
case VC_EVENT_READ_COMPLETE:
41544163
case HTTP_TUNNEL_EVENT_PRECOMPLETE:
41554164
// We should never get these event since we don't know
41564165
// how long the stream is

tests/gold_tests/connect/connect.test.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,67 @@ def run(self):
165165

166166

167167
ConnectViaPVTest().run()
168+
169+
170+
class ConnectViaPVTest2:
171+
# This test executes a HTTP/2 CONNECT request with Proxy Verifier.
172+
connectReplayFile = "replays/connect_h2.replay.yaml"
173+
174+
def __init__(self):
175+
self.setupOriginServer()
176+
self.setupTS()
177+
178+
def setupOriginServer(self):
179+
self.server = Test.MakeVerifierServerProcess(
180+
"connect-verifier-server2",
181+
self.connectReplayFile)
182+
# Verify server output
183+
self.server.Streams.stdout += Testers.ExcludesExpression(
184+
"test: connect-request",
185+
"Verify the CONNECT request doesn't reach the server.")
186+
self.server.Streams.stdout += Testers.ContainsExpression(
187+
"GET /get HTTP/1.1\nuuid: 1\ntest: real-request", reflags=re.MULTILINE,
188+
description="Verify the server gets the second(tunneled) request.")
189+
190+
def setupTS(self):
191+
self.ts = Test.MakeATSProcess("connect-ts2", enable_tls=True)
192+
193+
self.ts.Disk.records_config.update({
194+
'proxy.config.diags.debug.enabled': 1,
195+
'proxy.config.diags.debug.tags': 'http|hpack',
196+
'proxy.config.ssl.server.cert.path': f'{self.ts.Variables.SSLDir}',
197+
'proxy.config.ssl.server.private_key.path': f'{self.ts.Variables.SSLDir}',
198+
'proxy.config.http.server_ports': f"{self.ts.Variables.ssl_port}:ssl",
199+
'proxy.config.http.connect_ports': f"{self.server.Variables.http_port}",
200+
})
201+
202+
self.ts.addDefaultSSLFiles()
203+
self.ts.Disk.ssl_multicert_config.AddLine(
204+
'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
205+
)
206+
207+
self.ts.Disk.remap_config.AddLines([
208+
f"map / http://127.0.0.1:{self.server.Variables.http_port}/",
209+
])
210+
# Verify ts logs
211+
self.ts.Disk.traffic_out.Content += Testers.ContainsExpression(
212+
f"Proxy's Request.*\n.*\nCONNECT 127.0.0.1:{self.server.Variables.http_port} HTTP/1.1", reflags=re.MULTILINE,
213+
description="Verify that ATS recognizes the CONNECT request.")
214+
215+
def runTraffic(self):
216+
tr = Test.AddTestRun("Verify correct handling of CONNECT request on HTTP/2")
217+
tr.AddVerifierClientProcess(
218+
"connect-client2",
219+
self.connectReplayFile,
220+
https_ports=[self.ts.Variables.ssl_port],
221+
other_args='--thread-limit 1')
222+
tr.Processes.Default.StartBefore(self.server)
223+
tr.Processes.Default.StartBefore(self.ts)
224+
tr.StillRunningAfter = self.server
225+
tr.StillRunningAfter = self.ts
226+
227+
def run(self):
228+
self.runTraffic()
229+
230+
231+
ConnectViaPVTest2().run()
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
#
18+
# This replay file executes a HTTP/2 CONNECT request, whose DATA frame contains
19+
# a tunnelled HTTP/1 GET request.
20+
#
21+
meta:
22+
version: "1.0"
23+
24+
sessions:
25+
- protocol:
26+
- name: http
27+
version: 2
28+
- name: tls
29+
sni: www.example.com
30+
- name: tcp
31+
- name: ip
32+
33+
transactions:
34+
- client-request:
35+
frames:
36+
- HEADERS:
37+
headers:
38+
fields:
39+
- [:method, CONNECT]
40+
- [:authority, www.example.com:80]
41+
- [uuid, 1]
42+
- [test, connect-request]
43+
- DATA:
44+
content:
45+
encoding: plain
46+
data: "GET /get HTTP/1.1\r\nuuid: 1\r\ntest: real-request\r\n\r\n"
47+
# This is the server response for the tunnelled HTTP/1 request rather
48+
# than for the CONNECT request.
49+
server-response:
50+
status: 200
51+
reason: OK
52+
content:
53+
encoding: plain
54+
data: response_to_tunnelled_request
55+
size: 29
56+
# Verify the client receives the response for the tunneled GET request
57+
# from the origin server.
58+
proxy-response:
59+
status: 200
60+
content:
61+
verify: { value: "response_to_tunnelled_request", as: contains }

0 commit comments

Comments
 (0)