9
9
using System . Linq ;
10
10
using System . Net . Http ;
11
11
using System . Security . Cryptography ;
12
+ using System . Threading ;
12
13
using System . Threading . Tasks ;
13
14
using Xunit ;
14
15
using Xunit . Abstractions ;
@@ -23,7 +24,7 @@ public class IdentityUIScriptsTest : IDisposable
23
24
public IdentityUIScriptsTest ( ITestOutputHelper output )
24
25
{
25
26
_output = output ;
26
- _httpClient = new HttpClient ( ) ;
27
+ _httpClient = new HttpClient ( new RetryHandler ( new HttpClientHandler ( ) { } ) ) ;
27
28
}
28
29
29
30
public static IEnumerable < object [ ] > ScriptWithIntegrityData
@@ -40,15 +41,18 @@ public static IEnumerable<object[]> ScriptWithIntegrityData
40
41
[ MemberData ( nameof ( ScriptWithIntegrityData ) ) ]
41
42
public async Task IdentityUI_ScriptTags_SubresourceIntegrityCheck ( ScriptTag scriptTag )
42
43
{
43
- string expectedIntegrity ;
44
+ var sha256Integrity = await GetShaIntegrity ( scriptTag , SHA256 . Create ( ) , "sha256" ) ;
45
+ Assert . Equal ( scriptTag . Integrity , sha256Integrity ) ;
46
+ }
47
+
48
+ private async Task < string > GetShaIntegrity ( ScriptTag scriptTag , HashAlgorithm algorithm , string prefix )
49
+ {
44
50
using ( var respStream = await _httpClient . GetStreamAsync ( scriptTag . Src ) )
45
51
using ( var alg = SHA256 . Create ( ) )
46
52
{
47
53
var hash = alg . ComputeHash ( respStream ) ;
48
- expectedIntegrity = "sha256 -" + Convert . ToBase64String ( hash ) ;
54
+ return $ " { prefix } -" + Convert . ToBase64String ( hash ) ;
49
55
}
50
-
51
- Assert . Equal ( expectedIntegrity , scriptTag . Integrity ) ;
52
56
}
53
57
54
58
public static IEnumerable < object [ ] > ScriptWithFallbackSrcData
@@ -66,7 +70,7 @@ public static IEnumerable<object[]> ScriptWithFallbackSrcData
66
70
public async Task IdentityUI_ScriptTags_FallbackSourceContent_Matches_CDNContent ( ScriptTag scriptTag )
67
71
{
68
72
var slnDir = GetSolutionDir ( ) ;
69
- var wwwrootDir = Path . Combine ( slnDir , "src" , "UI" , "wwwroot" , "V4" ) ;
73
+ var wwwrootDir = Path . Combine ( slnDir , "src" , "UI" , "wwwroot" , scriptTag . Version ) ;
70
74
71
75
var cdnContent = await _httpClient . GetStringAsync ( scriptTag . Src ) ;
72
76
var fallbackSrcContent = File . ReadAllText (
@@ -77,6 +81,7 @@ public async Task IdentityUI_ScriptTags_FallbackSourceContent_Matches_CDNContent
77
81
78
82
public struct ScriptTag
79
83
{
84
+ public string Version ;
80
85
public string Src ;
81
86
public string Integrity ;
82
87
public string FallbackSrc ;
@@ -91,8 +96,9 @@ public override string ToString()
91
96
private static List < ScriptTag > GetScriptTags ( )
92
97
{
93
98
var slnDir = GetSolutionDir ( ) ;
94
- var uiDir = Path . Combine ( slnDir , "src" , "UI" , "Areas" , "Identity" , "Pages" , "V4" ) ;
95
- var cshtmlFiles = Directory . GetFiles ( uiDir , "*.cshtml" , SearchOption . AllDirectories ) ;
99
+ var uiDirV3 = Path . Combine ( slnDir , "src" , "UI" , "Areas" , "Identity" , "Pages" , "V3" ) ;
100
+ var uiDirV4 = Path . Combine ( slnDir , "src" , "UI" , "Areas" , "Identity" , "Pages" , "V4" ) ;
101
+ var cshtmlFiles = GetRazorFiles ( uiDirV3 ) . Concat ( GetRazorFiles ( uiDirV4 ) ) ;
96
102
97
103
var scriptTags = new List < ScriptTag > ( ) ;
98
104
foreach ( var cshtmlFile in cshtmlFiles )
@@ -104,6 +110,8 @@ private static List<ScriptTag> GetScriptTags()
104
110
Assert . NotEmpty ( scriptTags ) ;
105
111
106
112
return scriptTags ;
113
+
114
+ IEnumerable < string > GetRazorFiles ( string dir ) => Directory . GetFiles ( dir , "*.cshtml" , SearchOption . AllDirectories ) ;
107
115
}
108
116
109
117
private static List < ScriptTag > GetScriptTags ( string cshtmlFile )
@@ -123,6 +131,7 @@ private static List<ScriptTag> GetScriptTags(string cshtmlFile)
123
131
124
132
scriptTags . Add ( new ScriptTag
125
133
{
134
+ Version = cshtmlFile . Contains ( "V3" ) ? "V3" : "V4" ,
126
135
Src = scriptElement . Source ,
127
136
Integrity = scriptElement . Integrity ,
128
137
FallbackSrc = fallbackSrcAttribute ? . Value ,
@@ -155,5 +164,25 @@ public void Dispose()
155
164
{
156
165
_httpClient . Dispose ( ) ;
157
166
}
167
+
168
+ class RetryHandler : DelegatingHandler
169
+ {
170
+ public RetryHandler ( HttpMessageHandler innerHandler ) : base ( innerHandler ) { }
171
+
172
+ protected override async Task < HttpResponseMessage > SendAsync ( HttpRequestMessage request , CancellationToken cancellationToken )
173
+ {
174
+ HttpResponseMessage result = null ;
175
+ for ( var i = 0 ; i < 10 ; i ++ )
176
+ {
177
+ result = await base . SendAsync ( request , cancellationToken ) ;
178
+ if ( result . IsSuccessStatusCode )
179
+ {
180
+ return result ;
181
+ }
182
+ await Task . Delay ( 1000 ) ;
183
+ }
184
+ return result ;
185
+ }
186
+ }
158
187
}
159
188
}
0 commit comments