| 117 | } |
| 118 | |
| 119 | fn parse_http_response(response: &[u8]) -> anyhow::Result<(u16, HttpHeaders, &[u8])> { |
| 120 | let header_end = response |
| 121 | .windows(4) |
| 122 | .position(|window| window == b"\r\n\r\n") |
| 123 | .ok_or_else(|| anyhow::anyhow!("SimDeck service returned a malformed HTTP response."))?; |
| 124 | let header_bytes = &response[..header_end]; |
| 125 | let body = &response[header_end + 4..]; |
| 126 | let header_text = std::str::from_utf8(header_bytes).context("parse HTTP headers as UTF-8")?; |
| 127 | let mut lines = header_text.lines(); |
| 128 | let status_line = lines |
| 129 | .next() |
| 130 | .ok_or_else(|| anyhow::anyhow!("SimDeck service returned an empty HTTP response."))?; |
| 131 | let status = status_line |
| 132 | .split_whitespace() |
| 133 | .nth(1) |
| 134 | .ok_or_else(|| anyhow::anyhow!("HTTP response did not include a status code."))? |
| 135 | .parse::<u16>() |
| 136 | .context("parse HTTP status code")?; |
| 137 | let headers = lines |
| 138 | .filter_map(|line| { |
| 139 | let (name, value) = line.split_once(':')?; |
| 140 | Some((name.trim().to_ascii_lowercase(), value.trim().to_owned())) |
| 141 | }) |
| 142 | .collect(); |
| 143 | Ok((status, headers, body)) |
| 144 | } |
| 145 | |
| 146 | fn response_is_chunked(headers: &[(String, String)]) -> bool { |
| 147 | headers.iter().any(|(name, value)| { |