fix(shelf_static): ignore suffix ranges selecting zero bytes (#541)

Restore the start > end check at the end of the suffix branch in _fileRangeResponse so suffix ranges selecting zero bytes (such as bytes=-0 or any suffix range on a 0-byte file) are ignored and served with 200 OK instead of returning 206 Partial Content with a malformed Content-Range header.

- Add if (start > end) return null; guard to the suffix byte range branch
- Add unit tests for bytes=-0, suffix ranges on 0-byte files, and bytes=-1 boundary
- Update 1.2.0-wip changelog entry

Fixes #540
diff --git a/pkgs/shelf_static/CHANGELOG.md b/pkgs/shelf_static/CHANGELOG.md
index bb5e77a..4887464 100644
--- a/pkgs/shelf_static/CHANGELOG.md
+++ b/pkgs/shelf_static/CHANGELOG.md
@@ -1,7 +1,7 @@
 ## 1.2.0-wip
 
 * Fix unhandled `FormatException` / HTTP 500 error when parsing oversized integer
-  values in HTTP `Range` headers.
+  values in HTTP `Range` headers, and ignore suffix ranges that select zero bytes.
 * Replace static, blocking `dart:io` operations (such as `statSync()` and
   `File.existsSync()`) with their asynchronous equivalents. This prevents
   `createStaticHandler` from blocking the Dart isolate event loop, dramatically
diff --git a/pkgs/shelf_static/lib/src/static_handler.dart b/pkgs/shelf_static/lib/src/static_handler.dart
index e81c5fd..e5d5ee3 100644
--- a/pkgs/shelf_static/lib/src/static_handler.dart
+++ b/pkgs/shelf_static/lib/src/static_handler.dart
@@ -324,6 +324,7 @@
       if (start < 0) start = 0;
     }
     end = actualLength - 1;
+    if (start > end) return null;
   } else {
     final parsedStart = int.tryParse(startMatch);
     final parsedEnd = endMatch.isEmpty ? null : int.tryParse(endMatch);
diff --git a/pkgs/shelf_static/test/create_file_handler_test.dart b/pkgs/shelf_static/test/create_file_handler_test.dart
index b4ca917..de33914 100644
--- a/pkgs/shelf_static/test/create_file_handler_test.dart
+++ b/pkgs/shelf_static/test/create_file_handler_test.dart
@@ -225,6 +225,49 @@
       expect(response.statusCode, equals(HttpStatus.partialContent));
       expect(response.contentLength, equals(8));
     });
+
+    test('ignores request with suffix length 0 (bytes=-0)', () async {
+      final handler = createFileHandler(p.join(d.sandbox, 'file.txt'));
+      final response = await makeRequest(
+        handler,
+        '/file.txt',
+        headers: {'range': 'bytes=-0'},
+      );
+      expect(response.statusCode, equals(HttpStatus.ok));
+      expect(response.headers[HttpHeaders.contentRangeHeader], isNull);
+      expect(response.contentLength, equals(8));
+      expect(response.readAsString(), completion(equals('contents')));
+    });
+
+    test('ignores request with suffix range on empty file', () async {
+      await d.file('empty.txt', '').create();
+      final handler = createFileHandler(p.join(d.sandbox, 'empty.txt'));
+      final response = await makeRequest(
+        handler,
+        '/empty.txt',
+        headers: {'range': 'bytes=-5'},
+      );
+      expect(response.statusCode, equals(HttpStatus.ok));
+      expect(response.headers[HttpHeaders.contentRangeHeader], isNull);
+      expect(response.contentLength, equals(0));
+      expect(response.readAsString(), completion(isEmpty));
+    });
+
+    test('serves last byte for suffix range bytes=-1', () async {
+      final handler = createFileHandler(p.join(d.sandbox, 'file.txt'));
+      final response = await makeRequest(
+        handler,
+        '/file.txt',
+        headers: {'range': 'bytes=-1'},
+      );
+      expect(response.statusCode, equals(HttpStatus.partialContent));
+      expect(
+        response.headers[HttpHeaders.contentRangeHeader],
+        equals('bytes 7-7/8'),
+      );
+      expect(response.contentLength, equals(1));
+      expect(response.readAsString(), completion(equals('s')));
+    });
   });
 
   group('throws an ArgumentError for', () {