@@ -5,16 +5,17 @@ use std::collections::HashMap;
55
66#[ doc( inline) ]
77pub use conversions:: IntoResponse ;
8-
9- use self :: conversions:: TryFromIncomingResponse ;
10-
11- use super :: wit:: wasi:: http:: types;
128#[ doc( inline) ]
139pub use types:: {
1410 Error , Fields , Headers , IncomingRequest , IncomingResponse , Method , OutgoingBody ,
1511 OutgoingRequest , OutgoingResponse , Scheme , StatusCode , Trailers ,
1612} ;
1713
14+ use self :: conversions:: { TryFromIncomingResponse , TryIntoOutgoingRequest } ;
15+ use super :: wit:: wasi:: http:: types;
16+ use crate :: wit:: wasi:: io:: streams;
17+ use futures:: SinkExt ;
18+
1819/// A unified request object that can represent both incoming and outgoing requests.
1920///
2021/// This should be used in favor of `IncomingRequest` and `OutgoingRequest` when there
@@ -107,6 +108,34 @@ impl Request {
107108 uri,
108109 )
109110 }
111+
112+ /// Whether the request is an HTTPS request
113+ fn is_https ( & self ) -> bool {
114+ self . uri
115+ . 0
116+ . as_ref ( )
117+ . and_then ( |u| u. scheme ( ) )
118+ . map ( |s| s == & hyperium:: uri:: Scheme :: HTTPS )
119+ . unwrap_or ( true )
120+ }
121+
122+ /// The URI's authority
123+ fn authority ( & self ) -> Option < & str > {
124+ self . uri
125+ . 0
126+ . as_ref ( )
127+ . and_then ( |u| u. authority ( ) )
128+ . map ( |a| a. as_str ( ) )
129+ }
130+
131+ /// The request path and query combined
132+ pub fn path_and_query ( & self ) -> Option < & str > {
133+ self . uri
134+ . 0
135+ . as_ref ( )
136+ . and_then ( |u| u. path_and_query ( ) )
137+ . map ( |s| s. as_str ( ) )
138+ }
110139}
111140
112141/// A request builder
@@ -410,12 +439,12 @@ impl IncomingRequest {
410439 /// # Panics
411440 ///
412441 /// Panics if the body was already consumed.
413- pub fn into_body_stream ( self ) -> impl futures:: Stream < Item = anyhow :: Result < Vec < u8 > > > {
442+ pub fn into_body_stream ( self ) -> impl futures:: Stream < Item = Result < Vec < u8 > , streams :: Error > > {
414443 executor:: incoming_body ( self . consume ( ) . expect ( "request body was already consumed" ) )
415444 }
416445
417446 /// Return a `Vec<u8>` of the body or fails
418- pub async fn into_body ( self ) -> anyhow :: Result < Vec < u8 > > {
447+ pub async fn into_body ( self ) -> Result < Vec < u8 > , streams :: Error > {
419448 use futures:: TryStreamExt ;
420449 let mut stream = self . into_body_stream ( ) ;
421450 let mut body = Vec :: new ( ) ;
@@ -432,12 +461,12 @@ impl IncomingResponse {
432461 /// # Panics
433462 ///
434463 /// Panics if the body was already consumed.
435- pub fn into_body_stream ( self ) -> impl futures:: Stream < Item = anyhow :: Result < Vec < u8 > > > {
464+ pub fn into_body_stream ( self ) -> impl futures:: Stream < Item = Result < Vec < u8 > , streams :: Error > > {
436465 executor:: incoming_body ( self . consume ( ) . expect ( "response body was already consumed" ) )
437466 }
438467
439468 /// Return a `Vec<u8>` of the body or fails
440- pub async fn into_body ( self ) -> anyhow :: Result < Vec < u8 > > {
469+ pub async fn into_body ( self ) -> Result < Vec < u8 > , streams :: Error > {
441470 use futures:: TryStreamExt ;
442471 let mut stream = self . into_body_stream ( ) ;
443472 let mut body = Vec :: new ( ) ;
@@ -454,11 +483,22 @@ impl OutgoingResponse {
454483 /// # Panics
455484 ///
456485 /// Panics if the body was already taken.
457- pub fn take_body ( & self ) -> impl futures:: Sink < Vec < u8 > , Error = anyhow :: Error > {
486+ pub fn take_body ( & self ) -> impl futures:: Sink < Vec < u8 > , Error = Error > {
458487 executor:: outgoing_body ( self . write ( ) . expect ( "response body was already taken" ) )
459488 }
460489}
461490
491+ impl OutgoingRequest {
492+ /// Construct a `Sink` which writes chunks to the body of the specified response.
493+ ///
494+ /// # Panics
495+ ///
496+ /// Panics if the body was already taken.
497+ pub fn take_body ( & self ) -> impl futures:: Sink < Vec < u8 > , Error = Error > {
498+ executor:: outgoing_body ( self . write ( ) . expect ( "request body was already taken" ) )
499+ }
500+ }
501+
462502/// The out param for setting an `OutgoingResponse`
463503pub struct ResponseOutparam ( types:: ResponseOutparam ) ;
464504
@@ -482,8 +522,7 @@ impl ResponseOutparam {
482522 self ,
483523 response : OutgoingResponse ,
484524 buffer : Vec < u8 > ,
485- ) -> anyhow:: Result < ( ) > {
486- use futures:: SinkExt ;
525+ ) -> Result < ( ) , Error > {
487526 let mut body = response. take_body ( ) ;
488527 self . set ( response) ;
489528 body. send ( buffer) . await
@@ -496,20 +535,35 @@ impl ResponseOutparam {
496535}
497536
498537/// Send an outgoing request
538+ ///
539+ /// If `request`` is an `OutgoingRequest` and you are streaming the body to the
540+ /// outgoing request body sink, you need to ensure it is dropped before awaiting this function.
499541pub async fn send < I , O > ( request : I ) -> Result < O , SendError >
500542where
501- I : TryInto < OutgoingRequest > ,
543+ I : TryIntoOutgoingRequest ,
502544 I :: Error : Into < Box < dyn std:: error:: Error + Send + Sync > > + ' static ,
503545 O : TryFromIncomingResponse ,
504546 O :: Error : Into < Box < dyn std:: error:: Error + Send + Sync > > + ' static ,
505547{
506- let response = executor:: outgoing_request_send (
507- request
508- . try_into ( )
509- . map_err ( |e| SendError :: RequestConversion ( e. into ( ) ) ) ?,
510- )
511- . await
548+ let ( request, body_buffer) = I :: try_into_outgoing_request ( request)
549+ . map_err ( |e| SendError :: RequestConversion ( e. into ( ) ) ) ?;
550+ let response = if let Some ( body_buffer) = body_buffer {
551+ // It is part of the contract of the trait that implementors of `TryIntoOutgoingRequest`
552+ // do not call `OutgoingRequest::write`` if they return a buffered body.
553+ let mut body_sink = request. take_body ( ) ;
554+ let response = executor:: outgoing_request_send ( request) ;
555+ body_sink
556+ . send ( body_buffer)
557+ . await
558+ . map_err ( |e| SendError :: Http ( Error :: UnexpectedError ( e. to_string ( ) ) ) ) ?;
559+ // The body sink needs to be dropped before we await the response, otherwise we deadlock
560+ drop ( body_sink) ;
561+ response. await
562+ } else {
563+ executor:: outgoing_request_send ( request) . await
564+ }
512565 . map_err ( SendError :: Http ) ?;
566+
513567 TryFromIncomingResponse :: try_from_incoming_response ( response)
514568 . await
515569 . map_err ( |e : O :: Error | SendError :: ResponseConversion ( e. into ( ) ) )
@@ -614,6 +668,14 @@ pub mod responses {
614668 }
615669}
616670
671+ impl std:: fmt:: Display for streams:: Error {
672+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
673+ f. write_str ( & self . to_debug_string ( ) )
674+ }
675+ }
676+
677+ impl std:: error:: Error for streams:: Error { }
678+
617679#[ cfg( test) ]
618680mod tests {
619681 use super :: * ;
0 commit comments