Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add context to Response for including file and file_not_found to be identified by other shelf Middleware. #395

Closed
wants to merge 11 commits into from
Closed
37 changes: 35 additions & 2 deletions pkgs/shelf_static/lib/src/static_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ final _defaultMimeTypeResolver = MimeTypeResolver();
///
/// Specify a custom [contentTypeResolver] to customize automatic content type
/// detection.
///
/// The [Response.context] will be populated with "shelf_static:file" or
/// "shelf_static:file_not_found" with the resolved [File] for the [Response].
/// If the file is considered not found because it is outside of the
/// [fileSystemPath] and [serveFilesOutsidePath] is false, then neither key
/// will be included in the context.
Handler createStaticHandler(String fileSystemPath,
{bool serveFilesOutsidePath = false,
String? defaultDocument,
Expand Down Expand Up @@ -82,8 +88,20 @@ Handler createStaticHandler(String fileSystemPath,
}

if (fileFound == null) {
return Response.notFound('Not Found');
File? fileNotFound = File(fsPath);

// Do not expose a file path outside of the original fileSystemPath:
if (!serveFilesOutsidePath &&
!p.isWithin(fileSystemPath, fileNotFound.path)) {
fileNotFound = null;
}

return Response.notFound(
'Not Found',
context: _buildResponseContext(fileNotFound: fileNotFound),
);
}

final file = fileFound;

if (!serveFilesOutsidePath) {
Expand Down Expand Up @@ -120,6 +138,18 @@ Handler createStaticHandler(String fileSystemPath,
};
}

Map<String, Object>? _buildResponseContext({File? file, File? fileNotFound}) {
if (file == null && fileNotFound == null) return null;

// Ensure other shelf `Middleware` can identify
// the processed file in the `Response` by
// including `file` and `file_not_found` in the context:
return {
if (file != null) 'shelf_static:file': file,
if (fileNotFound != null) 'shelf_static:file_not_found': fileNotFound,
};
}

Response _redirectToAddTrailingSlash(Uri uri) {
final location = Uri(
scheme: uri.scheme,
Expand Down Expand Up @@ -184,7 +214,9 @@ Future<Response> _handleFile(Request request, File file,
if (ifModifiedSince != null) {
final fileChangeAtSecResolution = toSecondResolution(stat.modified);
if (!fileChangeAtSecResolution.isAfter(ifModifiedSince)) {
return Response.notModified();
return Response.notModified(
context: _buildResponseContext(file: file),
);
}
}

Expand All @@ -199,6 +231,7 @@ Future<Response> _handleFile(Request request, File file,
Response.ok(
request.method == 'HEAD' ? null : file.openRead(),
headers: headers..[HttpHeaders.contentLengthHeader] = '${stat.size}',
context: _buildResponseContext(file: file),
);
}

Expand Down