tangerine 2.0.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +479 -25
- package/index.d.ts +7 -0
- package/index.js +128 -43
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -398,6 +398,7 @@ Similar to the `options` argument from `new dns.promises.Resolver(options)` invo
|
|
|
398
398
|
| `returnHTTPErrors` | `Boolean` | `false` | Whether to return HTTP errors instead of mapping them to corresponding DNS errors. |
|
|
399
399
|
| `smartRotate` | `Boolean` | `true` | Whether to do smart server rotation if servers fail. |
|
|
400
400
|
| `defaultHTTPErrorMessage` | `String` | `"Unsuccessful HTTP response"` | Default fallback message if `statusCode` returned from HTTP request was not found in [http.STATUS_CODES](https://nodejs.org/api/http.html#httpstatus_codes). |
|
|
401
|
+
| `parallelResolution` | `Boolean` | `false` | Enable parallel resolution for querying DNS servers. |
|
|
401
402
|
|
|
402
403
|
|
|
403
404
|
## Cache
|
|
@@ -533,15 +534,25 @@ We have written extensive benchmarks to show that :tangerine: Tangerine is as fa
|
|
|
533
534
|
|
|
534
535
|
#### Latest Automated Benchmark Results
|
|
535
536
|
|
|
536
|
-
**Last Updated:**
|
|
537
|
+
**Last Updated:** 2026-02-26
|
|
537
538
|
|
|
538
539
|
| Node Version | Platform | Arch | Timestamp |
|
|
539
540
|
| ------------ | -------- | ---- | ------------ |
|
|
540
541
|
| v18.20.8 | linux | x64 | Dec 21, 2025 |
|
|
541
|
-
| v20.19.6 | linux | x64 |
|
|
542
|
+
| v20.19.6 | linux | x64 | Jan 22, 2026 |
|
|
543
|
+
| v20.20.0 | linux | x64 | Feb 25, 2026 |
|
|
542
544
|
| v22.21.1 | linux | x64 | Dec 21, 2025 |
|
|
545
|
+
| v22.22.0 | linux | x64 | Jan 23, 2026 |
|
|
543
546
|
| v24.12.0 | linux | x64 | Dec 21, 2025 |
|
|
547
|
+
| v24.13.0 | linux | x64 | Feb 19, 2026 |
|
|
548
|
+
| v24.13.1 | linux | x64 | Feb 26, 2026 |
|
|
544
549
|
| v25.2.1 | linux | x64 | Dec 21, 2025 |
|
|
550
|
+
| v25.3.0 | linux | x64 | Jan 14, 2026 |
|
|
551
|
+
| v25.4.0 | linux | x64 | Jan 20, 2026 |
|
|
552
|
+
| v25.5.0 | linux | x64 | Jan 27, 2026 |
|
|
553
|
+
| v25.6.0 | linux | x64 | Feb 4, 2026 |
|
|
554
|
+
| v25.6.1 | linux | x64 | Feb 11, 2026 |
|
|
555
|
+
| v25.7.0 | linux | x64 | Feb 25, 2026 |
|
|
545
556
|
|
|
546
557
|
<details>
|
|
547
558
|
<summary>Click to expand detailed benchmark results</summary>
|
|
@@ -597,12 +608,12 @@ Fastest without caching is: dns.promises.reverse without caching
|
|
|
597
608
|
|
|
598
609
|
```text
|
|
599
610
|
Started: lookup
|
|
600
|
-
tangerine.lookup POST with caching using Cloudflare x
|
|
601
|
-
tangerine.lookup POST without caching using Cloudflare x
|
|
602
|
-
tangerine.lookup GET with caching using Cloudflare x
|
|
603
|
-
tangerine.lookup GET without caching using Cloudflare x
|
|
604
|
-
dns.promises.lookup with caching using Cloudflare x
|
|
605
|
-
dns.promises.lookup without caching using Cloudflare x 3,
|
|
611
|
+
tangerine.lookup POST with caching using Cloudflare x 820 ops/sec ±195.45% (88 runs sampled)
|
|
612
|
+
tangerine.lookup POST without caching using Cloudflare x 212 ops/sec ±2.98% (79 runs sampled)
|
|
613
|
+
tangerine.lookup GET with caching using Cloudflare x 290,812 ops/sec ±0.74% (88 runs sampled)
|
|
614
|
+
tangerine.lookup GET without caching using Cloudflare x 260 ops/sec ±2.00% (82 runs sampled)
|
|
615
|
+
dns.promises.lookup with caching using Cloudflare x 8,961,323 ops/sec ±0.78% (86 runs sampled)
|
|
616
|
+
dns.promises.lookup without caching using Cloudflare x 3,143 ops/sec ±0.57% (85 runs sampled)
|
|
606
617
|
Fastest without caching is: dns.promises.lookup without caching using Cloudflare
|
|
607
618
|
```
|
|
608
619
|
|
|
@@ -610,29 +621,67 @@ Fastest without caching is: dns.promises.lookup without caching using Cloudflare
|
|
|
610
621
|
|
|
611
622
|
```text
|
|
612
623
|
Started: resolve
|
|
613
|
-
tangerine.resolve POST with caching using Cloudflare x 1,
|
|
614
|
-
tangerine.resolve POST without caching using Cloudflare x
|
|
615
|
-
tangerine.resolve GET with caching using Cloudflare x 1,
|
|
616
|
-
tangerine.resolve GET without caching using Cloudflare x
|
|
617
|
-
tangerine.resolve POST with caching using Google x 1,
|
|
618
|
-
tangerine.resolve POST without caching using Google x
|
|
619
|
-
tangerine.resolve GET with caching using Google x 1,
|
|
620
|
-
tangerine.resolve GET without caching using Google x
|
|
621
|
-
resolver.resolve with caching using Cloudflare x 7,
|
|
622
|
-
resolver.resolve without caching using Cloudflare x
|
|
623
|
-
Fastest without caching is: tangerine.resolve GET without caching using
|
|
624
|
+
tangerine.resolve POST with caching using Cloudflare x 1,034 ops/sec ±195.81% (89 runs sampled)
|
|
625
|
+
tangerine.resolve POST without caching using Cloudflare x 221 ops/sec ±1.83% (80 runs sampled)
|
|
626
|
+
tangerine.resolve GET with caching using Cloudflare x 1,044,036 ops/sec ±0.36% (89 runs sampled)
|
|
627
|
+
tangerine.resolve GET without caching using Cloudflare x 272 ops/sec ±1.57% (83 runs sampled)
|
|
628
|
+
tangerine.resolve POST with caching using Google x 1,035,748 ops/sec ±0.31% (89 runs sampled)
|
|
629
|
+
tangerine.resolve POST without caching using Google x 236 ops/sec ±0.93% (85 runs sampled)
|
|
630
|
+
tangerine.resolve GET with caching using Google x 1,027,998 ops/sec ±0.25% (89 runs sampled)
|
|
631
|
+
tangerine.resolve GET without caching using Google x 230 ops/sec ±1.04% (83 runs sampled)
|
|
632
|
+
resolver.resolve with caching using Cloudflare x 7,717,855 ops/sec ±0.67% (85 runs sampled)
|
|
633
|
+
resolver.resolve without caching using Cloudflare x 24.10 ops/sec ±180.34% (37 runs sampled)
|
|
634
|
+
Fastest without caching is: tangerine.resolve GET without caching using Cloudflare
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
**reverse:**
|
|
638
|
+
|
|
639
|
+
```text
|
|
640
|
+
spawnSync /bin/sh ETIMEDOUT
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
##### Node.js v20.20.0
|
|
644
|
+
|
|
645
|
+
**lookup:**
|
|
646
|
+
|
|
647
|
+
```text
|
|
648
|
+
Started: lookup
|
|
649
|
+
tangerine.lookup POST with caching using Cloudflare x 732 ops/sec ±195.51% (87 runs sampled)
|
|
650
|
+
tangerine.lookup POST without caching using Cloudflare x 117 ops/sec ±1.61% (79 runs sampled)
|
|
651
|
+
tangerine.lookup GET with caching using Cloudflare x 299,941 ops/sec ±1.15% (89 runs sampled)
|
|
652
|
+
tangerine.lookup GET without caching using Cloudflare x 117 ops/sec ±1.43% (80 runs sampled)
|
|
653
|
+
dns.promises.lookup with caching using Cloudflare x 1,383 ops/sec ±195.97% (87 runs sampled)
|
|
654
|
+
dns.promises.lookup without caching using Cloudflare x 2,374 ops/sec ±0.56% (86 runs sampled)
|
|
655
|
+
Fastest without caching is: dns.promises.lookup without caching using Cloudflare
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
**resolve:**
|
|
659
|
+
|
|
660
|
+
```text
|
|
661
|
+
Started: resolve
|
|
662
|
+
tangerine.resolve POST with caching using Cloudflare x 974 ops/sec ±195.82% (89 runs sampled)
|
|
663
|
+
tangerine.resolve POST without caching using Cloudflare x 130 ops/sec ±1.30% (87 runs sampled)
|
|
664
|
+
tangerine.resolve GET with caching using Cloudflare x 1,049,956 ops/sec ±1.53% (85 runs sampled)
|
|
665
|
+
tangerine.resolve GET without caching using Cloudflare x 117 ops/sec ±1.79% (79 runs sampled)
|
|
666
|
+
tangerine.resolve POST with caching using Google x 1,019,993 ops/sec ±0.48% (88 runs sampled)
|
|
667
|
+
tangerine.resolve POST without caching using Google x 106 ops/sec ±4.78% (83 runs sampled)
|
|
668
|
+
tangerine.resolve GET with caching using Google x 1,030,533 ops/sec ±0.28% (89 runs sampled)
|
|
669
|
+
tangerine.resolve GET without caching using Google x 123 ops/sec ±0.59% (83 runs sampled)
|
|
670
|
+
resolver.resolve with caching using Cloudflare x 7,658,904 ops/sec ±0.60% (86 runs sampled)
|
|
671
|
+
resolver.resolve without caching using Cloudflare x 13.28 ops/sec ±201.81% (11 runs sampled)
|
|
672
|
+
Fastest without caching is: tangerine.resolve POST without caching using Cloudflare
|
|
624
673
|
```
|
|
625
674
|
|
|
626
675
|
**reverse:**
|
|
627
676
|
|
|
628
677
|
```text
|
|
629
678
|
Started: reverse
|
|
630
|
-
tangerine.reverse GET with caching x
|
|
631
|
-
tangerine.reverse GET without caching x
|
|
632
|
-
resolver.reverse with caching x
|
|
633
|
-
resolver.reverse without caching x
|
|
634
|
-
dns.promises.reverse with caching x 7,
|
|
635
|
-
dns.promises.reverse without caching x
|
|
679
|
+
tangerine.reverse GET with caching x 991 ops/sec ±195.36% (87 runs sampled)
|
|
680
|
+
tangerine.reverse GET without caching x 129 ops/sec ±0.76% (86 runs sampled)
|
|
681
|
+
resolver.reverse with caching x 0.10 ops/sec ±0.01% (5 runs sampled)
|
|
682
|
+
resolver.reverse without caching x 2.80 ops/sec ±112.00% (74 runs sampled)
|
|
683
|
+
dns.promises.reverse with caching x 7,904,185 ops/sec ±0.60% (85 runs sampled)
|
|
684
|
+
dns.promises.reverse without caching x 8.68 ops/sec ±202.85% (14 runs sampled)
|
|
636
685
|
Fastest without caching is: tangerine.reverse GET without caching
|
|
637
686
|
```
|
|
638
687
|
|
|
@@ -674,6 +723,51 @@ Fastest without caching is: tangerine.resolve GET without caching using Google
|
|
|
674
723
|
spawnSync /bin/sh ETIMEDOUT
|
|
675
724
|
```
|
|
676
725
|
|
|
726
|
+
##### Node.js v22.22.0
|
|
727
|
+
|
|
728
|
+
**lookup:**
|
|
729
|
+
|
|
730
|
+
```text
|
|
731
|
+
Started: lookup
|
|
732
|
+
tangerine.lookup POST with caching using Cloudflare x 1,163 ops/sec ±195.31% (90 runs sampled)
|
|
733
|
+
tangerine.lookup POST without caching using Cloudflare x 54.38 ops/sec ±5.55% (88 runs sampled)
|
|
734
|
+
tangerine.lookup GET with caching using Cloudflare x 313,634 ops/sec ±0.27% (89 runs sampled)
|
|
735
|
+
tangerine.lookup GET without caching using Cloudflare x 55.58 ops/sec ±6.39% (71 runs sampled)
|
|
736
|
+
dns.promises.lookup with caching using Cloudflare x 9,649,217 ops/sec ±0.65% (88 runs sampled)
|
|
737
|
+
dns.promises.lookup without caching using Cloudflare x 3,178 ops/sec ±0.70% (85 runs sampled)
|
|
738
|
+
Fastest without caching is: dns.promises.lookup without caching using Cloudflare
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
**resolve:**
|
|
742
|
+
|
|
743
|
+
```text
|
|
744
|
+
Started: resolve
|
|
745
|
+
tangerine.resolve POST with caching using Cloudflare x 1,186 ops/sec ±195.79% (89 runs sampled)
|
|
746
|
+
tangerine.resolve POST without caching using Cloudflare x 55.82 ops/sec ±5.02% (84 runs sampled)
|
|
747
|
+
tangerine.resolve GET with caching using Cloudflare x 1,099,153 ops/sec ±0.22% (90 runs sampled)
|
|
748
|
+
tangerine.resolve GET without caching using Cloudflare x 59.25 ops/sec ±5.73% (73 runs sampled)
|
|
749
|
+
tangerine.resolve POST with caching using Google x 1,095 ops/sec ±195.81% (88 runs sampled)
|
|
750
|
+
tangerine.resolve POST without caching using Google x 60.23 ops/sec ±10.05% (80 runs sampled)
|
|
751
|
+
tangerine.resolve GET with caching using Google x 1,107,393 ops/sec ±0.67% (90 runs sampled)
|
|
752
|
+
tangerine.resolve GET without caching using Google x 54.10 ops/sec ±10.64% (74 runs sampled)
|
|
753
|
+
resolver.resolve with caching using Cloudflare x 8,395,785 ops/sec ±0.65% (89 runs sampled)
|
|
754
|
+
resolver.resolve without caching using Cloudflare x 67.84 ops/sec ±0.83% (80 runs sampled)
|
|
755
|
+
Fastest without caching is: resolver.resolve without caching using Cloudflare, tangerine.resolve POST without caching using Google
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
**reverse:**
|
|
759
|
+
|
|
760
|
+
```text
|
|
761
|
+
Started: reverse
|
|
762
|
+
tangerine.reverse GET with caching x 1,114 ops/sec ±195.34% (89 runs sampled)
|
|
763
|
+
tangerine.reverse GET without caching x 55.88 ops/sec ±4.70% (89 runs sampled)
|
|
764
|
+
resolver.reverse with caching x 8,531,724 ops/sec ±0.82% (84 runs sampled)
|
|
765
|
+
resolver.reverse without caching x 68.04 ops/sec ±0.73% (81 runs sampled)
|
|
766
|
+
dns.promises.reverse with caching x 8,533,071 ops/sec ±0.84% (81 runs sampled)
|
|
767
|
+
dns.promises.reverse without caching x 67.08 ops/sec ±0.82% (79 runs sampled)
|
|
768
|
+
Fastest without caching is: resolver.reverse without caching
|
|
769
|
+
```
|
|
770
|
+
|
|
677
771
|
##### Node.js v24.12.0
|
|
678
772
|
|
|
679
773
|
**lookup:**
|
|
@@ -712,6 +806,96 @@ Fastest without caching is: tangerine.resolve GET without caching using Google
|
|
|
712
806
|
spawnSync /bin/sh ETIMEDOUT
|
|
713
807
|
```
|
|
714
808
|
|
|
809
|
+
##### Node.js v24.13.0
|
|
810
|
+
|
|
811
|
+
**lookup:**
|
|
812
|
+
|
|
813
|
+
```text
|
|
814
|
+
Started: lookup
|
|
815
|
+
tangerine.lookup POST with caching using Cloudflare x 330,489 ops/sec ±1.61% (85 runs sampled)
|
|
816
|
+
tangerine.lookup POST without caching using Cloudflare x 248 ops/sec ±1.98% (80 runs sampled)
|
|
817
|
+
tangerine.lookup GET with caching using Cloudflare x 321,967 ops/sec ±0.26% (90 runs sampled)
|
|
818
|
+
tangerine.lookup GET without caching using Cloudflare x 224 ops/sec ±1.85% (76 runs sampled)
|
|
819
|
+
dns.promises.lookup with caching using Cloudflare x 734 ops/sec ±195.99% (87 runs sampled)
|
|
820
|
+
dns.promises.lookup without caching using Cloudflare x 2,207 ops/sec ±0.51% (85 runs sampled)
|
|
821
|
+
Fastest without caching is: dns.promises.lookup without caching using Cloudflare
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
**resolve:**
|
|
825
|
+
|
|
826
|
+
```text
|
|
827
|
+
Started: resolve
|
|
828
|
+
tangerine.resolve POST with caching using Cloudflare x 1,158,368 ops/sec ±0.28% (90 runs sampled)
|
|
829
|
+
tangerine.resolve POST without caching using Cloudflare x 246 ops/sec ±1.93% (80 runs sampled)
|
|
830
|
+
tangerine.resolve GET with caching using Cloudflare x 1,114,486 ops/sec ±0.29% (88 runs sampled)
|
|
831
|
+
tangerine.resolve GET without caching using Cloudflare x 252 ops/sec ±1.20% (85 runs sampled)
|
|
832
|
+
tangerine.resolve POST with caching using Google x 1,134,382 ops/sec ±0.24% (88 runs sampled)
|
|
833
|
+
tangerine.resolve POST without caching using Google x 168 ops/sec ±36.15% (70 runs sampled)
|
|
834
|
+
tangerine.resolve GET with caching using Google x 1,139,056 ops/sec ±0.61% (90 runs sampled)
|
|
835
|
+
tangerine.resolve GET without caching using Google x 297 ops/sec ±2.73% (75 runs sampled)
|
|
836
|
+
resolver.resolve with caching using Cloudflare x 8,289,380 ops/sec ±7.32% (88 runs sampled)
|
|
837
|
+
resolver.resolve without caching using Cloudflare x 316 ops/sec ±1.92% (79 runs sampled)
|
|
838
|
+
Fastest without caching is: resolver.resolve without caching using Cloudflare
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
**reverse:**
|
|
842
|
+
|
|
843
|
+
```text
|
|
844
|
+
Started: reverse
|
|
845
|
+
tangerine.reverse GET with caching x 339,770 ops/sec ±0.42% (90 runs sampled)
|
|
846
|
+
tangerine.reverse GET without caching x 240 ops/sec ±2.18% (77 runs sampled)
|
|
847
|
+
resolver.reverse with caching x 8,523,002 ops/sec ±0.83% (87 runs sampled)
|
|
848
|
+
resolver.reverse without caching x 15.57 ops/sec ±194.42% (31 runs sampled)
|
|
849
|
+
dns.promises.reverse with caching x 8,765,472 ops/sec ±1.03% (82 runs sampled)
|
|
850
|
+
dns.promises.reverse without caching x 5.51 ops/sec ±179.54% (80 runs sampled)
|
|
851
|
+
Fastest without caching is: tangerine.reverse GET without caching
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
##### Node.js v24.13.1
|
|
855
|
+
|
|
856
|
+
**lookup:**
|
|
857
|
+
|
|
858
|
+
```text
|
|
859
|
+
Started: lookup
|
|
860
|
+
tangerine.lookup POST with caching using Cloudflare x 328,178 ops/sec ±1.46% (90 runs sampled)
|
|
861
|
+
tangerine.lookup POST without caching using Cloudflare x 263 ops/sec ±1.89% (78 runs sampled)
|
|
862
|
+
tangerine.lookup GET with caching using Cloudflare x 315,952 ops/sec ±0.27% (90 runs sampled)
|
|
863
|
+
tangerine.lookup GET without caching using Cloudflare x 227 ops/sec ±3.20% (78 runs sampled)
|
|
864
|
+
dns.promises.lookup with caching using Cloudflare x 1,047 ops/sec ±195.98% (80 runs sampled)
|
|
865
|
+
dns.promises.lookup without caching using Cloudflare x 2,273 ops/sec ±0.36% (86 runs sampled)
|
|
866
|
+
Fastest without caching is: dns.promises.lookup without caching using Cloudflare
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
**resolve:**
|
|
870
|
+
|
|
871
|
+
```text
|
|
872
|
+
Started: resolve
|
|
873
|
+
tangerine.resolve POST with caching using Cloudflare x 1,146,234 ops/sec ±0.38% (88 runs sampled)
|
|
874
|
+
tangerine.resolve POST without caching using Cloudflare x 228 ops/sec ±1.86% (83 runs sampled)
|
|
875
|
+
tangerine.resolve GET with caching using Cloudflare x 1,121,526 ops/sec ±0.51% (87 runs sampled)
|
|
876
|
+
tangerine.resolve GET without caching using Cloudflare x 293 ops/sec ±1.77% (77 runs sampled)
|
|
877
|
+
tangerine.resolve POST with caching using Google x 1,116,293 ops/sec ±0.94% (87 runs sampled)
|
|
878
|
+
tangerine.resolve POST without caching using Google x 262 ops/sec ±4.45% (83 runs sampled)
|
|
879
|
+
tangerine.resolve GET with caching using Google x 1,127,465 ops/sec ±0.31% (90 runs sampled)
|
|
880
|
+
tangerine.resolve GET without caching using Google x 265 ops/sec ±1.09% (83 runs sampled)
|
|
881
|
+
resolver.resolve with caching using Cloudflare x 8,518,907 ops/sec ±1.01% (86 runs sampled)
|
|
882
|
+
resolver.resolve without caching using Cloudflare x 82.99 ops/sec ±142.66% (36 runs sampled)
|
|
883
|
+
Fastest without caching is: tangerine.resolve GET without caching using Cloudflare
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
**reverse:**
|
|
887
|
+
|
|
888
|
+
```text
|
|
889
|
+
Started: reverse
|
|
890
|
+
tangerine.reverse GET with caching x 337,325 ops/sec ±0.52% (89 runs sampled)
|
|
891
|
+
tangerine.reverse GET without caching x 233 ops/sec ±2.59% (79 runs sampled)
|
|
892
|
+
resolver.reverse with caching x 17.19 ops/sec ±196.00% (86 runs sampled)
|
|
893
|
+
resolver.reverse without caching x 6.84 ops/sec ±154.89% (71 runs sampled)
|
|
894
|
+
dns.promises.reverse with caching x 8,315,699 ops/sec ±1.33% (78 runs sampled)
|
|
895
|
+
dns.promises.reverse without caching x 0.77 ops/sec ±164.54% (37 runs sampled)
|
|
896
|
+
Fastest without caching is: tangerine.reverse GET without caching
|
|
897
|
+
```
|
|
898
|
+
|
|
715
899
|
##### Node.js v25.2.1
|
|
716
900
|
|
|
717
901
|
**lookup:**
|
|
@@ -757,6 +941,276 @@ dns.promises.reverse without caching x 5.11 ops/sec ±189.52% (76 runs sampled)
|
|
|
757
941
|
Fastest without caching is: resolver.reverse without caching, dns.promises.reverse without caching
|
|
758
942
|
```
|
|
759
943
|
|
|
944
|
+
##### Node.js v25.3.0
|
|
945
|
+
|
|
946
|
+
**lookup:**
|
|
947
|
+
|
|
948
|
+
```text
|
|
949
|
+
Started: lookup
|
|
950
|
+
tangerine.lookup POST with caching using Cloudflare x 1,178 ops/sec ±195.33% (88 runs sampled)
|
|
951
|
+
tangerine.lookup POST without caching using Cloudflare x 86.96 ops/sec ±1.37% (82 runs sampled)
|
|
952
|
+
tangerine.lookup GET with caching using Cloudflare x 340,510 ops/sec ±0.25% (90 runs sampled)
|
|
953
|
+
tangerine.lookup GET without caching using Cloudflare x 89.52 ops/sec ±1.42% (84 runs sampled)
|
|
954
|
+
dns.promises.lookup with caching using Cloudflare x 1,352 ops/sec ±195.97% (88 runs sampled)
|
|
955
|
+
dns.promises.lookup without caching using Cloudflare x 3,261 ops/sec ±0.86% (83 runs sampled)
|
|
956
|
+
Fastest without caching is: dns.promises.lookup without caching using Cloudflare
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
**resolve:**
|
|
960
|
+
|
|
961
|
+
```text
|
|
962
|
+
Started: resolve
|
|
963
|
+
tangerine.resolve POST with caching using Cloudflare x 1,099 ops/sec ±195.82% (89 runs sampled)
|
|
964
|
+
tangerine.resolve POST without caching using Cloudflare x 88.57 ops/sec ±0.78% (83 runs sampled)
|
|
965
|
+
tangerine.resolve GET with caching using Cloudflare x 1,182,619 ops/sec ±0.51% (91 runs sampled)
|
|
966
|
+
tangerine.resolve GET without caching using Cloudflare x 89.35 ops/sec ±9.03% (88 runs sampled)
|
|
967
|
+
tangerine.resolve POST with caching using Google x 1,174,915 ops/sec ±2.35% (89 runs sampled)
|
|
968
|
+
tangerine.resolve POST without caching using Google x 129 ops/sec ±9.63% (75 runs sampled)
|
|
969
|
+
tangerine.resolve GET with caching using Google x 1,187,141 ops/sec ±0.30% (89 runs sampled)
|
|
970
|
+
tangerine.resolve GET without caching using Google x 195 ops/sec ±0.85% (84 runs sampled)
|
|
971
|
+
resolver.resolve with caching using Cloudflare x 8,288,775 ops/sec ±10.11% (89 runs sampled)
|
|
972
|
+
resolver.resolve without caching using Cloudflare x 105 ops/sec ±0.63% (82 runs sampled)
|
|
973
|
+
Fastest without caching is: tangerine.resolve GET without caching using Google
|
|
974
|
+
```
|
|
975
|
+
|
|
976
|
+
**reverse:**
|
|
977
|
+
|
|
978
|
+
```text
|
|
979
|
+
Started: reverse
|
|
980
|
+
tangerine.reverse GET with caching x 1,146 ops/sec ±195.30% (90 runs sampled)
|
|
981
|
+
tangerine.reverse GET without caching x 88.05 ops/sec ±0.92% (83 runs sampled)
|
|
982
|
+
resolver.reverse with caching x 8,812,418 ops/sec ±0.46% (85 runs sampled)
|
|
983
|
+
resolver.reverse without caching x 3.08 ops/sec ±203.97% (19 runs sampled)
|
|
984
|
+
dns.promises.reverse with caching x 8,647,756 ops/sec ±2.62% (87 runs sampled)
|
|
985
|
+
dns.promises.reverse without caching x 0.09 ops/sec ±139.17% (6 runs sampled)
|
|
986
|
+
Fastest without caching is: tangerine.reverse GET without caching
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
##### Node.js v25.4.0
|
|
990
|
+
|
|
991
|
+
**lookup:**
|
|
992
|
+
|
|
993
|
+
```text
|
|
994
|
+
Started: lookup
|
|
995
|
+
tangerine.lookup POST with caching using Cloudflare x 338,506 ops/sec ±2.96% (86 runs sampled)
|
|
996
|
+
tangerine.lookup POST without caching using Cloudflare x 252 ops/sec ±1.53% (83 runs sampled)
|
|
997
|
+
tangerine.lookup GET with caching using Cloudflare x 328,384 ops/sec ±0.24% (89 runs sampled)
|
|
998
|
+
tangerine.lookup GET without caching using Cloudflare x 256 ops/sec ±2.15% (80 runs sampled)
|
|
999
|
+
dns.promises.lookup with caching using Cloudflare x 10,091,995 ops/sec ±0.78% (85 runs sampled)
|
|
1000
|
+
dns.promises.lookup without caching using Cloudflare x 3,114 ops/sec ±0.68% (84 runs sampled)
|
|
1001
|
+
Fastest without caching is: dns.promises.lookup without caching using Cloudflare
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
**resolve:**
|
|
1005
|
+
|
|
1006
|
+
```text
|
|
1007
|
+
Started: resolve
|
|
1008
|
+
tangerine.resolve POST with caching using Cloudflare x 1,178,120 ops/sec ±0.63% (89 runs sampled)
|
|
1009
|
+
tangerine.resolve POST without caching using Cloudflare x 268 ops/sec ±1.42% (84 runs sampled)
|
|
1010
|
+
tangerine.resolve GET with caching using Cloudflare x 1,158,024 ops/sec ±0.37% (88 runs sampled)
|
|
1011
|
+
tangerine.resolve GET without caching using Cloudflare x 263 ops/sec ±1.56% (82 runs sampled)
|
|
1012
|
+
tangerine.resolve POST with caching using Google x 1,126,115 ops/sec ±2.39% (89 runs sampled)
|
|
1013
|
+
tangerine.resolve POST without caching using Google x 203 ops/sec ±1.10% (85 runs sampled)
|
|
1014
|
+
tangerine.resolve GET with caching using Google x 1,124,263 ops/sec ±3.04% (88 runs sampled)
|
|
1015
|
+
tangerine.resolve GET without caching using Google x 231 ops/sec ±0.71% (85 runs sampled)
|
|
1016
|
+
resolver.resolve with caching using Cloudflare x 8,795,845 ops/sec ±0.45% (87 runs sampled)
|
|
1017
|
+
resolver.resolve without caching using Cloudflare x 299 ops/sec ±1.31% (69 runs sampled)
|
|
1018
|
+
Fastest without caching is: resolver.resolve without caching using Cloudflare
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
**reverse:**
|
|
1022
|
+
|
|
1023
|
+
```text
|
|
1024
|
+
Started: reverse
|
|
1025
|
+
tangerine.reverse GET with caching x 346,432 ops/sec ±0.67% (87 runs sampled)
|
|
1026
|
+
tangerine.reverse GET without caching x 257 ops/sec ±1.24% (83 runs sampled)
|
|
1027
|
+
resolver.reverse with caching x 8,836,675 ops/sec ±0.85% (83 runs sampled)
|
|
1028
|
+
resolver.reverse without caching x 11.00 ops/sec ±189.21% (38 runs sampled)
|
|
1029
|
+
dns.promises.reverse with caching x 8,483,041 ops/sec ±1.55% (84 runs sampled)
|
|
1030
|
+
dns.promises.reverse without caching x 1.79 ops/sec ±162.11% (82 runs sampled)
|
|
1031
|
+
Fastest without caching is: tangerine.reverse GET without caching
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
##### Node.js v25.5.0
|
|
1035
|
+
|
|
1036
|
+
**lookup:**
|
|
1037
|
+
|
|
1038
|
+
```text
|
|
1039
|
+
Started: lookup
|
|
1040
|
+
tangerine.lookup POST with caching using Cloudflare x 336,308 ops/sec ±3.97% (90 runs sampled)
|
|
1041
|
+
tangerine.lookup POST without caching using Cloudflare x 142 ops/sec ±24.14% (42 runs sampled)
|
|
1042
|
+
tangerine.lookup GET with caching using Cloudflare x 331,518 ops/sec ±0.25% (90 runs sampled)
|
|
1043
|
+
tangerine.lookup GET without caching using Cloudflare x 317 ops/sec ±5.55% (84 runs sampled)
|
|
1044
|
+
dns.promises.lookup with caching using Cloudflare x 10,144,557 ops/sec ±0.80% (85 runs sampled)
|
|
1045
|
+
dns.promises.lookup without caching using Cloudflare x 3,269 ops/sec ±0.69% (84 runs sampled)
|
|
1046
|
+
Fastest without caching is: dns.promises.lookup without caching using Cloudflare
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
**resolve:**
|
|
1050
|
+
|
|
1051
|
+
```text
|
|
1052
|
+
Started: resolve
|
|
1053
|
+
tangerine.resolve POST with caching using Cloudflare x 1,173,581 ops/sec ±0.26% (90 runs sampled)
|
|
1054
|
+
tangerine.resolve POST without caching using Cloudflare x 336 ops/sec ±1.29% (83 runs sampled)
|
|
1055
|
+
tangerine.resolve GET with caching using Cloudflare x 1,154,032 ops/sec ±0.25% (90 runs sampled)
|
|
1056
|
+
tangerine.resolve GET without caching using Cloudflare x 211 ops/sec ±21.96% (60 runs sampled)
|
|
1057
|
+
tangerine.resolve POST with caching using Google x 1,143,112 ops/sec ±2.40% (87 runs sampled)
|
|
1058
|
+
tangerine.resolve POST without caching using Google x 247 ops/sec ±18.82% (74 runs sampled)
|
|
1059
|
+
tangerine.resolve GET with caching using Google x 1,169,919 ops/sec ±0.28% (90 runs sampled)
|
|
1060
|
+
tangerine.resolve GET without caching using Google x 312 ops/sec ±9.26% (78 runs sampled)
|
|
1061
|
+
resolver.resolve with caching using Cloudflare x 8,264,910 ops/sec ±3.31% (82 runs sampled)
|
|
1062
|
+
resolver.resolve without caching using Cloudflare x 322 ops/sec ±40.27% (68 runs sampled)
|
|
1063
|
+
Fastest without caching is: tangerine.resolve POST without caching using Cloudflare, tangerine.resolve GET without caching using Google
|
|
1064
|
+
```
|
|
1065
|
+
|
|
1066
|
+
**reverse:**
|
|
1067
|
+
|
|
1068
|
+
```text
|
|
1069
|
+
Started: reverse
|
|
1070
|
+
tangerine.reverse GET with caching x 337,763 ops/sec ±4.41% (90 runs sampled)
|
|
1071
|
+
tangerine.reverse GET without caching x 334 ops/sec ±1.57% (82 runs sampled)
|
|
1072
|
+
resolver.reverse with caching x 8,764,547 ops/sec ±1.05% (84 runs sampled)
|
|
1073
|
+
resolver.reverse without caching x 20.89 ops/sec ±188.69% (22 runs sampled)
|
|
1074
|
+
dns.promises.reverse with caching x 8,832,576 ops/sec ±0.52% (87 runs sampled)
|
|
1075
|
+
dns.promises.reverse without caching x 6.82 ops/sec ±176.67% (70 runs sampled)
|
|
1076
|
+
Fastest without caching is: tangerine.reverse GET without caching
|
|
1077
|
+
```
|
|
1078
|
+
|
|
1079
|
+
##### Node.js v25.6.0
|
|
1080
|
+
|
|
1081
|
+
**lookup:**
|
|
1082
|
+
|
|
1083
|
+
```text
|
|
1084
|
+
Started: lookup
|
|
1085
|
+
tangerine.lookup POST with caching using Cloudflare x 1,022 ops/sec ±195.41% (85 runs sampled)
|
|
1086
|
+
tangerine.lookup POST without caching using Cloudflare x 46.93 ops/sec ±6.02% (78 runs sampled)
|
|
1087
|
+
tangerine.lookup GET with caching using Cloudflare x 329,068 ops/sec ±1.17% (87 runs sampled)
|
|
1088
|
+
tangerine.lookup GET without caching using Cloudflare x 47.00 ops/sec ±5.36% (77 runs sampled)
|
|
1089
|
+
dns.promises.lookup with caching using Cloudflare x 10,120,744 ops/sec ±2.31% (81 runs sampled)
|
|
1090
|
+
dns.promises.lookup without caching using Cloudflare x 3,219 ops/sec ±0.75% (85 runs sampled)
|
|
1091
|
+
Fastest without caching is: dns.promises.lookup without caching using Cloudflare
|
|
1092
|
+
```
|
|
1093
|
+
|
|
1094
|
+
**resolve:**
|
|
1095
|
+
|
|
1096
|
+
```text
|
|
1097
|
+
Started: resolve
|
|
1098
|
+
tangerine.resolve POST with caching using Cloudflare x 1,127 ops/sec ±195.81% (89 runs sampled)
|
|
1099
|
+
tangerine.resolve POST without caching using Cloudflare x 47.64 ops/sec ±5.35% (78 runs sampled)
|
|
1100
|
+
tangerine.resolve GET with caching using Cloudflare x 1,183,153 ops/sec ±0.42% (90 runs sampled)
|
|
1101
|
+
tangerine.resolve GET without caching using Cloudflare x 47.85 ops/sec ±5.73% (79 runs sampled)
|
|
1102
|
+
tangerine.resolve POST with caching using Google x 1,167 ops/sec ±195.80% (90 runs sampled)
|
|
1103
|
+
tangerine.resolve POST without caching using Google x 52.32 ops/sec ±14.47% (67 runs sampled)
|
|
1104
|
+
tangerine.resolve GET with caching using Google x 1,168,373 ops/sec ±2.94% (89 runs sampled)
|
|
1105
|
+
tangerine.resolve GET without caching using Google x 61.96 ops/sec ±11.75% (66 runs sampled)
|
|
1106
|
+
resolver.resolve with caching using Cloudflare x 8,658,908 ops/sec ±0.86% (83 runs sampled)
|
|
1107
|
+
resolver.resolve without caching using Cloudflare x 53.79 ops/sec ±0.56% (84 runs sampled)
|
|
1108
|
+
Fastest without caching is: tangerine.resolve GET without caching using Google
|
|
1109
|
+
```
|
|
1110
|
+
|
|
1111
|
+
**reverse:**
|
|
1112
|
+
|
|
1113
|
+
```text
|
|
1114
|
+
Started: reverse
|
|
1115
|
+
tangerine.reverse GET with caching x 1,107 ops/sec ±195.37% (90 runs sampled)
|
|
1116
|
+
tangerine.reverse GET without caching x 47.62 ops/sec ±5.44% (78 runs sampled)
|
|
1117
|
+
resolver.reverse with caching x 8,843,572 ops/sec ±1.73% (86 runs sampled)
|
|
1118
|
+
resolver.reverse without caching x 53.71 ops/sec ±0.41% (84 runs sampled)
|
|
1119
|
+
dns.promises.reverse with caching x 8,869,703 ops/sec ±2.34% (85 runs sampled)
|
|
1120
|
+
dns.promises.reverse without caching x 54.03 ops/sec ±0.34% (85 runs sampled)
|
|
1121
|
+
Fastest without caching is: dns.promises.reverse without caching
|
|
1122
|
+
```
|
|
1123
|
+
|
|
1124
|
+
##### Node.js v25.6.1
|
|
1125
|
+
|
|
1126
|
+
**lookup:**
|
|
1127
|
+
|
|
1128
|
+
```text
|
|
1129
|
+
Started: lookup
|
|
1130
|
+
tangerine.lookup POST with caching using Cloudflare x 1,041 ops/sec ±195.41% (90 runs sampled)
|
|
1131
|
+
tangerine.lookup POST without caching using Cloudflare x 46.90 ops/sec ±7.12% (78 runs sampled)
|
|
1132
|
+
tangerine.lookup GET with caching using Cloudflare x 329,597 ops/sec ±1.03% (89 runs sampled)
|
|
1133
|
+
tangerine.lookup GET without caching using Cloudflare x 47.84 ops/sec ±5.63% (79 runs sampled)
|
|
1134
|
+
dns.promises.lookup with caching using Cloudflare x 10,033,729 ops/sec ±1.66% (86 runs sampled)
|
|
1135
|
+
dns.promises.lookup without caching using Cloudflare x 3,175 ops/sec ±0.61% (87 runs sampled)
|
|
1136
|
+
Fastest without caching is: dns.promises.lookup without caching using Cloudflare
|
|
1137
|
+
```
|
|
1138
|
+
|
|
1139
|
+
**resolve:**
|
|
1140
|
+
|
|
1141
|
+
```text
|
|
1142
|
+
Started: resolve
|
|
1143
|
+
tangerine.resolve POST with caching using Cloudflare x 1,066 ops/sec ±195.81% (86 runs sampled)
|
|
1144
|
+
tangerine.resolve POST without caching using Cloudflare x 48.96 ops/sec ±6.55% (81 runs sampled)
|
|
1145
|
+
tangerine.resolve GET with caching using Cloudflare x 1,133,219 ops/sec ±0.30% (90 runs sampled)
|
|
1146
|
+
tangerine.resolve GET without caching using Cloudflare x 47.66 ops/sec ±5.34% (79 runs sampled)
|
|
1147
|
+
tangerine.resolve POST with caching using Google x 1,132 ops/sec ±195.81% (89 runs sampled)
|
|
1148
|
+
tangerine.resolve POST without caching using Google x 48.55 ops/sec ±13.69% (66 runs sampled)
|
|
1149
|
+
tangerine.resolve GET with caching using Google x 1,130,722 ops/sec ±2.88% (89 runs sampled)
|
|
1150
|
+
tangerine.resolve GET without caching using Google x 53.90 ops/sec ±10.91% (67 runs sampled)
|
|
1151
|
+
resolver.resolve with caching using Cloudflare x 8,443,169 ops/sec ±2.56% (86 runs sampled)
|
|
1152
|
+
resolver.resolve without caching using Cloudflare x 53.86 ops/sec ±0.65% (84 runs sampled)
|
|
1153
|
+
Fastest without caching is: resolver.resolve without caching using Cloudflare
|
|
1154
|
+
```
|
|
1155
|
+
|
|
1156
|
+
**reverse:**
|
|
1157
|
+
|
|
1158
|
+
```text
|
|
1159
|
+
Started: reverse
|
|
1160
|
+
tangerine.reverse GET with caching x 1,105 ops/sec ±195.39% (89 runs sampled)
|
|
1161
|
+
tangerine.reverse GET without caching x 48.86 ops/sec ±6.37% (80 runs sampled)
|
|
1162
|
+
resolver.reverse with caching x 8,883,018 ops/sec ±0.41% (86 runs sampled)
|
|
1163
|
+
resolver.reverse without caching x 54.19 ops/sec ±0.53% (85 runs sampled)
|
|
1164
|
+
dns.promises.reverse with caching x 8,752,480 ops/sec ±2.56% (86 runs sampled)
|
|
1165
|
+
dns.promises.reverse without caching x 54.28 ops/sec ±0.35% (85 runs sampled)
|
|
1166
|
+
Fastest without caching is: dns.promises.reverse without caching, resolver.reverse without caching
|
|
1167
|
+
```
|
|
1168
|
+
|
|
1169
|
+
##### Node.js v25.7.0
|
|
1170
|
+
|
|
1171
|
+
**lookup:**
|
|
1172
|
+
|
|
1173
|
+
```text
|
|
1174
|
+
Started: lookup
|
|
1175
|
+
tangerine.lookup POST with caching using Cloudflare x 873 ops/sec ±195.51% (87 runs sampled)
|
|
1176
|
+
tangerine.lookup POST without caching using Cloudflare x 58.82 ops/sec ±7.44% (74 runs sampled)
|
|
1177
|
+
tangerine.lookup GET with caching using Cloudflare x 333,990 ops/sec ±1.19% (86 runs sampled)
|
|
1178
|
+
tangerine.lookup GET without caching using Cloudflare x 53.11 ops/sec ±10.66% (70 runs sampled)
|
|
1179
|
+
dns.promises.lookup with caching using Cloudflare x 626 ops/sec ±195.99% (86 runs sampled)
|
|
1180
|
+
dns.promises.lookup without caching using Cloudflare x 2,470 ops/sec ±0.50% (87 runs sampled)
|
|
1181
|
+
Fastest without caching is: dns.promises.lookup without caching using Cloudflare
|
|
1182
|
+
```
|
|
1183
|
+
|
|
1184
|
+
**resolve:**
|
|
1185
|
+
|
|
1186
|
+
```text
|
|
1187
|
+
Started: resolve
|
|
1188
|
+
tangerine.resolve POST with caching using Cloudflare x 1,243 ops/sec ±195.79% (89 runs sampled)
|
|
1189
|
+
tangerine.resolve POST without caching using Cloudflare x 57.08 ops/sec ±11.16% (74 runs sampled)
|
|
1190
|
+
tangerine.resolve GET with caching using Cloudflare x 1,132,703 ops/sec ±0.28% (90 runs sampled)
|
|
1191
|
+
tangerine.resolve GET without caching using Cloudflare x 56.05 ops/sec ±5.64% (80 runs sampled)
|
|
1192
|
+
tangerine.resolve POST with caching using Google x 1,497 ops/sec ±195.73% (88 runs sampled)
|
|
1193
|
+
tangerine.resolve POST without caching using Google x 60.56 ops/sec ±7.75% (77 runs sampled)
|
|
1194
|
+
tangerine.resolve GET with caching using Google x 1,096,216 ops/sec ±2.92% (88 runs sampled)
|
|
1195
|
+
tangerine.resolve GET without caching using Google x 41.16 ops/sec ±24.89% (66 runs sampled)
|
|
1196
|
+
resolver.resolve with caching using Cloudflare x 8,732,718 ops/sec ±2.67% (86 runs sampled)
|
|
1197
|
+
resolver.resolve without caching using Cloudflare x 65.84 ops/sec ±1.17% (78 runs sampled)
|
|
1198
|
+
Fastest without caching is: resolver.resolve without caching using Cloudflare, tangerine.resolve POST without caching using Google
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
**reverse:**
|
|
1202
|
+
|
|
1203
|
+
```text
|
|
1204
|
+
Started: reverse
|
|
1205
|
+
tangerine.reverse GET with caching x 1,193 ops/sec ±195.33% (88 runs sampled)
|
|
1206
|
+
tangerine.reverse GET without caching x 56.89 ops/sec ±11.00% (73 runs sampled)
|
|
1207
|
+
resolver.reverse with caching x 8,938,970 ops/sec ±0.65% (88 runs sampled)
|
|
1208
|
+
resolver.reverse without caching x 66.14 ops/sec ±2.95% (78 runs sampled)
|
|
1209
|
+
dns.promises.reverse with caching x 8,783,854 ops/sec ±1.77% (87 runs sampled)
|
|
1210
|
+
dns.promises.reverse without caching x 67.14 ops/sec ±0.80% (79 runs sampled)
|
|
1211
|
+
Fastest without caching is: dns.promises.reverse without caching, resolver.reverse without caching
|
|
1212
|
+
```
|
|
1213
|
+
|
|
760
1214
|
</details>
|
|
761
1215
|
|
|
762
1216
|
<!-- BENCHMARK_RESULTS_END -->
|
package/index.d.ts
CHANGED
|
@@ -134,6 +134,13 @@ export type TangerineOptions = {
|
|
|
134
134
|
*/
|
|
135
135
|
smartRotate?: boolean;
|
|
136
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Whether to resolve against all configured servers in parallel and return
|
|
139
|
+
* the first successful response.
|
|
140
|
+
* @default false
|
|
141
|
+
*/
|
|
142
|
+
parallelResolution?: boolean;
|
|
143
|
+
|
|
137
144
|
/**
|
|
138
145
|
* Default error message for unsuccessful HTTP responses.
|
|
139
146
|
* @default 'Unsuccessful HTTP response'
|
package/index.js
CHANGED
|
@@ -13,6 +13,7 @@ const hostile = require('hostile');
|
|
|
13
13
|
const ipaddr = require('ipaddr.js');
|
|
14
14
|
const isStream = require('is-stream');
|
|
15
15
|
const mergeOptions = require('merge-options');
|
|
16
|
+
const pAny = require('p-any');
|
|
16
17
|
const pMap = require('p-map');
|
|
17
18
|
const pWaitFor = require('p-wait-for');
|
|
18
19
|
const packet = require('dns-packet');
|
|
@@ -451,6 +452,8 @@ class Tangerine extends dns.promises.Resolver {
|
|
|
451
452
|
returnHTTPErrors: false,
|
|
452
453
|
// whether to smart rotate and bump-to-end servers that have issues
|
|
453
454
|
smartRotate: true,
|
|
455
|
+
// whether to resolve all servers in parallel and use first successful result
|
|
456
|
+
parallelResolution: false,
|
|
454
457
|
// fallback if status code was not found in http.STATUS_CODES
|
|
455
458
|
defaultHTTPErrorMessage: 'Unsuccessful HTTP response'
|
|
456
459
|
},
|
|
@@ -490,6 +493,12 @@ class Tangerine extends dns.promises.Resolver {
|
|
|
490
493
|
if (!['verbatim', 'ipv4first'].includes(this.options.dnsOrder))
|
|
491
494
|
throw new Error('DNS order must be either verbatim or ipv4first');
|
|
492
495
|
|
|
496
|
+
if (this.options.parallelResolution === undefined)
|
|
497
|
+
this.options.parallelResolution = false;
|
|
498
|
+
|
|
499
|
+
if (typeof this.options.parallelResolution !== 'boolean')
|
|
500
|
+
throw new Error('parallelResolution must be a boolean');
|
|
501
|
+
|
|
493
502
|
// if `cache: false` then caching is disabled
|
|
494
503
|
// but note that this doesn't disable `got` dnsCache which is separate
|
|
495
504
|
// so to turn that off, you need to supply `dnsCache: undefined` in `got` object (?)
|
|
@@ -1131,7 +1140,7 @@ class Tangerine extends dns.promises.Resolver {
|
|
|
1131
1140
|
}
|
|
1132
1141
|
|
|
1133
1142
|
// <https://github.com/hildjj/dohdec/tree/main/pkg/dohdec>
|
|
1134
|
-
|
|
1143
|
+
|
|
1135
1144
|
async #query(name, rrtype = 'A', ecsSubnet, abortController) {
|
|
1136
1145
|
if (!dohdec) await pWaitFor(() => Boolean(dohdec));
|
|
1137
1146
|
debug('query', {
|
|
@@ -1158,46 +1167,105 @@ class Tangerine extends dns.promises.Resolver {
|
|
|
1158
1167
|
// <https://github.com/nodejs/node/issues/33353#issuecomment-627259827>
|
|
1159
1168
|
let buffer;
|
|
1160
1169
|
const errors = [];
|
|
1161
|
-
// NOTE: we would have used `p-map-series` but it did not support abort/break
|
|
1162
1170
|
const servers = [...this.options.servers];
|
|
1163
|
-
|
|
1171
|
+
|
|
1172
|
+
const parseBody = async (body) => {
|
|
1173
|
+
// <https://sindresorhus.com/blog/goodbye-nodejs-buffer>
|
|
1174
|
+
if (Buffer.isBuffer(body)) return body;
|
|
1175
|
+
if (typeof body.arrayBuffer === 'function')
|
|
1176
|
+
return Buffer.from(await body.arrayBuffer());
|
|
1177
|
+
if (isStream(body)) return getStream.buffer(body);
|
|
1178
|
+
const err = new TypeError('Unsupported body type');
|
|
1179
|
+
err.body = body;
|
|
1180
|
+
throw err;
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
const getRequestAbortController = (parallelAbortController) => {
|
|
1184
|
+
if (!parallelAbortController)
|
|
1185
|
+
return {
|
|
1186
|
+
requestAbortController: abortController,
|
|
1187
|
+
cleanupAbortController() {}
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
const requestAbortController = new AbortController();
|
|
1191
|
+
const parentSignal = abortController?.signal;
|
|
1192
|
+
const parallelSignal = parallelAbortController.signal;
|
|
1193
|
+
const onAbort = () => {
|
|
1194
|
+
if (!requestAbortController.signal.aborted)
|
|
1195
|
+
requestAbortController.abort(parentSignal.reason);
|
|
1196
|
+
};
|
|
1197
|
+
|
|
1198
|
+
const onParallelAbort = () => {
|
|
1199
|
+
if (!requestAbortController.signal.aborted)
|
|
1200
|
+
requestAbortController.abort(parallelSignal.reason);
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
if (parentSignal?.aborted) onAbort();
|
|
1204
|
+
else if (parentSignal)
|
|
1205
|
+
parentSignal.addEventListener('abort', onAbort, { once: true });
|
|
1206
|
+
|
|
1207
|
+
if (parallelSignal.aborted) onParallelAbort();
|
|
1208
|
+
else
|
|
1209
|
+
parallelSignal.addEventListener('abort', onParallelAbort, {
|
|
1210
|
+
once: true
|
|
1211
|
+
});
|
|
1212
|
+
|
|
1213
|
+
return {
|
|
1214
|
+
requestAbortController,
|
|
1215
|
+
cleanupAbortController() {
|
|
1216
|
+
parentSignal?.removeEventListener('abort', onAbort);
|
|
1217
|
+
parallelSignal.removeEventListener('abort', onParallelAbort);
|
|
1218
|
+
}
|
|
1219
|
+
};
|
|
1220
|
+
};
|
|
1221
|
+
|
|
1222
|
+
const addServerErrors = (server, ipErrors) => {
|
|
1223
|
+
if (ipErrors.length === 0) return;
|
|
1224
|
+
|
|
1225
|
+
// if the `server` had all errors, then remove it and add to end
|
|
1226
|
+
// (this ensures we don't keep retrying servers that keep timing out)
|
|
1227
|
+
// (which improves upon default c-ares behavior)
|
|
1228
|
+
if (this.options.servers.size > 1 && this.options.smartRotate) {
|
|
1229
|
+
const err = this.constructor.combineErrors([
|
|
1230
|
+
new Error('Rotating DNS servers due to issues'),
|
|
1231
|
+
...ipErrors
|
|
1232
|
+
]);
|
|
1233
|
+
this.options.logger.error(err, { server });
|
|
1234
|
+
this.options.servers.delete(server);
|
|
1235
|
+
this.options.servers.add(server);
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
errors.push(...ipErrors);
|
|
1239
|
+
};
|
|
1240
|
+
|
|
1241
|
+
const throwOnNotFound = (err) => {
|
|
1242
|
+
if (err.code === dns.NOTFOUND) throw err;
|
|
1243
|
+
};
|
|
1244
|
+
|
|
1245
|
+
const queryServer = async (server, parallelAbortController) => {
|
|
1164
1246
|
const ipErrors = [];
|
|
1247
|
+
let lastError;
|
|
1248
|
+
|
|
1165
1249
|
for (let i = 0; i < this.options.tries; i++) {
|
|
1250
|
+
const { requestAbortController, cleanupAbortController } =
|
|
1251
|
+
getRequestAbortController(parallelAbortController);
|
|
1252
|
+
|
|
1166
1253
|
try {
|
|
1167
|
-
// <https://github.com/sindresorhus/p-map-series/blob/bc1b9f5e19ed62363bff3d7dc5ecc1fd820ccb51/index.js#L1-L11>
|
|
1168
1254
|
// eslint-disable-next-line no-await-in-loop
|
|
1169
1255
|
const response = await this.#request(
|
|
1170
1256
|
pkt,
|
|
1171
1257
|
server,
|
|
1172
|
-
|
|
1258
|
+
requestAbortController,
|
|
1173
1259
|
this.options.timeout * 2 ** i
|
|
1174
1260
|
);
|
|
1175
1261
|
|
|
1176
|
-
// if aborted signal then returns early
|
|
1177
|
-
// eslint-disable-next-line max-depth
|
|
1178
1262
|
if (response) {
|
|
1179
1263
|
const { body, headers } = response;
|
|
1180
1264
|
const statusCode = response.status || response.statusCode;
|
|
1181
1265
|
debug('response', { statusCode, headers });
|
|
1182
1266
|
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
// <https://sindresorhus.com/blog/goodbye-nodejs-buffer>
|
|
1186
|
-
// eslint-disable-next-line max-depth
|
|
1187
|
-
if (Buffer.isBuffer(body)) buffer = body;
|
|
1188
|
-
else if (typeof body.arrayBuffer === 'function')
|
|
1189
|
-
// eslint-disable-next-line no-await-in-loop
|
|
1190
|
-
buffer = Buffer.from(await body.arrayBuffer());
|
|
1191
|
-
// eslint-disable-next-line no-await-in-loop
|
|
1192
|
-
else if (isStream(body)) buffer = await getStream.buffer(body);
|
|
1193
|
-
else {
|
|
1194
|
-
const err = new TypeError('Unsupported body type');
|
|
1195
|
-
err.body = body;
|
|
1196
|
-
throw err;
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
break;
|
|
1200
|
-
}
|
|
1267
|
+
if (body && statusCode >= 200 && statusCode < 300)
|
|
1268
|
+
return parseBody(body);
|
|
1201
1269
|
|
|
1202
1270
|
// <https://github.com/nodejs/undici/issues/3353>
|
|
1203
1271
|
if (
|
|
@@ -1221,18 +1289,18 @@ class Tangerine extends dns.promises.Resolver {
|
|
|
1221
1289
|
}
|
|
1222
1290
|
} catch (err) {
|
|
1223
1291
|
debug(err);
|
|
1292
|
+
lastError = err;
|
|
1224
1293
|
|
|
1225
1294
|
//
|
|
1226
1295
|
// NOTE: if NOTFOUND error occurs then don't attempt further requests
|
|
1227
1296
|
// <https://nodejs.org/api/dns.html#dnssetserversservers>
|
|
1228
1297
|
//
|
|
1229
1298
|
|
|
1230
|
-
|
|
1299
|
+
throwOnNotFound(err);
|
|
1231
1300
|
|
|
1232
1301
|
if (err.status >= 429) ipErrors.push(err);
|
|
1233
1302
|
|
|
1234
1303
|
// break out of the loop if status code was not retryable
|
|
1235
|
-
|
|
1236
1304
|
if (
|
|
1237
1305
|
!(
|
|
1238
1306
|
err.statusCode &&
|
|
@@ -1241,26 +1309,43 @@ class Tangerine extends dns.promises.Resolver {
|
|
|
1241
1309
|
!(err.code && this.constructor.RETRY_ERROR_CODES.has(err.code))
|
|
1242
1310
|
)
|
|
1243
1311
|
break;
|
|
1312
|
+
} finally {
|
|
1313
|
+
cleanupAbortController();
|
|
1244
1314
|
}
|
|
1245
1315
|
}
|
|
1246
1316
|
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
// (this ensures we don't keep retrying servers that keep timing out)
|
|
1252
|
-
// (which improves upon default c-ares behavior)
|
|
1253
|
-
if (this.options.servers.size > 1 && this.options.smartRotate) {
|
|
1254
|
-
const err = this.constructor.combineErrors([
|
|
1255
|
-
new Error('Rotating DNS servers due to issues'),
|
|
1256
|
-
...ipErrors
|
|
1257
|
-
]);
|
|
1258
|
-
this.options.logger.error(err, { server });
|
|
1259
|
-
this.options.servers.delete(server);
|
|
1260
|
-
this.options.servers.add(server);
|
|
1261
|
-
}
|
|
1317
|
+
addServerErrors(server, ipErrors);
|
|
1318
|
+
|
|
1319
|
+
throw lastError || new Error(`No response from ${server}`);
|
|
1320
|
+
};
|
|
1262
1321
|
|
|
1263
|
-
|
|
1322
|
+
if (this.options.parallelResolution) {
|
|
1323
|
+
const parallelAbortController = new AbortController();
|
|
1324
|
+
|
|
1325
|
+
try {
|
|
1326
|
+
buffer = await pAny(
|
|
1327
|
+
servers.map((server) =>
|
|
1328
|
+
queryServer(server, parallelAbortController)
|
|
1329
|
+
)
|
|
1330
|
+
);
|
|
1331
|
+
} catch (err) {
|
|
1332
|
+
if (errors.length > 0) throw this.constructor.combineErrors(errors);
|
|
1333
|
+
throw err;
|
|
1334
|
+
} finally {
|
|
1335
|
+
if (!parallelAbortController.signal.aborted)
|
|
1336
|
+
parallelAbortController.abort('CANCELLED');
|
|
1337
|
+
}
|
|
1338
|
+
} else {
|
|
1339
|
+
// NOTE: we would have used `p-map-series` but it did not support abort/break
|
|
1340
|
+
for (const server of servers) {
|
|
1341
|
+
try {
|
|
1342
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1343
|
+
buffer = await queryServer(server);
|
|
1344
|
+
break;
|
|
1345
|
+
} catch (err) {
|
|
1346
|
+
throwOnNotFound(err);
|
|
1347
|
+
debug(err);
|
|
1348
|
+
}
|
|
1264
1349
|
}
|
|
1265
1350
|
}
|
|
1266
1351
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tangerine",
|
|
3
3
|
"description": "Tangerine is the best Node.js drop-in replacement for dns.promises.Resolver using DNS over HTTPS (\"DoH\") via undici with built-in retries, timeouts, smart server rotation, AbortControllers, and caching support for multiple backends (with TTL and purge support).",
|
|
4
|
-
"version": "2.0
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"author": "Forward Email (https://forwardemail.net)",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/forwardemail/nodejs-dns-over-https-tangerine/issues"
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"ipaddr.js": "^2.2.0",
|
|
19
19
|
"is-stream": "2.0.1",
|
|
20
20
|
"merge-options": "3.0.4",
|
|
21
|
+
"p-any": "3",
|
|
21
22
|
"p-map": "4",
|
|
22
23
|
"p-wait-for": "3",
|
|
23
24
|
"port-numbers": "6.0.1",
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
"axios": "^1.7.3",
|
|
33
34
|
"benchmark": "^2.1.4",
|
|
34
35
|
"cross-env": "^7.0.3",
|
|
35
|
-
"eslint": "^
|
|
36
|
+
"eslint": "^8.57.1",
|
|
36
37
|
"eslint-config-xo-lass": "^2.0.1",
|
|
37
38
|
"fetch-mock": "^10.1.1",
|
|
38
39
|
"fixpack": "^4.0.0",
|