diff --git a/.travis.yml b/.travis.yml index 6503749a81..18de1e947d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,23 +1,35 @@ language: dart dart: - - dev - - 2.4.0 +- dev -dart_task: - - test: --platform vm,chrome - - dartanalyzer: --fatal-infos --fatal-warnings . - -matrix: +jobs: include: - # Only validate formatting using the dev release - - dart: dev - dart_task: dartfmt + - 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: "Vm Tests" + os: linux + script: pub run --enable-experiment=non-nullable test -p vm + - stage: test + name: "Web Tests" + os: linux + script: pub run --enable-experiment=non-nullable test -p chrome + +stages: +- analyze_and_format +- test # Only building master means that we don't run two builds for each pull request. branches: only: [master] cache: - directories: - - $HOME/.pub-cache + directories: + - $HOME/.pub-cache diff --git a/CHANGELOG.md b/CHANGELOG.md index f9cc8654fe..9d294c1f10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## 0.13.0-nullsafety-dev + +Pre-release for the null safety migration of this package. + +Note that 0.12.3 may not be the final stable null safety release version, we +reserve the right to release it as a 0.13.0 breaking change. + +This release will be pinned to only allow pre-release sdk versions starting from +2.10.0-2.0.dev, which is the first version where this package will appear in the +null safety allow list. + +- Added `const` constructor to `ByteStream`. + ## 0.12.2 * Fix error handler callback type for response stream errors to avoid masking diff --git a/analysis_options.yaml b/analysis_options.yaml index 60a8ea8a36..e4a95424f2 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,7 +1,11 @@ include: package:pedantic/analysis_options.yaml + analyzer: strong-mode: implicit-casts: false + enable-experiment: + - non-nullable + linter: rules: - annotate_overrides diff --git a/lib/http.dart b/lib/http.dart index 5539cfa8eb..4f918831a8 100644 --- a/lib/http.dart +++ b/lib/http.dart @@ -30,7 +30,7 @@ export 'src/streamed_response.dart'; /// the same server, you should use a single [Client] for all of those requests. /// /// For more fine-grained control over the request, use [Request] instead. -Future head(url, {Map headers}) => +Future head(Object url, {Map? headers}) => _withClient((client) => client.head(url, headers: headers)); /// Sends an HTTP GET request with the given headers to the given URL, which can @@ -41,7 +41,7 @@ Future head(url, {Map headers}) => /// the same server, you should use a single [Client] for all of those requests. /// /// For more fine-grained control over the request, use [Request] instead. -Future get(url, {Map headers}) => +Future get(Object url, {Map? headers}) => _withClient((client) => client.get(url, headers: headers)); /// Sends an HTTP POST request with the given headers and body to the given URL, @@ -63,8 +63,8 @@ Future get(url, {Map headers}) => /// /// For more fine-grained control over the request, use [Request] or /// [StreamedRequest] instead. -Future post(url, - {Map headers, body, Encoding encoding}) => +Future post(Object url, + {Map? headers, Object? body, Encoding? encoding}) => _withClient((client) => client.post(url, headers: headers, body: body, encoding: encoding)); @@ -87,8 +87,8 @@ Future post(url, /// /// For more fine-grained control over the request, use [Request] or /// [StreamedRequest] instead. -Future put(url, - {Map headers, body, Encoding encoding}) => +Future put(Object url, + {Map? headers, Object? body, Encoding? encoding}) => _withClient((client) => client.put(url, headers: headers, body: body, encoding: encoding)); @@ -111,8 +111,8 @@ Future put(url, /// /// For more fine-grained control over the request, use [Request] or /// [StreamedRequest] instead. -Future patch(url, - {Map headers, body, Encoding encoding}) => +Future patch(Object url, + {Map? headers, Object? body, Encoding? encoding}) => _withClient((client) => client.patch(url, headers: headers, body: body, encoding: encoding)); @@ -124,7 +124,7 @@ Future patch(url, /// the same server, you should use a single [Client] for all of those requests. /// /// For more fine-grained control over the request, use [Request] instead. -Future delete(url, {Map headers}) => +Future delete(Object url, {Map? headers}) => _withClient((client) => client.delete(url, headers: headers)); /// Sends an HTTP GET request with the given headers to the given URL, which can @@ -140,7 +140,7 @@ Future delete(url, {Map headers}) => /// /// For more fine-grained control over the request and response, use [Request] /// instead. -Future read(url, {Map headers}) => +Future read(Object url, {Map? headers}) => _withClient((client) => client.read(url, headers: headers)); /// Sends an HTTP GET request with the given headers to the given URL, which can @@ -156,7 +156,7 @@ Future read(url, {Map headers}) => /// /// For more fine-grained control over the request and response, use [Request] /// instead. -Future readBytes(url, {Map headers}) => +Future readBytes(Object url, {Map? headers}) => _withClient((client) => client.readBytes(url, headers: headers)); Future _withClient(Future Function(Client) fn) async { diff --git a/lib/src/base_client.dart b/lib/src/base_client.dart index 640afb869a..c47bb1071a 100644 --- a/lib/src/base_client.dart +++ b/lib/src/base_client.dart @@ -18,40 +18,42 @@ import 'streamed_response.dart'; /// maybe [close], and then they get various convenience methods for free. abstract class BaseClient implements Client { @override - Future head(url, {Map headers}) => + Future head(Object url, {Map? headers}) => _sendUnstreamed('HEAD', url, headers); @override - Future get(url, {Map headers}) => + Future get(Object url, {Map? headers}) => _sendUnstreamed('GET', url, headers); @override - Future post(url, - {Map headers, body, Encoding encoding}) => + Future post(Object url, + {Map? headers, Object? body, Encoding? encoding}) => _sendUnstreamed('POST', url, headers, body, encoding); @override - Future put(url, - {Map headers, body, Encoding encoding}) => + Future put(Object url, + {Map? headers, Object? body, Encoding? encoding}) => _sendUnstreamed('PUT', url, headers, body, encoding); @override - Future patch(url, - {Map headers, body, Encoding encoding}) => + Future patch(Object url, + {Map? headers, Object? body, Encoding? encoding}) => _sendUnstreamed('PATCH', url, headers, body, encoding); @override - Future delete(url, {Map headers}) => + Future delete(Object url, {Map? headers}) => _sendUnstreamed('DELETE', url, headers); + @override - Future read(url, {Map headers}) async { + Future read(Object url, {Map? headers}) async { final response = await get(url, headers: headers); _checkResponseSuccess(url, response); return response.body; } @override - Future readBytes(url, {Map headers}) async { + Future readBytes(Object url, + {Map? headers}) async { final response = await get(url, headers: headers); _checkResponseSuccess(url, response); return response.bodyBytes; @@ -69,8 +71,8 @@ abstract class BaseClient implements Client { /// Sends a non-streaming [Request] and returns a non-streaming [Response]. Future _sendUnstreamed( - String method, url, Map headers, - [body, Encoding encoding]) async { + String method, url, Map? headers, + [body, Encoding? encoding]) async { var request = Request(method, _fromUriOrString(url)); if (headers != null) request.headers.addAll(headers); diff --git a/lib/src/base_request.dart b/lib/src/base_request.dart index 93c545f634..9d1db4b5b1 100644 --- a/lib/src/base_request.dart +++ b/lib/src/base_request.dart @@ -4,6 +4,8 @@ import 'dart:collection'; +import 'package:meta/meta.dart'; + import 'byte_stream.dart'; import 'client.dart'; import 'streamed_response.dart'; @@ -29,10 +31,10 @@ abstract class BaseRequest { /// /// This defaults to `null`, which indicates that the size of the request is /// not known in advance. May not be assigned a negative value. - int get contentLength => _contentLength; - int _contentLength; + int? get contentLength => _contentLength; + int? _contentLength; - set contentLength(int value) { + set contentLength(int? value) { if (value != null && value < 0) { throw ArgumentError('Invalid content length $value.'); } @@ -93,16 +95,17 @@ abstract class BaseRequest { /// Freezes all mutable fields and returns a single-subscription [ByteStream] /// that emits the body of the request. /// - /// The base implementation of this returns null rather than a [ByteStream]; + /// The base implementation of this returns an empty [ByteStream]; /// subclasses are responsible for creating the return value, which should be /// single-subscription to ensure that no data is dropped. They should also /// freeze any additional mutable fields they add that don't make sense to /// change after the request headers are sent. + @mustCallSuper ByteStream finalize() { // TODO(nweiz): freeze headers if (finalized) throw StateError("Can't finalize a finalized Request."); _finalized = true; - return null; + return const ByteStream(Stream.empty()); } /// Sends this request. diff --git a/lib/src/base_response.dart b/lib/src/base_response.dart index 6e2fc13bff..5040245fae 100644 --- a/lib/src/base_response.dart +++ b/lib/src/base_response.dart @@ -10,18 +10,18 @@ import 'base_request.dart'; /// they're returned by [BaseClient.send] or other HTTP client methods. abstract class BaseResponse { /// The (frozen) request that triggered this response. - final BaseRequest request; + final BaseRequest? request; /// The HTTP status code for this response. final int statusCode; /// The reason phrase associated with the status code. - final String reasonPhrase; + final String? reasonPhrase; /// The size of the response body, in bytes. /// /// If the size of the request is not known in advance, this is `null`. - final int contentLength; + final int? contentLength; // TODO(nweiz): automatically parse cookies from headers @@ -42,7 +42,7 @@ abstract class BaseResponse { this.reasonPhrase}) { if (statusCode < 100) { throw ArgumentError('Invalid status code $statusCode.'); - } else if (contentLength != null && contentLength < 0) { + } else if (contentLength != null && contentLength! < 0) { throw ArgumentError('Invalid content length $contentLength.'); } } diff --git a/lib/src/browser_client.dart b/lib/src/browser_client.dart index 1fb25b5a3f..3980bc0e26 100644 --- a/lib/src/browser_client.dart +++ b/lib/src/browser_client.dart @@ -52,16 +52,17 @@ class BrowserClient extends BaseClient { request.headers.forEach(xhr.setRequestHeader); var completer = Completer(); + + // TODO(kevmoo): Waiting on https://github.com/dart-lang/linter/issues/2185 + // ignore: void_checks unawaited(xhr.onLoad.first.then((_) { - // TODO(nweiz): Set the response type to "arraybuffer" when issue 18542 - // is fixed. - var blob = xhr.response as Blob ?? Blob([]); + var blob = xhr.response as Blob; var reader = FileReader(); reader.onLoad.first.then((_) { var body = reader.result as Uint8List; completer.complete(StreamedResponse( - ByteStream.fromBytes(body), xhr.status, + ByteStream.fromBytes(body), xhr.status!, contentLength: body.length, request: request, headers: xhr.responseHeaders, @@ -76,6 +77,8 @@ class BrowserClient extends BaseClient { reader.readAsArrayBuffer(blob); })); + // TODO(kevmoo): Waiting on https://github.com/dart-lang/linter/issues/2185 + // ignore: void_checks unawaited(xhr.onError.first.then((_) { // Unfortunately, the underlying XMLHttpRequest API doesn't expose any // specific information about the error itself. diff --git a/lib/src/byte_stream.dart b/lib/src/byte_stream.dart index 0191a05cb9..172db0d6c0 100644 --- a/lib/src/byte_stream.dart +++ b/lib/src/byte_stream.dart @@ -8,7 +8,7 @@ import 'dart:typed_data'; /// A stream of chunks of bytes representing a single piece of data. class ByteStream extends StreamView> { - ByteStream(Stream> stream) : super(stream); + const ByteStream(Stream> stream) : super(stream); /// Returns a single-subscription byte stream that will emit the given bytes /// in a single chunk. diff --git a/lib/src/client.dart b/lib/src/client.dart index 89b1b10c63..3a1c33efb0 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -7,11 +7,8 @@ import 'dart:typed_data'; import 'base_client.dart'; import 'base_request.dart'; -// ignore: uri_does_not_exist import 'client_stub.dart' - // ignore: uri_does_not_exist if (dart.library.html) 'browser_client.dart' - // ignore: uri_does_not_exist if (dart.library.io) 'io_client.dart'; import 'response.dart'; import 'streamed_response.dart'; @@ -37,13 +34,13 @@ abstract class Client { /// can be a [Uri] or a [String]. /// /// For more fine-grained control over the request, use [send] instead. - Future head(url, {Map headers}); + Future head(Object url, {Map? headers}); /// Sends an HTTP GET request with the given headers to the given URL, which /// can be a [Uri] or a [String]. /// /// For more fine-grained control over the request, use [send] instead. - Future get(url, {Map headers}); + Future get(Object url, {Map? headers}); /// Sends an HTTP POST request with the given headers and body to the given /// URL, which can be a [Uri] or a [String]. @@ -63,8 +60,8 @@ abstract class Client { /// [encoding] defaults to [utf8]. /// /// For more fine-grained control over the request, use [send] instead. - Future post(url, - {Map headers, body, Encoding encoding}); + Future post(Object url, + {Map? headers, Object? body, Encoding? encoding}); /// Sends an HTTP PUT request with the given headers and body to the given /// URL, which can be a [Uri] or a [String]. @@ -84,8 +81,8 @@ abstract class Client { /// [encoding] defaults to [utf8]. /// /// For more fine-grained control over the request, use [send] instead. - Future put(url, - {Map headers, body, Encoding encoding}); + Future put(Object url, + {Map? headers, Object? body, Encoding? encoding}); /// Sends an HTTP PATCH request with the given headers and body to the given /// URL, which can be a [Uri] or a [String]. @@ -105,14 +102,14 @@ abstract class Client { /// [encoding] defaults to [utf8]. /// /// For more fine-grained control over the request, use [send] instead. - Future patch(url, - {Map headers, body, Encoding encoding}); + Future patch(Object url, + {Map? headers, Object? body, Encoding? encoding}); /// Sends an HTTP DELETE request with the given headers to the given URL, /// which can be a [Uri] or a [String]. /// /// For more fine-grained control over the request, use [send] instead. - Future delete(url, {Map headers}); + Future delete(Object url, {Map? headers}); /// Sends an HTTP GET request with the given headers to the given URL, which /// can be a [Uri] or a [String], and returns a Future that completes to the @@ -123,7 +120,7 @@ abstract class Client { /// /// For more fine-grained control over the request and response, use [send] or /// [get] instead. - Future read(url, {Map headers}); + Future read(Object url, {Map? headers}); /// Sends an HTTP GET request with the given headers to the given URL, which /// can be a [Uri] or a [String], and returns a Future that completes to the @@ -134,7 +131,7 @@ abstract class Client { /// /// For more fine-grained control over the request and response, use [send] or /// [get] instead. - Future readBytes(url, {Map headers}); + Future readBytes(Object url, {Map? headers}); /// Sends an HTTP request and asynchronously returns the response. Future send(BaseRequest request); diff --git a/lib/src/exception.dart b/lib/src/exception.dart index c92a861a5b..5ac1a6441d 100644 --- a/lib/src/exception.dart +++ b/lib/src/exception.dart @@ -7,7 +7,7 @@ class ClientException implements Exception { final String message; /// The URL of the HTTP request or response that failed. - final Uri uri; + final Uri? uri; ClientException(this.message, [this.uri]); diff --git a/lib/src/io_client.dart b/lib/src/io_client.dart index d4f166e786..10539ce0a3 100644 --- a/lib/src/io_client.dart +++ b/lib/src/io_client.dart @@ -18,9 +18,9 @@ BaseClient createClient() => IOClient(); /// A `dart:io`-based HTTP client. class IOClient extends BaseClient { /// The underlying `dart:io` HTTP client. - HttpClient _inner; + HttpClient? _inner; - IOClient([HttpClient inner]) : _inner = inner ?? HttpClient(); + IOClient([HttpClient? inner]) : _inner = inner ?? HttpClient(); /// Sends an HTTP request and asynchronously returns the response. @override @@ -28,10 +28,10 @@ class IOClient extends BaseClient { var stream = request.finalize(); try { - var ioRequest = (await _inner.openUrl(request.method, request.url)) + var ioRequest = (await _inner!.openUrl(request.method, request.url)) ..followRedirects = request.followRedirects ..maxRedirects = request.maxRedirects - ..contentLength = (request?.contentLength ?? -1) + ..contentLength = (request.contentLength ?? -1) ..persistentConnection = request.persistentConnection; request.headers.forEach((name, value) { ioRequest.headers.set(name, value); @@ -70,7 +70,7 @@ class IOClient extends BaseClient { @override void close() { if (_inner != null) { - _inner.close(force: true); + _inner!.close(force: true); _inner = null; } } diff --git a/lib/src/io_streamed_response.dart b/lib/src/io_streamed_response.dart index fc69837b69..21744858b3 100644 --- a/lib/src/io_streamed_response.dart +++ b/lib/src/io_streamed_response.dart @@ -10,19 +10,21 @@ import 'streamed_response.dart'; /// An HTTP response where the response body is received asynchronously after /// the headers have been received. class IOStreamedResponse extends StreamedResponse { - final HttpClientResponse _inner; + final HttpClientResponse? _inner; /// Creates a new streaming response. /// /// [stream] should be a single-subscription stream. + /// + /// If [inner] is not provided, [detachSocket] will throw. IOStreamedResponse(Stream> stream, int statusCode, - {int contentLength, - BaseRequest request, + {int? contentLength, + BaseRequest? request, Map headers = const {}, bool isRedirect = false, bool persistentConnection = true, - String reasonPhrase, - HttpClientResponse inner}) + String? reasonPhrase, + HttpClientResponse? inner}) : _inner = inner, super(stream, statusCode, contentLength: contentLength, @@ -33,5 +35,7 @@ class IOStreamedResponse extends StreamedResponse { reasonPhrase: reasonPhrase); /// Detaches the underlying socket from the HTTP server. - Future detachSocket() async => _inner.detachSocket(); + /// + /// Will throw if `inner` was not set or `null` when `this` was created. + Future detachSocket() async => _inner!.detachSocket(); } diff --git a/lib/src/multipart_file.dart b/lib/src/multipart_file.dart index 8e6642f001..772493adb0 100644 --- a/lib/src/multipart_file.dart +++ b/lib/src/multipart_file.dart @@ -7,10 +7,7 @@ import 'dart:convert'; import 'package:http_parser/http_parser.dart'; import 'byte_stream.dart'; -// ignore: uri_does_not_exist -import 'multipart_file_stub.dart' - // ignore: uri_does_not_exist - if (dart.library.io) 'multipart_file_io.dart'; +import 'multipart_file_stub.dart' if (dart.library.io) 'multipart_file_io.dart'; import 'utils.dart'; /// A file to be uploaded as part of a [MultipartRequest]. @@ -28,8 +25,8 @@ class MultipartFile { /// The basename of the file. /// - /// May be null. - final String filename; + /// May be `null`. + final String? filename; /// The content-type of the file. /// @@ -51,7 +48,7 @@ class MultipartFile { /// [contentType] currently defaults to `application/octet-stream`, but in the /// future may be inferred from [filename]. MultipartFile(this.field, Stream> stream, this.length, - {this.filename, MediaType contentType}) + {this.filename, MediaType? contentType}) : _stream = toByteStream(stream), contentType = contentType ?? MediaType('application', 'octet-stream'); @@ -60,7 +57,7 @@ class MultipartFile { /// [contentType] currently defaults to `application/octet-stream`, but in the /// future may be inferred from [filename]. factory MultipartFile.fromBytes(String field, List value, - {String filename, MediaType contentType}) { + {String? filename, MediaType? contentType}) { var stream = ByteStream.fromBytes(value); return MultipartFile(field, stream, value.length, filename: filename, contentType: contentType); @@ -73,7 +70,7 @@ class MultipartFile { /// [contentType] currently defaults to `text/plain; charset=utf-8`, but in /// the future may be inferred from [filename]. factory MultipartFile.fromString(String field, String value, - {String filename, MediaType contentType}) { + {String? filename, MediaType? contentType}) { contentType ??= MediaType('text', 'plain'); var encoding = encodingForCharset(contentType.parameters['charset'], utf8); contentType = contentType.change(parameters: {'charset': encoding.name}); @@ -92,7 +89,7 @@ class MultipartFile { /// Throws an [UnsupportedError] if `dart:io` isn't supported in this /// environment. static Future fromPath(String field, String filePath, - {String filename, MediaType contentType}) => + {String? filename, MediaType? contentType}) => multipartFileFromPath(field, filePath, filename: filename, contentType: contentType); diff --git a/lib/src/multipart_file_io.dart b/lib/src/multipart_file_io.dart index bffc394b6b..1eaa918a2a 100644 --- a/lib/src/multipart_file_io.dart +++ b/lib/src/multipart_file_io.dart @@ -11,7 +11,7 @@ import 'byte_stream.dart'; import 'multipart_file.dart'; Future multipartFileFromPath(String field, String filePath, - {String filename, MediaType contentType}) async { + {String? filename, MediaType? contentType}) async { filename ??= p.basename(filePath); var file = File(filePath); var length = await file.length(); diff --git a/lib/src/multipart_file_stub.dart b/lib/src/multipart_file_stub.dart index 30ed036c52..326914b50d 100644 --- a/lib/src/multipart_file_stub.dart +++ b/lib/src/multipart_file_stub.dart @@ -7,6 +7,6 @@ import 'package:http_parser/http_parser.dart'; import 'multipart_file.dart'; Future multipartFileFromPath(String field, String filePath, - {String filename, MediaType contentType}) => + {String? filename, MediaType? contentType}) => throw UnsupportedError( 'MultipartFile is only supported where dart:io is available.'); diff --git a/lib/src/multipart_request.dart b/lib/src/multipart_request.dart index 7e5469b29f..faacd0b505 100644 --- a/lib/src/multipart_request.dart +++ b/lib/src/multipart_request.dart @@ -76,7 +76,7 @@ class MultipartRequest extends BaseRequest { } @override - set contentLength(int value) { + set contentLength(int? value) { throw UnsupportedError('Cannot set the contentLength property of ' 'multipart requests.'); } @@ -135,7 +135,7 @@ class MultipartRequest extends BaseRequest { 'content-disposition: form-data; name="${_browserEncode(file.field)}"'; if (file.filename != null) { - header = '$header; filename="${_browserEncode(file.filename)}"'; + header = '$header; filename="${_browserEncode(file.filename!)}"'; } return '$header\r\n\r\n'; } diff --git a/lib/src/request.dart b/lib/src/request.dart index d882d88cf1..bbb8448abe 100644 --- a/lib/src/request.dart +++ b/lib/src/request.dart @@ -22,7 +22,7 @@ class Request extends BaseRequest { int get contentLength => bodyBytes.length; @override - set contentLength(int value) { + set contentLength(int? value) { throw UnsupportedError('Cannot set the contentLength property of ' 'non-streaming Request objects.'); } @@ -50,10 +50,10 @@ class Request extends BaseRequest { /// charset parameter on that header. Encoding get encoding { if (_contentType == null || - !_contentType.parameters.containsKey('charset')) { + !_contentType!.parameters.containsKey('charset')) { return _defaultEncoding; } - return requiredEncodingForCharset(_contentType.parameters['charset']); + return requiredEncodingForCharset(_contentType!.parameters['charset']!); } set encoding(Encoding value) { @@ -151,14 +151,18 @@ class Request extends BaseRequest { } /// The `Content-Type` header of the request (if it exists) as a [MediaType]. - MediaType get _contentType { + MediaType? get _contentType { var contentType = headers['content-type']; if (contentType == null) return null; return MediaType.parse(contentType); } - set _contentType(MediaType value) { - headers['content-type'] = value.toString(); + set _contentType(MediaType? value) { + if (value == null) { + headers.remove('content-type'); + } else { + headers['content-type'] = value.toString(); + } } /// Throw an error if this request has been finalized. diff --git a/lib/src/response.dart b/lib/src/response.dart index a88669eca2..01899887a7 100644 --- a/lib/src/response.dart +++ b/lib/src/response.dart @@ -29,11 +29,11 @@ class Response extends BaseResponse { /// Creates a new HTTP response with a string body. Response(String body, int statusCode, - {BaseRequest request, + {BaseRequest? request, Map headers = const {}, bool isRedirect = false, bool persistentConnection = true, - String reasonPhrase}) + String? reasonPhrase}) : this.bytes(_encodingForHeaders(headers).encode(body), statusCode, request: request, headers: headers, @@ -43,11 +43,11 @@ class Response extends BaseResponse { /// Create a new HTTP response with a byte array body. Response.bytes(List bodyBytes, int statusCode, - {BaseRequest request, + {BaseRequest? request, Map headers = const {}, bool isRedirect = false, bool persistentConnection = true, - String reasonPhrase}) + String? reasonPhrase}) : bodyBytes = toUint8List(bodyBytes), super(statusCode, contentLength: bodyBytes.length, diff --git a/lib/src/streamed_response.dart b/lib/src/streamed_response.dart index a11386e8cb..e082dced0e 100644 --- a/lib/src/streamed_response.dart +++ b/lib/src/streamed_response.dart @@ -19,12 +19,12 @@ class StreamedResponse extends BaseResponse { /// /// [stream] should be a single-subscription stream. StreamedResponse(Stream> stream, int statusCode, - {int contentLength, - BaseRequest request, + {int? contentLength, + BaseRequest? request, Map headers = const {}, bool isRedirect = false, bool persistentConnection = true, - String reasonPhrase}) + String? reasonPhrase}) : stream = toByteStream(stream), super(statusCode, contentLength: contentLength, diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 9d0648e359..e79108e88a 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -12,11 +12,11 @@ import 'byte_stream.dart'; /// /// mapToQuery({"foo": "bar", "baz": "bang"}); /// //=> "foo=bar&baz=bang" -String mapToQuery(Map map, {Encoding encoding}) { +String mapToQuery(Map map, {Encoding? encoding}) { var pairs = >[]; map.forEach((key, value) => pairs.add([ - Uri.encodeQueryComponent(key, encoding: encoding), - Uri.encodeQueryComponent(value, encoding: encoding) + Uri.encodeQueryComponent(key, encoding: encoding ?? utf8), + Uri.encodeQueryComponent(value, encoding: encoding ?? utf8) ])); return pairs.map((pair) => '${pair[0]}=${pair[1]}').join('&'); } @@ -25,7 +25,7 @@ String mapToQuery(Map map, {Encoding encoding}) { /// /// Returns [fallback] if [charset] is null or if no [Encoding] was found that /// corresponds to [charset]. -Encoding encodingForCharset(String charset, [Encoding fallback = latin1]) { +Encoding encodingForCharset(String? charset, [Encoding fallback = latin1]) { if (charset == null) return fallback; return Encoding.getByName(charset) ?? fallback; } diff --git a/pubspec.yaml b/pubspec.yaml index 07fffd3ca4..ec72d6e8c8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,15 +1,22 @@ name: http -version: 0.12.2 +version: 0.13.0-nullsafety-dev homepage: https://github.com/dart-lang/http description: A composable, multi-platform, Future-based API for HTTP requests. environment: - sdk: ">=2.4.0 <3.0.0" + sdk: '>=2.10.0-2.0.dev <2.11.0' +# Cannot be published until null safety ships +publish_to: none dependencies: - http_parser: ">=0.0.1 <4.0.0" - path: ">=0.9.0 <2.0.0" - pedantic: "^1.0.0" + http_parser: ^3.2.0-nullsafety + meta: ^1.3.0-nullsafety.3 + path: ^1.8.0-nullsafety.1 + pedantic: ^1.10.0-nullsafety dev_dependencies: - test: ^1.3.0 + test: ^1.16.0-nullsafety.4 + +dependency_overrides: + http_parser: + git: https://github.com/dart-lang/http_parser diff --git a/test/io/multipart_test.dart b/test/io/multipart_test.dart index 9128b2e4b9..f2333f5322 100644 --- a/test/io/multipart_test.dart +++ b/test/io/multipart_test.dart @@ -13,7 +13,7 @@ import 'package:test/test.dart'; import 'utils.dart'; void main() { - Directory tempDir; + late Directory tempDir; setUp(() { tempDir = Directory.systemTemp.createTempSync('http_test_'); }); diff --git a/test/io/utils.dart b/test/io/utils.dart index d057fa4c28..7ad3325f8c 100644 --- a/test/io/utils.dart +++ b/test/io/utils.dart @@ -14,10 +14,10 @@ import 'package:test/test.dart'; export '../utils.dart'; /// The current server instance. -HttpServer _server; +HttpServer? _server; /// The URL for the current server instance. -Uri get serverUrl => Uri.parse('http://localhost:${_server.port}'); +Uri get serverUrl => Uri.parse('http://localhost:${_server!.port}'); /// Starts a new HTTP server. Future startServer() async { @@ -78,7 +78,7 @@ Future startServer() async { requestBody = null; } else if (request.headers.contentType?.charset != null) { var encoding = - requiredEncodingForCharset(request.headers.contentType.charset); + requiredEncodingForCharset(request.headers.contentType!.charset!); requestBody = encoding.decode(requestBodyBytes); } else { requestBody = requestBodyBytes; @@ -109,7 +109,7 @@ Future startServer() async { /// Stops the current HTTP server. void stopServer() { if (_server != null) { - _server.close(); + _server!.close(); _server = null; } } diff --git a/test/utils.dart b/test/utils.dart index 290678f081..ea7cbeb676 100644 --- a/test/utils.dart +++ b/test/utils.dart @@ -48,7 +48,7 @@ class _Parse extends Matcher { _Parse(this._matcher); @override - bool matches(item, Map matchState) { + bool matches(Object? item, Map matchState) { if (item is String) { dynamic parsed; try { @@ -83,7 +83,7 @@ class _BodyMatches extends Matcher { _BodyMatches(this._pattern); @override - bool matches(item, Map matchState) { + bool matches(Object? item, Map matchState) { if (item is http.MultipartRequest) { return completes.matches(_checks(item), matchState); } @@ -94,8 +94,8 @@ class _BodyMatches extends Matcher { Future _checks(http.MultipartRequest item) async { var bodyBytes = await item.finalize().toBytes(); var body = utf8.decode(bodyBytes); - var contentType = MediaType.parse(item.headers['content-type']); - var boundary = contentType.parameters['boundary']; + var contentType = MediaType.parse(item.headers['content-type']!); + var boundary = contentType.parameters['boundary']!; var expected = cleanUpLiteral(_pattern) .replaceAll('\n', '\r\n') .replaceAll('{{boundary}}', boundary);