-
-
Notifications
You must be signed in to change notification settings - Fork 244
Expand file tree
/
Copy pathhttp.rs
More file actions
116 lines (100 loc) · 3.51 KB
/
http.rs
File metadata and controls
116 lines (100 loc) · 3.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#![expect(clippy::unwrap_used, reason = "contains legacy code which uses unwrap")]
use std::collections::HashMap;
use lazy_static::lazy_static;
use regex::Regex;
// Http statuses
pub const HTTP_STATUS_502_BAD_GATEWAY: u32 = 502;
pub const HTTP_STATUS_503_SERVICE_UNAVAILABLE: u32 = 503;
pub const HTTP_STATUS_504_GATEWAY_TIMEOUT: u32 = 504;
pub const HTTP_STATUS_507_INSUFFICIENT_STORAGE: u32 = 507;
pub const HTTP_STATUS_524_CLOUDFLARE_TIMEOUT: u32 = 524;
lazy_static! {
static ref LINK_TOKEN_RE: Regex = Regex::new(
r#"(?x)
(?:
<(?P<link>[^>]+)>
) | (?:
(?P<key>[a-z]+)
\s*=\s*
(?:
"(?P<qvalue>[^"]+)" |
(?P<value>[^\s,.]+)
)
) | (?:
\s*
(?:
(?P<comma>,) |
(?P<semi>;)
)
\s*
)
"#
)
.unwrap();
}
/// Parses a link header into a vector of hash maps.
///
/// The implied `link` tag is stored as `_link`.
pub fn parse_link_header(s: &str) -> Vec<HashMap<&str, &str>> {
let mut rv = vec![];
let mut item = HashMap::new();
for caps in LINK_TOKEN_RE.captures_iter(s) {
if let Some(link) = caps.name("link") {
item.insert("_link", link.as_str());
} else if let Some(key) = caps.name("key") {
item.insert(
key.as_str(),
caps.name("qvalue")
.unwrap_or_else(|| caps.name("value").unwrap())
.as_str(),
);
} else if caps.name("comma").is_some() {
rv.push(item);
item = HashMap::new();
}
}
if !item.is_empty() {
rv.push(item);
}
rv
}
/// Checks whether an url starts with http:// or https:// prefix
pub fn is_absolute_url(url: &str) -> bool {
url.starts_with("http://") || url.starts_with("https://")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_link_header() {
let rv = parse_link_header("<https://sentry.io/api/0/organizations/sentry/releases/?&cursor=100:-1:1>; rel=\"previous\"; results=\"false\"; cursor=\"100:-1:1\", <https://sentry.io/api/0/organizations/sentry/releases/?&cursor=100:1:0>; rel=\"next\"; results=\"true\"; cursor=\"100:1:0\"");
assert_eq!(rv.len(), 2);
let a = &rv[0];
let b = &rv[1];
assert_eq!(
a.get("_link").unwrap(),
&"https://sentry.io/api/0/organizations/sentry/releases/?&cursor=100:-1:1"
);
assert_eq!(a.get("cursor").unwrap(), &"100:-1:1");
assert_eq!(a.get("rel").unwrap(), &"previous");
assert_eq!(a.get("results").unwrap(), &"false");
assert_eq!(
b.get("_link").unwrap(),
&"https://sentry.io/api/0/organizations/sentry/releases/?&cursor=100:1:0"
);
assert_eq!(b.get("cursor").unwrap(), &"100:1:0");
assert_eq!(b.get("rel").unwrap(), &"next");
assert_eq!(b.get("results").unwrap(), &"true");
}
#[test]
fn test_is_absolute_url() {
assert!(is_absolute_url("https://sentry.io"));
assert!(is_absolute_url("http://sentry.io"));
assert!(is_absolute_url("https://sentry.io/path"));
assert!(is_absolute_url("http://sentry.io/path"));
assert!(is_absolute_url("http://sentry.io/path?query=foo"));
assert!(is_absolute_url("https://sentry.io/path?query=foo"));
assert!(!is_absolute_url("/path"));
assert!(!is_absolute_url("/path?query=foo"));
}
}