Enable and fix a number of lints
diff --git a/analysis_options.yaml b/analysis_options.yaml
index a10d4c5..d9715a7 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,2 +1,51 @@
 analyzer:
   strong-mode: true
+  errors:
+    unused_import: error
+    unused_local_variable: error
+    dead_code: error
+    override_on_non_overriding_method: error
+linter:
+  rules:
+    # Errors
+    - avoid_empty_else
+    - await_only_futures
+    - comment_references
+    - control_flow_in_finally
+    - empty_statements
+    - hash_and_equals
+    - iterable_contains_unrelated_type
+    - no_duplicate_case_values
+    - test_types_in_equals
+    - throw_in_finally
+    - unawaited_futures
+    - unnecessary_statements
+    - unrelated_type_equality_checks
+    - valid_regexps
+
+    # Style
+    #- annotate_overrides
+    #- avoid_function_literals_in_foreach_calls
+    - avoid_init_to_null
+    #- avoid_return_types_on_setters
+    - avoid_returning_null
+    - avoid_unused_constructor_parameters
+    - camel_case_types
+    - directives_ordering
+    #- empty_catches
+    - empty_constructor_bodies
+    - library_names
+    - library_prefixes
+    #- non_constant_identifier_names
+    - prefer_conditional_assignment
+    - prefer_final_fields
+    - prefer_is_empty
+    - prefer_is_not_empty
+    - prefer_typing_uninitialized_variables
+    - recursive_getters
+    #- slash_for_doc_comments
+    - type_init_formals
+    - unnecessary_brace_in_string_interps
+    - unnecessary_getters_setters
+    #- unnecessary_lambdas
+    - unnecessary_null_aware_assignments
diff --git a/lib/http_io.dart b/lib/http_io.dart
index 414ea82..8fe4ba0 100644
--- a/lib/http_io.dart
+++ b/lib/http_io.dart
@@ -15,8 +15,8 @@
         LinkedListEntry,
         UnmodifiableMapView;
 import 'dart:convert';
-import 'dart:math';
 import 'dart:io';
+import 'dart:math';
 import 'dart:typed_data';
 
 part 'src/crypto.dart';
@@ -151,7 +151,7 @@
  *
  * ## Connect to a server socket
  *
- * You can use the [listenOn] constructor to attach an HTTP server to
+ * You can use the [HttpServer.listenOn] constructor to attach an HTTP server to
  * a [ServerSocket].
  *
  *     import 'dart:io';
@@ -702,7 +702,7 @@
    */
   static HeaderValue parse(String value,
       {String parameterSeparator: ";",
-      String valueSeparator: null,
+      String valueSeparator,
       bool preserveBackslash: false}) {
     return _HeaderValue.parse(value,
         parameterSeparator: parameterSeparator,
@@ -1526,7 +1526,7 @@
   /**
    * Sets the function to be called when a site is requesting
    * authentication. The URL requested and the security realm from the
-   * server are passed in the arguments [url] and [realm].
+   * server are passed in the arguments `url` and `realm`.
    *
    * The function returns a [Future] which should complete when the
    * authentication has been resolved. If credentials cannot be
@@ -1548,7 +1548,7 @@
 
   /**
    * Sets the function used to resolve the proxy server to be used for
-   * opening a HTTP connection to the specified [url]. If this
+   * opening a HTTP connection to the specified `url`. If this
    * function is not set, direct connections will always be used.
    *
    * The string returned by [f] must be in the format used by browser
@@ -1636,8 +1636,8 @@
   /**
    * Sets the function to be called when a proxy is requesting
    * authentication. Information on the proxy in use and the security
-   * realm for the authentication are passed in the arguments [host],
-   * [port] and [realm].
+   * realm for the authentication are passed in the arguments `host`,
+   * `port` and `realm`.
    *
    * The function returns a [Future] which should complete when the
    * authentication has been resolved. If credentials cannot be
diff --git a/lib/src/crypto.dart b/lib/src/crypto.dart
index 3bbba75..cacdc1e 100644
--- a/lib/src/crypto.dart
+++ b/lib/src/crypto.dart
@@ -40,7 +40,7 @@
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
   ];
 
-  static Random _rng = new Random.secure();
+  static final Random _rng = new Random.secure();
 
   static Uint8List getRandomBytes(int count) {
     final Uint8List result = new Uint8List(count);
@@ -214,7 +214,7 @@
     _digestCalled = true;
     _finalizeData();
     _iterate();
-    assert(_pendingData.length == 0);
+    assert(_pendingData.isEmpty);
     return _resultAsBytes();
   }
 
@@ -360,8 +360,8 @@
     var c = _h[2];
     var d = _h[3];
 
-    var t0;
-    var t1;
+    int t0;
+    int t1;
 
     for (var i = 0; i < 64; i++) {
       if (i < 16) {
diff --git a/lib/src/http_date.dart b/lib/src/http_date.dart
index 1ea8bee..66dde0b 100644
--- a/lib/src/http_date.dart
+++ b/lib/src/http_date.dart
@@ -177,7 +177,7 @@
 
     int expectNum(String separator) {
       int pos;
-      if (separator.length > 0) {
+      if (separator.isNotEmpty) {
         pos = date.indexOf(separator, index);
       } else {
         pos = date.length;
@@ -287,6 +287,7 @@
 
     int toInt(String s) {
       int index = 0;
+      // ignore: empty_statements
       for (; index < s.length && isDigit(s[index]); index++);
       return int.parse(s.substring(0, index));
     }
diff --git a/lib/src/http_headers.dart b/lib/src/http_headers.dart
index 530775d..f899101 100644
--- a/lib/src/http_headers.dart
+++ b/lib/src/http_headers.dart
@@ -86,7 +86,7 @@
       if (index != -1) {
         values.removeRange(index, index + 1);
       }
-      if (values.length == 0) _headers.remove(name);
+      if (values.isEmpty) _headers.remove(name);
     }
     if (name == HttpHeaders.TRANSFER_ENCODING && value == "chunked") {
       _chunkedTransferEncoding = false;
@@ -104,7 +104,7 @@
   }
 
   void noFolding(String name) {
-    if (_noFoldingHeaders == null) _noFoldingHeaders = new List<String>();
+    _noFoldingHeaders ??= new List<String>();
     _noFoldingHeaders.add(name);
   }
 
@@ -618,9 +618,7 @@
   }
 
   static _HeaderValue parse(String value,
-      {parameterSeparator: ";",
-      valueSeparator: null,
-      preserveBackslash: false}) {
+      {parameterSeparator: ";", valueSeparator, preserveBackslash: false}) {
     // Parse the string.
     var result = new _HeaderValue();
     result._parse(value, parameterSeparator, valueSeparator, preserveBackslash);
@@ -630,23 +628,20 @@
   String get value => _value;
 
   void _ensureParameters() {
-    if (_parameters == null) {
-      _parameters = new HashMap<String, String>();
-    }
+    _parameters ??= new HashMap<String, String>();
   }
 
   Map<String, String> get parameters {
     _ensureParameters();
-    if (_unmodifiableParameters == null) {
-      _unmodifiableParameters = new UnmodifiableMapView(_parameters);
-    }
+    _unmodifiableParameters ??= new UnmodifiableMapView(_parameters);
+
     return _unmodifiableParameters;
   }
 
   String toString() {
     StringBuffer sb = new StringBuffer();
     sb.write(_value);
-    if (parameters != null && parameters.length > 0) {
+    if (parameters != null && parameters.isNotEmpty) {
       _parameters.forEach((String name, String value) {
         sb..write("; ")..write(name)..write("=")..write(value);
       });
@@ -783,8 +778,8 @@
       : _primaryType = primaryType,
         _subType = subType,
         super("") {
-    if (_primaryType == null) _primaryType = "";
-    if (_subType == null) _subType = "";
+    _primaryType ??= _primaryType = "";
+    _subType ??= "";
     _value = "$_primaryType/$_subType";
     if (parameters != null) {
       _ensureParameters();
@@ -917,7 +912,7 @@
     }
 
     name = parseName();
-    if (done() || name.length == 0) {
+    if (done() || name.isEmpty) {
       throw new HttpException("Failed to parse header value [$s]");
     }
     index++; // Skip the = character.
diff --git a/lib/src/http_impl.dart b/lib/src/http_impl.dart
index 93d813b..9371ef3 100644
--- a/lib/src/http_impl.dart
+++ b/lib/src/http_impl.dart
@@ -264,6 +264,7 @@
         // It's destroyed, clear it.
         _session = null;
         // Create new session object by calling recursive.
+        // ignore: recursive_getters
         return session;
       }
       // It's already mapped, use it.
@@ -751,7 +752,7 @@
     if (_encodingSet && _outgoing.headersWritten) {
       return _encoding;
     }
-    var charset;
+    String charset;
     if (headers.contentType != null && headers.contentType.charset != null) {
       charset = headers.contentType.charset;
     } else {
@@ -761,7 +762,7 @@
   }
 
   void add(List<int> data) {
-    if (data.length == 0) return;
+    if (data.isEmpty) return;
     super.add(data);
   }
 
@@ -796,7 +797,7 @@
   bool get _isConnectionClosed => _httpRequest._httpConnection._isClosing;
 
   List<Cookie> get cookies {
-    if (_cookies == null) _cookies = new List<Cookie>();
+    _cookies ??= new List<Cookie>();
     return _cookies;
   }
 
@@ -1023,7 +1024,7 @@
 
   int _maxRedirects = 5;
 
-  List<RedirectInfo> _responseRedirects = [];
+  final List<RedirectInfo> _responseRedirects = [];
 
   _HttpClientRequest(_HttpOutgoing outgoing, Uri uri, this.method, this._proxy,
       this._httpClient, this._httpClientConnection)
@@ -1038,10 +1039,8 @@
   }
 
   Future<HttpClientResponse> get done {
-    if (_response == null) {
-      _response = Future.wait([_responseCompleter.future, super.done],
-          eagerError: true).then((list) => list[0]);
-    }
+    _response ??= Future.wait([_responseCompleter.future, super.done],
+        eagerError: true).then((list) => list[0]);
     return _response;
   }
 
@@ -1102,7 +1101,7 @@
       String result = uri.path;
       if (result.isEmpty) result = "/";
       if (uri.hasQuery) {
-        result = "${result}?${uri.query}";
+        result = "$result?${uri.query}";
       }
       return result;
     }
@@ -1140,7 +1139,7 @@
     buffer.addByte(_CharCode.LF);
 
     // Add the cookies to the headers.
-    if (!cookies.isEmpty) {
+    if (cookies.isNotEmpty) {
       StringBuffer sb = new StringBuffer();
       for (int i = 0; i < cookies.length; i++) {
         if (i > 0) sb.write("; ");
@@ -1310,7 +1309,7 @@
 
     void onData(List<int> data) {
       if (_socketError) return;
-      if (data.length == 0) return;
+      if (data.isEmpty) return;
       if (chunked) {
         if (_gzip) {
           _gzipAdd = controller.add;
@@ -1660,13 +1659,13 @@
       String auth = _CryptoUtils
           .bytesToBase64(utf8.encode("${proxy.username}:${proxy.password}"));
       request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, "Basic $auth");
-    } else if (!proxy.isDirect && _httpClient._proxyCredentials.length > 0) {
+    } else if (!proxy.isDirect && _httpClient._proxyCredentials.isNotEmpty) {
       proxyCreds = _httpClient._findProxyCredentials(proxy);
       if (proxyCreds != null) {
         proxyCreds.authorize(request);
       }
     }
-    if (uri.userInfo != null && !uri.userInfo.isEmpty) {
+    if (uri.userInfo != null && uri.userInfo.isNotEmpty) {
       // If the URL contains user information use that for basic
       // authorization.
       String auth = _CryptoUtils.bytesToBase64(utf8.encode(uri.userInfo));
@@ -2006,7 +2005,7 @@
         queryStart = i;
       }
     }
-    String query = null;
+    String query;
     if (queryStart < fragmentStart) {
       query = path.substring(queryStart + 1, fragmentStart);
       path = path.substring(0, queryStart);
@@ -2296,25 +2295,25 @@
     }
 
     // Default to using the process current environment.
-    if (environment == null) environment = _platformEnvironmentCache;
+    environment ??= _platformEnvironmentCache;
 
     String proxyCfg;
 
     String noProxy = environment["no_proxy"];
-    if (noProxy == null) noProxy = environment["NO_PROXY"];
+    noProxy ??= environment["NO_PROXY"];
     if ((proxyCfg = checkNoProxy(noProxy)) != null) {
       return proxyCfg;
     }
 
     if (url.scheme == "http") {
       String proxy = environment["http_proxy"];
-      if (proxy == null) proxy = environment["HTTP_PROXY"];
+      proxy ??= environment["HTTP_PROXY"];
       if ((proxyCfg = checkProxy(proxy)) != null) {
         return proxyCfg;
       }
     } else if (url.scheme == "https") {
       String proxy = environment["https_proxy"];
-      if (proxy == null) proxy = environment["HTTPS_PROXY"];
+      proxy ??= environment["HTTPS_PROXY"];
       if ((proxyCfg = checkProxy(proxy)) != null) {
         return proxyCfg;
       }
@@ -2322,7 +2321,8 @@
     return "DIRECT";
   }
 
-  static Map<String, String> _platformEnvironmentCache = Platform.environment;
+  static final Map<String, String> _platformEnvironmentCache =
+      Platform.environment;
 }
 
 class _HttpConnection extends LinkedListEntry<_HttpConnection>
@@ -2333,10 +2333,10 @@
   static const _DETACHED = 3;
 
   // Use HashMap, as we don't need to keep order.
-  static Map<int, _HttpConnection> _connections =
+  static final Map<int, _HttpConnection> _connections =
       new HashMap<int, _HttpConnection>();
 
-  final /*_ServerSocket*/ _socket;
+  final Socket _socket;
   final _HttpServer _httpServer;
   final _HttpParser _httpParser;
   int _state = _IDLE;
@@ -2449,7 +2449,8 @@
     }
     r['server'] = _httpServer._toJSON(true);
     try {
-      r['socket'] = _socket._toJSON(true);
+      // TODO(kevmoo): don't rely on SDK implementation!
+      r['socket'] = (_socket as dynamic)._toJSON(true);
     } catch (_) {
       r['socket'] = {
         'id': _servicePath,
@@ -2484,7 +2485,7 @@
     with _ServiceObject
     implements HttpServer {
   // Use default Map so we keep order.
-  static Map<int, _HttpServer> _servers = new Map<int, _HttpServer>();
+  static final Map<int, _HttpServer> _servers = new Map<int, _HttpServer>();
 
   String serverHeader;
   final HttpHeaders defaultResponseHeaders = _initDefaultResponseHeaders();
@@ -2656,9 +2657,7 @@
 
   _HttpSessionManager get _sessionManager {
     // Lazy init.
-    if (_sessionManagerInstance == null) {
-      _sessionManagerInstance = new _HttpSessionManager();
-    }
+    _sessionManagerInstance ??= new _HttpSessionManager();
     return _sessionManagerInstance;
   }
 
@@ -2740,7 +2739,7 @@
     List<String> list = configuration.split(";");
     list.forEach((String proxy) {
       proxy = proxy.trim();
-      if (!proxy.isEmpty) {
+      if (proxy.isNotEmpty) {
         if (proxy.startsWith(PROXY_PREFIX)) {
           String username;
           String password;
diff --git a/lib/src/http_parser.dart b/lib/src/http_parser.dart
index 7972c4b..f6e2b8d 100644
--- a/lib/src/http_parser.dart
+++ b/lib/src/http_parser.dart
@@ -210,7 +210,7 @@
 }
 
 /**
- * HTTP parser which parses the data stream given to [consume].
+ * HTTP parser which parses the data stream given to consume.
  *
  * If an HTTP parser error occurs, the parser will signal an error to either
  * the current _HttpIncoming or the _parser itself.
@@ -508,7 +508,7 @@
 
         case _State.REQUEST_LINE_URI:
           if (byte == _CharCode.SP) {
-            if (_uri_or_reason_phrase.length == 0) {
+            if (_uri_or_reason_phrase.isEmpty) {
               throw new HttpException("Invalid request URI");
             }
             _state = _State.REQUEST_LINE_HTTP_VERSION;
@@ -586,6 +586,8 @@
 
         case _State.RESPONSE_LINE_ENDING:
           _expect(byte, _CharCode.LF);
+          // TODO(kevmoo): file a linter bug on this!
+          // ignore: unnecessary_statements
           _messageType == _MessageType.RESPONSE;
           if (_statusCode < 100 || _statusCode > 599) {
             throw new HttpException("Invalid response status code");
@@ -992,7 +994,7 @@
     assert(_incoming == null);
     assert(_bodyController == null);
     assert(!_bodyPaused);
-    var incoming;
+    _HttpIncoming incoming;
     _bodyController = new StreamController<List<int>>(
         sync: true,
         onListen: () {
diff --git a/lib/src/http_session.dart b/lib/src/http_session.dart
index 9ef6395..1d0bd86 100644
--- a/lib/src/http_session.dart
+++ b/lib/src/http_session.dart
@@ -11,6 +11,9 @@
 class _HttpSession implements HttpSession {
   // Destroyed marked. Used by the http connection to see if a session is valid.
   bool _destroyed = false;
+
+  // TODO(kevmoo): file a linter bug on this!
+  // ignore: prefer_final_fields
   bool _isNew = true;
   DateTime _lastSeen;
   Function _timeoutCallback;