@@ -41,6 +41,7 @@ protected virtual (RangeItemHeaderValue range, long rangeLength, bool serveBody)
41
41
ActionContext context ,
42
42
FileResult result ,
43
43
long ? fileLength ,
44
+ bool enableRangeProcessing ,
44
45
DateTimeOffset ? lastModified = null ,
45
46
EntityTagHeaderValue etag = null )
46
47
{
@@ -59,54 +60,55 @@ protected virtual (RangeItemHeaderValue range, long rangeLength, bool serveBody)
59
60
60
61
var request = context . HttpContext . Request ;
61
62
var httpRequestHeaders = request . GetTypedHeaders ( ) ;
63
+ var preconditionState = GetPreconditionState ( httpRequestHeaders , lastModified , etag ) ;
64
+
62
65
var response = context . HttpContext . Response ;
63
- var httpResponseHeaders = response . GetTypedHeaders ( ) ;
64
- if ( lastModified . HasValue )
65
- {
66
- httpResponseHeaders . LastModified = lastModified ;
67
- }
68
- if ( etag != null )
69
- {
70
- httpResponseHeaders . ETag = etag ;
71
- }
66
+ SetLastModifiedAndEtagHeaders ( response , lastModified , etag ) ;
72
67
73
68
var serveBody = ! HttpMethods . IsHead ( request . Method ) ;
74
- var preconditionState = GetPreconditionState ( context , httpRequestHeaders , lastModified , etag ) ;
69
+
70
+ // Short circuit if the preconditional headers process to 304 (NotModified) or 412 (PreconditionFailed)
75
71
if ( preconditionState == PreconditionState . NotModified )
76
72
{
77
73
serveBody = false ;
78
74
response . StatusCode = StatusCodes . Status304NotModified ;
75
+ return ( range : null , rangeLength : 0 , serveBody ) ;
79
76
}
80
77
else if ( preconditionState == PreconditionState . PreconditionFailed )
81
78
{
82
79
serveBody = false ;
83
80
response . StatusCode = StatusCodes . Status412PreconditionFailed ;
81
+ return ( range : null , rangeLength : 0 , serveBody ) ;
84
82
}
85
83
86
84
if ( fileLength . HasValue )
87
85
{
88
- SetAcceptRangeHeader ( context ) ;
89
- // Assuming the request is not a range request, the Content-Length header is set to the length of the entire file.
86
+ // Assuming the request is not a range request, and the response body is not empty, the Content-Length header is set to
87
+ // the length of the entire file.
90
88
// If the request is a valid range request, this header is overwritten with the length of the range as part of the
91
89
// range processing (see method SetContentLength).
92
90
if ( serveBody )
93
91
{
94
92
response . ContentLength = fileLength . Value ;
95
93
}
96
- if ( HttpMethods . IsHead ( request . Method ) || HttpMethods . IsGet ( request . Method ) )
94
+
95
+ // Handle range request
96
+ if ( enableRangeProcessing )
97
97
{
98
- if ( ( preconditionState == PreconditionState . Unspecified ||
99
- preconditionState == PreconditionState . ShouldProcess ) )
98
+ SetAcceptRangeHeader ( response ) ;
99
+
100
+ // If the request method is HEAD or GET, PreconditionState is Unspecified or ShouldProcess, and IfRange header is valid,
101
+ // range should be processed and Range headers should be set
102
+ if ( ( HttpMethods . IsHead ( request . Method ) || HttpMethods . IsGet ( request . Method ) )
103
+ && ( preconditionState == PreconditionState . Unspecified || preconditionState == PreconditionState . ShouldProcess )
104
+ && ( IfRangeValid ( httpRequestHeaders , lastModified , etag ) ) )
100
105
{
101
- if ( IfRangeValid ( context , httpRequestHeaders , lastModified , etag ) )
102
- {
103
- return SetRangeHeaders ( context , httpRequestHeaders , fileLength . Value ) ;
104
- }
106
+ return SetRangeHeaders ( context , httpRequestHeaders , fileLength . Value ) ;
105
107
}
106
108
}
107
109
}
108
110
109
- return ( range : null , rangeLength : 0 , serveBody : serveBody ) ;
111
+ return ( range : null , rangeLength : 0 , serveBody ) ;
110
112
}
111
113
112
114
private static void SetContentType ( ActionContext context , FileResult result )
@@ -130,38 +132,25 @@ private static void SetContentDispositionHeader(ActionContext context, FileResul
130
132
}
131
133
}
132
134
133
- private static void SetAcceptRangeHeader ( ActionContext context )
134
- {
135
- var response = context . HttpContext . Response ;
136
- response . Headers [ HeaderNames . AcceptRanges ] = AcceptRangeHeaderValue ;
137
- }
138
-
139
- private static PreconditionState GetEtagMatchState (
140
- IList < EntityTagHeaderValue > etagHeader ,
141
- EntityTagHeaderValue etag ,
142
- PreconditionState matchFoundState ,
143
- PreconditionState matchNotFoundState )
135
+ private static void SetLastModifiedAndEtagHeaders ( HttpResponse response , DateTimeOffset ? lastModified , EntityTagHeaderValue etag )
144
136
{
145
- if ( etagHeader != null && etagHeader . Any ( ) )
137
+ var httpResponseHeaders = response . GetTypedHeaders ( ) ;
138
+ if ( lastModified . HasValue )
146
139
{
147
- var state = matchNotFoundState ;
148
- foreach ( var entityTag in etagHeader )
149
- {
150
- if ( entityTag . Equals ( EntityTagHeaderValue . Any ) || entityTag . Compare ( etag , useStrongComparison : true ) )
151
- {
152
- state = matchFoundState ;
153
- break ;
154
- }
155
- }
156
-
157
- return state ;
140
+ httpResponseHeaders . LastModified = lastModified ;
158
141
}
142
+ if ( etag != null )
143
+ {
144
+ httpResponseHeaders . ETag = etag ;
145
+ }
146
+ }
159
147
160
- return PreconditionState . Unspecified ;
148
+ private static void SetAcceptRangeHeader ( HttpResponse response )
149
+ {
150
+ response . Headers [ HeaderNames . AcceptRanges ] = AcceptRangeHeaderValue ;
161
151
}
162
152
163
153
internal static bool IfRangeValid (
164
- ActionContext context ,
165
154
RequestHeaders httpRequestHeaders ,
166
155
DateTimeOffset ? lastModified = null ,
167
156
EntityTagHeaderValue etag = null )
@@ -193,7 +182,6 @@ internal static bool IfRangeValid(
193
182
194
183
// Internal for testing
195
184
internal static PreconditionState GetPreconditionState (
196
- ActionContext context ,
197
185
RequestHeaders httpRequestHeaders ,
198
186
DateTimeOffset ? lastModified = null ,
199
187
EntityTagHeaderValue etag = null )
@@ -247,6 +235,30 @@ internal static PreconditionState GetPreconditionState(
247
235
return state ;
248
236
}
249
237
238
+ private static PreconditionState GetEtagMatchState (
239
+ IList < EntityTagHeaderValue > etagHeader ,
240
+ EntityTagHeaderValue etag ,
241
+ PreconditionState matchFoundState ,
242
+ PreconditionState matchNotFoundState )
243
+ {
244
+ if ( etagHeader != null && etagHeader . Any ( ) )
245
+ {
246
+ var state = matchNotFoundState ;
247
+ foreach ( var entityTag in etagHeader )
248
+ {
249
+ if ( entityTag . Equals ( EntityTagHeaderValue . Any ) || entityTag . Compare ( etag , useStrongComparison : true ) )
250
+ {
251
+ state = matchFoundState ;
252
+ break ;
253
+ }
254
+ }
255
+
256
+ return state ;
257
+ }
258
+
259
+ return PreconditionState . Unspecified ;
260
+ }
261
+
250
262
private static PreconditionState GetMaxPreconditionState ( params PreconditionState [ ] states )
251
263
{
252
264
var max = PreconditionState . Unspecified ;
@@ -281,6 +293,7 @@ private static (RangeItemHeaderValue range, long rangeLength, bool serveBody) Se
281
293
return ( range : null , rangeLength : 0 , serveBody : true ) ;
282
294
}
283
295
296
+ // Requested range is not satisfiable
284
297
if ( range == null )
285
298
{
286
299
// 14.16 Content-Range - A server sending a response with status code 416 (Requested range not satisfiable)
@@ -292,23 +305,23 @@ private static (RangeItemHeaderValue range, long rangeLength, bool serveBody) Se
292
305
return ( range : null , rangeLength : 0 , serveBody : false ) ;
293
306
}
294
307
308
+ response . StatusCode = StatusCodes . Status206PartialContent ;
295
309
httpResponseHeaders . ContentRange = new ContentRangeHeaderValue (
296
310
range . From . Value ,
297
311
range . To . Value ,
298
312
fileLength ) ;
299
313
300
- response . StatusCode = StatusCodes . Status206PartialContent ;
301
314
// Overwrite the Content-Length header for valid range requests with the range length.
302
- var rangeLength = SetContentLength ( context , range ) ;
315
+ var rangeLength = SetContentLength ( response , range ) ;
316
+
303
317
return ( range , rangeLength , serveBody : true ) ;
304
318
}
305
319
306
- private static long SetContentLength ( ActionContext context , RangeItemHeaderValue range )
320
+ private static long SetContentLength ( HttpResponse response , RangeItemHeaderValue range )
307
321
{
308
322
var start = range . From . Value ;
309
323
var end = range . To . Value ;
310
324
var length = end - start + 1 ;
311
- var response = context . HttpContext . Response ;
312
325
response . ContentLength = length ;
313
326
return length ;
314
327
}
0 commit comments