Line data Source code
1 : part of 'dio_mixin.dart';
2 :
3 : typedef _WaitCallback<T> = FutureOr<T> Function();
4 :
5 : /// Add lock/unlock API for interceptors.
6 : class Lock {
7 : Future? _lock;
8 :
9 : late Completer _completer;
10 :
11 : /// Whether this interceptor has been locked.
12 16 : bool get locked => _lock != null;
13 :
14 : /// Lock the interceptor.
15 : ///
16 : /// Once the request/response/error interceptor is locked, the incoming request/response/error
17 : /// will wait before entering the interceptor until the interceptor is unlocked.
18 0 : void lock() {
19 0 : if (!locked) {
20 0 : _completer = Completer();
21 0 : _lock = _completer.future;
22 : }
23 : }
24 :
25 : /// Unlock the interceptor. please refer to [lock()]
26 0 : void unlock() {
27 0 : if (locked) {
28 0 : _completer.complete();
29 0 : _lock = null;
30 : }
31 : }
32 :
33 : /// Clean the interceptor queue.
34 0 : void clear([String msg = 'cancelled']) {
35 0 : if (locked) {
36 0 : _completer.completeError(msg);
37 0 : _lock = null;
38 : }
39 : }
40 :
41 : /// If the interceptor is locked, the incoming request/response/error task
42 : /// will wait before entering the interceptor until the interceptor is unlocked
43 : ///
44 : /// [callback] the function will return a `Future`
45 : /// @nodoc
46 0 : Future<T>? _wait<T>(_WaitCallback<T> callback) {
47 0 : if (locked) {
48 : // we use a future as a queue
49 0 : return _lock!.then((d) => callback());
50 : }
51 : return null;
52 : }
53 : }
54 :
55 : /// Internal enum
56 : /// @nodoc
57 11 : enum InterceptorResultType {
58 : next,
59 : resolve,
60 : resolveCallFollowing,
61 : reject,
62 : rejectCallFollowing,
63 : }
64 :
65 : /// Internal class, It is used to pass state between current and next interceptors.
66 : /// @nodoc
67 : class InterceptorState<T> {
68 8 : InterceptorState(this.data, [this.type = InterceptorResultType.next]);
69 :
70 : T data;
71 : InterceptorResultType type;
72 : }
73 :
74 : class _BaseHandler {
75 : final _completer = Completer<InterceptorState>();
76 : void Function()? _processNextInQueue;
77 :
78 24 : Future<InterceptorState> get future => _completer.future;
79 :
80 0 : bool get isCompleted => _completer.isCompleted;
81 : }
82 :
83 : /// Handler for request interceptor.
84 : class RequestInterceptorHandler extends _BaseHandler {
85 : /// Continue to call the next request interceptor.
86 2 : void next(RequestOptions requestOptions) {
87 6 : _completer.complete(InterceptorState<RequestOptions>(requestOptions));
88 2 : _processNextInQueue?.call();
89 : }
90 :
91 : /// Return the response directly! Other request interceptor(s) will not be executed,
92 : /// but response and error interceptor(s) may be executed, which depends on whether
93 : /// the value of parameter [callFollowingResponseInterceptor] is true.
94 : ///
95 : /// [response]: Response object to return.
96 : /// [callFollowingResponseInterceptor]: Whether to call the response interceptor(s).
97 7 : void resolve(Response response,
98 : [bool callFollowingResponseInterceptor = false]) {
99 14 : _completer.complete(
100 7 : InterceptorState<Response>(
101 : response,
102 : callFollowingResponseInterceptor
103 : ? InterceptorResultType.resolveCallFollowing
104 : : InterceptorResultType.resolve,
105 : ),
106 : );
107 7 : _processNextInQueue?.call();
108 : }
109 :
110 : /// Complete the request with an error! Other request/response interceptor(s) will not
111 : /// be executed, but error interceptor(s) may be executed, which depends on whether the
112 : /// value of parameter [callFollowingErrorInterceptor] is true.
113 : ///
114 : /// [error]: Error info to reject.
115 : /// [callFollowingErrorInterceptor]: Whether to call the error interceptor(s).
116 6 : void reject(DioError error, [bool callFollowingErrorInterceptor = false]) {
117 12 : _completer.completeError(
118 6 : InterceptorState<DioError>(
119 : error,
120 : callFollowingErrorInterceptor
121 : ? InterceptorResultType.rejectCallFollowing
122 : : InterceptorResultType.reject,
123 : ),
124 6 : error.stackTrace,
125 : );
126 6 : _processNextInQueue?.call();
127 : }
128 : }
129 :
130 : /// Handler for response interceptor.
131 : class ResponseInterceptorHandler extends _BaseHandler {
132 : /// Continue to call the next response interceptor.
133 2 : void next(Response response) {
134 4 : _completer.complete(
135 2 : InterceptorState<Response>(response),
136 : );
137 2 : _processNextInQueue?.call();
138 : }
139 :
140 : /// Return the response directly! Other response interceptor(s) will not be executed.
141 : /// [response]: Response object to return.
142 1 : void resolve(Response response) {
143 2 : _completer.complete(
144 1 : InterceptorState<Response>(
145 : response,
146 : InterceptorResultType.resolve,
147 : ),
148 : );
149 1 : _processNextInQueue?.call();
150 : }
151 :
152 : /// Complete the request with an error! Other response interceptor(s) will not
153 : /// be executed, but error interceptor(s) may be executed, which depends on whether the
154 : /// value of parameter [callFollowingErrorInterceptor] is true.
155 : ///
156 : /// [error]: Error info to reject.
157 : /// [callFollowingErrorInterceptor]: Whether to call the error interceptor(s).
158 1 : void reject(DioError error, [bool callFollowingErrorInterceptor = false]) {
159 2 : _completer.completeError(
160 1 : InterceptorState<DioError>(
161 : error,
162 : callFollowingErrorInterceptor
163 : ? InterceptorResultType.rejectCallFollowing
164 : : InterceptorResultType.reject,
165 : ),
166 1 : error.stackTrace,
167 : );
168 1 : _processNextInQueue?.call();
169 : }
170 : }
171 :
172 : /// Handler for error interceptor.
173 : class ErrorInterceptorHandler extends _BaseHandler {
174 : /// Continue to call the next error interceptor.
175 2 : void next(DioError err) {
176 4 : _completer.completeError(
177 2 : InterceptorState<DioError>(err),
178 2 : err.stackTrace,
179 : );
180 2 : _processNextInQueue?.call();
181 : }
182 :
183 : /// Complete the request with Response object and other error interceptor(s) will not be executed.
184 : /// This will be considered a successful request!
185 : ///
186 : /// [response]: Response object to return.
187 1 : void resolve(Response response) {
188 3 : _completer.complete(InterceptorState<Response>(
189 : response,
190 : InterceptorResultType.resolve,
191 : ));
192 1 : _processNextInQueue?.call();
193 : }
194 :
195 : /// Complete the request with a error directly! Other error interceptor(s) will not be executed.
196 1 : void reject(DioError error) {
197 2 : _completer.completeError(
198 1 : InterceptorState<DioError>(
199 : error,
200 : InterceptorResultType.reject,
201 : ),
202 1 : error.stackTrace,
203 : );
204 1 : _processNextInQueue?.call();
205 : }
206 : }
207 :
208 : /// Dio instance may have interceptor(s) by which you can intercept
209 : /// requests/responses/errors before they are handled by `then` or `catchError`.
210 : /// See also:
211 : /// - [InterceptorsWrapper] A helper class to create Interceptor(s).
212 : /// - [QueuedInterceptor] Serialize the request/response/error before they enter the interceptor.
213 : /// - [QueuedInterceptorsWrapper] A helper class to create QueuedInterceptor(s).
214 : class Interceptor {
215 : /// The callback will be executed before the request is initiated.
216 : ///
217 : /// If you want to continue the request, call [handler.next].
218 : ///
219 : /// If you want to complete the request with some custom data,
220 : /// you can resolve a [Response] object with [handler.resolve].
221 : ///
222 : /// If you want to complete the request with an error message,
223 : /// you can reject a [DioError] object with [handler.reject].
224 1 : void onRequest(
225 : RequestOptions options,
226 : RequestInterceptorHandler handler,
227 : ) =>
228 1 : handler.next(options);
229 :
230 : /// The callback will be executed on success.
231 : /// If you want to continue the response, call [handler.next].
232 : ///
233 : /// If you want to complete the response with some custom data directly,
234 : /// you can resolve a [Response] object with [handler.resolve] and other
235 : /// response interceptor(s) will not be executed.
236 : ///
237 : /// If you want to complete the response with an error message,
238 : /// you can reject a [DioError] object with [handler.reject].
239 1 : void onResponse(
240 : Response response,
241 : ResponseInterceptorHandler handler,
242 : ) =>
243 1 : handler.next(response);
244 :
245 : /// The callback will be executed on error.
246 : ///
247 : /// If you want to continue the error , call [handler.next].
248 : ///
249 : /// If you want to complete the response with some custom data directly,
250 : /// you can resolve a [Response] object with [handler.resolve] and other
251 : /// error interceptor(s) will be skipped.
252 : ///
253 : /// If you want to complete the response with an error message directly,
254 : /// you can reject a [DioError] object with [handler.reject], and other
255 : /// error interceptor(s) will be skipped.
256 0 : void onError(
257 : DioError err,
258 : ErrorInterceptorHandler handler,
259 : ) =>
260 0 : handler.next(err);
261 : }
262 :
263 : typedef InterceptorSendCallback = void Function(
264 : RequestOptions options,
265 : RequestInterceptorHandler handler,
266 : );
267 :
268 : typedef InterceptorSuccessCallback = void Function(
269 : Response e,
270 : ResponseInterceptorHandler handler,
271 : );
272 :
273 : typedef InterceptorErrorCallback = void Function(
274 : DioError e, ErrorInterceptorHandler handler);
275 :
276 : mixin _InterceptorWrapperMixin on Interceptor {
277 : InterceptorSendCallback? _onRequest;
278 :
279 : InterceptorSuccessCallback? _onResponse;
280 :
281 : InterceptorErrorCallback? _onError;
282 :
283 2 : @override
284 : void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
285 2 : if (_onRequest != null) {
286 2 : _onRequest!(options, handler);
287 : } else {
288 1 : handler.next(options);
289 : }
290 : }
291 :
292 2 : @override
293 : void onResponse(Response response, ResponseInterceptorHandler handler) {
294 2 : if (_onResponse != null) {
295 1 : _onResponse!(response, handler);
296 : } else {
297 2 : handler.next(response);
298 : }
299 : }
300 :
301 2 : @override
302 : void onError(DioError err, ErrorInterceptorHandler handler) {
303 2 : if (_onError != null) {
304 2 : _onError!(err, handler);
305 : } else {
306 0 : handler.next(err);
307 : }
308 : }
309 : }
310 :
311 : /// [InterceptorsWrapper] is a helper class, which is used to conveniently
312 : /// create interceptor(s).
313 : /// See also:
314 : /// - [Interceptor]
315 : /// - [QueuedInterceptor] Serialize the request/response/error before they enter the interceptor.
316 : /// - [QueuedInterceptorsWrapper] A helper class to create QueuedInterceptor(s).
317 : class InterceptorsWrapper extends Interceptor with _InterceptorWrapperMixin {
318 : InterceptorSendCallback? __onRequest;
319 :
320 : InterceptorSuccessCallback? __onResponse;
321 :
322 : InterceptorErrorCallback? __onError;
323 :
324 1 : InterceptorsWrapper({
325 : InterceptorSendCallback? onRequest,
326 : InterceptorSuccessCallback? onResponse,
327 : InterceptorErrorCallback? onError,
328 : }) : __onRequest = onRequest,
329 : __onResponse = onResponse,
330 : __onError = onError;
331 :
332 1 : @override
333 1 : InterceptorErrorCallback? get _onError => __onError;
334 :
335 1 : @override
336 1 : InterceptorSendCallback? get _onRequest => __onRequest;
337 :
338 1 : @override
339 1 : InterceptorSuccessCallback? get _onResponse => __onResponse;
340 : }
341 :
342 : /// Interceptors are a queue, and you can add any number of interceptors,
343 : /// All interceptors will be executed in first in first out order.
344 : class Interceptors extends ListMixin<Interceptor> {
345 : final _list = <Interceptor>[];
346 : final Lock _requestLock = Lock();
347 : final Lock _responseLock = Lock();
348 : final Lock _errorLock = Lock();
349 :
350 8 : @Deprecated(
351 : 'Will delete in v5.0. Use `QueuedInterceptor` instead, more detail see'
352 : ' https://github.com/flutterchina/dio/issues/1308')
353 8 : Lock get requestLock => _requestLock;
354 7 : @Deprecated(
355 : 'Will delete in v5.0. Use `QueuedInterceptor` instead, more detail see'
356 : ' https://github.com/flutterchina/dio/issues/1308')
357 7 : Lock get responseLock => _responseLock;
358 2 : @Deprecated(
359 : 'Will delete in v5.0. Use `QueuedInterceptor` instead, more detail see'
360 : ' https://github.com/flutterchina/dio/issues/1308')
361 2 : Lock get errorLock => _errorLock;
362 :
363 : @override
364 : int length = 0;
365 :
366 2 : @override
367 : Interceptor operator [](int index) {
368 4 : return _list[index];
369 : }
370 :
371 2 : @override
372 : void operator []=(int index, value) {
373 6 : if (_list.length == index) {
374 4 : _list.add(value);
375 : } else {
376 2 : _list[index] = value;
377 : }
378 : }
379 : }
380 :
381 : class _InterceptorParams<T, V> {
382 1 : _InterceptorParams(this.data, this.handler);
383 :
384 : T data;
385 : V handler;
386 : }
387 :
388 : class _TaskQueue {
389 : final queue = Queue<_InterceptorParams>();
390 : bool processing = false;
391 : }
392 :
393 : /// Serialize the request/response/error before they enter the interceptor.
394 : ///
395 : /// If there are multiple concurrent requests, the request is added to a queue before
396 : /// entering the interceptor. Only one request at a time enters the interceptor, and
397 : /// after that request is processed by the interceptor, the next request will enter
398 : /// the interceptor.
399 : class QueuedInterceptor extends Interceptor {
400 : _TaskQueue _requestQueue = _TaskQueue();
401 : _TaskQueue _responseQueue = _TaskQueue();
402 : _TaskQueue _errorQueue = _TaskQueue();
403 :
404 1 : void _handleRequest(
405 : RequestOptions options, RequestInterceptorHandler handler) {
406 3 : _handleQueue(_requestQueue, options, handler, onRequest);
407 : }
408 :
409 1 : void _handleResponse(Response response, ResponseInterceptorHandler handler) {
410 3 : _handleQueue(_responseQueue, response, handler, onResponse);
411 : }
412 :
413 1 : void _handleError(DioError err, ErrorInterceptorHandler handler) {
414 3 : _handleQueue(_errorQueue, err, handler, onError);
415 : }
416 :
417 1 : void _handleQueue<T, V extends _BaseHandler>(
418 : _TaskQueue taskQueue,
419 : T data,
420 : V handler,
421 : callback,
422 : ) {
423 1 : var task = _InterceptorParams<T, V>(data, handler);
424 2 : task.handler._processNextInQueue =
425 1 : _processNextTaskInQueueCallback(taskQueue, callback);
426 2 : taskQueue.queue.add(task);
427 1 : if (!taskQueue.processing) {
428 1 : taskQueue.processing = true;
429 2 : final _task = taskQueue.queue.removeFirst();
430 : try {
431 3 : callback(_task.data, _task.handler);
432 : } catch (e) {
433 0 : _task.handler._processNextInQueue();
434 : }
435 : }
436 : }
437 : }
438 :
439 1 : void Function() _processNextTaskInQueueCallback(_TaskQueue taskQueue, cb) {
440 1 : return () {
441 2 : if (taskQueue.queue.isNotEmpty) {
442 2 : final next = taskQueue.queue.removeFirst();
443 2 : assert(next.handler._processNextInQueue != null);
444 3 : cb(next.data, next.handler);
445 : } else {
446 1 : taskQueue.processing = false;
447 : }
448 : };
449 : }
450 :
451 : /// [QueuedInterceptorsWrapper] is a helper class, which is used to conveniently
452 : /// create QueuedInterceptor(s).
453 : /// See also:
454 : /// - [Interceptor]
455 : /// - [InterceptorsWrapper]
456 : /// - [QueuedInterceptors]
457 : class QueuedInterceptorsWrapper extends QueuedInterceptor
458 : with _InterceptorWrapperMixin {
459 : InterceptorSendCallback? __onRequest;
460 :
461 : InterceptorSuccessCallback? __onResponse;
462 :
463 : InterceptorErrorCallback? __onError;
464 :
465 1 : QueuedInterceptorsWrapper({
466 : InterceptorSendCallback? onRequest,
467 : InterceptorSuccessCallback? onResponse,
468 : InterceptorErrorCallback? onError,
469 : }) : __onRequest = onRequest,
470 : __onResponse = onResponse,
471 : __onError = onError;
472 :
473 1 : @override
474 1 : InterceptorErrorCallback? get _onError => __onError;
475 :
476 1 : @override
477 1 : InterceptorSendCallback? get _onRequest => __onRequest;
478 :
479 1 : @override
480 1 : InterceptorSuccessCallback? get _onResponse => __onResponse;
481 : }
|