Line data Source code
1 : import 'dart:async'; 2 : import 'dart:io'; 3 : import 'dart:typed_data'; 4 : import '../adapter.dart'; 5 : import '../options.dart'; 6 : import '../dio_error.dart'; 7 : import '../redirect_record.dart'; 8 : 9 : typedef OnHttpClientCreate = HttpClient? Function(HttpClient client); 10 : 11 0 : HttpClientAdapter createAdapter() => DefaultHttpClientAdapter(); 12 : 13 : /// The default HttpClientAdapter for Dio. 14 : class DefaultHttpClientAdapter implements HttpClientAdapter { 15 : /// [Dio] will create HttpClient when it is needed. 16 : /// If [onHttpClientCreate] is provided, [Dio] will call 17 : /// it when a HttpClient created. 18 : OnHttpClientCreate? onHttpClientCreate; 19 : 20 : HttpClient? _defaultHttpClient; 21 : 22 : bool _closed = false; 23 : 24 : @override 25 6 : Future<ResponseBody> fetch( 26 : RequestOptions options, 27 : Stream<Uint8List>? requestStream, 28 : Future? cancelFuture, 29 : ) async { 30 6 : if (_closed) { 31 0 : throw Exception( 32 : "Can't establish connection after [HttpClientAdapter] closed!"); 33 : } 34 12 : var _httpClient = _configHttpClient(cancelFuture, options.connectTimeout); 35 18 : var reqFuture = _httpClient.openUrl(options.method, options.uri); 36 : 37 1 : void _throwConnectingTimeout() { 38 1 : throw DioError( 39 : requestOptions: options, 40 2 : error: 'Connecting timed out [${options.connectTimeout}ms]', 41 : type: DioErrorType.connectTimeout, 42 : ); 43 : } 44 : 45 : late HttpClientRequest request; 46 : int timePassed = 0; 47 : try { 48 12 : if (options.connectTimeout > 0) { 49 4 : var start = DateTime.now().millisecond; 50 2 : request = await reqFuture 51 6 : .timeout(Duration(milliseconds: options.connectTimeout)); 52 6 : timePassed = DateTime.now().millisecond - start; 53 : } else { 54 4 : request = await reqFuture; 55 : } 56 : 57 : //Set Headers 58 12 : options.headers.forEach((k, v) { 59 6 : if (v != null) request.headers.set(k, '$v'); 60 : }); 61 2 : } on SocketException catch (e) { 62 4 : if (e.message.contains('timed out')) { 63 : _throwConnectingTimeout(); 64 : } 65 : rethrow; 66 0 : } on TimeoutException { 67 : _throwConnectingTimeout(); 68 : } 69 : 70 10 : request.followRedirects = options.followRedirects; 71 10 : request.maxRedirects = options.maxRedirects; 72 : 73 : if (requestStream != null) { 74 : // Transform the request data 75 4 : await request.addStream(requestStream); 76 : } 77 : // [receiveTimeout] represents a timeout during data transfer! That is to say the 78 : // client has connected to the server, and the server starts to send data to the client. 79 : // So, we should use connectTimeout. 80 10 : int responseTimeout = options.connectTimeout - timePassed; 81 5 : var future = request.close(); 82 5 : if (responseTimeout > 0) { 83 4 : future = future.timeout(Duration(milliseconds: responseTimeout)); 84 : } 85 : late HttpClientResponse responseStream; 86 : try { 87 5 : responseStream = await future; 88 1 : } on TimeoutException { 89 : _throwConnectingTimeout(); 90 : } 91 : 92 : var stream = 93 10 : responseStream.transform<Uint8List>(StreamTransformer.fromHandlers( 94 4 : handleData: (data, sink) { 95 8 : sink.add(Uint8List.fromList(data)); 96 : }, 97 : )); 98 : 99 5 : var headers = <String, List<String>>{}; 100 15 : responseStream.headers.forEach((key, values) { 101 5 : headers[key] = values; 102 : }); 103 5 : return ResponseBody( 104 : stream, 105 5 : responseStream.statusCode, 106 : headers: headers, 107 : isRedirect: 108 15 : responseStream.isRedirect || responseStream.redirects.isNotEmpty, 109 5 : redirects: responseStream.redirects 110 10 : .map((e) => RedirectRecord(e.statusCode, e.method, e.location)) 111 5 : .toList(), 112 5 : statusMessage: responseStream.reasonPhrase, 113 : ); 114 : } 115 : 116 6 : HttpClient _configHttpClient(Future? cancelFuture, int connectionTimeout) { 117 6 : var _connectionTimeout = connectionTimeout > 0 118 2 : ? Duration(milliseconds: connectionTimeout) 119 : : null; 120 : 121 : if (cancelFuture != null) { 122 2 : var _httpClient = HttpClient(); 123 2 : _httpClient.userAgent = null; 124 2 : if (onHttpClientCreate != null) { 125 : //user can return a HttpClient instance 126 0 : _httpClient = onHttpClientCreate!(_httpClient) ?? _httpClient; 127 : } 128 4 : _httpClient.idleTimeout = Duration(seconds: 0); 129 4 : cancelFuture.whenComplete(() { 130 8 : Future.delayed(Duration(seconds: 0)).then((e) { 131 : try { 132 2 : _httpClient.close(force: true); 133 : } catch (e) { 134 : //... 135 : } 136 : }); 137 : }); 138 2 : return _httpClient..connectionTimeout = _connectionTimeout; 139 : } 140 5 : if (_defaultHttpClient == null) { 141 10 : _defaultHttpClient = HttpClient(); 142 15 : _defaultHttpClient!.idleTimeout = Duration(seconds: 3); 143 5 : if (onHttpClientCreate != null) { 144 : //user can return a HttpClient instance 145 0 : _defaultHttpClient = 146 0 : onHttpClientCreate!(_defaultHttpClient!) ?? _defaultHttpClient; 147 : } 148 10 : _defaultHttpClient!.connectionTimeout = _connectionTimeout; 149 : } 150 5 : return _defaultHttpClient!; 151 : } 152 : 153 1 : @override 154 : void close({bool force = false}) { 155 2 : _closed = _closed; 156 1 : _defaultHttpClient?.close(force: force); 157 : } 158 : }