11// Copyright (c) Microsoft Corporation. All rights reserved.
22// Licensed under the MIT License.
33
4+ using Azure . Core ;
5+ using Azure . Core . Pipeline ;
46using System ;
7+ using System . Linq ;
8+ using System . Net ;
9+ using System . Net . Http ;
510using System . Threading ;
611using System . Threading . Tasks ;
712
813namespace Azure . Test . Perf
914{
1015 public abstract class PerfTest < TOptions > : IPerfTest where TOptions : PerfOptions
1116 {
17+ private readonly HttpPipelineTransport _insecureTransport ;
18+
19+ private readonly HttpClient _recordPlaybackHttpClient ;
20+ private readonly TestProxyPolicy _testProxyPolicy ;
21+
22+ private string _recordingId ;
23+
1224 protected TOptions Options { get ; private set ; }
1325
1426 public PerfTest ( TOptions options )
1527 {
1628 Options = options ;
29+
30+ if ( Options . Insecure )
31+ {
32+ var transport = ( new PerfClientOptions ( ) ) . Transport ;
33+
34+ // Disable SSL validation
35+ if ( transport is HttpClientTransport )
36+ {
37+ _insecureTransport = new HttpClientTransport ( new HttpClient ( new HttpClientHandler ( )
38+ {
39+ ServerCertificateCustomValidationCallback = ( message , cert , chain , errors ) => true
40+ } ) ) ;
41+ }
42+ else
43+ {
44+ // Assume _transport is HttpWebRequestTransport (currently internal class)
45+ ServicePointManager . ServerCertificateValidationCallback = ( message , cert , chain , errors ) => true ;
46+
47+ _insecureTransport = transport ;
48+ }
49+ }
50+
51+ if ( Options . TestProxy != null )
52+ {
53+ if ( Options . Insecure )
54+ {
55+ _recordPlaybackHttpClient = new HttpClient ( new HttpClientHandler ( )
56+ {
57+ ServerCertificateCustomValidationCallback = ( message , cert , chain , errors ) => true
58+ } ) ;
59+ }
60+ else
61+ {
62+ _recordPlaybackHttpClient = new HttpClient ( ) ;
63+ }
64+
65+ _testProxyPolicy = new TestProxyPolicy ( Options . TestProxy ) ;
66+ }
67+ }
68+
69+ protected TClientOptions ConfigureClientOptions < TClientOptions > ( TClientOptions clientOptions ) where TClientOptions : ClientOptions
70+ {
71+ if ( _insecureTransport != null )
72+ {
73+ clientOptions . Transport = _insecureTransport ;
74+ }
75+
76+ if ( _testProxyPolicy != null )
77+ {
78+ // TestProxyPolicy should be per-retry to run as late as possible in the pipeline. For example, some
79+ // clients compute a request signature as a per-retry policy, and TestProxyPolicy should run after the
80+ // signature is computed to avoid altering the signature.
81+ clientOptions . AddPolicy ( _testProxyPolicy , HttpPipelinePosition . PerRetry ) ;
82+ }
83+
84+ return clientOptions ;
1785 }
1886
1987 public virtual Task GlobalSetupAsync ( )
@@ -26,10 +94,48 @@ public virtual Task SetupAsync()
2694 return Task . CompletedTask ;
2795 }
2896
97+ public async Task RecordAndStartPlayback ( )
98+ {
99+ await StartRecording ( ) ;
100+
101+ _testProxyPolicy . RecordingId = _recordingId ;
102+ _testProxyPolicy . Mode = "record" ;
103+
104+ // Record one call to Run()
105+ if ( Options . Sync )
106+ {
107+ Run ( CancellationToken . None ) ;
108+ }
109+ else
110+ {
111+ await RunAsync ( CancellationToken . None ) ;
112+ }
113+
114+ await StopRecording ( ) ;
115+
116+ await StartPlayback ( ) ;
117+
118+ _testProxyPolicy . Mode = "playback" ;
119+ _testProxyPolicy . RecordingId = _recordingId ;
120+ }
121+
29122 public abstract void Run ( CancellationToken cancellationToken ) ;
30123
31124 public abstract Task RunAsync ( CancellationToken cancellationToken ) ;
32125
126+ public async Task StopPlayback ( )
127+ {
128+ var message = new HttpRequestMessage ( HttpMethod . Post , new Uri ( Options . TestProxy , "/playback/stop" ) ) ;
129+ message . Headers . Add ( "x-recording-id" , _recordingId ) ;
130+ message . Headers . Add ( "x-purge-inmemory-recording" , bool . TrueString ) ;
131+
132+ await _recordPlaybackHttpClient . SendAsync ( message ) ;
133+
134+ // Stop redirecting requests to test proxy
135+ _testProxyPolicy . Mode = null ;
136+ _testProxyPolicy . RecordingId = null ;
137+ }
138+
33139 public virtual Task CleanupAsync ( )
34140 {
35141 return Task . CompletedTask ;
@@ -73,5 +179,33 @@ protected static string GetEnvironmentVariable(string name)
73179 }
74180 return value ;
75181 }
182+
183+ private async Task StartRecording ( )
184+ {
185+ var message = new HttpRequestMessage ( HttpMethod . Post , new Uri ( Options . TestProxy , "/record/start" ) ) ;
186+
187+ var response = await _recordPlaybackHttpClient . SendAsync ( message ) ;
188+ _recordingId = response . Headers . GetValues ( "x-recording-id" ) . Single ( ) ;
189+ }
190+
191+ private async Task StopRecording ( )
192+ {
193+ var message = new HttpRequestMessage ( HttpMethod . Post , new Uri ( Options . TestProxy , "/record/stop" ) ) ;
194+ message . Headers . Add ( "x-recording-id" , _recordingId ) ;
195+
196+ await _recordPlaybackHttpClient . SendAsync ( message ) ;
197+ }
198+
199+ private async Task StartPlayback ( )
200+ {
201+ var message = new HttpRequestMessage ( HttpMethod . Post , new Uri ( Options . TestProxy , "/playback/start" ) ) ;
202+ message . Headers . Add ( "x-recording-id" , _recordingId ) ;
203+
204+ var response = await _recordPlaybackHttpClient . SendAsync ( message ) ;
205+ _recordingId = response . Headers . GetValues ( "x-recording-id" ) . Single ( ) ;
206+ }
207+
208+ // Dummy class used to fetch default HttpPipelineTransport
209+ private class PerfClientOptions : ClientOptions { }
76210 }
77211}
0 commit comments