1use fidl_fuchsia_net_http::{self as http, Header};
6use fuchsia_async as fasync;
7use fuchsia_component::client::connect_to_protocol;
8use futures::AsyncReadExt as _;
9use log::debug;
10
11pub mod errors;
12use errors::FetchUrlError;
13
14const HTTP_PARTIAL_CONTENT_OK: u32 = 206;
15const HTTP_OK: u32 = 200;
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub struct Range {
20 pub start: u64,
22 pub end: Option<u64>,
24}
25
26pub async fn fetch_url(
27 url: impl Into<String>,
28 range: Option<Range>,
29) -> Result<Vec<u8>, FetchUrlError> {
30 let http_svc = connect_to_protocol::<http::LoaderMarker>()
31 .map_err(FetchUrlError::FidlHttpServiceConnectionError)?;
32
33 let url_string = url.into();
34
35 let headers = if let Some(r) = &range {
37 let range_string = if let Some(end) = r.end {
38 format!("bytes={}-{}", r.start, end)
39 } else {
40 format!("bytes={}-", r.start)
41 };
42 Some(vec![Header { name: "Range".into(), value: range_string.into() }])
43 } else {
44 None
45 };
46
47 let url_request = http::Request {
48 url: Some(url_string),
49 method: Some(String::from("GET")),
50 headers: headers,
51 body: None,
52 deadline: None,
53 ..Default::default()
54 };
55
56 let response = http_svc.fetch(url_request).await.map_err(FetchUrlError::LoaderFIDLError)?;
57
58 debug!("got HTTP status {:?} for final URL {:?}", response.status_code, response.final_url);
59
60 if let Some(e) = response.error {
61 return Err(FetchUrlError::LoaderFetchError(e));
62 }
63
64 let zx_socket = response.body.ok_or(FetchUrlError::UrlReadBodyError)?;
65 let mut socket = fasync::Socket::from_socket(zx_socket);
66
67 if let Some(range) = range {
68 match response.status_code {
69 Some(HTTP_PARTIAL_CONTENT_OK) => {
70 let mut body = Vec::new();
71 let bytes_received = socket
72 .read_to_end(&mut body)
73 .await
74 .map_err(FetchUrlError::ReadFromSocketError)?
75 as u64;
76 let start = range.start;
77 if let Some(end) = range.end {
78 let expected = end - start + 1;
79 if bytes_received != expected {
80 return Err(FetchUrlError::SizeReadMismatch(bytes_received, expected));
81 }
82 }
83 debug!(
84 "successfully fetched partial content starting from {}, {} bytes total",
85 start,
86 body.len()
87 );
88 Ok(body)
89 }
90 Some(code) => Err(FetchUrlError::UnexpectedHttpStatusCode(code)),
91 None => Err(FetchUrlError::NoStatusResponse),
92 }
93 } else {
94 match response.status_code {
95 Some(HTTP_OK) => {
96 let mut body = Vec::new();
97 let bytes_received = socket
98 .read_to_end(&mut body)
99 .await
100 .map_err(FetchUrlError::ReadFromSocketError)?;
101 debug!("successfully fetched content, {} bytes total", bytes_received);
102 Ok(body)
103 }
104 Some(code) => Err(FetchUrlError::UnexpectedHttpStatusCode(code)),
105 None => Err(FetchUrlError::NoStatusResponse),
106 }
107 }
108}