@@ -7,19 +7,146 @@ use std::collections::HashMap;
7
7
pub use conversions:: IntoResponse ;
8
8
#[ doc( inline) ]
9
9
pub 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 ,
12
12
} ;
13
13
14
14
use self :: conversions:: { TryFromIncomingResponse , TryIntoOutgoingRequest } ;
15
15
use super :: wit:: wasi:: http0_2_0:: types;
16
16
use futures:: SinkExt ;
17
17
use spin_executor:: bindings:: wasi:: io:: streams:: { self , StreamError } ;
18
18
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
+
19
68
/// A unified request object that can represent both incoming and outgoing requests.
20
69
///
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
22
71
/// 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
+ /// ```
23
150
pub struct Request {
24
151
/// The method of the request
25
152
method : Method ,
@@ -179,7 +306,44 @@ impl Request {
179
306
}
180
307
}
181
308
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
+ /// ```
183
347
pub struct RequestBuilder {
184
348
request : Request ,
185
349
}
@@ -234,6 +398,43 @@ impl RequestBuilder {
234
398
///
235
399
/// This should be used in favor of `OutgoingResponse` and `IncomingResponse` when there
236
400
/// 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
+ /// ```
237
438
pub struct Response {
238
439
/// The status of the response
239
440
status : StatusCode ,
@@ -613,6 +814,59 @@ impl ResponseOutparam {
613
814
}
614
815
615
816
/// 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
+ /// ```
616
870
pub async fn send < I , O > ( request : I ) -> Result < O , SendError >
617
871
where
618
872
I : TryIntoOutgoingRequest ,
@@ -692,7 +946,7 @@ impl std::fmt::Display for NonUtf8BodyError {
692
946
}
693
947
694
948
mod router;
695
- /// Exports HTTP Router items.
949
+ # [ doc ( inline ) ]
696
950
pub use router:: * ;
697
951
698
952
/// A Body extractor
0 commit comments