1616# See the License for the specific language governing permissions and
1717# limitations under the License.
1818
19- import os
2019import re
20+ from enum import Enum
2121from typing import List , Optional
2222
2323
@@ -28,6 +28,7 @@ class Http2FlowControlTest:
2828 """Define an object to test HTTP/2 flow control behavior."""
2929
3030 _replay_file : str = 'http2_flow_control.replay.yaml'
31+ _replay_chunked_file : str = 'http2_flow_control_chunked.replay.yaml'
3132 _valid_policy_values : List [int ] = list (range (0 , 3 ))
3233 _flow_control_policy : Optional [int ] = None
3334 _flow_control_policy_is_malformed : bool = False
@@ -47,6 +48,13 @@ class Http2FlowControlTest:
4748 IS_HTTP2_TO_ORIGIN = True
4849 IS_HTTP1_TO_ORIGIN = False
4950
51+ class ServerType (Enum ):
52+ """Define the type of server to use in a TestRun."""
53+
54+ HTTP1_CONTENT_LENGTH = 0
55+ HTTP1_CHUNKED = 1
56+ HTTP2 = 2
57+
5058 def __init__ (
5159 self ,
5260 description : str ,
@@ -100,20 +108,26 @@ def _configure_dns(self, tr: 'TestRun') -> 'Process':
100108 Http2FlowControlTest ._dns_counter += 1
101109 return dns
102110
103- def _configure_server (self , tr : 'TestRun' ) -> 'Process' :
111+ def _configure_server (self , tr : 'TestRun' ,
112+ server_type : ServerType ) -> 'Process' :
104113 """Configure the test server."""
114+ if server_type == self .ServerType .HTTP1_CHUNKED :
115+ replay_file = self ._replay_chunked_file
116+ else :
117+ replay_file = self ._replay_file
118+
105119 server = tr .AddVerifierServerProcess (
106120 f'server-{ Http2FlowControlTest ._server_counter } ' ,
107- self . _replay_file )
121+ replay_file )
108122 Http2FlowControlTest ._server_counter += 1
109123 return server
110124
111- def _configure_trafficserver (self , tr : 'TestRun' , is_outbound : bool , is_http2_to_orign : bool ) -> 'Process' :
125+ def _configure_trafficserver (self , tr : 'TestRun' , is_outbound : bool ,
126+ server_type : ServerType ) -> 'Process' :
112127 """Configure a Traffic Server process."""
113128 ts = tr .MakeATSProcess (
114129 f'ts-{ Http2FlowControlTest ._ts_counter } ' ,
115- enable_tls = True ,
116- enable_cache = False )
130+ enable_tls = True )
117131 Http2FlowControlTest ._ts_counter += 1
118132
119133 ts .addDefaultSSLFiles ()
@@ -122,12 +136,13 @@ def _configure_trafficserver(self, tr: 'TestRun', is_outbound: bool, is_http2_to
122136 'proxy.config.ssl.server.private_key.path' : f'{ ts .Variables .SSLDir } ' ,
123137 'proxy.config.ssl.client.verify.server.policy' : 'PERMISSIVE' ,
124138 'proxy.config.dns.nameservers' : '127.0.0.1:{0}' .format (self ._dns .Variables .Port ),
139+ 'proxy.config.http.insert_age_in_response' : 0 ,
125140
126141 'proxy.config.diags.debug.enabled' : 3 ,
127142 'proxy.config.diags.debug.tags' : 'http' ,
128143 })
129144
130- if is_http2_to_orign :
145+ if server_type == self . ServerType . HTTP2 :
131146 ts .Disk .records_config .update ({
132147 'proxy.config.ssl.client.alpn_protocols' : 'h2,http/1.1' ,
133148 })
@@ -174,11 +189,11 @@ def _configure_trafficserver(self, tr: 'TestRun', is_outbound: bool, is_http2_to
174189 configuration = 'proxy.config.http2.flow_control.policy_in'
175190 ts .Disk .diags_log .Content = Testers .ContainsExpression (
176191 f"ERROR.*{ configuration } " ,
177- "There should be an error about an invalid flow control policy." )
192+ "Expected an error about an invalid flow control policy." )
178193
179194 return ts
180195
181- def _configure_client (self , tr ):
196+ def _configure_client (self , tr , ):
182197 """Configure a client process.
183198
184199 :param tr: The TestRun to associate the client with.
@@ -251,25 +266,35 @@ def _configure_log_expectations(self, host):
251266 stream_window_1 = session_window_size
252267 stream_window_2 = int (session_window_size / 2 )
253268 stream_window_3 = int (session_window_size / 3 )
254- host .Streams .stdout += Testers .ContainsExpression (
255- (f'INITIAL_WINDOW_SIZE:{ stream_window_1 } .*'
256- f'INITIAL_WINDOW_SIZE:{ stream_window_2 } .*'
257- f'INITIAL_WINDOW_SIZE:{ stream_window_3 } ' ),
258- f"{ hostname } should stream receive window updates" ,
259- reflags = re .DOTALL | re .MULTILINE )
269+ if self ._server :
270+ # Toward the server, there is a potential race condition
271+ # between sending of first-request and the sending of the
272+ # SETTINGS frame which reduces the stream window size.
273+ # Allow for either scenario.
274+ host .Streams .stdout += Testers .ContainsExpression (
275+ (f'INITIAL_WINDOW_SIZE:{ stream_window_1 } .*'
276+ f'INITIAL_WINDOW_SIZE:{ stream_window_2 } .*' ),
277+ f"{ hostname } should stream receive window updates" ,
278+ reflags = re .DOTALL | re .MULTILINE )
279+ else :
280+ host .Streams .stdout += Testers .ContainsExpression (
281+ (f'INITIAL_WINDOW_SIZE:{ stream_window_1 } .*'
282+ f'INITIAL_WINDOW_SIZE:{ stream_window_2 } .*'
283+ f'INITIAL_WINDOW_SIZE:{ stream_window_3 } ' ),
284+ f"{ hostname } should stream receive window updates" ,
285+ reflags = re .DOTALL | re .MULTILINE )
260286
261287 if self ._expected_initial_stream_window_size < 1000 :
262288 first_id = 5 if self ._server else 3
263289
264- # WINDOW_UPDATE timing is different between the server and the
265- # client. Toward the origin we send SETTINGS frames to update the
266- # INITIAL_WINDOW_SIZE with the headers so they are received earlier
267- # than with the client, wherein we send the updated
268- # INITIAL_WINDOW_SIZE after receiving headers from the client.
269290 if self ._server and self ._expected_flow_control_policy == 2 :
270- window_update_size = 33
291+ # Toward the server, there is a potential race condition
292+ # between sending of first-request and the sending of the
293+ # SETTINGS frame which reduces the stream window size. Allow
294+ # for either scenario.
295+ window_update_size = f'33|{ self ._expected_initial_stream_window_size } '
271296 else :
272- window_update_size = self ._expected_initial_stream_window_size
297+ window_update_size = f' { self ._expected_initial_stream_window_size } '
273298 # For the smaller session window sizes, we expect WINDOW_UPDATE frames.
274299 host .Streams .stdout += Testers .ContainsExpression (
275300 f'WINDOW_UPDATE.*id { first_id } : { window_update_size } ' ,
@@ -283,11 +308,12 @@ def _configure_log_expectations(self, host):
283308 f'WINDOW_UPDATE.*id { first_id + 4 } : { window_update_size } ' ,
284309 f"{ hostname } should receive a stream WINDOW_UPDATE." )
285310
286- def _configure_test_run_common (self , tr , is_outbound : bool , is_http2_to_origin : bool ):
311+ def _configure_test_run_common (self , tr , is_outbound : bool ,
312+ server_type : ServerType ) -> None :
287313 """Perform the common Process configuration."""
288314 self ._dns = self ._configure_dns (tr )
289- self ._server = self ._configure_server (tr )
290- self ._ts = self ._configure_trafficserver (tr , is_outbound , is_http2_to_origin )
315+ self ._server = self ._configure_server (tr , server_type )
316+ self ._ts = self ._configure_trafficserver (tr , is_outbound , server_type )
291317 if not self ._flow_control_policy_is_malformed :
292318 self ._configure_client (tr )
293319 tr .Processes .Default .StartBefore (self ._dns )
@@ -297,25 +323,36 @@ def _configure_test_run_common(self, tr, is_outbound: bool, is_http2_to_origin:
297323 tr .Processes .Default .StartBefore (self ._ts )
298324 tr .TimeOut = 20
299325
300- def _configure_inbound_http1_to_origin_test_run (self ):
326+ def _configure_inbound_http1_to_origin_test_run (self ) -> None :
301327 """Configure the TestRun for inbound stream configuration."""
302- tr = Test .AddTestRun (f'{ self ._description } - inbound' )
303- self ._configure_test_run_common (tr , self .IS_INBOUND , self .IS_HTTP1_TO_ORIGIN )
328+ tr = Test .AddTestRun (f'{ self ._description } - inbound, '
329+ 'HTTP/1 Content-Length origin' )
330+ self ._configure_test_run_common (tr , self .IS_INBOUND ,
331+ self .ServerType .HTTP1_CONTENT_LENGTH )
332+ self ._configure_log_expectations (tr .Processes .Default )
333+
334+ tr = Test .AddTestRun (f'{ self ._description } - inbound, '
335+ 'HTTP/1 chunked origin' )
336+ self ._configure_test_run_common (tr , self .IS_INBOUND ,
337+ self .ServerType .HTTP1_CHUNKED )
304338 self ._configure_log_expectations (tr .Processes .Default )
305339
306- def _configure_inbound_http2_to_origin_test_run (self ):
340+ def _configure_inbound_http2_to_origin_test_run (self ) -> None :
307341 """Configure the TestRun for inbound stream configuration."""
308- tr = Test .AddTestRun (f'{ self ._description } - inbound' )
309- self ._configure_test_run_common (tr , self .IS_INBOUND , self .IS_HTTP2_TO_ORIGIN )
342+ tr = Test .AddTestRun (f'{ self ._description } - inbound, HTTP/2 origin' )
343+ self ._configure_test_run_common (tr , self .IS_INBOUND ,
344+ self .ServerType .HTTP2 )
310345 self ._configure_log_expectations (tr .Processes .Default )
311346
312- def _configure_outbound_test_run (self ):
347+ def _configure_outbound_test_run (self ) -> None :
313348 """Configure the TestRun outbound stream configuration."""
314- tr = Test .AddTestRun (f'{ self ._description } - outbound' )
315- self ._configure_test_run_common (tr , self .IS_OUTBOUND , self .IS_HTTP2_TO_ORIGIN )
349+ tr = Test .AddTestRun (f'{ self ._description } - outbound, HTTP/2 origin' )
350+ self ._configure_test_run_common (tr , self .IS_OUTBOUND ,
351+ self .ServerType .HTTP2 )
316352 self ._configure_log_expectations (self ._server )
317353
318- def run (self ):
354+ def run (self ) -> None :
355+ """Configure the test run for various origin side configurations."""
319356 self ._configure_inbound_http1_to_origin_test_run ()
320357 self ._configure_inbound_http2_to_origin_test_run ()
321358 self ._configure_outbound_test_run ()
@@ -352,18 +389,18 @@ def run(self):
352389test .run ()
353390
354391test = Http2FlowControlTest (
355- description = "Flow control policy 0 (default): small initial_window_size_(in|out) " ,
392+ description = "Flow control policy 0 (default): small initial_window_size " ,
356393 initial_window_size = 500 , # The default is 65 KB.
357394 flow_control_policy = 0 )
358395test .run ()
359396test = Http2FlowControlTest (
360- description = "Flow control policy 1: 100 byte session, 10 byte stream windows " ,
397+ description = "Flow control policy 1: 100 byte session, 10 byte streams " ,
361398 max_concurrent_streams = 10 ,
362399 initial_window_size = 10 ,
363400 flow_control_policy = 1 )
364401test .run ()
365402test = Http2FlowControlTest (
366- description = "Flow control policy 2: 100 byte session, dynamic stream windows " ,
403+ description = "Flow control policy 2: 100 byte session, dynamic streams " ,
367404 max_concurrent_streams = 10 ,
368405 initial_window_size = 10 ,
369406 flow_control_policy = 2 )
0 commit comments