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 = dynamic 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 4 : Future<ResponseBody> fetch( 26 : RequestOptions options, 27 : Stream<List<int>> requestStream, 28 : Future cancelFuture, 29 : ) async { 30 4 : if (_closed) { 31 0 : throw Exception( 32 : "Can't establish connection after [HttpClientAdapter] closed!"); 33 : } 34 8 : var _httpClient = _configHttpClient(cancelFuture, options.connectTimeout); 35 12 : Future requestFuture = _httpClient.openUrl(options.method, options.uri); 36 : 37 0 : void _throwConnectingTimeout() { 38 0 : throw DioError( 39 : request: options, 40 0 : error: 'Connecting timed out [${options.connectTimeout}ms]', 41 : type: DioErrorType.CONNECT_TIMEOUT, 42 : ); 43 : } 44 : 45 : HttpClientRequest request; 46 : try { 47 4 : request = await requestFuture; 48 : //Set Headers 49 7 : options.headers.forEach((k, v) => request.headers.set(k, v)); 50 2 : } on SocketException catch (e) { 51 4 : if (e.message.contains('timed out')) _throwConnectingTimeout(); 52 : rethrow; 53 : } 54 : 55 4 : request.followRedirects = options.followRedirects; 56 4 : request.maxRedirects = options.maxRedirects; 57 : 58 4 : if (options.method != 'GET' && requestStream != null) { 59 : // Transform the request data 60 2 : await request.addStream(requestStream); 61 : } 62 2 : Future future = request.close(); 63 4 : if (options.connectTimeout > 0) { 64 3 : future = future.timeout(Duration(milliseconds: options.connectTimeout)); 65 : } 66 : HttpClientResponse responseStream; 67 : try { 68 2 : responseStream = await future; 69 0 : } on TimeoutException { 70 0 : _throwConnectingTimeout(); 71 : } 72 : 73 : // https://github.com/dart-lang/co19/issues/383 74 : var stream = 75 4 : responseStream.transform<Uint8List>(StreamTransformer.fromHandlers( 76 2 : handleData: (data, sink) { 77 4 : sink.add(Uint8List.fromList(data)); 78 : }, 79 : )); 80 : 81 2 : var headers = <String, List<String>>{}; 82 6 : responseStream.headers.forEach((key, values) { 83 2 : headers[key] = values; 84 : }); 85 2 : return ResponseBody( 86 : stream, 87 2 : responseStream.statusCode, 88 : headers: headers, 89 6 : isRedirect: responseStream.isRedirect||responseStream.redirects.isNotEmpty, 90 2 : redirects: responseStream.redirects 91 7 : .map((e) => RedirectRecord(e.statusCode, e.method, e.location)) 92 2 : .toList(), 93 2 : statusMessage: responseStream.reasonPhrase, 94 : ); 95 : } 96 : 97 4 : HttpClient _configHttpClient(Future cancelFuture, int connectionTimeout) { 98 4 : var _connectionTimeout = connectionTimeout > 0 99 1 : ? Duration(milliseconds: connectionTimeout) 100 : : null; 101 : if (cancelFuture != null) { 102 2 : var _httpClient = HttpClient(); 103 2 : if (onHttpClientCreate != null) { 104 : //user can return a HttpClient instance 105 0 : _httpClient = onHttpClientCreate(_httpClient) ?? _httpClient; 106 : } 107 4 : _httpClient.idleTimeout = Duration(seconds: 0); 108 4 : cancelFuture.whenComplete(() { 109 8 : Future.delayed(Duration(seconds: 0)).then((e) { 110 : try { 111 2 : _httpClient.close(force: true); 112 : } catch (e) { 113 : //... 114 : } 115 : }); 116 : }); 117 2 : return _httpClient..connectionTimeout = _connectionTimeout; 118 3 : } else if (_defaultHttpClient == null) { 119 6 : _defaultHttpClient = HttpClient(); 120 9 : _defaultHttpClient.idleTimeout = Duration(seconds: 3); 121 3 : if (onHttpClientCreate != null) { 122 : //user can return a HttpClient instance 123 0 : _defaultHttpClient = 124 0 : onHttpClientCreate(_defaultHttpClient) ?? _defaultHttpClient; 125 : } 126 6 : _defaultHttpClient.connectionTimeout = _connectionTimeout; 127 : } 128 3 : return _defaultHttpClient; 129 : } 130 : 131 1 : @override 132 : void close({bool force = false}) { 133 2 : _closed = _closed; 134 1 : _defaultHttpClient?.close(force: force); 135 : } 136 : }