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 requests 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();
}
}