1use crate::p2::bindings::http::types;
4use crate::{Error, FieldMap};
5use bytes::Bytes;
6use http_body::{Body, Frame};
7use http_body_util::BodyExt;
8use http_body_util::combinators::UnsyncBoxBody;
9use std::future::Future;
10use std::mem;
11use std::task::{Context, Poll};
12use std::{pin::Pin, sync::Arc};
13use tokio::sync::{mpsc, oneshot};
14use wasmtime::format_err;
15use wasmtime_wasi::p2::{InputStream, OutputStream, Pollable, StreamError};
16use wasmtime_wasi::runtime::{AbortOnDropJoinHandle, poll_noop};
17
18pub type HyperIncomingBody = UnsyncBoxBody<Bytes, Error>;
20
21pub type HyperOutgoingBody = UnsyncBoxBody<Bytes, Error>;
23
24#[derive(Debug)]
26pub struct HostIncomingBody {
27 body: IncomingBodyState,
28 worker: Option<AbortOnDropJoinHandle<()>>,
32}
33
34impl HostIncomingBody {
35 pub fn new(body: HyperIncomingBody) -> HostIncomingBody {
37 HostIncomingBody {
38 body: IncomingBodyState::Start(body),
39 worker: None,
40 }
41 }
42
43 pub fn retain_worker(&mut self, worker: AbortOnDropJoinHandle<()>) {
45 assert!(self.worker.is_none());
46 self.worker = Some(worker);
47 }
48
49 pub fn take_stream(&mut self) -> Option<HostIncomingBodyStream> {
51 match &mut self.body {
52 IncomingBodyState::Start(_) => {}
53 IncomingBodyState::InBodyStream(_) => return None,
54 }
55 let (tx, rx) = oneshot::channel();
56 let body = match mem::replace(&mut self.body, IncomingBodyState::InBodyStream(rx)) {
57 IncomingBodyState::Start(b) => b,
58 IncomingBodyState::InBodyStream(_) => unreachable!(),
59 };
60 Some(HostIncomingBodyStream {
61 state: IncomingBodyStreamState::Open { body, tx },
62 buffer: Bytes::new(),
63 error: None,
64 })
65 }
66
67 pub fn into_future_trailers(self) -> HostFutureTrailers {
69 HostFutureTrailers::Waiting(self)
70 }
71}
72
73#[derive(Debug)]
75enum IncomingBodyState {
76 Start(HyperIncomingBody),
79
80 InBodyStream(oneshot::Receiver<StreamEnd>),
84}
85
86#[derive(Debug)]
89enum StreamEnd {
90 Remaining(HyperIncomingBody),
93
94 Trailers(Option<http::HeaderMap>),
97
98 Error(Arc<Error>),
101}
102
103#[derive(Debug)]
106pub struct HostIncomingBodyStream {
107 state: IncomingBodyStreamState,
108 buffer: Bytes,
109 error: Option<Arc<Error>>,
110}
111
112impl HostIncomingBodyStream {
113 fn record_frame(&mut self, frame: Option<Result<Frame<Bytes>, Error>>) {
114 match frame {
115 Some(Ok(frame)) => match frame.into_data() {
116 Ok(bytes) => {
119 assert!(self.buffer.is_empty());
120 self.buffer = bytes;
121 }
122
123 Err(trailers) => {
127 let trailers = trailers.into_trailers().unwrap();
128 let tx = match mem::replace(&mut self.state, IncomingBodyStreamState::Closed) {
129 IncomingBodyStreamState::Open { body: _, tx } => tx,
130 IncomingBodyStreamState::Closed => unreachable!(),
131 };
132
133 let _ = tx.send(StreamEnd::Trailers(Some(trailers)));
136 }
137 },
138
139 Some(Err(e)) => {
143 let tx = match mem::replace(&mut self.state, IncomingBodyStreamState::Closed) {
146 IncomingBodyStreamState::Open { tx, .. } => Some(tx),
147 IncomingBodyStreamState::Closed => None,
148 };
149 let e = Arc::new(e);
150 if let Some(tx) = tx {
151 let _ = tx.send(StreamEnd::Error(Arc::clone(&e)));
152 }
153 self.error = Some(e);
154 }
155
156 None => {
160 self.state = IncomingBodyStreamState::Closed;
161 }
162 }
163 }
164}
165
166#[derive(Debug)]
167enum IncomingBodyStreamState {
168 Open {
176 body: HyperIncomingBody,
177 tx: oneshot::Sender<StreamEnd>,
178 },
179
180 Closed,
183}
184
185#[async_trait::async_trait]
186impl InputStream for HostIncomingBodyStream {
187 fn read(&mut self, size: usize) -> Result<Bytes, StreamError> {
188 loop {
189 if !self.buffer.is_empty() {
191 let len = size.min(self.buffer.len());
192 let chunk = self.buffer.split_to(len);
193 return Ok(chunk);
194 }
195
196 if let Some(e) = self.error.take() {
197 return Err(StreamError::LastOperationFailed(e.into()));
198 }
199
200 let body = match &mut self.state {
206 IncomingBodyStreamState::Open { body, .. } => body,
207 IncomingBodyStreamState::Closed => return Err(StreamError::Closed),
208 };
209
210 let future = body.frame();
211 futures::pin_mut!(future);
212 match poll_noop(future) {
213 Some(result) => {
214 self.record_frame(result);
215 }
216 None => return Ok(Bytes::new()),
217 }
218 }
219 }
220}
221
222#[async_trait::async_trait]
223impl Pollable for HostIncomingBodyStream {
224 async fn ready(&mut self) {
225 if !self.buffer.is_empty() || self.error.is_some() {
226 return;
227 }
228
229 if let IncomingBodyStreamState::Open { body, .. } = &mut self.state {
230 let frame = body.frame().await;
231 self.record_frame(frame);
232 }
233 }
234}
235
236impl Drop for HostIncomingBodyStream {
237 fn drop(&mut self) {
238 let prev = mem::replace(&mut self.state, IncomingBodyStreamState::Closed);
244 if let IncomingBodyStreamState::Open { body, tx } = prev {
245 let _ = tx.send(StreamEnd::Remaining(body));
246 }
247 }
248}
249
250#[derive(Debug)]
252pub enum HostFutureTrailers {
253 Waiting(HostIncomingBody),
267
268 Done(Result<Option<http::HeaderMap>, Arc<Error>>),
273
274 Consumed,
276}
277
278#[async_trait::async_trait]
279impl Pollable for HostFutureTrailers {
280 async fn ready(&mut self) {
281 let body = match self {
282 HostFutureTrailers::Waiting(body) => body,
283 HostFutureTrailers::Done(_) => return,
284 HostFutureTrailers::Consumed => return,
285 };
286
287 if let IncomingBodyState::InBodyStream(rx) = &mut body.body {
290 match rx.await {
291 Ok(StreamEnd::Trailers(Some(t))) => {
294 *self = Self::Done(Ok(Some(t)));
295 }
296 Ok(StreamEnd::Remaining(b)) => body.body = IncomingBodyState::Start(b),
299
300 Ok(StreamEnd::Error(e)) => {
303 *self = HostFutureTrailers::Done(Err(e));
304 }
305
306 Ok(StreamEnd::Trailers(None)) | Err(_) => {
308 *self = HostFutureTrailers::Done(Ok(None));
309 }
310 }
311 }
312
313 let body = match self {
316 HostFutureTrailers::Waiting(body) => body,
317 HostFutureTrailers::Done(_) => return,
318 HostFutureTrailers::Consumed => return,
319 };
320 let hyper_body = match &mut body.body {
321 IncomingBodyState::Start(body) => body,
322 IncomingBodyState::InBodyStream(_) => unreachable!(),
323 };
324 let result = loop {
325 match hyper_body.frame().await {
326 None => break Ok(None),
327 Some(Err(e)) => break Err(Arc::new(e)),
328 Some(Ok(frame)) => {
329 if let Ok(header_map) = frame.into_trailers() {
332 break Ok(Some(header_map));
333 }
334 }
335 }
336 };
337 *self = HostFutureTrailers::Done(result);
338 }
339}
340
341#[derive(Debug, Clone)]
342struct WrittenState {
343 expected: u64,
344 written: Arc<std::sync::atomic::AtomicU64>,
345}
346
347impl WrittenState {
348 fn new(expected_size: u64) -> Self {
349 Self {
350 expected: expected_size,
351 written: Arc::new(std::sync::atomic::AtomicU64::new(0)),
352 }
353 }
354
355 fn written(&self) -> u64 {
357 self.written.load(std::sync::atomic::Ordering::Relaxed)
358 }
359
360 fn update(&self, len: usize) -> bool {
363 let len = len as u64;
364 let old = self
365 .written
366 .fetch_add(len, std::sync::atomic::Ordering::Relaxed);
367 old + len <= self.expected
368 }
369}
370
371pub struct HostOutgoingBody {
373 body_output_stream: Option<Box<dyn OutputStream>>,
375 context: StreamContext,
376 written: Option<WrittenState>,
377 finish_sender: Option<tokio::sync::oneshot::Sender<FinishMessage>>,
378}
379
380impl HostOutgoingBody {
381 pub fn new(
383 context: StreamContext,
384 size: Option<u64>,
385 buffer_chunks: usize,
386 chunk_size: usize,
387 ) -> (Self, HyperOutgoingBody) {
388 assert!(buffer_chunks >= 1);
389
390 let written = size.map(WrittenState::new);
391
392 use tokio::sync::oneshot::error::RecvError;
393 struct BodyImpl {
394 body_receiver: mpsc::Receiver<Bytes>,
395 finish_receiver: Option<oneshot::Receiver<FinishMessage>>,
396 }
397 impl Body for BodyImpl {
398 type Data = Bytes;
399 type Error = Error;
400 fn poll_frame(
401 mut self: Pin<&mut Self>,
402 cx: &mut Context<'_>,
403 ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
404 match self.as_mut().body_receiver.poll_recv(cx) {
405 Poll::Pending => Poll::Pending,
406 Poll::Ready(Some(frame)) => Poll::Ready(Some(Ok(Frame::data(frame)))),
407
408 Poll::Ready(None) => {
410 if let Some(mut finish_receiver) = self.as_mut().finish_receiver.take() {
411 match Pin::new(&mut finish_receiver).poll(cx) {
412 Poll::Pending => {
413 self.as_mut().finish_receiver = Some(finish_receiver);
414 Poll::Pending
415 }
416 Poll::Ready(Ok(message)) => match message {
417 FinishMessage::Finished => Poll::Ready(None),
418 FinishMessage::Trailers(trailers) => {
419 Poll::Ready(Some(Ok(Frame::trailers(trailers))))
420 }
421 FinishMessage::Abort => {
422 Poll::Ready(Some(Err(Error::HttpProtocolError)))
423 }
424 },
425 Poll::Ready(Err(RecvError { .. })) => Poll::Ready(None),
426 }
427 } else {
428 Poll::Ready(None)
429 }
430 }
431 }
432 }
433 }
434
435 let capacity = channel_capacity(buffer_chunks.saturating_add(1));
439 let (body_sender, body_receiver) = mpsc::channel(capacity);
440 let (finish_sender, finish_receiver) = oneshot::channel();
441 let body_impl = BodyImpl {
442 body_receiver,
443 finish_receiver: Some(finish_receiver),
444 }
445 .boxed_unsync();
446
447 let output_stream = BodyWriteStream::new(context, chunk_size, body_sender, written.clone());
448
449 (
450 Self {
451 body_output_stream: Some(Box::new(output_stream)),
452 context,
453 written,
454 finish_sender: Some(finish_sender),
455 },
456 body_impl,
457 )
458 }
459
460 pub fn take_output_stream(&mut self) -> Option<Box<dyn OutputStream>> {
462 self.body_output_stream.take()
463 }
464
465 pub fn finish(mut self, trailers: Option<FieldMap>) -> Result<(), types::ErrorCode> {
467 drop(self.body_output_stream);
470
471 let sender = self
472 .finish_sender
473 .take()
474 .expect("outgoing-body trailer_sender consumed by a non-owning function");
475
476 if let Some(w) = self.written {
477 let written = w.written();
478 if written != w.expected {
479 let _ = sender.send(FinishMessage::Abort);
480 return Err(self.context.as_body_size_error(written));
481 }
482 }
483
484 let message = if let Some(ts) = trailers {
485 FinishMessage::Trailers(ts.into())
486 } else {
487 FinishMessage::Finished
488 };
489
490 let _ = sender.send(message);
492
493 Ok(())
494 }
495
496 pub fn abort(mut self) {
498 drop(self.body_output_stream);
501
502 let sender = self
503 .finish_sender
504 .take()
505 .expect("outgoing-body trailer_sender consumed by a non-owning function");
506
507 let _ = sender.send(FinishMessage::Abort);
508 }
509}
510
511#[derive(Debug)]
513enum FinishMessage {
514 Finished,
515 Trailers(hyper::HeaderMap),
516 Abort,
517}
518
519#[derive(Clone, Copy, Debug, Eq, PartialEq)]
521pub enum StreamContext {
522 Request,
524 Response,
526}
527
528impl StreamContext {
529 pub fn as_body_size_error(&self, size: u64) -> types::ErrorCode {
531 match self {
532 StreamContext::Request => types::ErrorCode::HttpRequestBodySize(Some(size)),
533 StreamContext::Response => types::ErrorCode::HttpResponseBodySize(Some(size)),
534 }
535 }
536}
537
538fn channel_capacity(requested: usize) -> usize {
541 requested.clamp(1, tokio::sync::Semaphore::MAX_PERMITS)
542}
543
544#[derive(Debug)]
546struct BodyWriteStream {
547 context: StreamContext,
548 writer: mpsc::Sender<Bytes>,
549 write_budget: usize,
550 written: Option<WrittenState>,
551}
552
553impl BodyWriteStream {
554 fn new(
556 context: StreamContext,
557 write_budget: usize,
558 writer: mpsc::Sender<Bytes>,
559 written: Option<WrittenState>,
560 ) -> Self {
561 assert!(writer.max_capacity() >= 1);
563 BodyWriteStream {
564 context,
565 writer,
566 write_budget,
567 written,
568 }
569 }
570}
571
572#[async_trait::async_trait]
573impl OutputStream for BodyWriteStream {
574 fn write(&mut self, bytes: Bytes) -> Result<(), StreamError> {
575 let len = bytes.len();
576 if len > self.write_budget {
577 return Err(StreamError::Trap(format_err!("write exceeded budget")));
578 }
579 match self.writer.try_send(bytes) {
580 Ok(()) => {
583 if let Some(written) = self.written.as_ref() {
584 if !written.update(len) {
585 let total = written.written();
586 return Err(StreamError::LastOperationFailed(format_err!(
587 self.context.as_body_size_error(total)
588 )));
589 }
590 }
591
592 Ok(())
593 }
594
595 Err(mpsc::error::TrySendError::Full(_)) => {
599 Err(StreamError::Trap(format_err!("write exceeded budget")))
600 }
601
602 Err(mpsc::error::TrySendError::Closed(_)) => Err(StreamError::Closed),
604 }
605 }
606
607 fn flush(&mut self) -> Result<(), StreamError> {
608 if self.writer.is_closed() {
611 Err(StreamError::Closed)
612 } else {
613 Ok(())
614 }
615 }
616
617 fn check_write(&mut self) -> Result<usize, StreamError> {
618 if self.writer.is_closed() {
619 Err(StreamError::Closed)
620 } else if self.writer.capacity() == 0 {
621 Ok(0)
629 } else {
630 Ok(self.write_budget)
631 }
632 }
633}
634
635#[async_trait::async_trait]
636impl Pollable for BodyWriteStream {
637 async fn ready(&mut self) {
638 let _ = self.writer.reserve().await;
642 }
643}
644
645#[cfg(test)]
646mod tests {
647 use super::channel_capacity;
648
649 #[test]
653 fn outgoing_body_channel_capacity_is_always_accepted() {
654 let max = tokio::sync::Semaphore::MAX_PERMITS;
655 for requested in [0, 1, 1024, max, max + 1, usize::MAX] {
656 let capacity = channel_capacity(requested);
657 assert!(
658 capacity >= 1 && capacity <= max,
659 "capacity {capacity} out of range"
660 );
661 let _ = tokio::sync::mpsc::channel::<u8>(capacity);
663 }
664 }
665}