(
ctx: &Ctx<'js>,
client: &HyperClient,
stream: Class<'js, ReadableStream<'js>>,
method: Method,
method_string: String,
uri: Uri,
headers: Option<&Headers>,
start: Inst
| 188 | } |
| 189 | |
| 190 | #[allow(clippy::too_many_arguments)] |
| 191 | async fn send_stream<'js>( |
| 192 | ctx: &Ctx<'js>, |
| 193 | client: &HyperClient, |
| 194 | stream: Class<'js, ReadableStream<'js>>, |
| 195 | method: Method, |
| 196 | method_string: String, |
| 197 | uri: Uri, |
| 198 | headers: Option<&Headers>, |
| 199 | start: Instant, |
| 200 | abort_receiver: Option<mc_oneshot::Receiver<Value<'js>>>, |
| 201 | ) -> Result<Response<'js>> { |
| 202 | let (tx, rx) = tokio::sync::mpsc::channel::<Bytes>(128); |
| 203 | let (err_tx, err_rx) = tokio::sync::oneshot::channel::<String>(); |
| 204 | let ctx2 = ctx.clone(); |
| 205 | |
| 206 | // Spawn stream reader in JS context |
| 207 | ctx.spawn_exit_simple({ |
| 208 | async move { |
| 209 | let get_reader: Function = stream.get("getReader")?; |
| 210 | let reader: Object = get_reader.call((This(stream.clone()),))?; |
| 211 | let read_fn: Function = reader.get("read")?; |
| 212 | |
| 213 | loop { |
| 214 | let promise: rquickjs::Promise = read_fn.call((This(reader.clone()),))?; |
| 215 | let read_result = match promise.into_future::<Object>().await.catch(&ctx2) { |
| 216 | Ok(r) => r, |
| 217 | Err(e) => { |
| 218 | let msg = match e { |
| 219 | CaughtError::Exception(ex) => ex.message().unwrap_or_default(), |
| 220 | CaughtError::Value(v) => v |
| 221 | .as_string() |
| 222 | .and_then(|s| s.to_string().ok()) |
| 223 | .unwrap_or_else(|| "Stream error".into()), |
| 224 | CaughtError::Error(e) => e.to_string(), |
| 225 | }; |
| 226 | let _ = err_tx.send(msg); |
| 227 | break; |
| 228 | }, |
| 229 | }; |
| 230 | let done: bool = read_result.get("done").unwrap_or(true); |
| 231 | if done { |
| 232 | break; |
| 233 | } |
| 234 | if let Ok(value) = read_result.get::<_, Value>("value") { |
| 235 | // Per fetch spec, stream chunks must be Uint8Array. Anything |
| 236 | // else (ArrayBuffer, Blob, String, null, etc.) is an error. |
| 237 | let bytes = match rquickjs::TypedArray::<u8>::from_value(value) { |
| 238 | Ok(typed_array) => typed_array.as_bytes().map(Bytes::copy_from_slice), |
| 239 | Err(_) => { |
| 240 | let _ = err_tx |
| 241 | .send("Failed to read body: chunk is not a Uint8Array".into()); |
| 242 | break; |
| 243 | }, |
| 244 | }; |
| 245 | if let Some(bytes) = bytes { |
| 246 | if tx.send(bytes).await.is_err() { |
| 247 | break; |
no test coverage detected