latest shelf and add new lints (#29)

diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml
index c3808a4..727e59e 100644
--- a/.github/workflows/test-package.yml
+++ b/.github/workflows/test-package.yml
@@ -62,7 +62,7 @@
 
   # Run tests on a matrix consisting of two dimensions:
   # 1. OS: ubuntu-latest, (macos-latest, windows-latest)
-  # 2. release channel: 2.0.0
+  # 2. release channel: 2.1.0
   test-legacy-sdk:
     needs: analyze
     runs-on: ${{ matrix.os }}
@@ -71,7 +71,7 @@
       matrix:
         # Add macos-latest and/or windows-latest if relevant for this package.
         os: [ubuntu-latest]
-        sdk: [2.0.0]
+        sdk: [2.1.0]
     steps:
       - uses: actions/checkout@v2
       - uses: dart-lang/setup-dart@v0.3
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4a33e02..0c7bf85 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,7 @@
-## 0.2.4-dev
+## 0.2.4
 
+* Support the latest shelf release (`1.x.x`).
+* Require at least Dart 2.1
 * Allow omitting `protocols` argument even if the `onConnection` callback takes
   a second argument.
 
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 0711aca..d183bf1 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,43 +1,80 @@
 include: package:pedantic/analysis_options.yaml
+
 analyzer:
   strong-mode:
     implicit-casts: false
+
 linter:
   rules:
-    - avoid_empty_else
-    - avoid_init_to_null
-    - avoid_null_checks_in_equality_operators
+    - avoid_bool_literals_in_conditional_expressions
+    - avoid_catching_errors
+    - avoid_classes_with_only_static_members
+    - avoid_function_literals_in_foreach_calls
+    - avoid_private_typedef_functions
+    - avoid_redundant_argument_values
+    - avoid_renaming_method_parameters
+    - avoid_returning_null
+    - avoid_returning_null_for_future
+    - avoid_returning_null_for_void
+    - avoid_returning_this
+    - avoid_single_cascade_in_expression_statements
     - avoid_unused_constructor_parameters
+    - avoid_void_async
     - await_only_futures
     - camel_case_types
     - cancel_subscriptions
+    - cascade_invocations
+    - comment_references
     - constant_identifier_names
     - control_flow_in_finally
     - directives_ordering
-    - empty_catches
-    - empty_constructor_bodies
     - empty_statements
+    - file_names
     - hash_and_equals
     - implementation_imports
+    - invariant_booleans
     - iterable_contains_unrelated_type
-    - library_names
-    - library_prefixes
+    - join_return_with_assignment
+    - lines_longer_than_80_chars
     - list_remove_unrelated_type
+    - literal_only_boolean_expressions
+    - missing_whitespace_between_adjacent_strings
+    - no_adjacent_strings_in_list
+    - no_runtimeType_toString
     - non_constant_identifier_names
+    - only_throw_errors
     - overridden_fields
     - package_api_docs
     - package_names
     - package_prefixed_library_names
-    - prefer_equal_for_default_values
-    - prefer_final_fields
-    - prefer_generic_function_type_aliases
-    - prefer_is_not_empty
-    - slash_for_doc_comments
+    - prefer_asserts_in_initializer_lists
+    - prefer_const_constructors
+    - prefer_const_declarations
+    - prefer_expression_function_bodies
+    - prefer_final_locals
+    - prefer_function_declarations_over_variables
+    - prefer_initializing_formals
+    - prefer_inlined_adds
+    - prefer_interpolation_to_compose_strings
+    - prefer_is_not_operator
+    - prefer_null_aware_operators
+    - prefer_relative_imports
+    - prefer_typing_uninitialized_variables
+    - prefer_void_to_null
+    - provide_deprecation_message
+    - sort_pub_dependencies
     - test_types_in_equals
     - throw_in_finally
-    - type_init_formals
+    - type_annotate_public_apis
+    - unnecessary_await_in_return
     - unnecessary_brace_in_string_interps
-    - unnecessary_const
-    - unnecessary_new
-    - unrelated_type_equality_checks
-    - valid_regexps
+    - unnecessary_getters_setters
+    - unnecessary_lambdas
+    - unnecessary_null_aware_assignments
+    - unnecessary_overrides
+    - unnecessary_parenthesis
+    - unnecessary_statements
+    - unnecessary_string_interpolations
+    - use_is_even_rather_than_modulo
+    - use_string_buffers
+    - void_checks
diff --git a/lib/shelf_web_socket.dart b/lib/shelf_web_socket.dart
index c9cbc19..061200e 100644
--- a/lib/shelf_web_socket.dart
+++ b/lib/shelf_web_socket.dart
@@ -7,7 +7,6 @@
 
 import 'src/web_socket_handler.dart';
 
-typedef _BinaryFunction = void Function(Null, Null);
 
 /// Creates a Shelf handler that upgrades HTTP requests to WebSocket
 /// connections.
@@ -45,8 +44,8 @@
     {Iterable<String> protocols,
     Iterable<String> allowedOrigins,
     Duration pingInterval}) {
-  if (onConnection is! _BinaryFunction) {
-    var innerOnConnection = onConnection;
+  if (onConnection is! void Function(Null, Null)) {
+    final innerOnConnection = onConnection;
     onConnection = (webSocket, _) => innerOnConnection(webSocket);
   }
 
diff --git a/lib/src/web_socket_handler.dart b/lib/src/web_socket_handler.dart
index 6f3620b..c18c221 100644
--- a/lib/src/web_socket_handler.dart
+++ b/lib/src/web_socket_handler.dart
@@ -28,17 +28,17 @@
   Response handle(Request request) {
     if (request.method != 'GET') return _notFound();
 
-    var connection = request.headers['Connection'];
+    final connection = request.headers['Connection'];
     if (connection == null) return _notFound();
-    var tokens =
+    final tokens =
         connection.toLowerCase().split(',').map((token) => token.trim());
     if (!tokens.contains('upgrade')) return _notFound();
 
-    var upgrade = request.headers['Upgrade'];
+    final upgrade = request.headers['Upgrade'];
     if (upgrade == null) return _notFound();
     if (upgrade.toLowerCase() != 'websocket') return _notFound();
 
-    var version = request.headers['Sec-WebSocket-Version'];
+    final version = request.headers['Sec-WebSocket-Version'];
     if (version == null) {
       return _badRequest('missing Sec-WebSocket-Version header.');
     } else if (version != '13') {
@@ -50,7 +50,7 @@
           '"${request.protocolVersion}".');
     }
 
-    var key = request.headers['Sec-WebSocket-Key'];
+    final key = request.headers['Sec-WebSocket-Key'];
     if (key == null) return _badRequest('missing Sec-WebSocket-Key header.');
 
     if (!request.canHijack) {
@@ -61,20 +61,20 @@
     // The Origin header is always set by browser connections. By filtering out
     // unexpected origins, we ensure that malicious JavaScript is unable to fake
     // a WebSocket handshake.
-    var origin = request.headers['Origin'];
+    final origin = request.headers['Origin'];
     if (origin != null &&
         _allowedOrigins != null &&
         !_allowedOrigins.contains(origin.toLowerCase())) {
       return _forbidden('invalid origin "$origin".');
     }
 
-    var protocol = _chooseProtocol(request);
+    final protocol = _chooseProtocol(request);
     request.hijack((channel) {
-      var sink = utf8.encoder.startChunkedConversion(channel.sink);
-      sink.add('HTTP/1.1 101 Switching Protocols\r\n'
-          'Upgrade: websocket\r\n'
-          'Connection: Upgrade\r\n'
-          'Sec-WebSocket-Accept: ${WebSocketChannel.signKey(key)}\r\n');
+      final sink = utf8.encoder.startChunkedConversion(channel.sink)
+        ..add('HTTP/1.1 101 Switching Protocols\r\n'
+            'Upgrade: websocket\r\n'
+            'Connection: Upgrade\r\n'
+            'Sec-WebSocket-Accept: ${WebSocketChannel.signKey(key)}\r\n');
       if (protocol != null) sink.add('Sec-WebSocket-Protocol: $protocol\r\n');
       sink.add('\r\n');
 
@@ -92,7 +92,7 @@
   ///
   /// If no matching protocol can be found, returns `null`.
   String _chooseProtocol(Request request) {
-    var requestProtocols = request.headers['Sec-WebSocket-Protocol'];
+    final requestProtocols = request.headers['Sec-WebSocket-Protocol'];
     if (requestProtocols == null) return null;
     if (_protocols == null) return null;
     for (var requestProtocol in requestProtocols.split(',')) {
diff --git a/pubspec.yaml b/pubspec.yaml
index e3c285a..e5bf0c4 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,18 +1,19 @@
 name: shelf_web_socket
-version: 0.2.4-dev
+version: 0.2.4
 
 description: >-
   A shelf handler that wires up a listener for every connection.
 homepage: https://github.com/dart-lang/shelf_web_socket
 
 environment:
-  sdk: ">=2.0.0 <3.0.0"
+  sdk: ">=2.1.0 <3.0.0"
 
 dependencies:
-  shelf: ^0.7.0
-  web_socket_channel: ^1.0.0
+  shelf: '>=0.7.0 <2.0.0'
   stream_channel: ">1.4.0 <3.0.0"
+  web_socket_channel: ^1.0.0
 
 dev_dependencies:
   http: ">=0.10.0 <0.13.0"
-  test: ^1.0.0
+  pedantic: ^1.4.0
+  test: ^1.6.0
diff --git a/test/web_socket_test.dart b/test/web_socket_test.dart
index e77cad9..ed62ac4 100644
--- a/test/web_socket_test.dart
+++ b/test/web_socket_test.dart
@@ -18,7 +18,7 @@
 
 void main() {
   test('can communicate with a dart:io WebSocket client', () async {
-    var server = await shelf_io.serve(webSocketHandler((webSocket) {
+    final server = await shelf_io.serve(webSocketHandler((webSocket) {
       webSocket.sink.add('hello!');
       webSocket.stream.first.then((request) {
         expect(request, equals('ping'));
@@ -28,7 +28,8 @@
     }), 'localhost', 0);
 
     try {
-      var webSocket = await WebSocket.connect('ws://localhost:${server.port}');
+      final webSocket =
+          await WebSocket.connect('ws://localhost:${server.port}');
       var n = 0;
       await webSocket.listen((message) {
         if (n == 0) {
@@ -49,7 +50,7 @@
   });
 
   test('negotiates the sub-protocol', () async {
-    var server = await shelf_io.serve(
+    final server = await shelf_io.serve(
         webSocketHandler((webSocket, protocol) {
           expect(protocol, equals('two'));
           webSocket.sink.close();
@@ -58,7 +59,7 @@
         0);
 
     try {
-      var webSocket = await WebSocket.connect('ws://localhost:${server.port}',
+      final webSocket = await WebSocket.connect('ws://localhost:${server.port}',
           protocols: ['one', 'two', 'three']);
       expect(webSocket.protocol, equals('two'));
       return webSocket.close();
@@ -68,12 +69,12 @@
   });
 
   test('handles protocol header without allowed protocols', () async {
-    var server = await shelf_io.serve(webSocketHandler((webSocket) {
+    final server = await shelf_io.serve(webSocketHandler((webSocket) {
       webSocket.sink.close();
     }), 'localhost', 0);
 
     try {
-      var webSocket = await WebSocket.connect('ws://localhost:${server.port}',
+      final webSocket = await WebSocket.connect('ws://localhost:${server.port}',
           protocols: ['one', 'two', 'three']);
       expect(webSocket.protocol, isNull);
       return webSocket.close();
@@ -83,13 +84,13 @@
   });
 
   test('allows two argument callbacks without protocols', () async {
-    var server = await shelf_io.serve(webSocketHandler((webSocket, protocol) {
+    final server = await shelf_io.serve(webSocketHandler((webSocket, protocol) {
       expect(protocol, isNull);
       webSocket.sink.close();
     }), 'localhost', 0);
 
     try {
-      var webSocket = await WebSocket.connect('ws://localhost:${server.port}',
+      final webSocket = await WebSocket.connect('ws://localhost:${server.port}',
           protocols: ['one', 'two', 'three']);
       expect(webSocket.protocol, isNull);
       return webSocket.close();
@@ -114,13 +115,13 @@
     tearDown(() => server.close());
 
     test('allows access with an allowed origin', () {
-      var headers = _handshakeHeaders;
+      final headers = _handshakeHeaders;
       headers['Origin'] = 'pub.dartlang.org';
       expect(http.get(url, headers: headers), hasStatus(101));
     });
 
     test('forbids access with a non-allowed origin', () {
-      var headers = _handshakeHeaders;
+      final headers = _handshakeHeaders;
       headers['Origin'] = 'dartlang.org';
       expect(http.get(url, headers: headers), hasStatus(403));
     });
@@ -130,13 +131,13 @@
     });
 
     test('ignores the case of the client origin', () {
-      var headers = _handshakeHeaders;
+      final headers = _handshakeHeaders;
       headers['Origin'] = 'PuB.DaRtLaNg.OrG';
       expect(http.get(url, headers: headers), hasStatus(101));
     });
 
     test('ignores the case of the server origin', () {
-      var headers = _handshakeHeaders;
+      final headers = _handshakeHeaders;
       headers['Origin'] = 'google.com';
       expect(http.get(url, headers: headers), hasStatus(101));
     });
@@ -144,12 +145,12 @@
 
   // Regression test for issue 21894.
   test('allows a Connection header with multiple values', () async {
-    var server = await shelf_io.serve(webSocketHandler((webSocket) {
+    final server = await shelf_io.serve(webSocketHandler((webSocket) {
       webSocket.sink.close();
     }), 'localhost', 0);
 
-    var url = Uri.http('localhost:${server.port}', '');
-    var headers = _handshakeHeaders;
+    final url = Uri.http('localhost:${server.port}', '');
+    final headers = _handshakeHeaders;
     headers['Connection'] = 'Other-Token, Upgrade';
     expect(http.get(url, headers: headers).whenComplete(server.close),
         hasStatus(101));
@@ -172,39 +173,36 @@
     });
 
     test('404s for non-Upgrade requests', () {
-      var headers = _handshakeHeaders;
-      headers.remove('Connection');
+      final headers = _handshakeHeaders..remove('Connection');
       expect(http.get(url, headers: headers), hasStatus(404));
     });
 
     test('404s for non-websocket upgrade requests', () {
-      var headers = _handshakeHeaders;
+      final headers = _handshakeHeaders;
       headers['Upgrade'] = 'fblthp';
       expect(http.get(url, headers: headers), hasStatus(404));
     });
 
     test('400s for a missing Sec-WebSocket-Version', () {
-      var headers = _handshakeHeaders;
-      headers.remove('Sec-WebSocket-Version');
+      final headers = _handshakeHeaders..remove('Sec-WebSocket-Version');
       expect(http.get(url, headers: headers), hasStatus(400));
     });
 
     test('404s for an unknown Sec-WebSocket-Version', () {
-      var headers = _handshakeHeaders;
+      final headers = _handshakeHeaders;
       headers['Sec-WebSocket-Version'] = '15';
       expect(http.get(url, headers: headers), hasStatus(404));
     });
 
     test('400s for a missing Sec-WebSocket-Key', () {
-      var headers = _handshakeHeaders;
-      headers.remove('Sec-WebSocket-Key');
+      final headers = _handshakeHeaders..remove('Sec-WebSocket-Key');
       expect(http.get(url, headers: headers), hasStatus(400));
     });
   });
 }
 
 Matcher hasStatus(int status) => completion(predicate((response) {
-      expect(response, TypeMatcher<http.Response>());
+      expect(response, isA<http.Response>());
       expect(response.statusCode, equals(status));
       return true;
     }));