Migrate to Null Safety (#14)

diff --git a/.travis.yml b/.travis.yml
index 66c5017..7f58d77 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,35 +1,32 @@
 language: dart
 
-os:
-  - linux
-  - osx
-
 dart:
-  - 2.0.0
   - dev
 
-dart_task:
-  - test
-
-matrix:
+jobs:
   include:
-  # Only validate formatting using the dev release
-  - dart: dev
-    os: linux
-    dart_task: dartfmt
-  - dart: dev
-    os: linux
-    dart_task:
-      dartanalyzer: --fatal-infos --fatal-warnings .
-  - dart: 2.0.0
-    os: linux
-    dart_task:
-      dartanalyzer: --fatal-warnings .
+    - stage: analyze_and_format
+      name: "Analyze"
+      os: linux
+      script: dartanalyzer --enable-experiment=non-nullable --fatal-warnings --fatal-infos .
+    - stage: analyze_and_format
+      name: "Format"
+      os: linux
+      script: dartfmt -n --set-exit-if-changed .
+    - stage: test
+      name: "Tests"
+      os: linux
+      script: pub run --enable-experiment=non-nullable test
+
+stages:
+  - analyze_and_format
+  - test
 
 # Only building master means that we don't run two builds for each pull request.
 branches:
   only: [master]
 
+# Incremental pub cache and builds.
 cache:
- directories:
-   - $HOME/.pub-cache
+  directories:
+  - $HOME/.pub-cache
diff --git a/lib/src/sync_http.dart b/lib/src/sync_http.dart
index 7e5cc81..168625b 100644
--- a/lib/src/sync_http.dart
+++ b/lib/src/sync_http.dart
@@ -33,9 +33,9 @@
   static const String _protocolVersion = '1.1';
 
   /// The length of the request body. Is set to `-1` when no body exists.
-  int get contentLength => hasBody ? _body.length : -1;
+  int get contentLength => hasBody ? _body!.length : -1;
 
-  HttpHeaders _headers;
+  HttpHeaders? _headers;
 
   /// The headers associated with the HTTP request.
   HttpHeaders get headers =>
@@ -52,7 +52,7 @@
 
   /// The body of the HTTP request. This can be empty if there is no body
   /// associated with the request.
-  final BytesBuilder _body;
+  final BytesBuilder? _body;
 
   /// The synchronous socket used to initiate the HTTP request.
   final RawSynchronousSocket _socket;
@@ -65,7 +65,7 @@
   /// Write content into the body of the HTTP request.
   void write(Object obj) {
     if (hasBody) {
-      _body.add(encoding.encoder.convert(obj.toString()));
+      _body!.add(encoding.encoder.convert(obj.toString()));
     } else {
       throw new StateError('write not allowed for method $method');
     }
@@ -98,7 +98,7 @@
     });
     buffer.write('\r\n');
     if (hasBody) {
-      buffer.write(new String.fromCharCodes(_body.takeBytes()));
+      buffer.write(new String.fromCharCodes(_body!.takeBytes()));
     }
     _socket.writeFromSync(buffer.toString().codeUnits);
     return new SyncHttpClientResponse(_socket);
@@ -109,12 +109,12 @@
   final Map<String, List<String>> _headers = <String, List<String>>{};
 
   final SyncHttpClientRequest _request;
-  ContentType contentType;
+  ContentType? contentType;
 
   _SyncHttpClientRequestHeaders(this._request);
 
   @override
-  List<String> operator [](String name) {
+  List<String>? operator [](String name) {
     switch (name) {
       case HttpHeaders.acceptCharsetHeader:
         return ['utf-8'];
@@ -157,13 +157,13 @@
       case HttpHeaders.hostHeader:
         throw new UnsupportedError('Unsupported or immutable property: $name');
       case HttpHeaders.contentTypeHeader:
-        contentType = value;
+        contentType = value as ContentType?;
         break;
       default:
         if (_headers[name] == null) {
           _headers[name] = <String>[];
         }
-        _headers[name].add(value);
+        _headers[name]!.add(value as String);
     }
   }
 
@@ -187,8 +187,8 @@
         break;
       default:
         if (_headers[name] != null) {
-          _headers[name].remove(value);
-          if (_headers[name].isEmpty) {
+          _headers[name]!.remove(value);
+          if (_headers[name]!.isEmpty) {
             _headers.remove(name);
           }
         }
@@ -226,7 +226,7 @@
   /// Returns the values associated with key [name], if it exists, otherwise
   /// returns null.
   @override
-  String value(String name) {
+  String? value(String name) {
     var val = this[name];
     if (val == null || val.isEmpty) {
       return null;
@@ -278,23 +278,23 @@
   }
 
   @override
-  void set date(DateTime _date) {
+  void set date(DateTime? _date) {
     throw new UnsupportedError('date is unsupported');
   }
 
   @override
-  DateTime get date => null;
+  DateTime? get date => null;
 
   @override
-  void set expires(DateTime _expires) {
+  void set expires(DateTime? _expires) {
     throw new UnsupportedError('expires is unsupported');
   }
 
   @override
-  DateTime get expires => null;
+  DateTime? get expires => null;
 
   @override
-  void set host(String _host) {
+  void set host(String? _host) {
     throw new UnsupportedError('host is automatically set');
   }
 
@@ -302,10 +302,10 @@
   String get host => _request.uri.host;
 
   @override
-  DateTime get ifModifiedSince => null;
+  DateTime? get ifModifiedSince => null;
 
   @override
-  void set ifModifiedSince(DateTime _ifModifiedSince) {
+  void set ifModifiedSince(DateTime? _ifModifiedSince) {
     throw new UnsupportedError('if modified since is unsupported');
   }
 
@@ -323,7 +323,7 @@
   }
 
   @override
-  void set port(int _port) {
+  void set port(int? _port) {
     throw new UnsupportedError('port is automatically set');
   }
 
@@ -348,19 +348,19 @@
 
   /// A short textual description of the status code associated with the HTTP
   /// response.
-  final String reasonPhrase;
+  final String? reasonPhrase;
 
   /// The resulting HTTP status code associated with the HTTP response.
-  final int statusCode;
+  final int? statusCode;
 
   /// The body of the HTTP response.
-  final String body;
+  final String? body;
 
   /// Creates an instance of [SyncHttpClientResponse] that contains the response
   /// sent by the HTTP server over [socket].
   factory SyncHttpClientResponse(RawSynchronousSocket socket) {
-    int statusCode;
-    String reasonPhrase;
+    int? statusCode;
+    String? reasonPhrase;
     StringBuffer body = new StringBuffer();
     Map<String, List<String>> headers = {};
 
@@ -395,7 +395,7 @@
         if (!headers.containsKey(name)) {
           headers[name] = [];
         }
-        headers[name].add(value);
+        headers[name]!.add(value);
       } else if (line.startsWith('HTTP/1.1') || line.startsWith('HTTP/1.0')) {
         statusCode = int.parse(
             line.substring('HTTP/1.x '.length, 'HTTP/1.x xxx'.length));
@@ -444,7 +444,7 @@
   _SyncHttpClientResponseHeaders(this._headers);
 
   @override
-  List<String> operator [](String name) => _headers[name];
+  List<String>? operator [](String name) => _headers[name];
 
   @override
   void add(String name, Object value, {bool preserveHeaderCase = false}) {
@@ -464,7 +464,7 @@
 
   @override
   int get contentLength {
-    String val = value(HttpHeaders.contentLengthHeader);
+    String? val = value(HttpHeaders.contentLengthHeader);
     if (val != null) {
       var parsed = int.tryParse(val);
       if (parsed != null) {
@@ -480,7 +480,7 @@
   }
 
   @override
-  ContentType get contentType {
+  ContentType? get contentType {
     var val = value(HttpHeaders.contentTypeHeader);
     if (val != null) {
       return ContentType.parse(val);
@@ -489,17 +489,17 @@
   }
 
   @override
-  void set contentType(ContentType _contentType) {
+  void set contentType(ContentType? _contentType) {
     throw new UnsupportedError('Response headers are immutable');
   }
 
   @override
-  void set date(DateTime _date) {
+  void set date(DateTime? _date) {
     throw new UnsupportedError('Response headers are immutable');
   }
 
   @override
-  DateTime get date {
+  DateTime? get date {
     var val = value(HttpHeaders.dateHeader);
     if (val != null) {
       return DateTime.parse(val);
@@ -508,12 +508,12 @@
   }
 
   @override
-  void set expires(DateTime _expires) {
+  void set expires(DateTime? _expires) {
     throw new UnsupportedError('Response headers are immutable');
   }
 
   @override
-  DateTime get expires {
+  DateTime? get expires {
     var val = value(HttpHeaders.expiresHeader);
     if (val != null) {
       return DateTime.parse(val);
@@ -525,12 +525,12 @@
   void forEach(void f(String name, List<String> values)) => _headers.forEach(f);
 
   @override
-  void set host(String _host) {
+  void set host(String? _host) {
     throw new UnsupportedError('Response headers are immutable');
   }
 
   @override
-  String get host {
+  String? get host {
     var val = value(HttpHeaders.hostHeader);
     if (val != null) {
       return Uri.parse(val).host;
@@ -539,7 +539,7 @@
   }
 
   @override
-  DateTime get ifModifiedSince {
+  DateTime? get ifModifiedSince {
     var val = value(HttpHeaders.ifModifiedSinceHeader);
     if (val != null) {
       return DateTime.parse(val);
@@ -548,7 +548,7 @@
   }
 
   @override
-  void set ifModifiedSince(DateTime _ifModifiedSince) {
+  void set ifModifiedSince(DateTime? _ifModifiedSince) {
     throw new UnsupportedError('Response headers are immutable');
   }
 
@@ -566,12 +566,12 @@
   }
 
   @override
-  void set port(int _port) {
+  void set port(int? _port) {
     throw new UnsupportedError('Response headers are immutable');
   }
 
   @override
-  int get port {
+  int? get port {
     var val = value(HttpHeaders.hostHeader);
     if (val != null) {
       return Uri.parse(val).port;
@@ -595,7 +595,7 @@
   }
 
   @override
-  String value(String name) {
+  String? value(String name) {
     var val = this[name];
     if (val == null || val.isEmpty) {
       return null;
diff --git a/pubspec.yaml b/pubspec.yaml
index a749a63..ad78f6e 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,8 +3,82 @@
 description: Synchronous HTTP client for Dart.
 homepage: https://github.com/dart-lang/sync_http
 
+# We are not ready to publish NNBD packages yet.
+publish_to: none
+
 environment:
-  sdk: '>=2.0.0 <3.0.0'
+  # This must remain a tight constraint until nnbd is stable.
+  sdk: '>=2.10.0-0 <2.10.0'
 
 dev_dependencies:
   test: ^1.2.0
+
+dependency_overrides:
+  test_api:
+    git:
+      url: git://github.com/dart-lang/test.git
+      path: pkgs/test_api
+  test_core:
+    git:
+      url: git://github.com/dart-lang/test.git
+      path: pkgs/test_core
+  test:
+    git:
+      url: git://github.com/dart-lang/test.git
+      path: pkgs/test
+  source_maps:
+    git:
+      url: git://github.com/dart-lang/source_maps.git
+  source_span:
+    git:
+      url: git://github.com/dart-lang/source_span.git
+  term_glyph:
+    git:
+      url: git://github.com/dart-lang/term_glyph.git
+  path:
+    git:
+      url: git://github.com/dart-lang/path.git
+  charcode:
+    git:
+      url: git://github.com/dart-lang/charcode.git
+  stream_channel:
+    git:
+      url: git://github.com/dart-lang/stream_channel.git
+  async:
+    git:
+      url: git://github.com/dart-lang/async.git
+  stack_trace:
+    git:
+      url: git://github.com/dart-lang/stack_trace.git
+  pool:
+    git:
+      url: git://github.com/dart-lang/pool.git
+  pedantic:
+    git:
+      url: git://github.com/dart-lang/pedantic.git
+  js:
+    git:
+      url: git://github.com/dart-lang/sdk.git
+      path: pkg/js
+  boolean_selector:
+    git:
+      url: git://github.com/dart-lang/boolean_selector.git
+  string_scanner:
+    git:
+      url: git://github.com/dart-lang/string_scanner.git
+  matcher:
+    git:
+      url: git://github.com/dart-lang/matcher.git
+  source_map_stack_trace:
+    git:
+      url: git://github.com/dart-lang/source_map_stack_trace.git
+  collection:
+    git:
+      url: git://github.com/dart-lang/collection.git
+  typed_data:
+    git:
+      url: git://github.com/dart-lang/typed_data.git
+  meta:
+    git:
+      url: git://github.com/dart-lang/sdk.git
+      path: pkg/meta
diff --git a/test/http_basic_test.dart b/test/http_basic_test.dart
index c7ff111..36a8cb9 100644
--- a/test/http_basic_test.dart
+++ b/test/http_basic_test.dart
@@ -9,14 +9,14 @@
 import "package:sync_http/sync_http.dart";
 import "package:test/test.dart";
 
-typedef void ServerCallback(int port);
+typedef void ServerCallback(int? port);
 
 class TestServerMain {
   TestServerMain() : _statusPort = new ReceivePort();
 
   ReceivePort _statusPort; // Port for receiving messages from the server.
-  SendPort _serverPort; // Port for sending messages to the server.
-  ServerCallback _startedCallback;
+  late SendPort _serverPort; // Port for sending messages to the server.
+  late ServerCallback _startedCallback;
 
   void setServerStartedHandler(ServerCallback startedCallback) {
     _startedCallback = startedCallback;
@@ -26,7 +26,7 @@
     ReceivePort receivePort = new ReceivePort();
     Isolate.spawn(startTestServer, receivePort.sendPort);
     receivePort.first.then((port) {
-      _serverPort = port;
+      _serverPort = port!;
 
       // Send server start message to the server.
       var command = new TestServerCommand.start();
@@ -78,10 +78,10 @@
   bool get isStopped => (_state == TestServerStatusState.stopped);
   bool get isError => (_state == TestServerStatusState.error);
 
-  int get port => _port;
+  int? get port => _port;
 
   TestServerStatusState _state;
-  int _port;
+  int? _port;
 }
 
 void startTestServer(SendPort replyTo) {
@@ -141,8 +141,8 @@
   // Check the "Host" header.
   void _hostHandler(HttpRequest request) {
     var response = request.response;
-    expect(1, equals(request.headers["Host"].length));
-    expect("dart.dev:1234", equals(request.headers["Host"][0]));
+    expect(1, equals(request.headers["Host"]!.length));
+    expect("dart.dev:1234", equals(request.headers["Host"]![0]));
     expect("dart.dev", equals(request.headers.host));
     expect(1234, equals(request.headers.port));
     response.statusCode = HttpStatus.ok;
@@ -176,7 +176,7 @@
 
   dispatch(var message) async {
     TestServerCommand command = message[0];
-    SendPort replyTo = message[1];
+    SendPort replyTo = message[1]!;
     if (command.isStart) {
       try {
         var addr = (await InternetAddress.lookup("localhost"))[0];
@@ -204,15 +204,15 @@
     }
   }
 
-  HttpServer _server; // HTTP server instance.
-  ReceivePort _dispatchPort;
-  Map _requestHandlers;
+  late HttpServer _server; // HTTP server instance.
+  late ReceivePort _dispatchPort;
+  late Map _requestHandlers;
 }
 
 Future testStartStop() async {
   Completer completer = new Completer();
   TestServerMain testServerMain = new TestServerMain();
-  testServerMain.setServerStartedHandler((int port) {
+  testServerMain.setServerStartedHandler((int? port) {
     testServerMain.close();
     completer.complete();
   });
@@ -223,7 +223,7 @@
 Future testGET() async {
   Completer completer = new Completer();
   TestServerMain testServerMain = new TestServerMain();
-  testServerMain.setServerStartedHandler((int port) {
+  testServerMain.setServerStartedHandler((int? port) {
     var request =
         SyncHttpClient.getUrl(new Uri.http("localhost:$port", "/0123456789"));
     var response = request.close();
@@ -244,7 +244,7 @@
 
   TestServerMain testServerMain = new TestServerMain();
 
-  void runTest(int port) {
+  void runTest(int? port) {
     int count = 0;
     void sendRequest() {
       var request =
@@ -273,7 +273,7 @@
 Future test404() async {
   Completer completer = new Completer();
   TestServerMain testServerMain = new TestServerMain();
-  testServerMain.setServerStartedHandler((int port) {
+  testServerMain.setServerStartedHandler((int? port) {
     var request = SyncHttpClient.getUrl(
         new Uri.http("localhost:$port", "/thisisnotfound"));
     var response = request.close();
@@ -289,7 +289,7 @@
 Future testReasonPhrase() async {
   Completer completer = new Completer();
   TestServerMain testServerMain = new TestServerMain();
-  testServerMain.setServerStartedHandler((int port) {
+  testServerMain.setServerStartedHandler((int? port) {
     var request = SyncHttpClient.getUrl(
         new Uri.http("localhost:$port", "/reasonformoving"));
     var response = request.close();
@@ -306,7 +306,7 @@
 Future testHuge() async {
   Completer completer = new Completer();
   TestServerMain testServerMain = new TestServerMain();
-  testServerMain.setServerStartedHandler((int port) {
+  testServerMain.setServerStartedHandler((int? port) {
     var request =
         SyncHttpClient.getUrl(new Uri.http("localhost:$port", "/huge"));
     var response = request.close();