@@ -7,19 +7,146 @@ use std::collections::HashMap;
77pub use conversions:: IntoResponse ;
88#[ doc( inline) ]
99pub use types:: {
10- ErrorCode , Fields , Headers , IncomingRequest , IncomingResponse , Method , OutgoingBody ,
11- OutgoingRequest , OutgoingResponse , Scheme , StatusCode , Trailers ,
10+ ErrorCode , Fields , Headers , IncomingResponse , Method , OutgoingBody , OutgoingRequest ,
11+ OutgoingResponse , Scheme , StatusCode , Trailers ,
1212} ;
1313
1414use self :: conversions:: { TryFromIncomingResponse , TryIntoOutgoingRequest } ;
1515use super :: wit:: wasi:: http0_2_0:: types;
1616use futures:: SinkExt ;
1717use spin_executor:: bindings:: wasi:: io:: streams:: { self , StreamError } ;
1818
19+ /// Represents an incoming HTTP request.
20+ ///
21+ /// If you don't need streaming access to the request body, you may find it
22+ /// easier to work with [Request] instead. To make outgoing requests, use
23+ /// [Request] (non-streaming) or [OutgoingRequest].
24+ ///
25+ /// # Examples
26+ ///
27+ /// Access the request body as a Rust stream:
28+ ///
29+ /// ```no_run
30+ /// # use spin_sdk::http::{IncomingRequest, ResponseOutparam};
31+ /// async fn handle_request(req: IncomingRequest, response_outparam: ResponseOutparam) {
32+ /// use futures::stream::StreamExt;
33+ ///
34+ /// let mut stream = req.into_body_stream();
35+ /// loop {
36+ /// let chunk = stream.next().await;
37+ /// match chunk {
38+ /// None => {
39+ /// println!("end of request body");
40+ /// break;
41+ /// }
42+ /// Some(Ok(chunk)) => {
43+ /// // process the data from the stream in a very realistic way
44+ /// println!("read {} bytes", chunk.len());
45+ /// }
46+ /// Some(Err(e)) => {
47+ /// println!("error reading body: {e:?}");
48+ /// break;
49+ /// }
50+ /// }
51+ /// }
52+ /// }
53+ /// ```
54+ ///
55+ /// Access the body in a non-streaming way. This can be useful where your component
56+ /// must take IncomingRequest because some scenarios need streaming, but you
57+ /// have other scenarios that do not.
58+ ///
59+ /// ```no_run
60+ /// # use spin_sdk::http::{IncomingRequest, ResponseOutparam};
61+ /// async fn handle_request(req: IncomingRequest, response_outparam: ResponseOutparam) {
62+ /// let body = req.into_body().await.unwrap();
63+ /// }
64+ /// ```
65+ #[ doc( inline) ]
66+ pub use types:: IncomingRequest ;
67+
1968/// A unified request object that can represent both incoming and outgoing requests.
2069///
21- /// This should be used in favor of ` IncomingRequest` and ` OutgoingRequest` when there
70+ /// This should be used in favor of [ IncomingRequest] and [ OutgoingRequest] when there
2271/// is no need for streaming bodies.
72+ ///
73+ /// # Examples
74+ ///
75+ /// Read the method, a header, and the body an incoming HTTP request, without streaming:
76+ ///
77+ /// ```no_run
78+ /// # use spin_sdk::http::{Method, Request, Response};
79+ ///
80+ /// fn handle_request(req: Request) -> anyhow::Result<Response> {
81+ /// let method = req.method();
82+ /// let content_type = req.header("content-type");
83+ /// if *method == Method::Post {
84+ /// let body = String::from_utf8_lossy(req.body());
85+ /// }
86+ /// todo!()
87+ /// }
88+ /// ```
89+ ///
90+ /// Send an outgoing GET request (no body) to `example.com`:
91+ ///
92+ /// ```no_run
93+ /// use spin_sdk::http::{Request, Response};
94+ ///
95+ /// # #[tokio::main]
96+ /// # async fn main() -> anyhow::Result<()> {
97+ /// let request = Request::get("https://example.com");
98+ /// let response: Response = spin_sdk::http::send(request).await?;
99+ /// # Ok(())
100+ /// # }
101+ /// ```
102+ ///
103+ /// Send an outgoing POST request with a non-streaming body to `example.com`.
104+ ///
105+ /// ```no_run
106+ /// use spin_sdk::http::{Request, Response};
107+ ///
108+ /// # #[tokio::main]
109+ /// # async fn main() -> anyhow::Result<()> {
110+ /// let request = Request::post("https://example.com", "it's a-me, Spin")
111+ /// .header("content-type", "text/plain")
112+ /// .build();
113+ /// let response: Response = spin_sdk::http::send(request).await?;
114+ /// # Ok(())
115+ /// # }
116+ /// ```
117+ ///
118+ /// Build and send an outgoing request without using the helper shortcut.
119+ ///
120+ /// ```no_run
121+ /// use spin_sdk::http::{Method, Request, Response};
122+ ///
123+ /// # #[tokio::main]
124+ /// # async fn main() -> anyhow::Result<()> {
125+ /// let mut request = Request::new(Method::Put, "https://example.com/message/safety");
126+ /// request.set_header("content-type", "text/plain");
127+ /// *request.body_mut() = "beware the crocodile".as_bytes().to_vec();
128+ /// let response: Response = spin_sdk::http::send(request).await?;
129+ /// # Ok(())
130+ /// # }
131+ /// ```
132+ ///
133+ /// Build and send an outgoing request using the fluent builder.
134+ ///
135+ /// ```no_run
136+ /// use spin_sdk::http::{Method, Request, Response};
137+ ///
138+ /// # #[tokio::main]
139+ /// # async fn main() -> anyhow::Result<()> {
140+ /// let request = Request::builder()
141+ /// .uri("https://example.com/message/motivational")
142+ /// .method(Method::Put)
143+ /// .header("content-type", "text/plain")
144+ /// .body("the capybaras of creativity fly higher than the bluebirds of banality")
145+ /// .build();
146+ /// let response: Response = spin_sdk::http::send(request).await?;
147+ /// # Ok(())
148+ /// # }
149+ /// ```
23150pub struct Request {
24151 /// The method of the request
25152 method : Method ,
@@ -179,7 +306,44 @@ impl Request {
179306 }
180307}
181308
182- /// A request builder
309+ /// A builder for non-streaming outgoing HTTP requests. You can obtain
310+ /// a RequestBuilder from the [Request::builder()] method, or from
311+ /// method-specific helpers such as [Request::get()] or [Request::post()].
312+ ///
313+ /// # Examples
314+ ///
315+ /// Use a method helper to build an outgoing POST request:
316+ ///
317+ /// ```no_run
318+ /// use spin_sdk::http::{Request, Response};
319+ ///
320+ /// # #[tokio::main]
321+ /// # async fn main() -> anyhow::Result<()> {
322+ /// let request = Request::post("https://example.com", "it's a-me, Spin")
323+ /// .header("content-type", "text/plain")
324+ /// .build();
325+ /// let response: Response = spin_sdk::http::send(request).await?;
326+ /// # Ok(())
327+ /// # }
328+ /// ```
329+ ///
330+ /// Build and send an outgoing request using the RequestBuilder.
331+ ///
332+ /// ```no_run
333+ /// use spin_sdk::http::{Method, Request, Response};
334+ ///
335+ /// # #[tokio::main]
336+ /// # async fn main() -> anyhow::Result<()> {
337+ /// let request = Request::builder()
338+ /// .uri("https://example.com/message/motivational")
339+ /// .method(Method::Put)
340+ /// .header("content-type", "text/plain")
341+ /// .body("the capybaras of creativity fly higher than the bluebirds of banality")
342+ /// .build();
343+ /// let response: Response = spin_sdk::http::send(request).await?;
344+ /// # Ok(())
345+ /// # }
346+ /// ```
183347pub struct RequestBuilder {
184348 request : Request ,
185349}
@@ -234,6 +398,43 @@ impl RequestBuilder {
234398///
235399/// This should be used in favor of `OutgoingResponse` and `IncomingResponse` when there
236400/// is no need for streaming bodies.
401+ ///
402+ /// # Examples
403+ ///
404+ /// Send a response to an incoming HTTP request:
405+ ///
406+ /// ```no_run
407+ /// use spin_sdk::http::{Request, Response};
408+ ///
409+ /// fn handle_request(req: Request) -> anyhow::Result<Response> {
410+ /// Ok(Response::builder()
411+ /// .status(200)
412+ /// .header("content-type", "text/plain")
413+ /// .body("Hello, world")
414+ /// .build())
415+ /// }
416+ /// ```
417+ ///
418+ /// Parse a response from an outgoing HTTP request:
419+ ///
420+ /// ```no_run
421+ /// # use spin_sdk::http::{Request, Response};
422+ /// #[derive(serde::Deserialize)]
423+ /// struct User {
424+ /// name: String,
425+ /// }
426+ ///
427+ /// # #[tokio::main]
428+ /// # async fn main() -> anyhow::Result<()> {
429+ /// let request = Request::get("https://example.com");
430+ /// let response: Response = spin_sdk::http::send(request).await?;
431+ /// if *response.status() == 200 {
432+ /// let body = response.body();
433+ /// let user: User = serde_json::from_slice(body)?;
434+ /// }
435+ /// # Ok(())
436+ /// # }
437+ /// ```
237438pub struct Response {
238439 /// The status of the response
239440 status : StatusCode ,
@@ -613,6 +814,59 @@ impl ResponseOutparam {
613814}
614815
615816/// Send an outgoing request
817+ ///
818+ /// # Examples
819+ ///
820+ /// Get the example.com home page:
821+ ///
822+ /// ```no_run
823+ /// use spin_sdk::http::{Request, Response};
824+ ///
825+ /// # #[tokio::main]
826+ /// # async fn main() -> anyhow::Result<()> {
827+ /// let request = Request::get("example.com").build();
828+ /// let response: Response = spin_sdk::http::send(request).await?;
829+ /// println!("{}", response.body().len());
830+ /// # Ok(())
831+ /// # }
832+ /// ```
833+ ///
834+ /// Use the `http` crate Request type to send a data transfer value:
835+ ///
836+ /// ```no_run
837+ /// use hyperium::Request;
838+ ///
839+ /// #[derive(serde::Serialize)]
840+ /// struct User {
841+ /// name: String,
842+ /// }
843+ ///
844+ /// impl spin_sdk::http::conversions::TryIntoBody for User {
845+ /// type Error = serde_json::Error;
846+ ///
847+ /// fn try_into_body(self) -> Result<Vec<u8>, Self::Error> {
848+ /// serde_json::to_vec(&self)
849+ /// }
850+ /// }
851+ ///
852+ /// # #[tokio::main]
853+ /// # async fn main() -> anyhow::Result<()> {
854+ /// let user = User {
855+ /// name: "Alice".to_owned(),
856+ /// };
857+ ///
858+ /// let request = hyperium::Request::builder()
859+ /// .method("POST")
860+ /// .uri("https://example.com/users")
861+ /// .header("content-type", "application/json")
862+ /// .body(user)?;
863+ ///
864+ /// let response: hyperium::Response<()> = spin_sdk::http::send(request).await?;
865+ ///
866+ /// println!("{}", response.status().is_success());
867+ /// # Ok(())
868+ /// # }
869+ /// ```
616870pub async fn send < I , O > ( request : I ) -> Result < O , SendError >
617871where
618872 I : TryIntoOutgoingRequest ,
@@ -692,7 +946,7 @@ impl std::fmt::Display for NonUtf8BodyError {
692946}
693947
694948mod router;
695- /// Exports HTTP Router items.
949+ # [ doc ( inline ) ]
696950pub use router:: * ;
697951
698952/// A Body extractor
0 commit comments