Reverse-proxies a Metro HTTP path over the SimDeck origin. The upstream request omits the browser `Origin` so Metro's dev-middleware does not reject DevTools resources.
(
port: u16,
path: &str,
query: Option<&str>,
method: &str,
body: Option<&[u8]>,
content_type: Option<&str>,
)
| 912 | /// request omits the browser `Origin` so Metro's dev-middleware does not reject |
| 913 | /// DevTools resources. |
| 914 | pub async fn fetch_metro_resource( |
| 915 | port: u16, |
| 916 | path: &str, |
| 917 | query: Option<&str>, |
| 918 | method: &str, |
| 919 | body: Option<&[u8]>, |
| 920 | content_type: Option<&str>, |
| 921 | ) -> Result<ProxiedAsset, String> { |
| 922 | if !is_metro_proxy_path(path) { |
| 923 | return Err("Not a safe Metro proxy path.".to_owned()); |
| 924 | } |
| 925 | if !is_metro_proxy_method(method) { |
| 926 | return Err("Not an allowed Metro proxy method.".to_owned()); |
| 927 | } |
| 928 | let target = match query { |
| 929 | Some(query) => format!("{path}?{query}"), |
| 930 | None => path.to_owned(), |
| 931 | }; |
| 932 | let body = body.unwrap_or(&[]); |
| 933 | let (address, mut stream) = |
| 934 | connect_loopback_devtools_service(port, METRO_ASSET_TIMEOUT, "Metro").await?; |
| 935 | let mut request = format!( |
| 936 | "{method} {target} HTTP/1.1\r\nHost: {address}\r\nAccept: */*\r\nConnection: close\r\n" |
| 937 | ); |
| 938 | if !body.is_empty() { |
| 939 | request.push_str(&format!("Content-Length: {}\r\n", body.len())); |
| 940 | if let Some(content_type) = content_type { |
| 941 | request.push_str(&format!("Content-Type: {content_type}\r\n")); |
| 942 | } |
| 943 | } |
| 944 | request.push_str("\r\n"); |
| 945 | timeout(METRO_ASSET_TIMEOUT, stream.write_all(request.as_bytes())) |
| 946 | .await |
| 947 | .map_err(|_| "Timed out requesting Metro asset.".to_owned())? |
| 948 | .map_err(|error| format!("Unable to request Metro asset: {error}"))?; |
| 949 | if !body.is_empty() { |
| 950 | timeout(METRO_ASSET_TIMEOUT, stream.write_all(body)) |
| 951 | .await |
| 952 | .map_err(|_| "Timed out writing Metro request body.".to_owned())? |
| 953 | .map_err(|error| format!("Unable to write Metro request body: {error}"))?; |
| 954 | } |
| 955 | |
| 956 | let mut response = Vec::new(); |
| 957 | let mut chunk = [0_u8; 16384]; |
| 958 | loop { |
| 959 | let count = timeout(METRO_ASSET_TIMEOUT, stream.read(&mut chunk)) |
| 960 | .await |
| 961 | .map_err(|_| "Timed out reading Metro asset.".to_owned())? |
| 962 | .map_err(|error| format!("Unable to read Metro asset: {error}"))?; |
| 963 | if count == 0 { |
| 964 | break; |
| 965 | } |
| 966 | response.extend_from_slice(&chunk[..count]); |
| 967 | if response.len() > METRO_ASSET_MAX_BYTES { |
| 968 | return Err("Metro asset exceeded the size limit.".to_owned()); |
| 969 | } |
| 970 | if response_has_complete_body(&response) { |
| 971 | break; |
no test coverage detected