1use crate::{Dummy, Fake, Faker};
2use http::uri;
3use rand::seq::IndexedRandom;
4use rand::Rng;
5use std::mem;
6use std::net::Ipv4Addr;
7use std::str::FromStr;
8
9const RFC_STATUS_CODES: &[u16] = &[
10 100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 304, 305,
11 307, 308, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416,
12 417, 418, 421, 422, 423, 424, 426, 428, 429, 431, 451, 500, 501, 502, 503, 504, 505, 506, 507,
13 508, 510, 511,
14];
15
16const VALID_SCHEME_CHARACTERS: &[char] = &[
17 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
18 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
19 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4',
20 '5', '6', '7', '8', '9', '.', '-', '+',
21];
22
23impl Dummy<Faker> for http::Method {
24 fn dummy_with_rng<R: Rng + ?Sized>(_: &Faker, rng: &mut R) -> Self {
25 let i: u8 = (0..9).fake_with_rng(rng);
26 match i {
27 0 => http::Method::GET,
28 1 => http::Method::POST,
29 2 => http::Method::PUT,
30 3 => http::Method::DELETE,
31 4 => http::Method::HEAD,
32 5 => http::Method::OPTIONS,
33 6 => http::Method::CONNECT,
34 7 => http::Method::PATCH,
35 _ => http::Method::TRACE,
36 }
37 }
38}
39
40impl Dummy<Faker> for http::HeaderValue {
41 fn dummy_with_rng<R: Rng + ?Sized>(config: &Faker, rng: &mut R) -> Self {
42 let val = config.fake_with_rng::<String, _>(rng);
43 http::HeaderValue::try_from(val).unwrap()
44 }
45}
46
47impl Dummy<Faker> for http::HeaderName {
48 fn dummy_with_rng<R: Rng + ?Sized>(config: &Faker, rng: &mut R) -> Self {
49 let val = config.fake_with_rng::<String, _>(rng);
50 http::HeaderName::try_from(val).unwrap()
51 }
52}
53
54impl<T: Dummy<Faker>> Dummy<Faker> for http::HeaderMap<T> {
55 fn dummy_with_rng<R: Rng + ?Sized>(config: &Faker, rng: &mut R) -> Self {
56 let len = rng.random_range(1..10);
57 let mut map = http::HeaderMap::with_capacity(len);
58 for _ in 0..len {
59 let name: http::HeaderName = config.fake_with_rng(rng);
60 if rng.random_bool(0.7) {
61 map.insert(name, config.fake_with_rng(rng));
62 } else {
63 for _ in 0..rng.random_range(1..5) {
64 map.append(&name, config.fake_with_rng(rng));
65 }
66 }
67 }
68 map
69 }
70}
71
72impl Dummy<Faker> for http::StatusCode {
73 fn dummy_with_rng<R: Rng + ?Sized>(_: &Faker, rng: &mut R) -> Self {
74 let code = RFC_STATUS_CODES.choose(rng).unwrap();
75 http::StatusCode::from_u16(*code).unwrap()
76 }
77}
78
79impl Dummy<&[u16]> for Result<http::StatusCode, http::status::InvalidStatusCode> {
80 fn dummy_with_rng<R: Rng + ?Sized>(codes: &&[u16], rng: &mut R) -> Self {
81 let code = codes.choose(rng).expect("no codes provided");
82 http::StatusCode::from_u16(*code)
83 }
84}
85
86impl Dummy<&[u16]> for http::StatusCode {
87 fn dummy_with_rng<R: Rng + ?Sized>(codes: &&[u16], rng: &mut R) -> Self {
88 let code = codes.choose(rng).expect("no codes provided");
89 http::StatusCode::from_u16(*code).expect("invalid status code")
90 }
91}
92
93impl Dummy<Faker> for http::Version {
94 fn dummy_with_rng<R: Rng + ?Sized>(_: &Faker, rng: &mut R) -> Self {
95 let i: u8 = (0..4).fake_with_rng(rng);
96 match i {
97 0 => http::Version::HTTP_2,
98 1 => http::Version::HTTP_10,
99 2 => http::Version::HTTP_09,
100 _ => http::Version::HTTP_11,
101 }
102 }
103}
104
105impl Dummy<Faker> for http::uri::Authority {
106 fn dummy_with_rng<R: Rng + ?Sized>(config: &Faker, rng: &mut R) -> Self {
107 let mut authority = String::new();
108
109 if rng.random_bool(0.5) {
110 let user = config.fake_with_rng::<String, _>(rng);
112 let username = url_escape::encode_userinfo(&user);
113 authority.push_str(&username);
114 if rng.random_bool(0.5) {
115 authority.push(':');
116 let pass = config.fake_with_rng::<String, _>(rng);
117 let password = url_escape::encode_userinfo(&pass);
118 authority.push_str(&password);
119 }
120 authority.push('@');
121 }
122
123 let host_type = (0..3).fake_with_rng(rng);
124 match host_type {
125 0 => {
126 authority.push_str("localhost");
127 if rng.random_bool(0.5) {
129 let port_num = config.fake_with_rng::<u16, _>(rng);
130 authority.push(':');
131 authority.push_str(&port_num.to_string());
132 }
133 }
134 1 => {
135 let ip: Ipv4Addr = config.fake_with_rng(rng);
137 authority.push_str(&ip.to_string());
138 }
139 _ => {
140 if rng.random_bool(0.5) {
141 authority.push_str("www.");
142 }
143 let host_len = (1..100).fake_with_rng(rng);
144 authority
145 .extend((0..host_len).map(|_| *VALID_SCHEME_CHARACTERS.choose(rng).unwrap()));
146
147 if rng.random_bool(0.5) {
149 let port_num = config.fake_with_rng::<u16, _>(rng);
150 authority.push(':');
151 authority.push_str(&port_num.to_string());
152 }
153 }
154 }
155 uri::Authority::try_from(authority).unwrap()
156 }
157}
158
159impl Dummy<Faker> for http::uri::Scheme {
160 fn dummy_with_rng<R: Rng + ?Sized>(_: &Faker, rng: &mut R) -> Self {
161 let scheme = match (0..7).fake_with_rng(rng) {
163 0 => "http".to_string(),
164 1 => "https".to_string(),
165 2 => "ws".to_string(),
166 3 => "wss".to_string(),
167 4 => "ftp".to_string(),
168 5 => "git".to_string(),
169 _ => {
170 let len = rng.random_range(1..29);
175 let mut scheme = String::with_capacity(len);
176 scheme.push(rng.random_range(b'a'..=b'z') as char);
177 if rng.random_bool(0.5) {
178 scheme.make_ascii_uppercase();
179 }
180 scheme.extend((1..len).map(|_| *VALID_SCHEME_CHARACTERS.choose(rng).unwrap()));
181
182 scheme
183 }
184 };
185 uri::Scheme::from_str(&scheme).unwrap()
186 }
187}
188
189impl Dummy<Faker> for http::uri::PathAndQuery {
190 fn dummy_with_rng<R: Rng + ?Sized>(config: &Faker, rng: &mut R) -> Self {
191 let mut path = format!(
192 "/{}",
193 url_escape::encode_path(&config.fake_with_rng::<String, _>(rng))
194 );
195
196 if rng.random_bool(0.5) {
197 path.push('?');
198 let mut query_parts = vec![];
199 let query_len = (2..5).fake_with_rng(rng);
200 for _ in 1..query_len {
201 let key = url_escape::encode_component(&config.fake_with_rng::<String, _>(rng))
202 .to_string();
203 let value = url_escape::encode_component(&config.fake_with_rng::<String, _>(rng))
204 .to_string();
205 query_parts.push(format!("{}={}", key, value));
206 }
207 path.push_str(&query_parts.join("&"));
208 }
209 uri::PathAndQuery::try_from(path).unwrap()
210 }
211}
212
213impl Dummy<Faker> for http::Uri {
214 fn dummy_with_rng<R: Rng + ?Sized>(config: &Faker, rng: &mut R) -> Self {
215 let scheme = config.fake_with_rng::<uri::Scheme, _>(rng);
216 let authority = config.fake_with_rng::<uri::Authority, _>(rng);
217 let path = config.fake_with_rng::<uri::PathAndQuery, _>(rng);
218
219 uri::Builder::new()
220 .scheme(scheme)
221 .authority(authority)
222 .path_and_query(path)
223 .build()
224 .unwrap()
225 }
226}
227
228impl<T: Dummy<Faker>> Dummy<Faker> for http::Request<T> {
229 fn dummy_with_rng<R: Rng + ?Sized>(config: &Faker, rng: &mut R) -> Self {
230 let method: http::Method = config.fake_with_rng(rng);
231 let uri: http::Uri = config.fake_with_rng(rng);
232 let mut req = http::Request::builder()
233 .method(method)
234 .uri(uri)
235 .version(config.fake_with_rng(rng));
236 let mut headers: http::HeaderMap = config.fake_with_rng(rng);
237 mem::swap(req.headers_mut().unwrap(), &mut headers);
238 req.body(config.fake_with_rng(rng)).unwrap()
239 }
240}
241
242impl<T: Dummy<Faker>> Dummy<Faker> for http::Response<T> {
243 fn dummy_with_rng<R: Rng + ?Sized>(config: &Faker, rng: &mut R) -> Self {
244 let status: http::StatusCode = config.fake_with_rng(rng);
245 let mut res = http::Response::builder()
246 .status(status)
247 .version(config.fake_with_rng(rng));
248 let mut headers: http::HeaderMap = config.fake_with_rng(rng);
249 mem::swap(res.headers_mut().unwrap(), &mut headers);
250 res.body(config.fake_with_rng(rng)).unwrap()
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 #[test]
259 fn valid_urls_generated() {
260 for _ in 0..1000 {
261 let _url = Faker.fake::<http::Uri>();
262 println!("{}", _url);
263 }
264 }
265}