Skip to main content

api_scanner/scanner/common/
http_utils.rs

1use std::collections::HashMap;
2
3use serde_json::Value;
4
5use crate::http_client::HttpResponse;
6
7pub fn get_content_type(headers: &HashMap<String, String>) -> String {
8    headers
9        .get("content-type")
10        .map(|s| s.to_ascii_lowercase())
11        .unwrap_or_default()
12}
13
14pub fn is_json_content_type(content_type: &str) -> bool {
15    let media_type = content_type
16        .split(';')
17        .next()
18        .unwrap_or(content_type)
19        .trim();
20
21    media_type
22        .as_bytes()
23        .windows(4)
24        .any(|window| window.eq_ignore_ascii_case(b"json"))
25}
26
27pub fn is_html_content_type(content_type: &str) -> bool {
28    let lower = content_type.to_ascii_lowercase();
29    lower.contains("text/html") || lower.contains("application/xhtml")
30}
31
32pub fn is_json_response(headers: &HashMap<String, String>, body: &str) -> bool {
33    let ct = get_content_type(headers);
34    is_json_content_type(&ct) || serde_json::from_str::<Value>(body).is_ok()
35}
36
37pub fn parse_json_body(body: &str, content_type: Option<&str>) -> Option<Value> {
38    let parsed = serde_json::from_str::<Value>(body).ok();
39    let is_json = content_type.map(is_json_content_type).unwrap_or(false);
40
41    if is_json || parsed.is_some() {
42        parsed
43    } else {
44        None
45    }
46}
47
48pub fn parse_json_response(resp: &HttpResponse) -> Option<Value> {
49    parse_json_body(
50        &resp.body,
51        resp.headers.get("content-type").map(|s| s.as_str()),
52    )
53}