Parse an absolute-form proxy request URI into its components. For example, `"http://10.86.8.223:8000/screenshot/"` yields `("http", "10.86.8.223", 8000, "/screenshot/")`. Handles: - Default port 80 for `http`, 443 for `https` - IPv6 bracket notation (`[::1]`) - Missing path (defaults to `/`) - Query strings (preserved in path)
(uri: &str)
| 2862 | /// - Missing path (defaults to `/`) |
| 2863 | /// - Query strings (preserved in path) |
| 2864 | fn parse_proxy_uri(uri: &str) -> Result<(String, String, u16, String)> { |
| 2865 | // Extract scheme |
| 2866 | let (scheme, rest) = uri |
| 2867 | .split_once("://") |
| 2868 | .ok_or_else(|| miette::miette!("Missing scheme in proxy URI: {uri}"))?; |
| 2869 | let scheme = scheme.to_ascii_lowercase(); |
| 2870 | |
| 2871 | // Split authority from path |
| 2872 | let (authority, path) = if rest.starts_with('[') { |
| 2873 | // IPv6: [::1]:port/path |
| 2874 | let bracket_end = rest |
| 2875 | .find(']') |
| 2876 | .ok_or_else(|| miette::miette!("Unclosed IPv6 bracket in URI: {uri}"))?; |
| 2877 | let after_bracket = &rest[bracket_end + 1..]; |
| 2878 | after_bracket.find('/').map_or((rest, "/"), |slash_pos| { |
| 2879 | ( |
| 2880 | &rest[..=bracket_end + slash_pos], |
| 2881 | &after_bracket[slash_pos..], |
| 2882 | ) |
| 2883 | }) |
| 2884 | } else if let Some(slash_pos) = rest.find('/') { |
| 2885 | (&rest[..slash_pos], &rest[slash_pos..]) |
| 2886 | } else { |
| 2887 | (rest, "/") |
| 2888 | }; |
| 2889 | |
| 2890 | // Parse host and port from authority |
| 2891 | let (host, port) = if authority.starts_with('[') { |
| 2892 | // IPv6: [::1]:port or [::1] |
| 2893 | let bracket_end = authority |
| 2894 | .find(']') |
| 2895 | .ok_or_else(|| miette::miette!("Unclosed IPv6 bracket: {uri}"))?; |
| 2896 | let host = &authority[1..bracket_end]; // strip brackets |
| 2897 | let port_str = &authority[bracket_end + 1..]; |
| 2898 | let port = if let Some(port_str) = port_str.strip_prefix(':') { |
| 2899 | port_str |
| 2900 | .parse::<u16>() |
| 2901 | .map_err(|_| miette::miette!("Invalid port in URI: {uri}"))? |
| 2902 | } else { |
| 2903 | match scheme.as_str() { |
| 2904 | "https" => 443, |
| 2905 | _ => 80, |
| 2906 | } |
| 2907 | }; |
| 2908 | (host.to_string(), port) |
| 2909 | } else if let Some((h, p)) = authority.rsplit_once(':') { |
| 2910 | let port = p |
| 2911 | .parse::<u16>() |
| 2912 | .map_err(|_| miette::miette!("Invalid port in URI: {uri}"))?; |
| 2913 | (h.to_string(), port) |
| 2914 | } else { |
| 2915 | let port = match scheme.as_str() { |
| 2916 | "https" => 443, |
| 2917 | _ => 80, |
| 2918 | }; |
| 2919 | (authority.to_string(), port) |
| 2920 | }; |
| 2921 |