wreq 1.2.4 → 1.2.5

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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +3 -7
  3. data/Cargo.toml +6 -1
  4. data/crates/wreq-util/.github/FUNDING.yml +15 -0
  5. data/crates/wreq-util/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  6. data/crates/wreq-util/.github/ISSUE_TEMPLATE/custom.md +10 -0
  7. data/crates/wreq-util/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  8. data/crates/wreq-util/.github/dependabot.yml +22 -0
  9. data/crates/wreq-util/.github/workflows/ci.yml +82 -0
  10. data/crates/wreq-util/.github/workflows/release-plz.yml +52 -0
  11. data/crates/wreq-util/.gitignore +24 -0
  12. data/crates/wreq-util/CHANGELOG.md +74 -0
  13. data/crates/wreq-util/Cargo.toml +94 -0
  14. data/crates/wreq-util/LICENSE +201 -0
  15. data/crates/wreq-util/README.md +62 -0
  16. data/crates/wreq-util/examples/emulate.rs +39 -0
  17. data/crates/wreq-util/examples/tower_delay.rs +191 -0
  18. data/crates/wreq-util/release-plz.toml +2 -0
  19. data/crates/wreq-util/rustfmt.toml +5 -0
  20. data/crates/wreq-util/src/emulate/compress.rs +85 -0
  21. data/crates/wreq-util/src/emulate/macros.rs +370 -0
  22. data/crates/wreq-util/src/emulate/profile/chrome/header.rs +59 -0
  23. data/crates/wreq-util/src/emulate/profile/chrome/http2.rs +65 -0
  24. data/crates/wreq-util/src/emulate/profile/chrome/tls.rs +153 -0
  25. data/crates/wreq-util/src/emulate/profile/chrome.rs +2028 -0
  26. data/crates/wreq-util/src/emulate/profile/firefox/header.rs +21 -0
  27. data/crates/wreq-util/src/emulate/profile/firefox/http2.rs +113 -0
  28. data/crates/wreq-util/src/emulate/profile/firefox/tls.rs +253 -0
  29. data/crates/wreq-util/src/emulate/profile/firefox.rs +518 -0
  30. data/crates/wreq-util/src/emulate/profile/okhttp.rs +241 -0
  31. data/crates/wreq-util/src/emulate/profile/opera/header.rs +30 -0
  32. data/crates/wreq-util/src/emulate/profile/opera/http2.rs +50 -0
  33. data/crates/wreq-util/src/emulate/profile/opera/tls.rs +97 -0
  34. data/crates/wreq-util/src/emulate/profile/opera.rs +299 -0
  35. data/crates/wreq-util/src/emulate/profile/safari/header.rs +59 -0
  36. data/crates/wreq-util/src/emulate/profile/safari/http2.rs +116 -0
  37. data/crates/wreq-util/src/emulate/profile/safari/tls.rs +177 -0
  38. data/crates/wreq-util/src/emulate/profile/safari.rs +222 -0
  39. data/crates/wreq-util/src/emulate/profile.rs +47 -0
  40. data/crates/wreq-util/src/emulate.rs +369 -0
  41. data/crates/wreq-util/src/lib.rs +14 -0
  42. data/crates/wreq-util/src/rand.rs +24 -0
  43. data/crates/wreq-util/src/tower/delay/future.rs +43 -0
  44. data/crates/wreq-util/src/tower/delay/layer.rs +201 -0
  45. data/crates/wreq-util/src/tower/delay/service.rs +190 -0
  46. data/crates/wreq-util/src/tower/delay.rs +98 -0
  47. data/crates/wreq-util/src/tower.rs +7 -0
  48. data/crates/wreq-util/tests/client.rs +51 -0
  49. data/crates/wreq-util/tests/emulate_chrome.rs +270 -0
  50. data/crates/wreq-util/tests/emulate_firefox.rs +92 -0
  51. data/crates/wreq-util/tests/emulate_okhttp.rs +46 -0
  52. data/crates/wreq-util/tests/emulate_opera.rs +113 -0
  53. data/crates/wreq-util/tests/emulate_safari.rs +151 -0
  54. data/crates/wreq-util/tests/support/mod.rs +55 -0
  55. data/crates/wreq-util/tests/support/server.rs +232 -0
  56. data/examples/emulate_request.rb +1 -1
  57. data/lib/wreq.rb +20 -18
  58. data/lib/wreq_ruby/body.rb +3 -0
  59. data/lib/wreq_ruby/client.rb +18 -18
  60. data/lib/wreq_ruby/cookie.rb +2 -2
  61. data/lib/wreq_ruby/emulate.rb +3 -1
  62. data/lib/wreq_ruby/header.rb +205 -114
  63. data/lib/wreq_ruby/response.rb +28 -3
  64. data/src/emulate.rs +1 -0
  65. data/src/error.rs +2 -6
  66. data/src/header/helper.rs +112 -0
  67. data/src/header.rs +198 -133
  68. data/test/header_test.rb +228 -192
  69. data/test/stream_test.rb +36 -0
  70. metadata +54 -1
@@ -0,0 +1,222 @@
1
+ #[macro_use]
2
+ mod http2;
3
+ #[macro_use]
4
+ mod tls;
5
+ mod header;
6
+
7
+ use header::*;
8
+ use tls::*;
9
+
10
+ use super::*;
11
+
12
+ mod_generator!(
13
+ safari15_3,
14
+ tls_options!(1, CIPHER_LIST_1),
15
+ http2_options!(4),
16
+ header_initializer_for_15,
17
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.3 Safari/605.1.15"
18
+ );
19
+
20
+ mod_generator!(
21
+ safari15_5,
22
+ safari15_3::build_emulation,
23
+ header_initializer_for_15,
24
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.5 Safari/605.1.15"
25
+ );
26
+
27
+ mod_generator!(
28
+ safari15_6_1,
29
+ tls_options!(1, CIPHER_LIST_2),
30
+ http2_options!(4),
31
+ header_initializer_for_15,
32
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6.1 Safari/605.1.15"
33
+ );
34
+
35
+ mod_generator!(
36
+ safari16,
37
+ safari15_6_1::build_emulation,
38
+ header_initializer_for_16_17,
39
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15"
40
+ );
41
+
42
+ mod_generator!(
43
+ safari16_5,
44
+ safari15_6_1::build_emulation,
45
+ header_initializer_for_16_17,
46
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15"
47
+ );
48
+
49
+ mod_generator!(
50
+ safari17_4_1,
51
+ safari15_6_1::build_emulation,
52
+ header_initializer_for_16_17,
53
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Safari/605.1.15"
54
+ );
55
+
56
+ mod_generator!(
57
+ safari_ios_16_5,
58
+ tls_options!(1, CIPHER_LIST_2),
59
+ http2_options!(1),
60
+ header_initializer_for_16_17,
61
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 16_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Mobile/15E148 Safari/604.1"
62
+ );
63
+
64
+ mod_generator!(
65
+ safari17_0,
66
+ tls_options!(1, CIPHER_LIST_2),
67
+ http2_options!(5),
68
+ header_initializer_for_16_17,
69
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15"
70
+ );
71
+
72
+ mod_generator!(
73
+ safari17_2_1,
74
+ safari17_0::build_emulation,
75
+ header_initializer_for_16_17,
76
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15"
77
+ );
78
+
79
+ mod_generator!(
80
+ safari17_5,
81
+ safari17_0::build_emulation,
82
+ header_initializer_for_16_17,
83
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15"
84
+ );
85
+
86
+ mod_generator!(
87
+ safari17_6,
88
+ safari17_0::build_emulation,
89
+ header_initializer_for_16_17,
90
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Safari/605.1.15"
91
+ );
92
+
93
+ mod_generator!(
94
+ safari_ios_17_2,
95
+ tls_options!(1, CIPHER_LIST_2),
96
+ http2_options!(2),
97
+ header_initializer_for_16_17,
98
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1"
99
+ );
100
+
101
+ mod_generator!(
102
+ safari_ios_17_4_1,
103
+ safari_ios_17_2::build_emulation,
104
+ header_initializer_for_16_17,
105
+ "Mozilla/5.0 (iPad; CPU OS 17_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Mobile/15E148 Safari/604.1"
106
+ );
107
+
108
+ mod_generator!(
109
+ safari18,
110
+ tls_options!(1, CIPHER_LIST_2),
111
+ http2_options!(3),
112
+ header_initializer_for_18,
113
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15"
114
+ );
115
+
116
+ mod_generator!(
117
+ safari_ipad_18,
118
+ safari18::build_emulation,
119
+ header_initializer_for_18,
120
+ "Mozilla/5.0 (iPad; CPU OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1"
121
+ );
122
+
123
+ mod_generator!(
124
+ safari_ios_18_1_1,
125
+ safari18::build_emulation,
126
+ header_initializer_for_18,
127
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Mobile/15E148 Safari/604.1"
128
+ );
129
+
130
+ mod_generator!(
131
+ safari18_2,
132
+ tls_options!(2, CIPHER_LIST_2, SIGALGS_LIST_2),
133
+ http2_options!(3),
134
+ header_initializer_for_18,
135
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.2 Safari/605.1.15"
136
+ );
137
+
138
+ mod_generator!(
139
+ safari18_3,
140
+ safari18_2::build_emulation,
141
+ header_initializer_for_18,
142
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3 Safari/605.1.15"
143
+ );
144
+
145
+ mod_generator!(
146
+ safari18_3_1,
147
+ safari18_2::build_emulation,
148
+ header_initializer_for_18,
149
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3.1 Safari/605.1.15"
150
+ );
151
+
152
+ mod_generator!(
153
+ safari18_5,
154
+ tls_options!(2, CIPHER_LIST_2, SIGALGS_LIST_2),
155
+ http2_options!(6),
156
+ header_initializer_for_18,
157
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Safari/605.1.15"
158
+ );
159
+
160
+ mod_generator!(
161
+ safari26,
162
+ tls_options!(3, CIPHER_LIST_3, SIGALGS_LIST_2, CURVES_2),
163
+ http2_options!(6),
164
+ header_initializer_for_18,
165
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Safari/605.1.15"
166
+ );
167
+
168
+ mod_generator!(
169
+ safari_ipad_26,
170
+ safari26::build_emulation,
171
+ header_initializer_for_18,
172
+ "Mozilla/5.0 (iPad; CPU OS 18_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1"
173
+ );
174
+
175
+ mod_generator!(
176
+ safari_ios_26,
177
+ safari26::build_emulation,
178
+ header_initializer_for_18,
179
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 26_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1"
180
+ );
181
+
182
+ mod_generator!(
183
+ safari26_1,
184
+ safari18_5::build_emulation,
185
+ header_initializer_for_18,
186
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.1 Safari/605.1.15"
187
+ );
188
+
189
+ mod_generator!(
190
+ safari_ios_26_2,
191
+ safari26::build_emulation,
192
+ header_initializer_for_18,
193
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Mobile/15E148 Safari/604.1"
194
+ );
195
+
196
+ mod_generator!(
197
+ safari26_2,
198
+ safari18_5::build_emulation,
199
+ header_initializer_for_18,
200
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Safari/605.1.15"
201
+ );
202
+
203
+ mod_generator!(
204
+ safari26_3,
205
+ safari18_5::build_emulation,
206
+ header_initializer_for_18,
207
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.3 Safari/605.1.15"
208
+ );
209
+
210
+ mod_generator!(
211
+ safari26_4,
212
+ safari18_5::build_emulation,
213
+ header_initializer_for_18,
214
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.4 Safari/605.1.15"
215
+ );
216
+
217
+ mod_generator!(
218
+ safari_ipad_26_2,
219
+ safari26::build_emulation,
220
+ header_initializer_for_18,
221
+ "Mozilla/5.0 (iPad; CPU OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Mobile/15E148 Safari/604.1"
222
+ );
@@ -0,0 +1,47 @@
1
+ //! Emulation for different browsers.
2
+
3
+ pub mod chrome;
4
+ pub mod firefox;
5
+ pub mod okhttp;
6
+ pub mod opera;
7
+ pub mod safari;
8
+
9
+ use typed_builder::TypedBuilder;
10
+ #[cfg(feature = "emulation-compression")]
11
+ use wreq::header::ACCEPT_ENCODING;
12
+ use wreq::{
13
+ Group,
14
+ header::{ACCEPT, ACCEPT_LANGUAGE, HeaderMap, HeaderName, HeaderValue, USER_AGENT},
15
+ http2::{
16
+ Http2Options, Priorities, Priority, PseudoId, PseudoOrder, SettingId, SettingsOrder,
17
+ StreamDependency, StreamId,
18
+ },
19
+ tls::{
20
+ AlpnProtocol, AlpsProtocol, ExtensionType, KeyShare, TlsOptions, TlsVersion,
21
+ compress::CertificateCompressor,
22
+ },
23
+ };
24
+
25
+ use super::{
26
+ Emulation, Platform,
27
+ compress::{BrotliCompressor, ZlibCompressor, ZstdCompressor},
28
+ };
29
+
30
+ fn build_standard_emulation(
31
+ group: &'static str,
32
+ tls_options: TlsOptions,
33
+ http2_options: Option<Http2Options>,
34
+ default_headers: Option<HeaderMap>,
35
+ ) -> wreq::Emulation {
36
+ let mut builder = wreq::Emulation::builder().tls_options(tls_options);
37
+
38
+ if let Some(http2_options) = http2_options {
39
+ builder = builder.http2_options(http2_options);
40
+ }
41
+
42
+ if let Some(headers) = default_headers {
43
+ builder = builder.headers(headers);
44
+ }
45
+
46
+ builder.build(Group::new(group))
47
+ }
@@ -0,0 +1,369 @@
1
+ #[macro_use]
2
+ mod macros;
3
+ pub mod compress;
4
+ pub mod profile;
5
+
6
+ use profile::{chrome::*, firefox::*, okhttp::*, opera::*, safari::*};
7
+ #[cfg(feature = "emulation-serde")]
8
+ use serde::{Deserialize, Serialize};
9
+ use typed_builder::TypedBuilder;
10
+
11
+ define_enum!(
12
+ /// Selects which client profile the request should look like.
13
+ ///
14
+ /// This controls the built-in TLS, HTTP/2, and header presets used for the
15
+ /// request. Variants cover browser-style profiles as well as other clients,
16
+ /// such as OkHttp.
17
+ dispatch,
18
+ Profile, Chrome100,
19
+ Emulation,
20
+
21
+ // Chrome versions
22
+ Chrome100 => ("chrome_100", v100::emulation),
23
+ Chrome101 => ("chrome_101", v101::emulation),
24
+ Chrome104 => ("chrome_104", v104::emulation),
25
+ Chrome105 => ("chrome_105", v105::emulation),
26
+ Chrome106 => ("chrome_106", v106::emulation),
27
+ Chrome107 => ("chrome_107", v107::emulation),
28
+ Chrome108 => ("chrome_108", v108::emulation),
29
+ Chrome109 => ("chrome_109", v109::emulation),
30
+ Chrome110 => ("chrome_110", v110::emulation),
31
+ Chrome114 => ("chrome_114", v114::emulation),
32
+ Chrome116 => ("chrome_116", v116::emulation),
33
+ Chrome117 => ("chrome_117", v117::emulation),
34
+ Chrome118 => ("chrome_118", v118::emulation),
35
+ Chrome119 => ("chrome_119", v119::emulation),
36
+ Chrome120 => ("chrome_120", v120::emulation),
37
+ Chrome123 => ("chrome_123", v123::emulation),
38
+ Chrome124 => ("chrome_124", v124::emulation),
39
+ Chrome126 => ("chrome_126", v126::emulation),
40
+ Chrome127 => ("chrome_127", v127::emulation),
41
+ Chrome128 => ("chrome_128", v128::emulation),
42
+ Chrome129 => ("chrome_129", v129::emulation),
43
+ Chrome130 => ("chrome_130", v130::emulation),
44
+ Chrome131 => ("chrome_131", v131::emulation),
45
+ Chrome132 => ("chrome_132", v132::emulation),
46
+ Chrome133 => ("chrome_133", v133::emulation),
47
+ Chrome134 => ("chrome_134", v134::emulation),
48
+ Chrome135 => ("chrome_135", v135::emulation),
49
+ Chrome136 => ("chrome_136", v136::emulation),
50
+ Chrome137 => ("chrome_137", v137::emulation),
51
+ Chrome138 => ("chrome_138", v138::emulation),
52
+ Chrome139 => ("chrome_139", v139::emulation),
53
+ Chrome140 => ("chrome_140", v140::emulation),
54
+ Chrome141 => ("chrome_141", v141::emulation),
55
+ Chrome142 => ("chrome_142", v142::emulation),
56
+ Chrome143 => ("chrome_143", v143::emulation),
57
+ Chrome144 => ("chrome_144", v144::emulation),
58
+ Chrome145 => ("chrome_145", v145::emulation),
59
+ Chrome146 => ("chrome_146", v146::emulation),
60
+ Chrome147 => ("chrome_147", v147::emulation),
61
+ Chrome148 => ("chrome_148", v148::emulation),
62
+ Chrome149 => ("chrome_149", v149::emulation),
63
+ Chrome150 => ("chrome_150", v150::emulation),
64
+
65
+ // Edge versions
66
+ Edge101 => ("edge_101", edge101::emulation),
67
+ Edge122 => ("edge_122", edge122::emulation),
68
+ Edge127 => ("edge_127", edge127::emulation),
69
+ Edge131 => ("edge_131", edge131::emulation),
70
+ Edge134 => ("edge_134", edge134::emulation),
71
+ Edge135 => ("edge_135", edge135::emulation),
72
+ Edge136 => ("edge_136", edge136::emulation),
73
+ Edge137 => ("edge_137", edge137::emulation),
74
+ Edge138 => ("edge_138", edge138::emulation),
75
+ Edge139 => ("edge_139", edge139::emulation),
76
+ Edge140 => ("edge_140", edge140::emulation),
77
+ Edge141 => ("edge_141", edge141::emulation),
78
+ Edge142 => ("edge_142", edge142::emulation),
79
+ Edge143 => ("edge_143", edge143::emulation),
80
+ Edge144 => ("edge_144", edge144::emulation),
81
+ Edge145 => ("edge_145", edge145::emulation),
82
+ Edge146 => ("edge_146", edge146::emulation),
83
+ Edge147 => ("edge_147", edge147::emulation),
84
+ Edge148 => ("edge_148", edge148::emulation),
85
+
86
+ // Opera versions
87
+ Opera116 => ("opera_116", opera116::emulation),
88
+ Opera117 => ("opera_117", opera117::emulation),
89
+ Opera118 => ("opera_118", opera118::emulation),
90
+ Opera119 => ("opera_119", opera119::emulation),
91
+ Opera120 => ("opera_120", opera120::emulation),
92
+ Opera121 => ("opera_121", opera121::emulation),
93
+ Opera122 => ("opera_122", opera122::emulation),
94
+ Opera123 => ("opera_123", opera123::emulation),
95
+ Opera124 => ("opera_124", opera124::emulation),
96
+ Opera125 => ("opera_125", opera125::emulation),
97
+ Opera126 => ("opera_126", opera126::emulation),
98
+ Opera127 => ("opera_127", opera127::emulation),
99
+ Opera128 => ("opera_128", opera128::emulation),
100
+ Opera129 => ("opera_129", opera129::emulation),
101
+ Opera130 => ("opera_130", opera130::emulation),
102
+ Opera131 => ("opera_131", opera131::emulation),
103
+
104
+ // Firefox versions
105
+ Firefox109 => ("firefox_109", ff109::emulation),
106
+ Firefox117 => ("firefox_117", ff117::emulation),
107
+ Firefox128 => ("firefox_128", ff128::emulation),
108
+ Firefox133 => ("firefox_133", ff133::emulation),
109
+ Firefox135 => ("firefox_135", ff135::emulation),
110
+ FirefoxPrivate135 => ("firefox_private_135", ff_private_135::emulation),
111
+ FirefoxAndroid135 => ("firefox_android_135", ff_android_135::emulation),
112
+ Firefox136 => ("firefox_136", ff136::emulation),
113
+ FirefoxPrivate136 => ("firefox_private_136", ff_private_136::emulation),
114
+ Firefox139 => ("firefox_139", ff139::emulation),
115
+ Firefox142 => ("firefox_142", ff142::emulation),
116
+ Firefox143 => ("firefox_143", ff143::emulation),
117
+ Firefox144 => ("firefox_144", ff144::emulation),
118
+ Firefox145 => ("firefox_145", ff145::emulation),
119
+ Firefox146 => ("firefox_146", ff146::emulation),
120
+ Firefox147 => ("firefox_147", ff147::emulation),
121
+ Firefox148 => ("firefox_148", ff148::emulation),
122
+ Firefox149 => ("firefox_149", ff149::emulation),
123
+ Firefox150 => ("firefox_150", ff150::emulation),
124
+ Firefox151 => ("firefox_151", ff151::emulation),
125
+
126
+ // Safari versions
127
+ SafariIos17_2 => ("safari_ios_17.2", safari_ios_17_2::emulation),
128
+ SafariIos17_4_1 => ("safari_ios_17.4.1", safari_ios_17_4_1::emulation),
129
+ SafariIos16_5 => ("safari_ios_16.5", safari_ios_16_5::emulation),
130
+ Safari15_3 => ("safari_15.3", safari15_3::emulation),
131
+ Safari15_5 => ("safari_15.5", safari15_5::emulation),
132
+ Safari15_6_1 => ("safari_15.6.1", safari15_6_1::emulation),
133
+ Safari16 => ("safari_16", safari16::emulation),
134
+ Safari16_5 => ("safari_16.5", safari16_5::emulation),
135
+ Safari17_0 => ("safari_17.0", safari17_0::emulation),
136
+ Safari17_2_1 => ("safari_17.2.1", safari17_2_1::emulation),
137
+ Safari17_4_1 => ("safari_17.4.1", safari17_4_1::emulation),
138
+ Safari17_5 => ("safari_17.5", safari17_5::emulation),
139
+ Safari17_6 => ("safari_17.6", safari17_6::emulation),
140
+ Safari18 => ("safari_18", safari18::emulation),
141
+ SafariIPad18 => ("safari_ipad_18", safari_ipad_18::emulation),
142
+ Safari18_2 => ("safari_18.2", safari18_2::emulation),
143
+ SafariIos18_1_1 => ("safari_ios_18.1.1", safari_ios_18_1_1::emulation),
144
+ Safari18_3 => ("safari_18.3", safari18_3::emulation),
145
+ Safari18_3_1 => ("safari_18.3.1", safari18_3_1::emulation),
146
+ Safari18_5 => ("safari_18.5", safari18_5::emulation),
147
+ Safari26 => ("safari_26", safari26::emulation),
148
+ Safari26_1 => ("safari_26.1", safari26_1::emulation),
149
+ Safari26_2 => ("safari_26.2", safari26_2::emulation),
150
+ Safari26_3 => ("safari_26.3", safari26_3::emulation),
151
+ Safari26_4 => ("safari_26.4", safari26_4::emulation),
152
+ SafariIPad26 => ("safari_ipad_26", safari_ipad_26::emulation),
153
+ SafariIpad26_2 => ("safari_ipad_26.2", safari_ipad_26_2::emulation),
154
+ SafariIos26 => ("safari_ios_26", safari_ios_26::emulation),
155
+ SafariIos26_2 => ("safari_ios_26.2", safari_ios_26_2::emulation),
156
+
157
+ // OkHttp versions
158
+ OkHttp3_9 => ("okhttp_3.9", okhttp3_9::emulation),
159
+ OkHttp3_11 => ("okhttp_3.11", okhttp3_11::emulation),
160
+ OkHttp3_13 => ("okhttp_3.13", okhttp3_13::emulation),
161
+ OkHttp3_14 => ("okhttp_3.14", okhttp3_14::emulation),
162
+ OkHttp4_9 => ("okhttp_4.9", okhttp4_9::emulation),
163
+ OkHttp4_10 => ("okhttp_4.10", okhttp4_10::emulation),
164
+ OkHttp4_12 => ("okhttp_4.12", okhttp4_12::emulation),
165
+ OkHttp5 => ("okhttp_5", okhttp5::emulation)
166
+
167
+ );
168
+
169
+ impl wreq::IntoEmulation for Profile {
170
+ #[inline]
171
+ fn into_emulation(self) -> wreq::Emulation {
172
+ Emulation::builder().profile(self).build().into_emulation()
173
+ }
174
+ }
175
+
176
+ define_enum!(
177
+ /// Selects which platform the client should look like.
178
+ ///
179
+ /// This mainly affects platform-specific headers and user-agent details.
180
+ /// In most cases you can keep the default unless you need to match a
181
+ /// specific Windows, macOS, Linux, Android, or iOS profile.
182
+ plain,
183
+ Platform, MacOS,
184
+ Windows => "windows",
185
+ MacOS => "macos",
186
+ Linux => "linux",
187
+ Android => "android",
188
+ IOS => "ios"
189
+ );
190
+
191
+ impl Platform {
192
+ #[inline]
193
+ const fn platform(&self) -> &'static str {
194
+ match self {
195
+ Platform::MacOS => "\"macOS\"",
196
+ Platform::Linux => "\"Linux\"",
197
+ Platform::Windows => "\"Windows\"",
198
+ Platform::Android => "\"Android\"",
199
+ Platform::IOS => "\"iOS\"",
200
+ }
201
+ }
202
+
203
+ #[inline]
204
+ const fn is_mobile(&self) -> bool {
205
+ matches!(self, Platform::Android | Platform::IOS)
206
+ }
207
+ }
208
+
209
+ /// Represents the configuration options for emulating a client profile and platform.
210
+ ///
211
+ /// The `Emulation` struct allows you to configure various aspects of profile and platform
212
+ /// emulation, including the profile, platform, and whether to enable certain features
213
+ /// like HTTP/2 or headers.
214
+ #[derive(Default, Clone, TypedBuilder)]
215
+ pub struct Emulation {
216
+ /// Whether to change the profile (browser/okhttp) information.
217
+ #[builder(default)]
218
+ profile: Profile,
219
+
220
+ /// Whether to change the platform (Windows/macOS/Linux/Android/iOS) information.
221
+ #[builder(default)]
222
+ platform: Platform,
223
+
224
+ /// Whether to enable HTTP/2.
225
+ #[builder(default = true)]
226
+ http2: bool,
227
+
228
+ /// Whether to include default headers.
229
+ #[builder(default = true)]
230
+ headers: bool,
231
+ }
232
+
233
+ impl Emulation {
234
+ /// Returns a random variant of the `Profile` enum.
235
+ ///
236
+ /// # Examples
237
+ ///
238
+ /// ```
239
+ /// use wreq_util::Emulation;
240
+ ///
241
+ /// let random_emulation = Emulation::random();
242
+ /// println!("{:?}", random_emulation);
243
+ /// ```
244
+ pub fn random() -> Emulation {
245
+ let rand = crate::rand::fast_random();
246
+ Emulation::builder()
247
+ .profile(Profile::VARIANTS[(rand as usize) % Profile::VARIANTS.len()])
248
+ .platform(Platform::VARIANTS[((rand >> 32) as usize) % Platform::VARIANTS.len()])
249
+ .build()
250
+ }
251
+
252
+ /// Returns a market-share weighted random `Emulation`.
253
+ ///
254
+ /// Unlike [`Emulation::random`], selection is biased toward popular browser
255
+ /// families and their most recent versions, and each profile is only paired
256
+ /// with platforms it ships on.
257
+ ///
258
+ /// Browser family and version weights are derived from StatCounter Global
259
+ /// Stats data retrieved in June 2026:
260
+ ///
261
+ /// Browser market share:
262
+ /// <https://gs.statcounter.com/browser-market-share#monthly-202506-202606>
263
+ ///
264
+ /// Browser version market share:
265
+ /// <https://gs.statcounter.com/browser-version-market-share#monthly-202506-202606>
266
+ ///
267
+ /// StatCounter requests attribution for use of its data. See:
268
+ /// <https://creativecommons.org/licenses/by-sa/3.0/>
269
+ ///
270
+ /// # Examples
271
+ ///
272
+ /// ```
273
+ /// use wreq_util::Emulation;
274
+ ///
275
+ /// let random_emulation = Emulation::weighted_random();
276
+ /// println!("{:?}", random_emulation);
277
+ /// ```
278
+ pub fn weighted_random() -> Emulation {
279
+ use Platform::*;
280
+ use Profile::*;
281
+
282
+ struct Class {
283
+ weight: u32,
284
+ platforms: &'static [Platform],
285
+ profiles: &'static [Profile],
286
+ }
287
+
288
+ // Weights based on StatCounter June 2026 data.
289
+ //
290
+ // Each weight is the family's share percentage multiplied by 100 and
291
+ // rounded to an integer (e.g. Chrome 71.41% -> 7141, Edge 5.02% -> 502,
292
+ // Firefox 2.35% -> 235, Opera 1.73% -> 173). Only relative magnitudes
293
+ // matter, so the common x100 scale is arbitrary but keeps two decimals
294
+ // of precision without floats.
295
+ //
296
+ // Safari's 14.77% is split by platform using the browser-version data:
297
+ // iPhone 11.96% + iPad 0.44% = 12.40% mobile (-> 1240), leaving the
298
+ // remaining 2.37% for desktop/macOS (-> 237).
299
+ const CLASSES: &[Class] = &[
300
+ Class {
301
+ weight: 7141,
302
+ platforms: &[Windows, MacOS, Linux, Android],
303
+ profiles: &[
304
+ Chrome149, Chrome148, Chrome147, Chrome146, Chrome145, Chrome144, Chrome143,
305
+ ],
306
+ },
307
+ Class {
308
+ weight: 1240,
309
+ platforms: &[IOS],
310
+ profiles: &[
311
+ SafariIos26_2,
312
+ SafariIos26,
313
+ SafariIpad26_2,
314
+ SafariIPad26,
315
+ SafariIos18_1_1,
316
+ SafariIPad18,
317
+ ],
318
+ },
319
+ Class {
320
+ weight: 502,
321
+ platforms: &[Windows, MacOS],
322
+ profiles: &[Edge148, Edge147, Edge146, Edge145, Edge144, Edge143],
323
+ },
324
+ Class {
325
+ weight: 237,
326
+ platforms: &[MacOS],
327
+ profiles: &[
328
+ Safari26_4, Safari26_3, Safari26_2, Safari26_1, Safari26, Safari18_5,
329
+ ],
330
+ },
331
+ Class {
332
+ weight: 235,
333
+ platforms: &[Windows, MacOS, Linux],
334
+ profiles: &[
335
+ Firefox151, Firefox150, Firefox149, Firefox148, Firefox147, Firefox146,
336
+ ],
337
+ },
338
+ Class {
339
+ weight: 173,
340
+ platforms: &[Windows, MacOS, Linux, Android],
341
+ profiles: &[Opera131, Opera130, Opera129, Opera128, Opera127, Opera126],
342
+ },
343
+ ];
344
+
345
+ let (r1, r2) = (crate::rand::fast_random(), crate::rand::fast_random());
346
+ let total: u32 = CLASSES.iter().map(|c| c.weight).sum();
347
+ let mut t = (r1 % total as u64) as u32;
348
+ let class = CLASSES
349
+ .iter()
350
+ .find(|c| {
351
+ t = t.checked_sub(c.weight).unwrap_or(u32::MAX);
352
+ t == u32::MAX
353
+ })
354
+ .unwrap_or(&CLASSES[0]);
355
+ let n = class.profiles.len();
356
+ let idx = ((r1 >> 32) as usize % n).min((r2 >> 32) as usize % n);
357
+ Emulation::builder()
358
+ .profile(class.profiles[idx])
359
+ .platform(class.platforms[(r2 as usize) % class.platforms.len()])
360
+ .build()
361
+ }
362
+ }
363
+
364
+ impl wreq::IntoEmulation for Emulation {
365
+ #[inline]
366
+ fn into_emulation(self) -> wreq::Emulation {
367
+ self.profile.match_emulation(self)
368
+ }
369
+ }
@@ -0,0 +1,14 @@
1
+ #![deny(unused)]
2
+ #![deny(unsafe_code)]
3
+ #![cfg_attr(docsrs, feature(doc_cfg))]
4
+ #![cfg_attr(test, deny(warnings))]
5
+ #![cfg_attr(not(test), warn(unused_crate_dependencies))]
6
+ #![doc = include_str!("../README.md")]
7
+
8
+ #[cfg(feature = "emulation")]
9
+ pub mod emulate;
10
+ mod rand;
11
+ pub mod tower;
12
+
13
+ #[cfg(feature = "emulation")]
14
+ pub use self::emulate::{Emulation, Platform, Profile};
@@ -0,0 +1,24 @@
1
+ use std::{
2
+ cell::Cell,
3
+ collections::hash_map::RandomState,
4
+ hash::{BuildHasher, Hasher},
5
+ };
6
+
7
+ // from: https://github.com/seanmonstar/reqwest/blob/5d5bf355744b181d31533501133ad9fbf99e8849/src/util.rs#L28
8
+ pub(crate) fn fast_random() -> u64 {
9
+ thread_local! {
10
+ static KEY: RandomState = RandomState::new();
11
+ static COUNTER: Cell<u64> = const { Cell::new(0) };
12
+ }
13
+
14
+ KEY.with(|key| {
15
+ COUNTER.with(|ctr| {
16
+ let n = ctr.get().wrapping_add(1);
17
+ ctr.set(n);
18
+
19
+ let mut h = key.build_hasher();
20
+ h.write_u64(n);
21
+ h.finish()
22
+ })
23
+ })
24
+ }