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.
- checksums.yaml +4 -4
- data/Cargo.lock +3 -7
- data/Cargo.toml +6 -1
- data/crates/wreq-util/.github/FUNDING.yml +15 -0
- data/crates/wreq-util/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- data/crates/wreq-util/.github/ISSUE_TEMPLATE/custom.md +10 -0
- data/crates/wreq-util/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/crates/wreq-util/.github/dependabot.yml +22 -0
- data/crates/wreq-util/.github/workflows/ci.yml +82 -0
- data/crates/wreq-util/.github/workflows/release-plz.yml +52 -0
- data/crates/wreq-util/.gitignore +24 -0
- data/crates/wreq-util/CHANGELOG.md +74 -0
- data/crates/wreq-util/Cargo.toml +94 -0
- data/crates/wreq-util/LICENSE +201 -0
- data/crates/wreq-util/README.md +62 -0
- data/crates/wreq-util/examples/emulate.rs +39 -0
- data/crates/wreq-util/examples/tower_delay.rs +191 -0
- data/crates/wreq-util/release-plz.toml +2 -0
- data/crates/wreq-util/rustfmt.toml +5 -0
- data/crates/wreq-util/src/emulate/compress.rs +85 -0
- data/crates/wreq-util/src/emulate/macros.rs +370 -0
- data/crates/wreq-util/src/emulate/profile/chrome/header.rs +59 -0
- data/crates/wreq-util/src/emulate/profile/chrome/http2.rs +65 -0
- data/crates/wreq-util/src/emulate/profile/chrome/tls.rs +153 -0
- data/crates/wreq-util/src/emulate/profile/chrome.rs +2028 -0
- data/crates/wreq-util/src/emulate/profile/firefox/header.rs +21 -0
- data/crates/wreq-util/src/emulate/profile/firefox/http2.rs +113 -0
- data/crates/wreq-util/src/emulate/profile/firefox/tls.rs +253 -0
- data/crates/wreq-util/src/emulate/profile/firefox.rs +518 -0
- data/crates/wreq-util/src/emulate/profile/okhttp.rs +241 -0
- data/crates/wreq-util/src/emulate/profile/opera/header.rs +30 -0
- data/crates/wreq-util/src/emulate/profile/opera/http2.rs +50 -0
- data/crates/wreq-util/src/emulate/profile/opera/tls.rs +97 -0
- data/crates/wreq-util/src/emulate/profile/opera.rs +299 -0
- data/crates/wreq-util/src/emulate/profile/safari/header.rs +59 -0
- data/crates/wreq-util/src/emulate/profile/safari/http2.rs +116 -0
- data/crates/wreq-util/src/emulate/profile/safari/tls.rs +177 -0
- data/crates/wreq-util/src/emulate/profile/safari.rs +222 -0
- data/crates/wreq-util/src/emulate/profile.rs +47 -0
- data/crates/wreq-util/src/emulate.rs +369 -0
- data/crates/wreq-util/src/lib.rs +14 -0
- data/crates/wreq-util/src/rand.rs +24 -0
- data/crates/wreq-util/src/tower/delay/future.rs +43 -0
- data/crates/wreq-util/src/tower/delay/layer.rs +201 -0
- data/crates/wreq-util/src/tower/delay/service.rs +190 -0
- data/crates/wreq-util/src/tower/delay.rs +98 -0
- data/crates/wreq-util/src/tower.rs +7 -0
- data/crates/wreq-util/tests/client.rs +51 -0
- data/crates/wreq-util/tests/emulate_chrome.rs +270 -0
- data/crates/wreq-util/tests/emulate_firefox.rs +92 -0
- data/crates/wreq-util/tests/emulate_okhttp.rs +46 -0
- data/crates/wreq-util/tests/emulate_opera.rs +113 -0
- data/crates/wreq-util/tests/emulate_safari.rs +151 -0
- data/crates/wreq-util/tests/support/mod.rs +55 -0
- data/crates/wreq-util/tests/support/server.rs +232 -0
- data/examples/emulate_request.rb +1 -1
- data/lib/wreq.rb +20 -18
- data/lib/wreq_ruby/body.rb +3 -0
- data/lib/wreq_ruby/client.rb +18 -18
- data/lib/wreq_ruby/cookie.rb +2 -2
- data/lib/wreq_ruby/emulate.rb +3 -1
- data/lib/wreq_ruby/header.rb +205 -114
- data/lib/wreq_ruby/response.rb +28 -3
- data/src/emulate.rs +1 -0
- data/src/error.rs +2 -6
- data/src/header/helper.rs +112 -0
- data/src/header.rs +198 -133
- data/test/header_test.rb +228 -192
- data/test/stream_test.rb +36 -0
- 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
|
+
}
|