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