shelf_static: ignore invalid If-Modified-Since headers (#557)

A request with a malformed If-Modified-Since header made the static and
file handlers throw the FormatException from Request.ifModifiedSince,
which shelf_io turns into a 500. RFC 9110 section 13.1.3 requires an
invalid value to be ignored, so treat it as absent.
diff --git a/pkgs/shelf_static/CHANGELOG.md b/pkgs/shelf_static/CHANGELOG.md
index 48df44f..f6b1ae4 100644
--- a/pkgs/shelf_static/CHANGELOG.md
+++ b/pkgs/shelf_static/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 1.2.1-wip
+
+* Ignore invalid `If-Modified-Since` headers instead of failing the request
+  with a `FormatException`.
+
 ## 1.2.0
 
 * Fix unhandled `FormatException` / HTTP 500 error when parsing oversized integer
diff --git a/pkgs/shelf_static/lib/src/static_handler.dart b/pkgs/shelf_static/lib/src/static_handler.dart
index e5d5ee3..6716343 100644
--- a/pkgs/shelf_static/lib/src/static_handler.dart
+++ b/pkgs/shelf_static/lib/src/static_handler.dart
@@ -235,7 +235,7 @@
   FileStat? fileStat,
 }) async {
   final stat = fileStat ?? await file.stat();
-  final ifModifiedSince = request.ifModifiedSince;
+  final ifModifiedSince = _ifModifiedSince(request);
   final ifNoneMatch = request.headers[HttpHeaders.ifNoneMatchHeader];
 
   generateETag ??= _defaultGenerateETag;
@@ -279,6 +279,18 @@
       );
 }
 
+/// Returns the parsed `If-Modified-Since` header of [request], or `null` if
+/// it is absent or not a valid HTTP date.
+///
+/// Per RFC 9110 § 13.1.3, an invalid `If-Modified-Since` value must be ignored.
+DateTime? _ifModifiedSince(Request request) {
+  try {
+    return request.ifModifiedSince;
+  } on FormatException {
+    return null;
+  }
+}
+
 String _defaultGenerateETag(File file, FileStat stat) =>
     'W/"${stat.size}-${stat.modified.millisecondsSinceEpoch}"';
 
diff --git a/pkgs/shelf_static/pubspec.yaml b/pkgs/shelf_static/pubspec.yaml
index 78c17e3..3ea84ae 100644
--- a/pkgs/shelf_static/pubspec.yaml
+++ b/pkgs/shelf_static/pubspec.yaml
@@ -1,5 +1,5 @@
 name: shelf_static
-version: 1.2.0
+version: 1.2.1-wip
 description: Static file server support for the shelf package and ecosystem.
 repository: https://github.com/dart-lang/shelf/tree/master/pkgs/shelf_static
 issue_tracker: https://github.com/dart-lang/shelf/issues?q=is%3Aissue+is%3Aopen+label%3Apackage%3Ashelf_static
diff --git a/pkgs/shelf_static/test/basic_file_test.dart b/pkgs/shelf_static/test/basic_file_test.dart
index d76ffd9..059f34a 100644
--- a/pkgs/shelf_static/test/basic_file_test.dart
+++ b/pkgs/shelf_static/test/basic_file_test.dart
@@ -163,6 +163,18 @@
       expect(response.contentLength, 0);
     });
 
+    test('invalid date is ignored', () async {
+      final handler = createStaticHandler(d.sandbox);
+
+      final response = await makeRequest(
+        handler,
+        '/root.txt',
+        headers: {HttpHeaders.ifModifiedSinceHeader: 'not a date'},
+      );
+      expect(response.statusCode, HttpStatus.ok);
+      expect(await response.readAsString(), 'root txt');
+    });
+
     test('after file modification', () async {
       // This test updates a file on disk to ensure the file stamp is updated
       // which was previously not the case on Windows due to the files "changed"