Line data Source code
1 : import 'dart:async';
2 : import 'package:dio/dio.dart';
3 : import 'package:test/test.dart';
4 : import 'mock_adapter.dart';
5 : import 'echo_adapter.dart';
6 :
7 : class MyInterceptor extends Interceptor {
8 : int requestCount = 0;
9 :
10 1 : @override
11 : void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
12 2 : requestCount++;
13 1 : return super.onRequest(options, handler);
14 : }
15 : }
16 :
17 0 : void main() {
18 2 : group('#test Request Interceptor', () {
19 : Dio dio;
20 :
21 2 : test('#test interceptor chain', () async {
22 1 : dio = Dio();
23 2 : dio.options.baseUrl = EchoAdapter.mockBase;
24 2 : dio.httpClientAdapter = EchoAdapter();
25 1 : dio.interceptors
26 2 : ..add(InterceptorsWrapper(
27 1 : onRequest: (reqOpt, handler) {
28 1 : switch (reqOpt.path) {
29 1 : case '/resolve':
30 2 : handler.resolve(Response(requestOptions: reqOpt, data: 1));
31 : break;
32 1 : case '/resolve-next':
33 1 : handler.resolve(
34 1 : Response(requestOptions: reqOpt, data: 2),
35 : true,
36 : );
37 : break;
38 1 : case '/resolve-next/always':
39 1 : handler.resolve(
40 1 : Response(requestOptions: reqOpt, data: 2),
41 : true,
42 : );
43 : break;
44 1 : case '/resolve-next/reject':
45 1 : handler.resolve(
46 1 : Response(requestOptions: reqOpt, data: 2),
47 : true,
48 : );
49 : break;
50 1 : case '/resolve-next/reject-next':
51 1 : handler.resolve(
52 1 : Response(requestOptions: reqOpt, data: 2),
53 : true,
54 : );
55 : break;
56 1 : case '/reject':
57 2 : handler.reject(DioError(requestOptions: reqOpt, error: 3));
58 : break;
59 1 : case '/reject-next':
60 1 : handler.reject(
61 1 : DioError(requestOptions: reqOpt, error: 4),
62 : true,
63 : );
64 : break;
65 1 : case '/reject-next/reject':
66 1 : handler.reject(
67 1 : DioError(requestOptions: reqOpt, error: 5),
68 : true,
69 : );
70 : break;
71 1 : case '/reject-next-response':
72 1 : handler.reject(
73 1 : DioError(requestOptions: reqOpt, error: 5),
74 : true,
75 : );
76 : break;
77 : default:
78 1 : handler.next(reqOpt); //continue
79 : }
80 : },
81 1 : onResponse: (response, ResponseInterceptorHandler handler) {
82 1 : var options = response.requestOptions;
83 1 : switch (options.path) {
84 1 : case '/resolve':
85 : throw 'unexpected1';
86 1 : case '/resolve-next':
87 2 : response.data++;
88 1 : handler.resolve(response); //3
89 : break;
90 1 : case '/resolve-next/always':
91 2 : response.data++;
92 1 : handler.next(response); //3
93 : break;
94 1 : case '/resolve-next/reject':
95 2 : handler.reject(DioError(
96 : requestOptions: options,
97 : error: '/resolve-next/reject',
98 : ));
99 : break;
100 1 : case '/resolve-next/reject-next':
101 1 : handler.reject(
102 1 : DioError(requestOptions: options, error: ''),
103 : true,
104 : );
105 : break;
106 : default:
107 1 : handler.next(response); //continue
108 : }
109 : },
110 1 : onError: (err, handler) {
111 3 : if (err.requestOptions.path == '/reject-next-response') {
112 2 : handler.resolve(Response(
113 1 : requestOptions: err.requestOptions,
114 : data: 100,
115 : ));
116 3 : } else if (err.requestOptions.path == '/resolve-next/reject-next') {
117 1 : err.error = 1;
118 1 : handler.next(err);
119 : } else {
120 3 : if (err.requestOptions.path == '/reject-next/reject') {
121 1 : handler.reject(err);
122 : } else {
123 2 : err.error++;
124 1 : handler.next(err);
125 : }
126 : }
127 : },
128 : ))
129 2 : ..add(InterceptorsWrapper(
130 2 : onRequest: (options, handler) => handler.next(options),
131 1 : onResponse: (response, handler) {
132 1 : var options = response.requestOptions;
133 1 : switch (options.path) {
134 1 : case '/resolve-next/always':
135 2 : response.data++;
136 1 : handler.next(response); //4
137 : break;
138 : default:
139 1 : handler.next(response); //continue
140 : }
141 : },
142 1 : onError: (err, handler) {
143 3 : if (err.requestOptions.path == '/resolve-next/reject-next') {
144 2 : err.error++;
145 1 : handler.next(err);
146 : } else {
147 2 : err.error++;
148 1 : handler.next(err);
149 : }
150 : },
151 : ));
152 2 : var response = await dio.get('/resolve');
153 2 : assert(response.data == 1);
154 2 : response = await dio.get('/resolve-next');
155 :
156 2 : assert(response.data == 3);
157 :
158 2 : response = await dio.get('/resolve-next/always');
159 2 : assert(response.data == 4);
160 :
161 2 : response = await dio.post('/post', data: 'xxx');
162 2 : assert(response.data == 'xxx');
163 :
164 2 : response = await dio.get('/reject-next-response');
165 2 : assert(response.data == 100);
166 :
167 1 : expect(
168 4 : dio.get('/reject').catchError((e) => throw e.error as num),
169 1 : throwsA(3),
170 : );
171 :
172 1 : expect(
173 4 : dio.get('/reject-next').catchError((e) => throw e.error as num),
174 1 : throwsA(6),
175 : );
176 :
177 1 : expect(
178 4 : dio.get('/reject-next/reject').catchError((e) => throw e.error as num),
179 1 : throwsA(5),
180 : );
181 :
182 1 : expect(
183 : dio
184 1 : .get('/resolve-next/reject')
185 3 : .catchError((e) => throw e.error as Object),
186 1 : throwsA('/resolve-next/reject'),
187 : );
188 :
189 1 : expect(
190 : dio
191 1 : .get('/resolve-next/reject-next')
192 3 : .catchError((e) => throw e.error as num),
193 1 : throwsA(2),
194 : );
195 : });
196 :
197 2 : test('unexpected error', () async {
198 1 : var dio = Dio();
199 2 : dio.options.baseUrl = EchoAdapter.mockBase;
200 2 : dio.httpClientAdapter = EchoAdapter();
201 2 : dio.interceptors.add(
202 1 : InterceptorsWrapper(
203 1 : onRequest: (reqOpt, handler) {
204 2 : if (reqOpt.path == '/error') {
205 : throw 'unexpected';
206 : }
207 2 : handler.next(reqOpt.copyWith(path: '/xxx'));
208 : },
209 1 : onError: (err, handler) {
210 1 : err.error = 'unexpected error';
211 1 : handler.next(err);
212 : },
213 : ),
214 : );
215 :
216 1 : expect(
217 4 : dio.get('/error').catchError((e) => throw e.error as String),
218 1 : throwsA('unexpected error'),
219 : );
220 :
221 1 : expect(
222 5 : dio.get('/').then((e) => throw e.requestOptions.path),
223 1 : throwsA('/xxx'),
224 : );
225 : });
226 :
227 2 : test('#test request interceptor', () async {
228 1 : dio = Dio();
229 2 : dio.options.baseUrl = MockAdapter.mockBase;
230 2 : dio.httpClientAdapter = MockAdapter();
231 4 : dio.interceptors.add(InterceptorsWrapper(onRequest: (
232 : RequestOptions options,
233 : RequestInterceptorHandler handler,
234 : ) {
235 1 : switch (options.path) {
236 1 : case '/fakepath1':
237 1 : handler.resolve(
238 1 : Response(
239 : requestOptions: options,
240 : data: 'fake data',
241 : ),
242 : );
243 : break;
244 1 : case '/fakepath2':
245 : dio
246 1 : .get('/test')
247 2 : .then(handler.resolve)
248 1 : .catchError((e) => handler.reject(e as DioError));
249 : break;
250 1 : case '/fakepath3':
251 2 : handler.reject(DioError(
252 : requestOptions: options,
253 : error: 'test error',
254 : ));
255 : break;
256 1 : case '/fakepath4':
257 2 : handler.reject(DioError(
258 : requestOptions: options,
259 : error: 'test error',
260 : ));
261 : break;
262 1 : case '/test?tag=1':
263 : {
264 3 : dio.get('/token').then((response) {
265 5 : options.headers['token'] = response.data['data']['token'];
266 1 : handler.next(options);
267 : });
268 : break;
269 : }
270 : default:
271 1 : handler.next(options); //continue
272 : }
273 : }));
274 :
275 2 : var response = await dio.get('/fakepath1');
276 2 : expect(response.data, 'fake data');
277 :
278 2 : response = await dio.get('/fakepath2');
279 3 : expect(response.data['errCode'], 0);
280 :
281 1 : expect(
282 4 : dio.get('/fakepath3').catchError((e) => throw (e as DioError).message),
283 1 : throwsA('test error'),
284 : );
285 1 : expect(
286 4 : dio.get('/fakepath4').catchError((e) => throw (e as DioError).message),
287 1 : throwsA('test error'),
288 : );
289 :
290 2 : response = await dio.get('/test');
291 3 : expect(response.data['errCode'], 0);
292 2 : response = await dio.get('/test?tag=1');
293 3 : expect(response.data['errCode'], 0);
294 : });
295 : });
296 :
297 2 : group('#test response interceptor', () {
298 : Dio dio;
299 2 : test('#test Response Interceptor', () async {
300 : const URL_NOT_FIND = '/404/';
301 : const URL_NOT_FIND_1 = URL_NOT_FIND + '1';
302 : const URL_NOT_FIND_2 = URL_NOT_FIND + '2';
303 : const URL_NOT_FIND_3 = URL_NOT_FIND + '3';
304 :
305 1 : dio = Dio();
306 2 : dio.httpClientAdapter = MockAdapter();
307 2 : dio.options.baseUrl = MockAdapter.mockBase;
308 :
309 3 : dio.interceptors.add(InterceptorsWrapper(
310 1 : onResponse: (response, handler) {
311 3 : response.data = response.data['data'];
312 1 : handler.next(response);
313 : },
314 1 : onError: (DioError e, ErrorInterceptorHandler handler) {
315 2 : if (e.response?.requestOptions != null) {
316 3 : switch (e.response!.requestOptions.path) {
317 1 : case URL_NOT_FIND:
318 1 : return handler.next(e);
319 1 : case URL_NOT_FIND_1:
320 1 : return handler.resolve(
321 1 : Response(
322 1 : requestOptions: e.requestOptions,
323 : data: 'fake data',
324 : ),
325 : );
326 1 : case URL_NOT_FIND_2:
327 1 : return handler.resolve(
328 1 : Response(
329 : data: 'fake data',
330 1 : requestOptions: e.requestOptions,
331 : ),
332 : );
333 1 : case URL_NOT_FIND_3:
334 1 : return handler.next(
335 4 : e..error = 'custom error info [${e.response!.statusCode}]',
336 : );
337 : }
338 : }
339 0 : handler.next(e);
340 : },
341 : ));
342 2 : var response = await dio.get('/test');
343 3 : expect(response.data['path'], '/test');
344 1 : expect(
345 : dio
346 1 : .get(URL_NOT_FIND)
347 4 : .catchError((e) => throw (e as DioError).response!.statusCode!),
348 1 : throwsA(404),
349 : );
350 3 : response = await dio.get(URL_NOT_FIND + '1');
351 2 : expect(response.data, 'fake data');
352 3 : response = await dio.get(URL_NOT_FIND + '2');
353 2 : expect(response.data, 'fake data');
354 1 : expect(
355 : dio
356 2 : .get(URL_NOT_FIND + '3')
357 3 : .catchError((e) => throw (e as DioError).message),
358 1 : throwsA('custom error info [404]'),
359 : );
360 : });
361 2 : test('multi response interceptor', () async {
362 1 : dio = Dio();
363 2 : dio.httpClientAdapter = MockAdapter();
364 2 : dio.options.baseUrl = MockAdapter.mockBase;
365 1 : dio.interceptors
366 2 : ..add(InterceptorsWrapper(
367 1 : onResponse: (resp, handler) {
368 3 : resp.data = resp.data['data'];
369 1 : handler.next(resp);
370 : },
371 : ))
372 2 : ..add(InterceptorsWrapper(
373 1 : onResponse: (resp, handler) {
374 2 : resp.data['extra_1'] = 'extra';
375 1 : handler.next(resp);
376 : },
377 : ))
378 2 : ..add(InterceptorsWrapper(
379 1 : onResponse: (resp, handler) {
380 2 : resp.data['extra_2'] = 'extra';
381 1 : handler.next(resp);
382 : },
383 : ));
384 2 : final resp = await dio.get('/test');
385 3 : expect(resp.data['path'], '/test');
386 3 : expect(resp.data['extra_1'], 'extra');
387 3 : expect(resp.data['extra_2'], 'extra');
388 : });
389 : });
390 2 : group('# test queued interceptors', () {
391 : // test('test request lock', () async {
392 : // String? csrfToken;
393 : // final dio = Dio();
394 : // var tokenRequestCounts = 0;
395 : // // dio instance to request token
396 : // final tokenDio = Dio();
397 : // dio.options.baseUrl = tokenDio.options.baseUrl = MockAdapter.mockBase;
398 : // dio.httpClientAdapter = tokenDio.httpClientAdapter = MockAdapter();
399 : // var myInter = MyInterceptor();
400 : // dio.interceptors.add(myInter);
401 : // dio.interceptors.add(InterceptorsWrapper(
402 : // onRequest: (options, handler) {
403 : // if (csrfToken == null) {
404 : // dio.lock();
405 : // tokenRequestCounts++;
406 : // tokenDio.get('/token').then((d) {
407 : // options.headers['csrfToken'] =
408 : // csrfToken = d.data['data']['token'] as String;
409 : // handler.next(options);
410 : // }).catchError((e) {
411 : // handler.reject(e as DioError, true);
412 : // }).whenComplete(() {
413 : // dio.unlock();
414 : // }); // unlock the dio
415 : // } else {
416 : // options.headers['csrfToken'] = csrfToken;
417 : // handler.next(options);
418 : // }
419 : // },
420 : // ));
421 : //
422 : // var result = 0;
423 : // void _onResult(d) {
424 : // if (tokenRequestCounts > 0) ++result;
425 : // }
426 : //
427 : // await Future.wait([
428 : // dio.get('/test?tag=1').then(_onResult),
429 : // dio.get('/test?tag=2').then(_onResult),
430 : // dio.get('/test?tag=3').then(_onResult)
431 : // ]);
432 : // expect(tokenRequestCounts, 1);
433 : // expect(result, 3);
434 : // assert(myInter.requestCount > 0);
435 : // dio.interceptors[0] = myInter;
436 : // dio.interceptors.clear();
437 : // assert(dio.interceptors.isEmpty == true);
438 : // });
439 2 : test('test queued interceptor for requests ', () async {
440 : String? csrfToken;
441 1 : final dio = Dio();
442 : var tokenRequestCounts = 0;
443 : // dio instance to request token
444 1 : final tokenDio = Dio();
445 4 : dio.options.baseUrl = tokenDio.options.baseUrl = MockAdapter.mockBase;
446 3 : dio.httpClientAdapter = tokenDio.httpClientAdapter = MockAdapter();
447 1 : var myInter = MyInterceptor();
448 2 : dio.interceptors.add(myInter);
449 3 : dio.interceptors.add(QueuedInterceptorsWrapper(
450 1 : onRequest: (options, handler) {
451 : if (csrfToken == null) {
452 1 : tokenRequestCounts++;
453 3 : tokenDio.get('/token').then((d) {
454 2 : options.headers['csrfToken'] =
455 3 : csrfToken = d.data['data']['token'] as String;
456 1 : handler.next(options);
457 1 : }).catchError((e) {
458 0 : handler.reject(e as DioError, true);
459 : }); // unlock the dio
460 : } else {
461 2 : options.headers['csrfToken'] = csrfToken;
462 1 : handler.next(options);
463 : }
464 : },
465 : ));
466 :
467 : var result = 0;
468 1 : void _onResult(d) {
469 2 : if (tokenRequestCounts > 0) ++result;
470 : }
471 :
472 3 : await Future.wait([
473 2 : dio.get('/test?tag=1').then(_onResult),
474 2 : dio.get('/test?tag=2').then(_onResult),
475 2 : dio.get('/test?tag=3').then(_onResult)
476 : ]);
477 1 : expect(tokenRequestCounts, 1);
478 1 : expect(result, 3);
479 2 : assert(myInter.requestCount > 0);
480 2 : dio.interceptors[0] = myInter;
481 2 : dio.interceptors.clear();
482 3 : assert(dio.interceptors.isEmpty == true);
483 : });
484 :
485 2 : test('test queued interceptors for error', () async {
486 : String? csrfToken;
487 1 : final dio = Dio();
488 : var tokenRequestCounts = 0;
489 : // dio instance to request token
490 1 : final tokenDio = Dio();
491 4 : dio.options.baseUrl = tokenDio.options.baseUrl = MockAdapter.mockBase;
492 3 : dio.httpClientAdapter = tokenDio.httpClientAdapter = MockAdapter();
493 2 : dio.interceptors.add(
494 1 : QueuedInterceptorsWrapper(
495 1 : onRequest: (opt, handler) {
496 2 : opt.headers['csrfToken'] = csrfToken;
497 1 : handler.next(opt);
498 : },
499 1 : onError: (error, handler) {
500 : // Assume 401 stands for token expired
501 3 : if (error.response?.statusCode == 401) {
502 2 : final options = error.response!.requestOptions;
503 : // If the token has been updated, repeat directly.
504 3 : if (csrfToken != options.headers['csrfToken']) {
505 2 : options.headers['csrfToken'] = csrfToken;
506 : //repeat
507 : dio
508 1 : .fetch(options)
509 2 : .then(handler.resolve)
510 1 : .catchError((e) => handler.reject(e as DioError));
511 : return;
512 : }
513 : // update token and repeat
514 1 : tokenRequestCounts++;
515 3 : tokenDio.get('/token').then((d) {
516 : //update csrfToken
517 2 : options.headers['csrfToken'] =
518 3 : csrfToken = d.data['data']['token'] as String;
519 2 : }).then((e) {
520 : //repeat
521 : dio
522 1 : .fetch(options)
523 2 : .then(handler.resolve)
524 1 : .catchError((e) => handler.reject(e as DioError));
525 : });
526 : } else {
527 0 : handler.next(error);
528 : }
529 : },
530 : ),
531 : );
532 :
533 : var result = 0;
534 1 : void _onResult(d) {
535 2 : if (tokenRequestCounts > 0) ++result;
536 : }
537 :
538 3 : await Future.wait([
539 2 : dio.get('/test-auth?tag=1').then(_onResult),
540 2 : dio.get('/test-auth?tag=2').then(_onResult),
541 2 : dio.get('/test-auth?tag=3').then(_onResult)
542 : ]);
543 1 : expect(tokenRequestCounts, 1);
544 1 : expect(result, 3);
545 : });
546 : });
547 :
548 : // group('test queued interceptors for error', () {
549 : // // test('test error lock', () async {
550 : // // String? csrfToken;
551 : // // final dio = Dio();
552 : // // var tokenRequestCounts = 0;
553 : // // // dio instance to request token
554 : // // final tokenDio = Dio();
555 : // // dio.options.baseUrl = tokenDio.options.baseUrl = MockAdapter.mockBase;
556 : // // dio.httpClientAdapter = tokenDio.httpClientAdapter = MockAdapter();
557 : // // dio.interceptors.add(
558 : // // InterceptorsWrapper(
559 : // // onRequest: (opt, handler) {
560 : // // opt.headers['csrfToken'] = csrfToken;
561 : // // handler.next(opt);
562 : // // },
563 : // // onError: (error, handler) {
564 : // // // Assume 401 stands for token expired
565 : // // if (error.response?.statusCode == 401) {
566 : // // final options = error.response!.requestOptions;
567 : // // // If the token has been updated, repeat directly.
568 : // // if (csrfToken != options.headers['csrfToken']) {
569 : // // options.headers['csrfToken'] = csrfToken;
570 : // // //repeat
571 : // // dio
572 : // // .fetch(options)
573 : // // .then(handler.resolve)
574 : // // .catchError((e) => handler.reject(e as DioError));
575 : // // return;
576 : // // }
577 : // // // update token and repeat
578 : // // // Lock to block the incoming request until the token updated
579 : // // dio.lock();
580 : // // dio.interceptors.responseLock.lock();
581 : // // dio.interceptors.errorLock.lock();
582 : // // tokenRequestCounts++;
583 : // // tokenDio.get('/token').then((d) {
584 : // // //update csrfToken
585 : // // options.headers['csrfToken'] =
586 : // // csrfToken = d.data['data']['token'] as String;
587 : // // }).whenComplete(() {
588 : // // dio.unlock();
589 : // // dio.interceptors.responseLock.unlock();
590 : // // dio.interceptors.errorLock.unlock();
591 : // // }).then((e) {
592 : // // //repeat
593 : // // dio
594 : // // .fetch(options)
595 : // // .then(handler.resolve)
596 : // // .catchError((e) => handler.reject(e as DioError));
597 : // // });
598 : // // } else {
599 : // // handler.next(error);
600 : // // }
601 : // // },
602 : // // ),
603 : // // );
604 : // //
605 : // // var result = 0;
606 : // // void _onResult(d) {
607 : // // if (tokenRequestCounts > 0) ++result;
608 : // // }
609 : // //
610 : // // await Future.wait([
611 : // // dio.get('/test-auth?tag=1').then(_onResult),
612 : // // dio.get('/test-auth?tag=2').then(_onResult),
613 : // // dio.get('/test-auth?tag=3').then(_onResult)
614 : // // ]);
615 : // // expect(tokenRequestCounts, 1);
616 : // // expect(result, 3);
617 : // // });
618 : //
619 : // test('test queued interceptors for error', () async {
620 : // String? csrfToken;
621 : // final dio = Dio();
622 : // var tokenRequestCounts = 0;
623 : // // dio instance to request token
624 : // final tokenDio = Dio();
625 : // dio.options.baseUrl = tokenDio.options.baseUrl = MockAdapter.mockBase;
626 : // dio.httpClientAdapter = tokenDio.httpClientAdapter = MockAdapter();
627 : // dio.interceptors.add(
628 : // InterceptorsWrapper(
629 : // onRequest: (opt, handler) {
630 : // opt.headers['csrfToken'] = csrfToken;
631 : // handler.next(opt);
632 : // },
633 : // onError: (error, handler) {
634 : // // Assume 401 stands for token expired
635 : // if (error.response?.statusCode == 401) {
636 : // final options = error.response!.requestOptions;
637 : // // If the token has been updated, repeat directly.
638 : // if (csrfToken != options.headers['csrfToken']) {
639 : // options.headers['csrfToken'] = csrfToken;
640 : // //repeat
641 : // dio
642 : // .fetch(options)
643 : // .then(handler.resolve)
644 : // .catchError((e) => handler.reject(e as DioError));
645 : // return;
646 : // }
647 : // // update token and repeat
648 : // // Lock to block the incoming request until the token updated
649 : // dio.lock();
650 : // dio.interceptors.responseLock.lock();
651 : // dio.interceptors.errorLock.lock();
652 : // tokenRequestCounts++;
653 : // tokenDio.get('/token').then((d) {
654 : // //update csrfToken
655 : // options.headers['csrfToken'] =
656 : // csrfToken = d.data['data']['token'] as String;
657 : // }).whenComplete(() {
658 : // dio.unlock();
659 : // dio.interceptors.responseLock.unlock();
660 : // dio.interceptors.errorLock.unlock();
661 : // }).then((e) {
662 : // //repeat
663 : // dio
664 : // .fetch(options)
665 : // .then(handler.resolve)
666 : // .catchError((e) => handler.reject(e as DioError));
667 : // });
668 : // } else {
669 : // handler.next(error);
670 : // }
671 : // },
672 : // ),
673 : // );
674 : //
675 : // var result = 0;
676 : // void _onResult(d) {
677 : // if (tokenRequestCounts > 0) ++result;
678 : // }
679 : //
680 : // await Future.wait([
681 : // dio.get('/test-auth?tag=1').then(_onResult),
682 : // dio.get('/test-auth?tag=2').then(_onResult),
683 : // dio.get('/test-auth?tag=3').then(_onResult)
684 : // ]);
685 : // expect(tokenRequestCounts, 1);
686 : // expect(result, 3);
687 : // });
688 : // });
689 : }
|