5
5
6
6
using System . Globalization ;
7
7
using Microsoft . Build . Framework ;
8
+ using Microsoft . NET . Sdk . StaticWebAssets . Tasks . Utils ;
8
9
9
10
namespace Microsoft . AspNetCore . StaticWebAssets . Tasks ;
10
11
@@ -19,6 +20,10 @@ public class ApplyCompressionNegotiation : Task
19
20
[ Output ]
20
21
public ITaskItem [ ] UpdatedEndpoints { get ; set ; }
21
22
23
+ // Reusable collections and contexts for optimization
24
+ private readonly List < StaticWebAssetEndpointSelector > _selectorsList = new ( ) ;
25
+ private readonly List < StaticWebAssetEndpointResponseHeader > _headersList = new ( ) ;
26
+
22
27
public override bool Execute ( )
23
28
{
24
29
var assetsById = StaticWebAsset . ToAssetDictionary ( CandidateAssets ) ;
@@ -53,11 +58,17 @@ public override bool Execute()
53
58
54
59
if ( ! HasContentEncodingResponseHeader ( compressedEndpoint ) )
55
60
{
56
- // Add the Content-Encoding and Vary headers
57
- compressedEndpoint . ResponseHeaders = [
58
- ..compressedEndpoint . ResponseHeaders ,
59
- ..compressionHeaders
60
- ] ;
61
+ // Add the Content-Encoding and Vary headers using reusable list
62
+ _headersList . Clear ( ) ;
63
+ // Parse existing headers from string to avoid accessing array property
64
+ StaticWebAssetEndpointResponseHeader . PopulateFromMetadataValue ( compressedEndpoint . ResponseHeadersString , _headersList ) ;
65
+ // Add compression headers
66
+ var currentCompressionHeaders = GetOrCreateCompressionHeaders ( compressionHeadersByEncoding , compressedAsset ) ;
67
+ _headersList . AddRange ( currentCompressionHeaders ) ;
68
+ // Serialize back to string
69
+ using var headerContext = new JsonWriterContext ( ) ;
70
+ var headersString = StaticWebAssetEndpointResponseHeader . ToMetadataValue ( _headersList , headerContext ) ;
71
+ compressedEndpoint . SetResponseHeadersString ( headersString ) ;
61
72
}
62
73
63
74
var compressedHeaders = GetCompressedHeaders ( compressedEndpoint ) ;
@@ -137,12 +148,16 @@ public override bool Execute()
137
148
return true ;
138
149
}
139
150
140
- private static HashSet < string > GetCompressedHeaders ( StaticWebAssetEndpoint compressedEndpoint )
151
+ private HashSet < string > GetCompressedHeaders ( StaticWebAssetEndpoint compressedEndpoint )
141
152
{
142
- var result = new HashSet < string > ( compressedEndpoint . ResponseHeaders . Length , StringComparer . Ordinal ) ;
143
- for ( var i = 0 ; i < compressedEndpoint . ResponseHeaders . Length ; i ++ )
153
+ // Parse headers from string to avoid accessing array property
154
+ _headersList . Clear ( ) ;
155
+ StaticWebAssetEndpointResponseHeader . PopulateFromMetadataValue ( compressedEndpoint . ResponseHeadersString , _headersList ) ;
156
+
157
+ var result = new HashSet < string > ( _headersList . Count , StringComparer . Ordinal ) ;
158
+ for ( var i = 0 ; i < _headersList . Count ; i ++ )
144
159
{
145
- var responseHeader = compressedEndpoint . ResponseHeaders [ i ] ;
160
+ var responseHeader = _headersList [ i ] ;
146
161
result . Add ( responseHeader . Name ) ;
147
162
}
148
163
@@ -210,31 +225,46 @@ private StaticWebAssetEndpoint CreateUpdatedEndpoint(
210
225
Quality = quality
211
226
} ;
212
227
Log . LogMessage ( MessageImportance . Low , " Created Content-Encoding selector for compressed asset '{0}' with size '{1}' is '{2}'" , encodingSelector . Value , encodingSelector . Quality , relatedEndpointCandidate . Route ) ;
228
+
229
+ // Build selectors using reusable list to avoid array allocation
230
+ _selectorsList . Clear ( ) ;
231
+ StaticWebAssetEndpointSelector . PopulateFromMetadataValue ( relatedEndpointCandidate . SelectorsString , _selectorsList ) ;
232
+ _selectorsList . Add ( encodingSelector ) ;
233
+ using var selectorContext = new JsonWriterContext ( ) ;
234
+ var selectorsString = StaticWebAssetEndpointSelector . ToMetadataValue ( _selectorsList , selectorContext ) ;
235
+
213
236
var endpointCopy = new StaticWebAssetEndpoint
214
237
{
215
238
AssetFile = compressedAsset . Identity ,
216
239
Route = relatedEndpointCandidate . Route ,
217
- Selectors = [
218
- ..relatedEndpointCandidate . Selectors ,
219
- encodingSelector
220
- ] ,
221
- EndpointProperties = relatedEndpointCandidate . EndpointProperties
222
240
} ;
223
- var headers = new List < StaticWebAssetEndpointResponseHeader > ( 7 ) ;
224
- ApplyCompressedEndpointHeaders ( headers , compressedEndpoint , relatedEndpointCandidate . Route ) ;
225
- ApplyRelatedEndpointCandidateHeaders ( headers , relatedEndpointCandidate , compressedHeaders ) ;
226
- endpointCopy . ResponseHeaders = [ .. headers ] ;
241
+
242
+ // Set selectors and properties using string methods to avoid array allocations
243
+ endpointCopy . SetSelectorsString ( selectorsString ) ;
244
+ endpointCopy . SetEndpointPropertiesString ( relatedEndpointCandidate . EndpointPropertiesString ) ;
245
+
246
+ // Build headers using reusable list
247
+ _headersList . Clear ( ) ;
248
+ ApplyCompressedEndpointHeaders ( _headersList , compressedEndpoint , relatedEndpointCandidate . Route ) ;
249
+ ApplyRelatedEndpointCandidateHeaders ( _headersList , relatedEndpointCandidate , compressedHeaders ) ;
250
+ using var headerContext = new JsonWriterContext ( ) ;
251
+ var headersString = StaticWebAssetEndpointResponseHeader . ToMetadataValue ( _headersList , headerContext ) ;
252
+ endpointCopy . SetResponseHeadersString ( headersString ) ;
227
253
228
254
// Update the endpoint
229
255
Log . LogMessage ( MessageImportance . Low , " Updated related endpoint '{0}' with Content-Encoding selector '{1}={2}'" , relatedEndpointCandidate . Route , encodingSelector . Value , encodingSelector . Quality ) ;
230
256
return endpointCopy ;
231
257
}
232
258
233
- private static bool HasContentEncodingResponseHeader ( StaticWebAssetEndpoint compressedEndpoint )
259
+ private bool HasContentEncodingResponseHeader ( StaticWebAssetEndpoint compressedEndpoint )
234
260
{
235
- for ( var i = 0 ; i < compressedEndpoint . ResponseHeaders . Length ; i ++ )
261
+ // Parse headers from string to avoid accessing array property
262
+ _headersList . Clear ( ) ;
263
+ StaticWebAssetEndpointResponseHeader . PopulateFromMetadataValue ( compressedEndpoint . ResponseHeadersString , _headersList ) ;
264
+
265
+ for ( var i = 0 ; i < _headersList . Count ; i ++ )
236
266
{
237
- var responseHeader = compressedEndpoint . ResponseHeaders [ i ] ;
267
+ var responseHeader = _headersList [ i ] ;
238
268
if ( string . Equals ( responseHeader . Name , "Content-Encoding" , StringComparison . Ordinal ) )
239
269
{
240
270
return true ;
@@ -244,11 +274,15 @@ private static bool HasContentEncodingResponseHeader(StaticWebAssetEndpoint comp
244
274
return false ;
245
275
}
246
276
247
- private static bool HasContentEncodingSelector ( StaticWebAssetEndpoint compressedEndpoint )
277
+ private bool HasContentEncodingSelector ( StaticWebAssetEndpoint compressedEndpoint )
248
278
{
249
- for ( var i = 0 ; i < compressedEndpoint . Selectors . Length ; i ++ )
279
+ // Parse selectors from string to avoid accessing array property
280
+ _selectorsList . Clear ( ) ;
281
+ StaticWebAssetEndpointSelector . PopulateFromMetadataValue ( compressedEndpoint . SelectorsString , _selectorsList ) ;
282
+
283
+ for ( var i = 0 ; i < _selectorsList . Count ; i ++ )
250
284
{
251
- var selector = compressedEndpoint . Selectors [ i ] ;
285
+ var selector = _selectorsList [ i ] ;
252
286
if ( string . Equals ( selector . Name , "Content-Encoding" , StringComparison . Ordinal ) )
253
287
{
254
288
return true ;
@@ -289,14 +323,19 @@ private static string ResolveQuality(StaticWebAsset compressedAsset) =>
289
323
290
324
private static bool IsCompatible ( StaticWebAssetEndpoint compressedEndpoint , StaticWebAssetEndpoint relatedEndpointCandidate )
291
325
{
292
- var compressedFingerprint = ResolveFingerprint ( compressedEndpoint ) ;
293
- var relatedFingerprint = ResolveFingerprint ( relatedEndpointCandidate ) ;
326
+ var tempPropertiesList = new List < StaticWebAssetEndpointProperty > ( ) ;
327
+ var compressedFingerprint = ResolveFingerprint ( compressedEndpoint , tempPropertiesList ) ;
328
+ var relatedFingerprint = ResolveFingerprint ( relatedEndpointCandidate , tempPropertiesList ) ;
294
329
return string . Equals ( compressedFingerprint . Value , relatedFingerprint . Value , StringComparison . Ordinal ) ;
295
330
}
296
331
297
- private static StaticWebAssetEndpointProperty ResolveFingerprint ( StaticWebAssetEndpoint compressedEndpoint )
332
+ private static StaticWebAssetEndpointProperty ResolveFingerprint ( StaticWebAssetEndpoint compressedEndpoint , List < StaticWebAssetEndpointProperty > tempList )
298
333
{
299
- foreach ( var property in compressedEndpoint . EndpointProperties )
334
+ // Parse properties from string to avoid accessing array property
335
+ tempList . Clear ( ) ;
336
+ StaticWebAssetEndpointProperty . PopulateFromMetadataValue ( compressedEndpoint . EndpointPropertiesString , tempList ) ;
337
+
338
+ foreach ( var property in tempList )
300
339
{
301
340
if ( string . Equals ( property . Name , "fingerprint" , StringComparison . Ordinal ) )
302
341
{
@@ -308,7 +347,11 @@ private static StaticWebAssetEndpointProperty ResolveFingerprint(StaticWebAssetE
308
347
309
348
private void ApplyCompressedEndpointHeaders ( List < StaticWebAssetEndpointResponseHeader > headers , StaticWebAssetEndpoint compressedEndpoint , string relatedEndpointCandidateRoute )
310
349
{
311
- foreach ( var header in compressedEndpoint . ResponseHeaders )
350
+ // Parse headers from string to avoid accessing array property
351
+ var tempHeadersList = new List < StaticWebAssetEndpointResponseHeader > ( ) ;
352
+ StaticWebAssetEndpointResponseHeader . PopulateFromMetadataValue ( compressedEndpoint . ResponseHeadersString , tempHeadersList ) ;
353
+
354
+ foreach ( var header in tempHeadersList )
312
355
{
313
356
if ( string . Equals ( header . Name , "Content-Type" , StringComparison . Ordinal ) )
314
357
{
@@ -326,7 +369,11 @@ private void ApplyCompressedEndpointHeaders(List<StaticWebAssetEndpointResponseH
326
369
327
370
private void ApplyRelatedEndpointCandidateHeaders ( List < StaticWebAssetEndpointResponseHeader > headers , StaticWebAssetEndpoint relatedEndpointCandidate , HashSet < string > compressedHeaders )
328
371
{
329
- foreach ( var header in relatedEndpointCandidate . ResponseHeaders )
372
+ // Parse headers from string to avoid accessing array property
373
+ var tempHeadersList = new List < StaticWebAssetEndpointResponseHeader > ( ) ;
374
+ StaticWebAssetEndpointResponseHeader . PopulateFromMetadataValue ( relatedEndpointCandidate . ResponseHeadersString , tempHeadersList ) ;
375
+
376
+ foreach ( var header in tempHeadersList )
330
377
{
331
378
// We need to keep the headers that are specific to the compressed asset like Content-Length,
332
379
// Last-Modified and ETag. Any other header we should add it.
0 commit comments