serveFile method
Serve the content of file
to request
.
Can be used in overrides of directoryHandler to redirect to an index file.
In the request contains the HttpHeaders.ifModifiedSince header, serveFile will send a HttpStatus.notModified response if the file was not changed.
Note that if it was unable to read from file
, the request
s response
is closed with error-code HttpStatus.notFound.
Implementation
void serveFile(File file, HttpRequest request) async {
var response = request.response;
// TODO(ajohnsen): Set up Zone support for these errors.
try {
var lastModified = await file.lastModified();
if (request.headers.ifModifiedSince != null &&
!lastModified.isAfter(request.headers.ifModifiedSince!)) {
response.statusCode = HttpStatus.notModified;
await response.close();
return null;
}
response.headers.set(HttpHeaders.lastModifiedHeader, lastModified);
response.headers.set(HttpHeaders.acceptRangesHeader, 'bytes');
var length = await file.length();
var range = request.headers.value(HttpHeaders.rangeHeader);
if (range != null) {
// We only support one range, where the standard support several.
var matches = RegExp(r'^bytes=(\d*)\-(\d*)$').firstMatch(range);
// If the range header have the right format, handle it.
if (matches != null &&
(matches[1]!.isNotEmpty || matches[2]!.isNotEmpty)) {
// Serve sub-range.
int start; // First byte position - inclusive.
int end; // Last byte position - inclusive.
if (matches[1]!.isEmpty) {
start = length - int.parse(matches[2]!);
if (start < 0) start = 0;
end = length - 1;
} else {
start = int.parse(matches[1]!);
end = matches[2]!.isEmpty ? length - 1 : int.parse(matches[2]!);
}
// If the range is syntactically invalid the Range header
// MUST be ignored (RFC 2616 section 14.35.1).
if (start <= end) {
if (end >= length) {
end = length - 1;
}
if (start >= length) {
response.statusCode = HttpStatus.requestedRangeNotSatisfiable;
await response.close();
return;
}
// Override Content-Length with the actual bytes sent.
response.headers
.set(HttpHeaders.contentLengthHeader, end - start + 1);
// Set 'Partial Content' status code.
response
..statusCode = HttpStatus.partialContent
..headers.set(
HttpHeaders.contentRangeHeader, 'bytes $start-$end/$length');
// Pipe the 'range' of the file.
if (request.method == 'HEAD') {
await response.close();
} else {
try {
await file
.openRead(start, end + 1)
.cast<List<int>>()
.pipe(_VirtualDirectoryFileStream(response, file.path));
} catch (_) {
// TODO(kevmoo): log errors
}
}
return;
}
}
}
response.headers.set(HttpHeaders.contentLengthHeader, length);
if (request.method == 'HEAD') {
await response.close();
} else {
try {
await file
.openRead()
.cast<List<int>>()
.pipe(_VirtualDirectoryFileStream(response, file.path));
} catch (_) {
// TODO(kevmoo): log errors
}
}
} catch (_) {
response.statusCode = HttpStatus.notFound;
await response.close();
}
}