// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; import 'package:meta/meta.dart'; import '../http.dart' as http; import 'base_client.dart'; import 'base_request.dart'; import 'client_stub.dart' if (dart.library.js_interop) 'browser_client.dart' if (dart.library.io) 'io_client.dart'; import 'exception.dart'; import 'response.dart'; import 'streamed_response.dart'; /// The interface for HTTP clients that take care of maintaining persistent /// connections across multiple requests to the same server. /// /// If you only need to send a single request, it's usually easier to use /// [http.head], [http.get], [http.post], [http.put], [http.patch], or /// [http.delete] instead. /// /// All methods will emit a [ClientException] if there is a transport-level /// failure when communication with the server. For example, if the server could /// not be reached. /// /// When creating an HTTP client class with additional functionality, you must /// extend [BaseClient] rather than [Client]. In most cases, you can wrap /// another instance of [Client] and add functionality on top of that. This /// allows all classes implementing [Client] to be mutually composable. abstract interface class Client { /// Creates a new platform appropriate client. /// /// Creates an `IOClient` if `dart:io` is available and a `BrowserClient` if /// `dart:js_interop` is available, otherwise it will throw an unsupported /// error. factory Client() => zoneClient ?? createClient(); /// Sends an HTTP HEAD request with the given headers to the given URL. /// /// For more fine-grained control over the request, use [send] instead. Future head(Uri url, {Map? headers}); /// Sends an HTTP GET request with the given headers to the given URL. /// /// For more fine-grained control over the request, use [send] instead. Future get(Uri url, {Map? headers}); /// Sends an HTTP POST request with the given headers and body to the given /// URL. /// /// [body] sets the body of the request. It can be a `String`, a `List` /// or a `Map`. /// /// If [body] is a `String`, it's encoded using [encoding] and used as the /// body of the request. The content-type of the request will default to /// "text/plain". /// /// If [body] is a `List`, it's used as a list of bytes for the body of the /// request. /// /// If [body] is a `Map`, it's encoded as form fields using [encoding]. The /// content-type of the request will be set to /// `"application/x-www-form-urlencoded"`; this cannot be overridden. /// /// [encoding] defaults to [utf8]. /// /// For more fine-grained control over the request, use [send] instead. Future post(Uri url, {Map? headers, Object? body, Encoding? encoding}); /// Sends an HTTP PUT request with the given headers and body to the given /// URL. /// /// [body] sets the body of the request. It can be a `String`, a `List` /// or a `Map`. If it's a `String`, it's encoded using /// [encoding] and used as the body of the request. The content-type of the /// request will default to "text/plain". /// /// If [body] is a `List`, it's used as a list of bytes for the body of the /// request. /// /// If [body] is a `Map`, it's encoded as form fields using [encoding]. The /// content-type of the request will be set to /// `"application/x-www-form-urlencoded"`; this cannot be overridden. /// /// [encoding] defaults to [utf8]. /// /// For more fine-grained control over the request, use [send] instead. Future put(Uri url, {Map? headers, Object? body, Encoding? encoding}); /// Sends an HTTP PATCH request with the given headers and body to the given /// URL. /// /// [body] sets the body of the request. It can be a `String`, a `List` /// or a `Map`. If it's a `String`, it's encoded using /// [encoding] and used as the body of the request. The content-type of the /// request will default to "text/plain". /// /// If [body] is a `List`, it's used as a list of bytes for the body of the /// request. /// /// If [body] is a `Map`, it's encoded as form fields using [encoding]. The /// content-type of the request will be set to /// `"application/x-www-form-urlencoded"`; this cannot be overridden. /// /// [encoding] defaults to [utf8]. /// /// For more fine-grained control over the request, use [send] instead. Future patch(Uri url, {Map? headers, Object? body, Encoding? encoding}); /// Sends an HTTP DELETE request with the given headers to the given URL. /// /// For more fine-grained control over the request, use [send] instead. Future delete(Uri url, {Map? headers, Object? body, Encoding? encoding}); /// Sends an HTTP GET request with the given headers to the given URL and /// returns a Future that completes to the body of the response as a String. /// /// The Future will emit a [ClientException] if the response doesn't have a /// success status code. /// /// For more fine-grained control over the request and response, use [send] or /// [get] instead. Future read(Uri url, {Map? headers}); /// Sends an HTTP GET request with the given headers to the given URL and /// returns a Future that completes to the body of the response as a list of /// bytes. /// /// The Future will emit a [ClientException] if the response doesn't have a /// success status code. /// /// For more fine-grained control over the request and response, use [send] or /// [get] instead. Future readBytes(Uri url, {Map? headers}); /// Sends an HTTP request and asynchronously returns the response. Future send(BaseRequest request); /// Closes the client and cleans up any resources associated with it. /// /// Some clients maintain a pool of network connections that will not be /// disconnected until the client is closed. This may cause programs using /// using the Dart SDK (`dart run`, `dart test`, `dart compile`, etc.) to /// not terminate until the client is closed. Programs run using the Flutter /// SDK can still terminate even with an active connection pool. /// /// Once [close] is called, no other methods should be called. If [close] is /// called while other asynchronous methods are running, the behavior is /// undefined. void close(); } /// The [Client] for the current [Zone], if one has been set. /// /// NOTE: This property is explicitly hidden from the public API. @internal Client? get zoneClient { final client = Zone.current[#_clientToken]; return client == null ? null : (client as Client Function())(); } /// Runs [body] in its own [Zone] with the [Client] returned by [clientFactory] /// set as the default [Client]. /// /// For example: /// /// ``` /// class MyAndroidHttpClient extends BaseClient { /// @override /// Future send(http.BaseRequest request) { /// // your implementation here /// } /// } /// /// void main() { /// var clientFactory = Client.new; // Constructs the default client. /// if (Platform.isAndroid) { /// clientFactory = MyAndroidHttpClient.new; /// } /// runWithClient(myFunction, clientFactory); /// } /// /// void myFunction() { /// // Uses the `Client` configured in `main`. /// final response = await get(Uri.https('www.example.com', '')); /// final client = Client(); /// } /// ``` /// /// The [Client] returned by [clientFactory] is used by the [Client.new] factory /// and the convenience HTTP functions (e.g. [http.get]). If [clientFactory] /// returns `Client()` then the default [Client] is used. /// /// Only fresh `Client` instances using the default constructor are impacted. /// HTTP requests made using `dart:io` or `dart:html` APIs, /// or using specifically instantiated client implementations, are not affected. /// /// If [runWithClient] is used and the environment defines /// `no_default_http_client=true` then generated binaries may be smaller e.g. /// ```shell /// $ dart compile exe --define=no_default_http_client=true ... /// ``` /// /// If `no_default_http_client=true` is set then any call to the [Client] /// factory (i.e. `Client()`) outside of the [Zone] created by [runWithClient] /// will throw [StateError]. /// /// > [!IMPORTANT] /// > Flutter does not guarantee that callbacks are executed in a particular /// > [Zone]. /// > /// > Instead of using [runWithClient], Flutter developers can use a framework, /// > such as [`package:provider`](https://pub.dev/packages/provider), to make /// > a [Client] available throughout their applications. /// > /// > See the /// > [Flutter Http Example](https://github.com/dart-lang/http/tree/master/pkgs/flutter_http_example). R runWithClient(R Function() body, Client Function() clientFactory, {ZoneSpecification? zoneSpecification}) => runZoned(body, zoneValues: {#_clientToken: Zone.current.bindCallback(clientFactory)}, zoneSpecification: zoneSpecification);