1use std::collections::Bound;
8use std::iter::once;
9use std::str;
10
11use arrayvec::ArrayVec;
12use bytes::Bytes;
13use data_url::DataUrl;
14use headers::{HeaderMapExt as _, Range};
15use http::header::{CONTENT_LENGTH, CONTENT_RANGE, CONTENT_TYPE};
16use http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
17use ion::class::Reflector;
18use ion::{ClassDefinition as _, Context, Local, Object};
19use tokio::fs::read;
20use url::Url;
21
22use crate::ContextExt as _;
23use crate::globals::fetch::header::HeadersKind;
24use crate::globals::fetch::response::network_error;
25use crate::globals::fetch::{Headers, Request, Response};
26use crate::globals::file::Blob;
27use crate::globals::url::parse_uuid_from_url_path;
28
29pub async fn scheme_fetch(cx: &Context, scheme: &str, request: &Request, url: Url) -> Response {
30 match scheme {
31 "about" if url.path() == "blank" => about_blank_fetch(cx, url),
32 "blob" => blob_fetch(cx, request, url),
33 "data" => data_fetch(cx, url),
34 "file" => file_fetch(cx, request, url).await,
35 _ => network_error(),
36 }
37}
38
39fn about_blank_fetch(cx: &Context, url: Url) -> Response {
40 let response = Response::new_from_bytes(Bytes::default(), url);
41 let headers = Headers {
42 reflector: Reflector::default(),
43 headers: HeaderMap::from_iter(once((
44 CONTENT_TYPE,
45 HeaderValue::from_static("text/html;charset=UTF-8"),
46 ))),
47 kind: HeadersKind::Immutable,
48 };
49 response.headers.set(Headers::new_object(cx, Box::new(headers)));
50 response
51}
52
53fn blob_fetch(cx: &Context, request: &Request, url: Url) -> Response {
54 if request.method != Method::GET {
55 return network_error();
56 }
57
58 let Some(uuid) = parse_uuid_from_url_path(&url) else {
59 return network_error();
60 };
61 let blob = unsafe {
62 match cx.get_private().blob_store.get(&uuid) {
63 Some(blob) => blob,
64 _ => return network_error(),
65 }
66 };
67
68 let blob = Object::from(unsafe { Local::from_heap(blob) });
69 let blob = Blob::get_private(cx, &blob).unwrap();
70
71 let kind = blob.kind.as_deref().unwrap_or("");
72 let Ok(kind) = HeaderValue::from_str(kind) else {
73 return network_error();
74 };
75
76 let mut response_headers = ArrayVec::<_, 3>::new();
77 response_headers.push((CONTENT_TYPE, kind));
78
79 let mut bytes = blob.bytes.clone();
80
81 let headers = Object::from(unsafe { Local::from_heap(&request.headers) });
82 let headers = &Headers::get_mut_private(cx, &headers).unwrap().headers;
83
84 let (status, range_requested) = match get_ranged_bytes(headers, &mut bytes, &mut response_headers) {
85 Ok((status, range_requested)) => (status, range_requested),
86 Err(err) => return err,
87 };
88
89 response_headers.push((CONTENT_LENGTH, HeaderValue::from(bytes.len())));
90
91 let mut response = Response::new_from_bytes(bytes, url);
92 response.status = Some(status);
93 response.range_requested = range_requested;
94
95 let headers = Headers {
96 reflector: Reflector::default(),
97 headers: HeaderMap::from_iter(response_headers),
98 kind: HeadersKind::Immutable,
99 };
100 response.headers.set(Headers::new_object(cx, Box::new(headers)));
101 response
102}
103
104fn data_fetch(cx: &Context, url: Url) -> Response {
105 let Ok(data_url) = DataUrl::process(url.as_str()) else {
106 return network_error();
107 };
108 let Ok((body, _)) = data_url.decode_to_vec() else {
109 return network_error();
110 };
111
112 let mime = data_url.mime_type();
113 let mime = format!("{}/{}", mime.type_, mime.subtype);
114
115 let response = Response::new_from_bytes(Bytes::from(body), url);
116 let headers = Headers {
117 reflector: Reflector::default(),
118 headers: HeaderMap::from_iter(once((CONTENT_TYPE, HeaderValue::from_str(&mime).unwrap()))),
119 kind: HeadersKind::Immutable,
120 };
121 response.headers.set(Headers::new_object(cx, Box::new(headers)));
122 response
123}
124
125async fn file_fetch(cx: &Context, request: &Request, url: Url) -> Response {
126 if request.method != Method::GET {
127 return network_error();
128 }
129
130 match url.to_file_path() {
131 Ok(path) => match read(path).await {
132 Ok(bytes) => {
133 let mut bytes = Bytes::from(bytes);
134
135 let headers = Object::from(unsafe { Local::from_heap(&request.headers) });
136 let headers = &Headers::get_mut_private(cx, &headers).unwrap().headers;
137
138 let mut response_headers = ArrayVec::<_, 2>::new();
139 let (status, range_requested) = match get_ranged_bytes(headers, &mut bytes, &mut response_headers) {
140 Ok((status, range_requested)) => (status, range_requested),
141 Err(err) => return err,
142 };
143
144 response_headers.push((CONTENT_LENGTH, HeaderValue::from(bytes.len())));
145
146 let mut response = Response::new_from_bytes(bytes, url);
147 response.status = Some(status);
148 response.range_requested = range_requested;
149
150 let headers = Headers {
151 reflector: Reflector::default(),
152 headers: HeaderMap::from_iter(response_headers),
153 kind: HeadersKind::Immutable,
154 };
155 response.headers.set(Headers::new_object(cx, Box::new(headers)));
156
157 response
158 }
159 Err(_) => network_error(),
160 },
161 Err(_) => network_error(),
162 }
163}
164
165#[expect(clippy::result_large_err)]
166fn get_ranged_bytes<const N: usize>(
167 headers: &HeaderMap, bytes: &mut Bytes, response_headers: &mut ArrayVec<(HeaderName, HeaderValue), N>,
168) -> Result<(StatusCode, bool), Response> {
169 match headers.typed_try_get::<Range>() {
170 Ok(Some(range)) => {
171 let len = bytes.len();
172 if let Some((start, end)) = range.satisfiable_ranges(len as u64).next() {
173 let (start, end) = (start.map(|s| s as usize), end.map(|e| e as usize));
174 *bytes = bytes.slice((start, end));
175
176 let (start, end) = match (start, end) {
177 (Bound::Included(s), Bound::Included(e)) => (s, e),
178 (Bound::Included(s), Bound::Unbounded) => (s, len - 1),
179 _ => unreachable!(),
180 };
181 let Ok(range) = HeaderValue::from_str(&format!("{start}-{end}/{len}")) else {
182 return Err(network_error());
183 };
184
185 response_headers.push((CONTENT_RANGE, range));
186
187 Ok((StatusCode::PARTIAL_CONTENT, true))
188 } else {
189 Err(network_error())
190 }
191 }
192 Ok(None) => Ok((StatusCode::OK, false)),
193 Err(_) => Err(network_error()),
194 }
195}