4
4
using System ;
5
5
using System . Collections . Generic ;
6
6
using System . Diagnostics ;
7
+ using System . IO ;
7
8
using System . Linq ;
8
9
using System . Net ;
9
10
using System . Net . Http ;
13
14
using Microsoft . AspNetCore . Certificates . Generation ;
14
15
using Microsoft . AspNetCore . Server . IntegrationTesting ;
15
16
using Microsoft . Extensions . CommandLineUtils ;
17
+ using Microsoft . Extensions . Logging ;
16
18
using Microsoft . Extensions . Logging . Abstractions ;
17
19
using OpenQA . Selenium ;
18
20
using OpenQA . Selenium . Edge ;
@@ -28,6 +30,10 @@ public class AspNetProcess : IDisposable
28
30
private readonly HttpClient _httpClient ;
29
31
private readonly ITestOutputHelper _output ;
30
32
33
+ private string _certificatePath ;
34
+ private string _certificatePassword = Guid . NewGuid ( ) . ToString ( ) ;
35
+ private string _certificateThumbprint ;
36
+
31
37
internal readonly Uri ListeningUri ;
32
38
internal ProcessEx Process { get ; }
33
39
@@ -37,33 +43,57 @@ public AspNetProcess(
37
43
string dllPath ,
38
44
IDictionary < string , string > environmentVariables ,
39
45
bool published = true ,
40
- bool hasListeningUri = true )
46
+ bool hasListeningUri = true ,
47
+ ILogger logger = null )
41
48
{
42
49
_output = output ;
43
50
_httpClient = new HttpClient ( new HttpClientHandler ( )
44
51
{
45
52
AllowAutoRedirect = true ,
46
53
UseCookies = true ,
47
54
CookieContainer = new CookieContainer ( ) ,
48
- ServerCertificateCustomValidationCallback = ( m , c , ch , p ) => true ,
55
+ ServerCertificateCustomValidationCallback = ( request , certificate , chain , errors ) => certificate ? . Thumbprint == _certificateThumbprint ,
49
56
} )
50
57
{
51
58
Timeout = TimeSpan . FromMinutes ( 2 )
52
59
} ;
53
60
54
- var now = DateTimeOffset . Now ;
55
- new CertificateManager ( ) . EnsureAspNetCoreHttpsDevelopmentCertificate ( now , now . AddYears ( 1 ) ) ;
61
+ _certificatePath = Path . Combine ( workingDirectory , $ " { Guid . NewGuid ( ) } .pfx" ) ;
62
+ EnsureDevelopmentCertificates ( ) ;
56
63
57
64
output . WriteLine ( "Running ASP.NET application..." ) ;
58
65
59
66
var arguments = published ? $ "exec { dllPath } " : "run" ;
60
- Process = ProcessEx . Run ( output , workingDirectory , DotNetMuxer . MuxerPathOrDefault ( ) , arguments , envVars : environmentVariables ) ;
67
+
68
+ logger ? . LogInformation ( $ "AspNetProcess - process: { DotNetMuxer . MuxerPathOrDefault ( ) } arguments: { arguments } ") ;
69
+
70
+ var finalEnvironmentVariables = new Dictionary < string , string > ( environmentVariables )
71
+ {
72
+ [ "ASPNETCORE_Kestrel__Certificates__Default__Path" ] = _certificatePath ,
73
+ [ "ASPNETCORE_Kestrel__Certificates__Default__Password" ] = _certificatePassword
74
+ } ;
75
+
76
+ Process = ProcessEx . Run ( output , workingDirectory , DotNetMuxer . MuxerPathOrDefault ( ) , arguments , envVars : finalEnvironmentVariables ) ;
77
+
78
+ logger ? . LogInformation ( "AspNetProcess - process started" ) ;
79
+
61
80
if ( hasListeningUri )
62
81
{
63
- ListeningUri = GetListeningUri ( output ) ?? throw new InvalidOperationException ( "Couldn't find the listening URL." ) ;
82
+ logger ? . LogInformation ( "AspNetProcess - Getting listening uri" ) ;
83
+ ListeningUri = ResolveListeningUrl ( output ) ;
84
+ logger ? . LogInformation ( $ "AspNetProcess - Got { ListeningUri } ") ;
64
85
}
65
86
}
66
87
88
+ internal void EnsureDevelopmentCertificates ( )
89
+ {
90
+ var now = DateTimeOffset . Now ;
91
+ var manager = new CertificateManager ( ) ;
92
+ var certificate = manager . CreateAspNetCoreHttpsDevelopmentCertificate ( now , now . AddYears ( 1 ) , "CN=localhost" ) ;
93
+ _certificateThumbprint = certificate . Thumbprint ;
94
+ manager . ExportCertificate ( certificate , path : _certificatePath , includePrivateKey : true , _certificatePassword ) ;
95
+ }
96
+
67
97
public void VisitInBrowser ( IWebDriver driver )
68
98
{
69
99
_output . WriteLine ( $ "Opening browser at { ListeningUri } ...") ;
@@ -173,7 +203,7 @@ private async Task<T> RequestWithRetries<T>(Func<HttpClient, Task<T>> requester,
173
203
throw new InvalidOperationException ( "Max retries reached." ) ;
174
204
}
175
205
176
- private Uri GetListeningUri ( ITestOutputHelper output )
206
+ private Uri ResolveListeningUrl ( ITestOutputHelper output )
177
207
{
178
208
// Wait until the app is accepting HTTP requests
179
209
output . WriteLine ( "Waiting until ASP.NET application is accepting connections..." ) ;
@@ -202,21 +232,27 @@ private Uri GetListeningUri(ITestOutputHelper output)
202
232
203
233
private string GetListeningMessage ( )
204
234
{
235
+ var buffer = new List < string > ( ) ;
205
236
try
206
237
{
207
- return Process
208
- // This will timeout at most after 5 minutes.
209
- . OutputLinesAsEnumerable
210
- . Where ( line => line != null )
211
- // This used to do StartsWith, but this is less strict and can prevent issues (very rare) where
212
- // console logging interleaves with other console output in a bad way. For example:
213
- // dbugNow listening on: http://127.0.0.1:12857
214
- . FirstOrDefault ( line => line . Trim ( ) . Contains ( ListeningMessagePrefix , StringComparison . Ordinal ) ) ;
238
+ foreach ( var line in Process . OutputLinesAsEnumerable )
239
+ {
240
+ if ( line != null )
241
+ {
242
+ buffer . Add ( line ) ;
243
+ if ( line . Trim ( ) . Contains ( ListeningMessagePrefix , StringComparison . Ordinal ) )
244
+ {
245
+ return line ;
246
+ }
247
+ }
248
+ }
215
249
}
216
250
catch ( OperationCanceledException )
217
251
{
218
- return null ;
219
252
}
253
+
254
+ throw new InvalidOperationException ( @$ "Couldn't find listening url:
255
+ { string . Join ( Environment . NewLine , buffer ) } ") ;
220
256
}
221
257
222
258
private bool IsSuccessStatusCode ( HttpResponseMessage response )
@@ -237,7 +273,8 @@ internal Task<HttpResponseMessage> SendRequest(string path)
237
273
238
274
public async Task AssertStatusCode ( string requestUrl , HttpStatusCode statusCode , string acceptContentType = null )
239
275
{
240
- var response = await RequestWithRetries ( client => {
276
+ var response = await RequestWithRetries ( client =>
277
+ {
241
278
var request = new HttpRequestMessage (
242
279
HttpMethod . Get ,
243
280
new Uri ( ListeningUri , requestUrl ) ) ;
0 commit comments