Coverage Report

Created: 2024-12-17 06:15

/rust/registry/src/index.crates.io-6f17d22bba15001f/http-0.2.12/src/uri/path.rs
Line
Count
Source (jump to first uncovered line)
1
use std::convert::TryFrom;
2
use std::str::FromStr;
3
use std::{cmp, fmt, hash, str};
4
5
use bytes::Bytes;
6
7
use super::{ErrorKind, InvalidUri};
8
use crate::byte_str::ByteStr;
9
10
/// Represents the path component of a URI
11
#[derive(Clone)]
12
pub struct PathAndQuery {
13
    pub(super) data: ByteStr,
14
    pub(super) query: u16,
15
}
16
17
const NONE: u16 = ::std::u16::MAX;
18
19
impl PathAndQuery {
20
    // Not public while `bytes` is unstable.
21
1.50M
    pub(super) fn from_shared(mut src: Bytes) -> Result<Self, InvalidUri> {
22
1.50M
        let mut query = NONE;
23
1.50M
        let mut fragment = None;
24
1.50M
25
1.50M
        // block for iterator borrow
26
1.50M
        {
27
1.50M
            let mut iter = src.as_ref().iter().enumerate();
28
29
            // path ...
30
3.83M
            for (i, &b) in &mut iter {
31
                // See https://url.spec.whatwg.org/#path-state
32
2.34M
                match b {
33
                    b'?' => {
34
11.5k
                        debug_assert_eq!(query, NONE);
35
11.5k
                        query = i as u16;
36
11.5k
                        break;
37
                    }
38
                    b'#' => {
39
208
                        fragment = Some(i);
40
208
                        break;
41
                    }
42
43
                    // This is the range of bytes that don't need to be
44
                    // percent-encoded in the path. If it should have been
45
                    // percent-encoded, then error.
46
                    0x21 |
47
2.32M
                    0x24..=0x3B |
48
                    0x3D |
49
1.10M
                    0x40..=0x5F |
50
1.02M
                    0x61..=0x7A |
51
                    0x7C |
52
2.32M
                    0x7E => {},
53
54
                    // These are code points that are supposed to be
55
                    // percent-encoded in the path but there are clients
56
                    // out there sending them as is and httparse accepts
57
                    // to parse those requests, so they are allowed here
58
                    // for parity.
59
                    //
60
                    // For reference, those are code points that are used
61
                    // to send requests with JSON directly embedded in
62
                    // the URI path. Yes, those things happen for real.
63
                    b'"' |
64
2.00k
                    b'{' | b'}' => {},
65
66
1.79k
                    _ => return Err(ErrorKind::InvalidUriChar.into()),
67
                }
68
            }
69
70
            // query ...
71
1.49M
            if query != NONE {
72
643k
                for (i, &b) in iter {
73
631k
                    match b {
74
                        // While queries *should* be percent-encoded, most
75
                        // bytes are actually allowed...
76
                        // See https://url.spec.whatwg.org/#query-state
77
                        //
78
                        // Allowed: 0x21 / 0x24 - 0x3B / 0x3D / 0x3F - 0x7E
79
                        0x21 |
80
631k
                        0x24..=0x3B |
81
                        0x3D |
82
631k
                        0x3F..=0x7E => {},
83
84
                        b'#' => {
85
118
                            fragment = Some(i);
86
118
                            break;
87
                        }
88
89
242
                        _ => return Err(ErrorKind::InvalidUriChar.into()),
90
                    }
91
                }
92
1.48M
            }
93
        }
94
95
1.49M
        if let Some(i) = fragment {
96
326
            src.truncate(i);
97
1.49M
        }
98
99
1.49M
        Ok(PathAndQuery {
100
1.49M
            data: unsafe { ByteStr::from_utf8_unchecked(src) },
101
1.49M
            query: query,
102
1.49M
        })
103
1.50M
    }
104
105
    /// Convert a `PathAndQuery` from a static string.
106
    ///
107
    /// This function will not perform any copying, however the string is
108
    /// checked to ensure that it is valid.
109
    ///
110
    /// # Panics
111
    ///
112
    /// This function panics if the argument is an invalid path and query.
113
    ///
114
    /// # Examples
115
    ///
116
    /// ```
117
    /// # use http::uri::*;
118
    /// let v = PathAndQuery::from_static("/hello?world");
119
    ///
120
    /// assert_eq!(v.path(), "/hello");
121
    /// assert_eq!(v.query(), Some("world"));
122
    /// ```
123
    #[inline]
124
0
    pub fn from_static(src: &'static str) -> Self {
125
0
        let src = Bytes::from_static(src.as_bytes());
126
0
127
0
        PathAndQuery::from_shared(src).unwrap()
128
0
    }
129
130
    /// Attempt to convert a `Bytes` buffer to a `PathAndQuery`.
131
    ///
132
    /// This will try to prevent a copy if the type passed is the type used
133
    /// internally, and will copy the data if it is not.
134
0
    pub fn from_maybe_shared<T>(src: T) -> Result<Self, InvalidUri>
135
0
    where
136
0
        T: AsRef<[u8]> + 'static,
137
0
    {
138
0
        if_downcast_into!(T, Bytes, src, {
139
0
            return PathAndQuery::from_shared(src);
140
        });
141
142
0
        PathAndQuery::try_from(src.as_ref())
143
0
    }
Unexecuted instantiation: <http::uri::path::PathAndQuery>::from_maybe_shared::<_>
Unexecuted instantiation: <http::uri::path::PathAndQuery>::from_maybe_shared::<bytes::bytes::Bytes>
Unexecuted instantiation: <http::uri::path::PathAndQuery>::from_maybe_shared::<bytes::bytes::Bytes>
Unexecuted instantiation: <http::uri::path::PathAndQuery>::from_maybe_shared::<bytes::bytes::Bytes>
144
145
458k
    pub(super) fn empty() -> Self {
146
458k
        PathAndQuery {
147
458k
            data: ByteStr::new(),
148
458k
            query: NONE,
149
458k
        }
150
458k
    }
151
152
2.84M
    pub(super) fn slash() -> Self {
153
2.84M
        PathAndQuery {
154
2.84M
            data: ByteStr::from_static("/"),
155
2.84M
            query: NONE,
156
2.84M
        }
157
2.84M
    }
158
159
737k
    pub(super) fn star() -> Self {
160
737k
        PathAndQuery {
161
737k
            data: ByteStr::from_static("*"),
162
737k
            query: NONE,
163
737k
        }
164
737k
    }
165
166
    /// Returns the path component
167
    ///
168
    /// The path component is **case sensitive**.
169
    ///
170
    /// ```notrust
171
    /// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
172
    ///                                        |--------|
173
    ///                                             |
174
    ///                                           path
175
    /// ```
176
    ///
177
    /// If the URI is `*` then the path component is equal to `*`.
178
    ///
179
    /// # Examples
180
    ///
181
    /// ```
182
    /// # use http::uri::*;
183
    ///
184
    /// let path_and_query: PathAndQuery = "/hello/world".parse().unwrap();
185
    ///
186
    /// assert_eq!(path_and_query.path(), "/hello/world");
187
    /// ```
188
    #[inline]
189
1.07M
    pub fn path(&self) -> &str {
190
1.07M
        let ret = if self.query == NONE {
191
1.07M
            &self.data[..]
192
        } else {
193
6.02k
            &self.data[..self.query as usize]
194
        };
195
196
1.07M
        if ret.is_empty() {
197
279k
            return "/";
198
799k
        }
199
799k
200
799k
        ret
201
1.07M
    }
<http::uri::path::PathAndQuery>::path
Line
Count
Source
189
1.07M
    pub fn path(&self) -> &str {
190
1.07M
        let ret = if self.query == NONE {
191
1.07M
            &self.data[..]
192
        } else {
193
6.02k
            &self.data[..self.query as usize]
194
        };
195
196
1.07M
        if ret.is_empty() {
197
279k
            return "/";
198
799k
        }
199
799k
200
799k
        ret
201
1.07M
    }
Unexecuted instantiation: <http::uri::path::PathAndQuery>::path
Unexecuted instantiation: <http::uri::path::PathAndQuery>::path
Unexecuted instantiation: <http::uri::path::PathAndQuery>::path
Unexecuted instantiation: <http::uri::path::PathAndQuery>::path
202
203
    /// Returns the query string component
204
    ///
205
    /// The query component contains non-hierarchical data that, along with data
206
    /// in the path component, serves to identify a resource within the scope of
207
    /// the URI's scheme and naming authority (if any). The query component is
208
    /// indicated by the first question mark ("?") character and terminated by a
209
    /// number sign ("#") character or by the end of the URI.
210
    ///
211
    /// ```notrust
212
    /// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
213
    ///                                                   |-------------------|
214
    ///                                                             |
215
    ///                                                           query
216
    /// ```
217
    ///
218
    /// # Examples
219
    ///
220
    /// With a query string component
221
    ///
222
    /// ```
223
    /// # use http::uri::*;
224
    /// let path_and_query: PathAndQuery = "/hello/world?key=value&foo=bar".parse().unwrap();
225
    ///
226
    /// assert_eq!(path_and_query.query(), Some("key=value&foo=bar"));
227
    /// ```
228
    ///
229
    /// Without a query string component
230
    ///
231
    /// ```
232
    /// # use http::uri::*;
233
    /// let path_and_query: PathAndQuery = "/hello/world".parse().unwrap();
234
    ///
235
    /// assert!(path_and_query.query().is_none());
236
    /// ```
237
    #[inline]
238
1.23M
    pub fn query(&self) -> Option<&str> {
239
1.23M
        if self.query == NONE {
240
1.22M
            None
241
        } else {
242
6.02k
            let i = self.query + 1;
243
6.02k
            Some(&self.data[i as usize..])
244
        }
245
1.23M
    }
<http::uri::path::PathAndQuery>::query
Line
Count
Source
238
1.23M
    pub fn query(&self) -> Option<&str> {
239
1.23M
        if self.query == NONE {
240
1.22M
            None
241
        } else {
242
6.02k
            let i = self.query + 1;
243
6.02k
            Some(&self.data[i as usize..])
244
        }
245
1.23M
    }
Unexecuted instantiation: <http::uri::path::PathAndQuery>::query
Unexecuted instantiation: <http::uri::path::PathAndQuery>::query
246
247
    /// Returns the path and query as a string component.
248
    ///
249
    /// # Examples
250
    ///
251
    /// With a query string component
252
    ///
253
    /// ```
254
    /// # use http::uri::*;
255
    /// let path_and_query: PathAndQuery = "/hello/world?key=value&foo=bar".parse().unwrap();
256
    ///
257
    /// assert_eq!(path_and_query.as_str(), "/hello/world?key=value&foo=bar");
258
    /// ```
259
    ///
260
    /// Without a query string component
261
    ///
262
    /// ```
263
    /// # use http::uri::*;
264
    /// let path_and_query: PathAndQuery = "/hello/world".parse().unwrap();
265
    ///
266
    /// assert_eq!(path_and_query.as_str(), "/hello/world");
267
    /// ```
268
    #[inline]
269
246k
    pub fn as_str(&self) -> &str {
270
246k
        let ret = &self.data[..];
271
246k
        if ret.is_empty() {
272
0
            return "/";
273
246k
        }
274
246k
        ret
275
246k
    }
Unexecuted instantiation: <http::uri::path::PathAndQuery>::as_str
Unexecuted instantiation: <http::uri::path::PathAndQuery>::as_str
Unexecuted instantiation: <http::uri::path::PathAndQuery>::as_str
<http::uri::path::PathAndQuery>::as_str
Line
Count
Source
269
246k
    pub fn as_str(&self) -> &str {
270
246k
        let ret = &self.data[..];
271
246k
        if ret.is_empty() {
272
0
            return "/";
273
246k
        }
274
246k
        ret
275
246k
    }
Unexecuted instantiation: <http::uri::path::PathAndQuery>::as_str
Unexecuted instantiation: <http::uri::path::PathAndQuery>::as_str
Unexecuted instantiation: <http::uri::path::PathAndQuery>::as_str
276
}
277
278
impl<'a> TryFrom<&'a [u8]> for PathAndQuery {
279
    type Error = InvalidUri;
280
    #[inline]
281
538k
    fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
282
538k
        PathAndQuery::from_shared(Bytes::copy_from_slice(s))
283
538k
    }
Unexecuted instantiation: <http::uri::path::PathAndQuery as core::convert::TryFrom<&[u8]>>::try_from
Unexecuted instantiation: <http::uri::path::PathAndQuery as core::convert::TryFrom<&[u8]>>::try_from
<http::uri::path::PathAndQuery as core::convert::TryFrom<&[u8]>>::try_from
Line
Count
Source
281
538k
    fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
282
538k
        PathAndQuery::from_shared(Bytes::copy_from_slice(s))
283
538k
    }
Unexecuted instantiation: <http::uri::path::PathAndQuery as core::convert::TryFrom<&[u8]>>::try_from
Unexecuted instantiation: <http::uri::path::PathAndQuery as core::convert::TryFrom<&[u8]>>::try_from
Unexecuted instantiation: <http::uri::path::PathAndQuery as core::convert::TryFrom<&[u8]>>::try_from
284
}
285
286
impl<'a> TryFrom<&'a str> for PathAndQuery {
287
    type Error = InvalidUri;
288
    #[inline]
289
538k
    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
290
538k
        TryFrom::try_from(s.as_bytes())
291
538k
    }
Unexecuted instantiation: <http::uri::path::PathAndQuery as core::convert::TryFrom<&str>>::try_from
<http::uri::path::PathAndQuery as core::convert::TryFrom<&str>>::try_from
Line
Count
Source
289
538k
    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
290
538k
        TryFrom::try_from(s.as_bytes())
291
538k
    }
Unexecuted instantiation: <http::uri::path::PathAndQuery as core::convert::TryFrom<&str>>::try_from
292
}
293
294
impl<'a> TryFrom<Vec<u8>> for PathAndQuery {
295
    type Error = InvalidUri;
296
    #[inline]
297
0
    fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
298
0
        PathAndQuery::from_shared(vec.into())
299
0
    }
300
}
301
302
impl TryFrom<String> for PathAndQuery {
303
    type Error = InvalidUri;
304
    #[inline]
305
0
    fn try_from(s: String) -> Result<Self, Self::Error> {
306
0
        PathAndQuery::from_shared(s.into())
307
0
    }
Unexecuted instantiation: <http::uri::path::PathAndQuery as core::convert::TryFrom<alloc::string::String>>::try_from
Unexecuted instantiation: <http::uri::path::PathAndQuery as core::convert::TryFrom<alloc::string::String>>::try_from
308
}
309
310
impl TryFrom<&String> for PathAndQuery {
311
    type Error = InvalidUri;
312
    #[inline]
313
0
    fn try_from(s: &String) -> Result<Self, Self::Error> {
314
0
        TryFrom::try_from(s.as_bytes())
315
0
    }
316
}
317
318
impl FromStr for PathAndQuery {
319
    type Err = InvalidUri;
320
    #[inline]
321
0
    fn from_str(s: &str) -> Result<Self, InvalidUri> {
322
0
        TryFrom::try_from(s)
323
0
    }
Unexecuted instantiation: <http::uri::path::PathAndQuery as core::str::traits::FromStr>::from_str
Unexecuted instantiation: <http::uri::path::PathAndQuery as core::str::traits::FromStr>::from_str
Unexecuted instantiation: <http::uri::path::PathAndQuery as core::str::traits::FromStr>::from_str
324
}
325
326
impl fmt::Debug for PathAndQuery {
327
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328
0
        fmt::Display::fmt(self, f)
329
0
    }
330
}
331
332
impl fmt::Display for PathAndQuery {
333
0
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
334
0
        if !self.data.is_empty() {
335
0
            match self.data.as_bytes()[0] {
336
0
                b'/' | b'*' => write!(fmt, "{}", &self.data[..]),
337
0
                _ => write!(fmt, "/{}", &self.data[..]),
338
            }
339
        } else {
340
0
            write!(fmt, "/")
341
        }
342
0
    }
343
}
344
345
impl hash::Hash for PathAndQuery {
346
0
    fn hash<H: hash::Hasher>(&self, state: &mut H) {
347
0
        self.data.hash(state);
348
0
    }
349
}
350
351
// ===== PartialEq / PartialOrd =====
352
353
impl PartialEq for PathAndQuery {
354
    #[inline]
355
0
    fn eq(&self, other: &PathAndQuery) -> bool {
356
0
        self.data == other.data
357
0
    }
358
}
359
360
impl Eq for PathAndQuery {}
361
362
impl PartialEq<str> for PathAndQuery {
363
    #[inline]
364
0
    fn eq(&self, other: &str) -> bool {
365
0
        self.as_str() == other
366
0
    }
Unexecuted instantiation: <http::uri::path::PathAndQuery as core::cmp::PartialEq<str>>::eq
Unexecuted instantiation: <http::uri::path::PathAndQuery as core::cmp::PartialEq<str>>::eq
Unexecuted instantiation: <http::uri::path::PathAndQuery as core::cmp::PartialEq<str>>::eq
367
}
368
369
impl<'a> PartialEq<PathAndQuery> for &'a str {
370
    #[inline]
371
0
    fn eq(&self, other: &PathAndQuery) -> bool {
372
0
        self == &other.as_str()
373
0
    }
374
}
375
376
impl<'a> PartialEq<&'a str> for PathAndQuery {
377
    #[inline]
378
0
    fn eq(&self, other: &&'a str) -> bool {
379
0
        self.as_str() == *other
380
0
    }
381
}
382
383
impl PartialEq<PathAndQuery> for str {
384
    #[inline]
385
0
    fn eq(&self, other: &PathAndQuery) -> bool {
386
0
        self == other.as_str()
387
0
    }
388
}
389
390
impl PartialEq<String> for PathAndQuery {
391
    #[inline]
392
0
    fn eq(&self, other: &String) -> bool {
393
0
        self.as_str() == other.as_str()
394
0
    }
395
}
396
397
impl PartialEq<PathAndQuery> for String {
398
    #[inline]
399
0
    fn eq(&self, other: &PathAndQuery) -> bool {
400
0
        self.as_str() == other.as_str()
401
0
    }
402
}
403
404
impl PartialOrd for PathAndQuery {
405
    #[inline]
406
0
    fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
407
0
        self.as_str().partial_cmp(other.as_str())
408
0
    }
409
}
410
411
impl PartialOrd<str> for PathAndQuery {
412
    #[inline]
413
0
    fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
414
0
        self.as_str().partial_cmp(other)
415
0
    }
416
}
417
418
impl PartialOrd<PathAndQuery> for str {
419
    #[inline]
420
0
    fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
421
0
        self.partial_cmp(other.as_str())
422
0
    }
423
}
424
425
impl<'a> PartialOrd<&'a str> for PathAndQuery {
426
    #[inline]
427
0
    fn partial_cmp(&self, other: &&'a str) -> Option<cmp::Ordering> {
428
0
        self.as_str().partial_cmp(*other)
429
0
    }
430
}
431
432
impl<'a> PartialOrd<PathAndQuery> for &'a str {
433
    #[inline]
434
0
    fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
435
0
        self.partial_cmp(&other.as_str())
436
0
    }
437
}
438
439
impl PartialOrd<String> for PathAndQuery {
440
    #[inline]
441
0
    fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
442
0
        self.as_str().partial_cmp(other.as_str())
443
0
    }
444
}
445
446
impl PartialOrd<PathAndQuery> for String {
447
    #[inline]
448
0
    fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
449
0
        self.as_str().partial_cmp(other.as_str())
450
0
    }
451
}
452
453
#[cfg(test)]
454
mod tests {
455
    use super::*;
456
457
    #[test]
458
    fn equal_to_self_of_same_path() {
459
        let p1: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
460
        let p2: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
461
        assert_eq!(p1, p2);
462
        assert_eq!(p2, p1);
463
    }
464
465
    #[test]
466
    fn not_equal_to_self_of_different_path() {
467
        let p1: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
468
        let p2: PathAndQuery = "/world&foo=bar".parse().unwrap();
469
        assert_ne!(p1, p2);
470
        assert_ne!(p2, p1);
471
    }
472
473
    #[test]
474
    fn equates_with_a_str() {
475
        let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
476
        assert_eq!(&path_and_query, "/hello/world&foo=bar");
477
        assert_eq!("/hello/world&foo=bar", &path_and_query);
478
        assert_eq!(path_and_query, "/hello/world&foo=bar");
479
        assert_eq!("/hello/world&foo=bar", path_and_query);
480
    }
481
482
    #[test]
483
    fn not_equal_with_a_str_of_a_different_path() {
484
        let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
485
        // as a reference
486
        assert_ne!(&path_and_query, "/hello&foo=bar");
487
        assert_ne!("/hello&foo=bar", &path_and_query);
488
        // without reference
489
        assert_ne!(path_and_query, "/hello&foo=bar");
490
        assert_ne!("/hello&foo=bar", path_and_query);
491
    }
492
493
    #[test]
494
    fn equates_with_a_string() {
495
        let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
496
        assert_eq!(path_and_query, "/hello/world&foo=bar".to_string());
497
        assert_eq!("/hello/world&foo=bar".to_string(), path_and_query);
498
    }
499
500
    #[test]
501
    fn not_equal_with_a_string_of_a_different_path() {
502
        let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
503
        assert_ne!(path_and_query, "/hello&foo=bar".to_string());
504
        assert_ne!("/hello&foo=bar".to_string(), path_and_query);
505
    }
506
507
    #[test]
508
    fn compares_to_self() {
509
        let p1: PathAndQuery = "/a/world&foo=bar".parse().unwrap();
510
        let p2: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
511
        assert!(p1 < p2);
512
        assert!(p2 > p1);
513
    }
514
515
    #[test]
516
    fn compares_with_a_str() {
517
        let path_and_query: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
518
        // by ref
519
        assert!(&path_and_query < "/c/world&foo=bar");
520
        assert!("/c/world&foo=bar" > &path_and_query);
521
        assert!(&path_and_query > "/a/world&foo=bar");
522
        assert!("/a/world&foo=bar" < &path_and_query);
523
524
        // by val
525
        assert!(path_and_query < "/c/world&foo=bar");
526
        assert!("/c/world&foo=bar" > path_and_query);
527
        assert!(path_and_query > "/a/world&foo=bar");
528
        assert!("/a/world&foo=bar" < path_and_query);
529
    }
530
531
    #[test]
532
    fn compares_with_a_string() {
533
        let path_and_query: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
534
        assert!(path_and_query < "/c/world&foo=bar".to_string());
535
        assert!("/c/world&foo=bar".to_string() > path_and_query);
536
        assert!(path_and_query > "/a/world&foo=bar".to_string());
537
        assert!("/a/world&foo=bar".to_string() < path_and_query);
538
    }
539
540
    #[test]
541
    fn ignores_valid_percent_encodings() {
542
        assert_eq!("/a%20b", pq("/a%20b?r=1").path());
543
        assert_eq!("qr=%31", pq("/a/b?qr=%31").query().unwrap());
544
    }
545
546
    #[test]
547
    fn ignores_invalid_percent_encodings() {
548
        assert_eq!("/a%%b", pq("/a%%b?r=1").path());
549
        assert_eq!("/aaa%", pq("/aaa%").path());
550
        assert_eq!("/aaa%", pq("/aaa%?r=1").path());
551
        assert_eq!("/aa%2", pq("/aa%2").path());
552
        assert_eq!("/aa%2", pq("/aa%2?r=1").path());
553
        assert_eq!("qr=%3", pq("/a/b?qr=%3").query().unwrap());
554
    }
555
556
    #[test]
557
    fn json_is_fine() {
558
        assert_eq!(r#"/{"bread":"baguette"}"#, pq(r#"/{"bread":"baguette"}"#).path());
559
    }
560
561
    fn pq(s: &str) -> PathAndQuery {
562
        s.parse().expect(&format!("parsing {}", s))
563
    }
564
}