@@ -83,13 +83,13 @@ private async Task HandleRequestAsync(HttpContext context, RequestDelegate next)
83
83
var request = context . Request ;
84
84
var response = context . Response ;
85
85
86
- if ( ! context . Request . Path . StartsWithSegments ( _rootPath , StringComparison . InvariantCultureIgnoreCase ) )
86
+ if ( ! context . Request . Path . StartsWithSegments ( _rootPath , StringComparison . InvariantCultureIgnoreCase , out PathString path ) )
87
87
{
88
88
await next ( context ) . ConfigureAwait ( false ) ;
89
89
return ;
90
90
}
91
91
92
- string containerPath = $ "{ _containerRootPath . TrimEnd ( '/' ) } /{ ( request . Path . Value . Remove ( 0 , _rootPath . Length ) ) . TrimStart ( '/' ) } ";
92
+ string containerPath = $ "{ _containerRootPath . TrimEnd ( '/' ) } /{ path . Value ? . TrimStart ( '/' ) } ";
93
93
var blob = _fileSystemProvider . GetFileSystem ( _name ) . GetBlobClient ( containerPath ) ;
94
94
95
95
var blobRequestConditions = GetAccessCondition ( context . Request ) ;
@@ -114,7 +114,7 @@ private async Task HandleRequestAsync(HttpContext context, RequestDelegate next)
114
114
// a Content-Range header is needed with the new length
115
115
ignoreRange = true ;
116
116
properties = await blob . GetPropertiesAsync ( ) . ConfigureAwait ( false ) ;
117
- response . Headers . Append ( "Content-Range" , $ "bytes */{ properties . Value . ContentLength } ") ;
117
+ response . Headers . Append ( HeaderNames . ContentRange , $ "bytes */{ properties . Value . ContentLength } ") ;
118
118
}
119
119
catch ( RequestFailedException ex ) when ( ex . Status == ( int ) HttpStatusCode . NotModified )
120
120
{
@@ -128,15 +128,15 @@ private async Task HandleRequestAsync(HttpContext context, RequestDelegate next)
128
128
// and not a request failed with status NotModified :(
129
129
catch ( Exception ex ) when ( ex . Message == "The condition specified using HTTP conditional header(s) is not met." )
130
130
{
131
- if ( blobRequestConditions != null
132
- && ( blobRequestConditions . IfMatch . HasValue || blobRequestConditions . IfUnmodifiedSince . HasValue ) )
131
+ if ( blobRequestConditions != null &&
132
+ ( blobRequestConditions . IfMatch . HasValue || blobRequestConditions . IfUnmodifiedSince . HasValue ) )
133
133
{
134
134
// If-Range or If-Unmodified-Since is not met
135
135
// if the resource has been modified, we need to send the whole file back with a 200 OK
136
136
// a Content-Range header is needed with the new length
137
137
ignoreRange = true ;
138
138
properties = await blob . GetPropertiesAsync ( ) . ConfigureAwait ( false ) ;
139
- response . Headers . Append ( "Content-Range" , $ "bytes */{ properties . Value . ContentLength } ") ;
139
+ response . Headers . Append ( HeaderNames . ContentRange , $ "bytes */{ properties . Value . ContentLength } ") ;
140
140
}
141
141
else
142
142
{
@@ -155,22 +155,24 @@ private async Task HandleRequestAsync(HttpContext context, RequestDelegate next)
155
155
156
156
var responseHeaders = response . GetTypedHeaders ( ) ;
157
157
158
- responseHeaders . CacheControl =
159
- new CacheControlHeaderValue
160
- {
161
- Public = true ,
162
- MustRevalidate = true ,
163
- MaxAge = _maxAge ,
164
- } ;
158
+ responseHeaders . CacheControl = new CacheControlHeaderValue
159
+ {
160
+ Public = true ,
161
+ MustRevalidate = true ,
162
+ MaxAge = _maxAge ,
163
+ } ;
165
164
166
165
responseHeaders . LastModified = properties . Value . LastModified ;
167
- responseHeaders . ETag = new EntityTagHeaderValue ( properties . Value . ETag . ToString ( "H" ) ) ;
168
- responseHeaders . Append ( HeaderNames . Vary , "Accept-Encoding" ) ;
169
166
170
- var requestHeaders = request . GetTypedHeaders ( ) ;
167
+ if ( EntityTagHeaderValue . TryParse ( properties . Value . ETag . ToString ( "H" ) , out EntityTagHeaderValue entityTagHeaderValue ) )
168
+ {
169
+ responseHeaders . ETag = entityTagHeaderValue ;
170
+ }
171
171
172
- var rangeHeader = requestHeaders . Range ;
172
+ responseHeaders . Append ( HeaderNames . Vary , HeaderNames . AcceptEncoding ) ;
173
173
174
+ var requestHeaders = request . GetTypedHeaders ( ) ;
175
+ var rangeHeader = requestHeaders . Range ;
174
176
if ( ! ignoreRange && rangeHeader != null )
175
177
{
176
178
if ( ! ValidateRanges ( rangeHeader . Ranges , properties . Value . ContentLength ) )
@@ -223,6 +225,7 @@ private async Task HandleRequestAsync(HttpContext context, RequestDelegate next)
223
225
return ;
224
226
}
225
227
}
228
+
226
229
response . StatusCode = ( int ) HttpStatusCode . OK ;
227
230
response . ContentType = properties . Value . ContentType ;
228
231
responseHeaders . ContentLength = properties . Value . ContentLength ;
@@ -234,11 +237,11 @@ private async Task HandleRequestAsync(HttpContext context, RequestDelegate next)
234
237
235
238
private static BlobRequestConditions ? GetAccessCondition ( HttpRequest request )
236
239
{
237
- var range = request . Headers [ " Range" ] ;
240
+ var range = request . Headers [ HeaderNames . Range ] ;
238
241
if ( string . IsNullOrEmpty ( range ) )
239
242
{
240
243
// etag
241
- var ifNoneMatch = request . Headers [ "If-None-Match" ] ;
244
+ var ifNoneMatch = request . Headers [ HeaderNames . IfNoneMatch ] ;
242
245
if ( ! string . IsNullOrEmpty ( ifNoneMatch ) )
243
246
{
244
247
return new BlobRequestConditions
@@ -247,40 +250,46 @@ private async Task HandleRequestAsync(HttpContext context, RequestDelegate next)
247
250
} ;
248
251
}
249
252
250
- var ifModifiedSince = request . Headers [ "If-Modified-Since" ] ;
251
- if ( ! string . IsNullOrEmpty ( ifModifiedSince ) )
253
+ var ifModifiedSince = request . Headers [ HeaderNames . IfModifiedSince ] ;
254
+ if ( ! string . IsNullOrEmpty ( ifModifiedSince ) &&
255
+ DateTimeOffset . TryParse ( ifModifiedSince , CultureInfo . InvariantCulture , DateTimeStyles . None , out DateTimeOffset ifModifiedSinceDate ) )
252
256
{
253
257
return new BlobRequestConditions
254
258
{
255
- IfModifiedSince = DateTimeOffset . Parse ( ifModifiedSince , CultureInfo . InvariantCulture )
259
+ IfModifiedSince = ifModifiedSinceDate
256
260
} ;
257
261
}
258
262
}
259
263
else
260
264
{
261
265
// handle If-Range header, it can be either an etag or a date
262
266
// see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Range and https://tools.ietf.org/html/rfc7233#section-3.2
263
- var ifRange = request . Headers [ "If-Range" ] ;
267
+ var ifRange = request . Headers [ HeaderNames . IfRange ] ;
264
268
if ( ! string . IsNullOrEmpty ( ifRange ) )
265
269
{
266
- var conditions = new BlobRequestConditions ( ) ;
267
-
268
- if ( DateTimeOffset . TryParse ( ifRange , out var date ) )
270
+ if ( DateTimeOffset . TryParse ( ifRange , CultureInfo . InvariantCulture , DateTimeStyles . None , out DateTimeOffset ifRangeDate ) )
269
271
{
270
- conditions . IfUnmodifiedSince = date ;
272
+ return new BlobRequestConditions ( )
273
+ {
274
+ IfUnmodifiedSince = ifRangeDate
275
+ } ;
271
276
}
272
277
else
273
278
{
274
- conditions . IfMatch = new ETag ( ifRange ) ;
279
+ return new BlobRequestConditions ( )
280
+ {
281
+ IfMatch = new ETag ( ifRange )
282
+ } ;
275
283
}
276
284
}
277
285
278
- var ifUnmodifiedSince = request . Headers [ "If-Unmodified-Since" ] ;
279
- if ( ! string . IsNullOrEmpty ( ifUnmodifiedSince ) )
286
+ var ifUnmodifiedSince = request . Headers [ HeaderNames . IfUnmodifiedSince ] ;
287
+ if ( ! string . IsNullOrEmpty ( ifUnmodifiedSince ) &&
288
+ DateTimeOffset . TryParse ( ifUnmodifiedSince , CultureInfo . InvariantCulture , DateTimeStyles . None , out DateTimeOffset ifUnmodifiedSinceDate ) )
280
289
{
281
290
return new BlobRequestConditions
282
291
{
283
- IfUnmodifiedSince = DateTimeOffset . Parse ( ifUnmodifiedSince , CultureInfo . InvariantCulture )
292
+ IfUnmodifiedSince = ifUnmodifiedSinceDate
284
293
} ;
285
294
}
286
295
}
@@ -291,14 +300,21 @@ private async Task HandleRequestAsync(HttpContext context, RequestDelegate next)
291
300
private static bool ValidateRanges ( ICollection < RangeItemHeaderValue > ranges , long length )
292
301
{
293
302
if ( ranges . Count == 0 )
303
+ {
294
304
return false ;
305
+ }
295
306
296
307
foreach ( var range in ranges )
297
308
{
298
309
if ( range . From > range . To )
310
+ {
299
311
return false ;
312
+ }
313
+
300
314
if ( range . To >= length )
315
+ {
301
316
return false ;
317
+ }
302
318
}
303
319
304
320
return true ;
@@ -337,8 +353,7 @@ private static ContentRangeHeaderValue GetRangeHeader(BlobProperties properties,
337
353
return new ContentRangeHeaderValue ( from , to , properties . ContentLength ) ;
338
354
}
339
355
340
- private static async Task DownloadRangeToStreamAsync ( BlobClient blob , BlobProperties properties ,
341
- Stream outputStream , ContentRangeHeaderValue contentRange , CancellationToken cancellationToken )
356
+ private static async Task DownloadRangeToStreamAsync ( BlobClient blob , BlobProperties properties , Stream outputStream , ContentRangeHeaderValue contentRange , CancellationToken cancellationToken )
342
357
{
343
358
var offset = contentRange . From . GetValueOrDefault ( 0L ) ;
344
359
var length = properties . ContentLength ;
@@ -359,8 +374,7 @@ private static async Task DownloadRangeToStreamAsync(BlobClient blob, BlobProper
359
374
await DownloadRangeToStreamAsync ( blob , outputStream , offset , length , cancellationToken ) . ConfigureAwait ( false ) ;
360
375
}
361
376
362
- private static async Task DownloadRangeToStreamAsync ( BlobClient blob , Stream outputStream ,
363
- long offset , long length , CancellationToken cancellationToken )
377
+ private static async Task DownloadRangeToStreamAsync ( BlobClient blob , Stream outputStream , long offset , long length , CancellationToken cancellationToken )
364
378
{
365
379
try
366
380
{
0 commit comments