Coverage Report

Created: 2026-05-16 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ureq-proto-0.6.0/src/ext.rs
Line
Count
Source
1
use http::{HeaderName, HeaderValue, Method, StatusCode, header};
2
3
#[cfg(feature = "server")]
4
pub(crate) trait StatusCodeExt {
5
    /// Check if the status code requires a body according to HTTP spec.
6
    ///
7
    /// According to the HTTP specification, the following status codes must not include a message body:
8
    /// - 1xx (Informational): 100, 101, etc.
9
    /// - 204 (No Content)
10
    /// - 304 (Not Modified)
11
    ///
12
    /// All other status codes can include a message body.
13
    fn body_allowed(&self) -> bool;
14
}
15
16
#[cfg(feature = "server")]
17
impl StatusCodeExt for StatusCode {
18
    fn body_allowed(&self) -> bool {
19
        !self.is_informational()
20
            && *self != StatusCode::NO_CONTENT
21
            && *self != StatusCode::NOT_MODIFIED
22
    }
23
}
24
25
pub(crate) trait MethodExt {
26
    #[cfg(feature = "client")]
27
    fn is_http10(&self) -> bool;
28
    #[cfg(feature = "client")]
29
    fn is_http11(&self) -> bool;
30
    fn need_request_body(&self) -> bool;
31
    fn allow_request_body(&self) -> bool;
32
    #[cfg(feature = "client")]
33
    fn verify_version(&self, version: http::Version) -> Result<(), crate::Error>;
34
}
35
36
impl MethodExt for Method {
37
    #[cfg(feature = "client")]
38
0
    fn is_http10(&self) -> bool {
39
0
        self == Method::GET || self == Method::HEAD || self == Method::POST
40
0
    }
41
42
    #[cfg(feature = "client")]
43
0
    fn is_http11(&self) -> bool {
44
0
        self == Method::PUT
45
0
            || self == Method::DELETE
46
0
            || self == Method::CONNECT
47
0
            || self == Method::OPTIONS
48
0
            || self == Method::TRACE
49
0
            || self == Method::PATCH
50
0
    }
51
52
0
    fn need_request_body(&self) -> bool {
53
0
        self == Method::POST || self == Method::PUT || self == Method::PATCH
54
0
    }
55
56
0
    fn allow_request_body(&self) -> bool {
57
0
        self != Method::HEAD && self != Method::CONNECT
58
0
    }
59
60
    #[cfg(feature = "client")]
61
0
    fn verify_version(&self, v: http::Version) -> Result<(), crate::Error> {
62
        use crate::Error;
63
        use http::Version;
64
0
        if v != Version::HTTP_10 && v != Version::HTTP_11 {
65
0
            return Err(Error::UnsupportedVersion);
66
0
        }
67
68
0
        let method_ok = self.is_http10() || v == Version::HTTP_11 && self.is_http11();
69
70
0
        if !method_ok {
71
0
            return Err(Error::MethodVersionMismatch(self.clone(), v));
72
0
        }
73
74
0
        Ok(())
75
0
    }
76
}
77
78
pub(crate) trait HeaderIterExt {
79
    fn has(self, key: HeaderName, value: &str) -> bool;
80
    fn has_expect_100(self) -> bool;
81
}
82
83
impl<'a, I: Iterator<Item = (&'a HeaderName, &'a HeaderValue)>> HeaderIterExt for I {
84
0
    fn has(self, key: HeaderName, value: &str) -> bool {
85
0
        self.filter(|i| i.0 == key).any(|i| i.1 == value)
86
0
    }
87
88
0
    fn has_expect_100(self) -> bool {
89
0
        self.has(header::EXPECT, "100-continue")
90
0
    }
91
}
92
93
#[cfg(feature = "client")]
94
pub(crate) trait StatusExt {
95
    /// Detect 307/308 redirect
96
    fn is_redirect_retaining_status(&self) -> bool;
97
}
98
99
#[cfg(feature = "client")]
100
impl StatusExt for StatusCode {
101
0
    fn is_redirect_retaining_status(&self) -> bool {
102
0
        *self == StatusCode::TEMPORARY_REDIRECT || *self == StatusCode::PERMANENT_REDIRECT
103
0
    }
104
}
105
106
#[cfg(feature = "client")]
107
pub trait SchemeExt {
108
    fn default_port(&self) -> Option<u16>;
109
}
110
111
#[cfg(feature = "client")]
112
impl SchemeExt for http::uri::Scheme {
113
0
    fn default_port(&self) -> Option<u16> {
114
        use http::uri::Scheme;
115
0
        if *self == Scheme::HTTPS {
116
0
            Some(443)
117
0
        } else if *self == Scheme::HTTP {
118
0
            Some(80)
119
        } else {
120
0
            debug!("Unknown scheme: {}", self);
121
0
            None
122
        }
123
0
    }
124
}
125
126
#[cfg(feature = "client")]
127
pub(crate) trait AuthorityExt {
128
    fn userinfo(&self) -> Option<&str>;
129
    fn username(&self) -> Option<&str>;
130
    fn password(&self) -> Option<&str>;
131
}
132
133
// NB: Treating &str with direct indexes is OK, since Uri parsed the Authority,
134
// and ensured it's all ASCII (or %-encoded).
135
#[cfg(feature = "client")]
136
impl AuthorityExt for http::uri::Authority {
137
0
    fn userinfo(&self) -> Option<&str> {
138
0
        let s = self.as_str();
139
0
        s.rfind('@').map(|i| &s[..i])
140
0
    }
141
142
0
    fn username(&self) -> Option<&str> {
143
0
        self.userinfo()
144
0
            .map(|a| a.rfind(':').map(|i| &a[..i]).unwrap_or(a))
145
0
    }
146
147
0
    fn password(&self) -> Option<&str> {
148
0
        self.userinfo()
149
0
            .and_then(|a| a.rfind(':').map(|i| &a[i + 1..]))
150
0
    }
151
}