1pub mod conversions;
3
4use std::collections::HashMap;
5
6#[doc(inline)]
7pub use conversions::IntoResponse;
8#[doc(inline)]
9pub use types::{
10 ErrorCode, Fields, Headers, IncomingRequest, IncomingResponse, Method, OutgoingBody,
11 OutgoingRequest, OutgoingResponse, Scheme, StatusCode, Trailers,
12};
13
14use self::conversions::{TryFromIncomingResponse, TryIntoOutgoingRequest};
15use super::wit::wasi::http0_2_0::types;
16use futures::SinkExt;
17use spin_executor::bindings::wasi::io::streams::{self, StreamError};
18
19pub struct Request {
24 method: Method,
26 uri: (Option<hyperium::Uri>, String),
30 headers: HashMap<String, HeaderValue>,
32 body: Vec<u8>,
34}
35
36impl Request {
37 pub fn new(method: Method, uri: impl Into<String>) -> Self {
39 Self {
40 method,
41 uri: Self::parse_uri(uri.into()),
42 headers: HashMap::new(),
43 body: Vec::new(),
44 }
45 }
46
47 pub fn builder() -> RequestBuilder {
49 RequestBuilder::new(Method::Get, "/")
50 }
51
52 pub fn get(uri: impl Into<String>) -> RequestBuilder {
54 RequestBuilder::new(Method::Get, uri)
55 }
56
57 pub fn post(uri: impl Into<String>, body: impl conversions::IntoBody) -> RequestBuilder {
59 let mut builder = RequestBuilder::new(Method::Post, uri);
60 builder.body(body);
61 builder
62 }
63
64 pub fn put(uri: impl Into<String>, body: impl conversions::IntoBody) -> RequestBuilder {
66 let mut builder = RequestBuilder::new(Method::Put, uri);
67 builder.body(body);
68 builder
69 }
70
71 pub fn patch(uri: impl Into<String>, body: impl conversions::IntoBody) -> RequestBuilder {
73 let mut builder = RequestBuilder::new(Method::Patch, uri);
74 builder.body(body);
75 builder
76 }
77
78 pub fn delete(uri: impl Into<String>) -> RequestBuilder {
80 RequestBuilder::new(Method::Delete, uri)
81 }
82
83 pub fn method(&self) -> &Method {
85 &self.method
86 }
87
88 pub fn uri(&self) -> &str {
90 &self.uri.1
91 }
92
93 pub fn path(&self) -> &str {
95 self.uri.0.as_ref().map(|u| u.path()).unwrap_or_default()
96 }
97
98 pub fn query(&self) -> &str {
100 self.uri
101 .0
102 .as_ref()
103 .and_then(|u| u.query())
104 .unwrap_or_default()
105 }
106
107 pub fn headers(&self) -> impl Iterator<Item = (&str, &HeaderValue)> {
109 self.headers.iter().map(|(k, v)| (k.as_str(), v))
110 }
111
112 pub fn header(&self, name: &str) -> Option<&HeaderValue> {
116 self.headers.get(&name.to_lowercase())
117 }
118
119 pub fn set_header(&mut self, name: impl Into<String>, value: impl Into<String>) {
121 self.headers.insert(
122 name.into(),
123 HeaderValue {
124 inner: HeaderValueRep::String(value.into()),
125 },
126 );
127 }
128
129 pub fn body(&self) -> &[u8] {
131 &self.body
132 }
133
134 pub fn body_mut(&mut self) -> &mut Vec<u8> {
136 &mut self.body
137 }
138
139 pub fn into_body(self) -> Vec<u8> {
141 self.body
142 }
143
144 fn parse_uri(uri: String) -> (Option<hyperium::Uri>, String) {
145 (
146 hyperium::Uri::try_from(&uri)
147 .or_else(|_| hyperium::Uri::try_from(&format!("http://{uri}")))
148 .ok(),
149 uri,
150 )
151 }
152
153 fn is_https(&self) -> bool {
155 self.uri
156 .0
157 .as_ref()
158 .and_then(|u| u.scheme())
159 .map(|s| s == &hyperium::uri::Scheme::HTTPS)
160 .unwrap_or(true)
161 }
162
163 fn authority(&self) -> Option<&str> {
165 self.uri
166 .0
167 .as_ref()
168 .and_then(|u| u.authority())
169 .map(|a| a.as_str())
170 }
171
172 pub fn path_and_query(&self) -> Option<&str> {
174 self.uri
175 .0
176 .as_ref()
177 .and_then(|u| u.path_and_query())
178 .map(|s| s.as_str())
179 }
180}
181
182pub struct RequestBuilder {
184 request: Request,
185}
186
187impl RequestBuilder {
188 pub fn new(method: Method, uri: impl Into<String>) -> Self {
190 Self {
191 request: Request::new(method, uri.into()),
192 }
193 }
194
195 pub fn method(&mut self, method: Method) -> &mut Self {
197 self.request.method = method;
198 self
199 }
200
201 pub fn uri(&mut self, uri: impl Into<String>) -> &mut Self {
203 self.request.uri = Request::parse_uri(uri.into());
204 self
205 }
206
207 pub fn headers(&mut self, headers: impl conversions::IntoHeaders) -> &mut Self {
209 self.request.headers = into_header_rep(headers);
210 self
211 }
212
213 pub fn header(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self {
215 self.request
216 .headers
217 .insert(key.into().to_lowercase(), HeaderValue::string(value.into()));
218 self
219 }
220
221 pub fn body(&mut self, body: impl conversions::IntoBody) -> &mut Self {
223 self.request.body = body.into_body();
224 self
225 }
226
227 pub fn build(&mut self) -> Request {
229 std::mem::replace(&mut self.request, Request::new(Method::Get, "/"))
230 }
231}
232
233pub struct Response {
238 status: StatusCode,
240 headers: HashMap<String, HeaderValue>,
242 body: Vec<u8>,
244}
245
246impl Response {
247 pub fn new(status: impl conversions::IntoStatusCode, body: impl conversions::IntoBody) -> Self {
249 Self {
250 status: status.into_status_code(),
251 headers: HashMap::new(),
252 body: body.into_body(),
253 }
254 }
255
256 pub fn status(&self) -> &StatusCode {
258 &self.status
259 }
260
261 pub fn headers(&self) -> impl Iterator<Item = (&str, &HeaderValue)> {
263 self.headers.iter().map(|(k, v)| (k.as_str(), v))
264 }
265
266 pub fn header(&self, name: &str) -> Option<&HeaderValue> {
270 self.headers.get(&name.to_lowercase())
271 }
272
273 pub fn set_header(&mut self, name: impl Into<String>, value: impl Into<String>) {
275 self.headers.insert(
276 name.into(),
277 HeaderValue {
278 inner: HeaderValueRep::String(value.into()),
279 },
280 );
281 }
282
283 pub fn body(&self) -> &[u8] {
285 &self.body
286 }
287
288 pub fn body_mut(&mut self) -> &mut Vec<u8> {
290 &mut self.body
291 }
292
293 pub fn into_body(self) -> Vec<u8> {
295 self.body
296 }
297
298 pub fn into_builder(self) -> ResponseBuilder {
301 ResponseBuilder { response: self }
302 }
303
304 pub fn builder() -> ResponseBuilder {
306 ResponseBuilder::new(200)
307 }
308}
309
310impl std::fmt::Debug for Response {
311 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
312 f.debug_struct("Response")
313 .field("status", &self.status)
314 .field("headers", &self.headers)
315 .field("body.len()", &self.body.len())
316 .finish()
317 }
318}
319
320pub struct ResponseBuilder {
322 response: Response,
323}
324
325impl ResponseBuilder {
326 pub fn new(status: impl conversions::IntoStatusCode) -> Self {
328 ResponseBuilder {
329 response: Response::new(status, Vec::new()),
330 }
331 }
332
333 pub fn status(&mut self, status: impl conversions::IntoStatusCode) -> &mut Self {
335 self.response.status = status.into_status_code();
336 self
337 }
338
339 pub fn headers(&mut self, headers: impl conversions::IntoHeaders) -> &mut Self {
341 self.response.headers = into_header_rep(headers.into_headers());
342 self
343 }
344
345 pub fn header(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self {
347 self.response
348 .headers
349 .insert(key.into().to_lowercase(), HeaderValue::string(value.into()));
350 self
351 }
352
353 pub fn body(&mut self, body: impl conversions::IntoBody) -> &mut Self {
355 self.response.body = body.into_body();
356 self
357 }
358
359 pub fn build(&mut self) -> Response {
361 std::mem::replace(&mut self.response, Response::new(200, Vec::new()))
362 }
363}
364
365#[derive(Debug, PartialEq, Eq, Clone)]
370pub struct HeaderValue {
371 inner: HeaderValueRep,
372}
373
374#[derive(Debug, PartialEq, Eq, Clone)]
375enum HeaderValueRep {
376 String(String),
378 Bytes(Vec<u8>),
380}
381
382impl HeaderValue {
383 pub fn string(str: String) -> HeaderValue {
385 HeaderValue {
386 inner: HeaderValueRep::String(str),
387 }
388 }
389
390 pub fn bytes(bytes: Vec<u8>) -> HeaderValue {
392 HeaderValue {
393 inner: String::from_utf8(bytes)
394 .map(HeaderValueRep::String)
395 .unwrap_or_else(|e| HeaderValueRep::Bytes(e.into_bytes())),
396 }
397 }
398
399 pub fn as_str(&self) -> Option<&str> {
403 match &self.inner {
404 HeaderValueRep::String(s) => Some(s),
405 HeaderValueRep::Bytes(b) => std::str::from_utf8(b).ok(),
406 }
407 }
408
409 pub fn as_bytes(&self) -> &[u8] {
411 self.as_ref()
412 }
413
414 pub fn into_utf8_lossy(self) -> String {
416 match self.inner {
417 HeaderValueRep::String(s) => s,
418 HeaderValueRep::Bytes(b) => String::from_utf8_lossy(&b).into_owned(),
419 }
420 }
421
422 pub fn into_bytes(self) -> Vec<u8> {
424 match self.inner {
425 HeaderValueRep::String(s) => s.into_bytes(),
426 HeaderValueRep::Bytes(b) => b,
427 }
428 }
429}
430
431impl AsRef<[u8]> for HeaderValue {
432 fn as_ref(&self) -> &[u8] {
433 match &self.inner {
434 HeaderValueRep::String(s) => s.as_bytes(),
435 HeaderValueRep::Bytes(b) => b,
436 }
437 }
438}
439
440fn into_header_rep(headers: impl conversions::IntoHeaders) -> HashMap<String, HeaderValue> {
441 headers
442 .into_headers()
443 .into_iter()
444 .map(|(k, v)| {
445 let v = String::from_utf8(v)
446 .map(HeaderValueRep::String)
447 .unwrap_or_else(|e| HeaderValueRep::Bytes(e.into_bytes()));
448 (k.to_lowercase(), HeaderValue { inner: v })
449 })
450 .collect()
451}
452
453impl std::hash::Hash for Method {
454 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
455 core::mem::discriminant(self).hash(state);
456 }
457}
458
459impl Eq for Method {}
460
461impl PartialEq for Method {
462 fn eq(&self, other: &Self) -> bool {
463 match (self, other) {
464 (Self::Other(l), Self::Other(r)) => l == r,
465 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
466 }
467 }
468}
469
470impl std::fmt::Display for Method {
471 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
472 f.write_str(match self {
473 Method::Get => "GET",
474 Method::Post => "POST",
475 Method::Put => "PUT",
476 Method::Delete => "DELETE",
477 Method::Patch => "PATCH",
478 Method::Head => "HEAD",
479 Method::Options => "OPTIONS",
480 Method::Connect => "CONNECT",
481 Method::Trace => "TRACE",
482 Method::Other(o) => o,
483 })
484 }
485}
486
487impl IncomingRequest {
488 pub fn uri(&self) -> String {
490 let scheme_and_authority =
491 if let (Some(scheme), Some(authority)) = (self.scheme(), self.authority()) {
492 let scheme = match &scheme {
493 Scheme::Http => "http://",
494 Scheme::Https => "https://",
495 Scheme::Other(s) => s.as_str(),
496 };
497 format!("{scheme}{authority}")
498 } else {
499 String::new()
500 };
501 let path_and_query = self.path_with_query().unwrap_or_default();
502 format!("{scheme_and_authority}{path_and_query}")
503 }
504
505 pub fn into_body_stream(self) -> impl futures::Stream<Item = Result<Vec<u8>, streams::Error>> {
511 executor::incoming_body(self.consume().expect("request body was already consumed"))
512 }
513
514 pub async fn into_body(self) -> Result<Vec<u8>, streams::Error> {
516 use futures::TryStreamExt;
517 let mut stream = self.into_body_stream();
518 let mut body = Vec::new();
519 while let Some(chunk) = stream.try_next().await? {
520 body.extend(chunk);
521 }
522 Ok(body)
523 }
524}
525
526impl IncomingResponse {
527 pub fn take_body_stream(&self) -> impl futures::Stream<Item = Result<Vec<u8>, streams::Error>> {
539 executor::incoming_body(self.consume().expect("response body was already consumed"))
540 }
541
542 pub async fn into_body(self) -> Result<Vec<u8>, streams::Error> {
548 use futures::TryStreamExt;
549 let mut stream = self.take_body_stream();
550 let mut body = Vec::new();
551 while let Some(chunk) = stream.try_next().await? {
552 body.extend(chunk);
553 }
554 Ok(body)
555 }
556}
557
558impl OutgoingResponse {
559 pub fn take_body(&self) -> impl futures::Sink<Vec<u8>, Error = StreamError> {
565 executor::outgoing_body(self.body().expect("response body was already taken"))
566 }
567}
568
569impl OutgoingRequest {
570 pub fn take_body(&self) -> impl futures::Sink<Vec<u8>, Error = StreamError> {
576 executor::outgoing_body(self.body().expect("request body was already taken"))
577 }
578}
579
580pub struct ResponseOutparam(types::ResponseOutparam);
582
583impl ResponseOutparam {
584 #[doc(hidden)]
585 pub unsafe fn from_handle(handle: u32) -> Self {
588 Self(types::ResponseOutparam::from_handle(handle))
589 }
590
591 pub fn set(self, response: OutgoingResponse) {
593 types::ResponseOutparam::set(self.0, Ok(response));
594 }
595
596 pub async fn set_with_body(
600 self,
601 response: OutgoingResponse,
602 buffer: Vec<u8>,
603 ) -> Result<(), StreamError> {
604 let mut body = response.take_body();
605 self.set(response);
606 body.send(buffer).await
607 }
608
609 pub fn into_inner(self) -> types::ResponseOutparam {
611 self.0
612 }
613}
614
615pub async fn send<I, O>(request: I) -> Result<O, SendError>
617where
618 I: TryIntoOutgoingRequest,
619 I::Error: Into<Box<dyn std::error::Error + Send + Sync>> + 'static,
620 O: TryFromIncomingResponse,
621 O::Error: Into<Box<dyn std::error::Error + Send + Sync>> + 'static,
622{
623 let (request, body_buffer) = I::try_into_outgoing_request(request)
624 .map_err(|e| SendError::RequestConversion(e.into()))?;
625 let response = if let Some(body_buffer) = body_buffer {
626 let mut body_sink = request.take_body();
629 let response = executor::outgoing_request_send(request);
630 body_sink.send(body_buffer).await.map_err(SendError::Io)?;
631 drop(body_sink);
632 response.await.map_err(SendError::Http)?
633 } else {
634 executor::outgoing_request_send(request)
635 .await
636 .map_err(SendError::Http)?
637 };
638
639 TryFromIncomingResponse::try_from_incoming_response(response)
640 .await
641 .map_err(|e: O::Error| SendError::ResponseConversion(e.into()))
642}
643
644#[derive(thiserror::Error, Debug)]
646pub enum SendError {
647 #[error(transparent)]
649 RequestConversion(Box<dyn std::error::Error + Send + Sync>),
650 #[error(transparent)]
652 ResponseConversion(Box<dyn std::error::Error + Send + Sync>),
653 #[error(transparent)]
655 Io(StreamError),
656 #[error(transparent)]
658 Http(ErrorCode),
659}
660
661#[doc(hidden)]
662mod executor;
664#[doc(hidden)]
665pub use executor::run;
666
667#[cfg(feature = "json")]
669#[derive(Debug)]
670pub struct JsonBodyError(serde_json::Error);
671
672#[cfg(feature = "json")]
673impl std::error::Error for JsonBodyError {}
674
675#[cfg(feature = "json")]
676impl std::fmt::Display for JsonBodyError {
677 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
678 f.write_str("could not convert body to json")
679 }
680}
681
682#[derive(Debug)]
684pub struct NonUtf8BodyError;
685
686impl std::error::Error for NonUtf8BodyError {}
687
688impl std::fmt::Display for NonUtf8BodyError {
689 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
690 f.write_str("body was expected to be utf8 but was not")
691 }
692}
693
694mod router;
695pub use router::*;
697
698#[derive(Debug)]
700pub struct Body<T>(pub T);
701
702impl<T> std::ops::Deref for Body<T> {
703 type Target = T;
704
705 fn deref(&self) -> &Self::Target {
706 &self.0
707 }
708}
709
710#[derive(Debug)]
712pub struct Json<T>(pub T);
713
714impl<T> std::ops::Deref for Json<T> {
715 type Target = T;
716
717 fn deref(&self) -> &Self::Target {
718 &self.0
719 }
720}
721
722pub mod responses {
724 use super::Response;
725
726 pub fn not_found() -> Response {
728 Response::new(404, "Not Found")
729 }
730
731 pub fn internal_server_error() -> Response {
733 Response::new(500, "Internal Server Error")
734 }
735
736 pub fn method_not_allowed() -> Response {
738 Response::new(405, "Method Not Allowed")
739 }
740
741 pub(crate) fn bad_request(msg: Option<String>) -> Response {
742 Response::new(400, msg.map(|m| m.into_bytes()))
743 }
744}
745
746#[cfg(test)]
747mod tests {
748 use super::*;
749
750 #[test]
751 fn request_uri_parses() {
752 let uri = "/hello?world=1";
753 let req = Request::new(Method::Get, uri);
754 assert_eq!(req.uri(), uri);
755 assert_eq!(req.path(), "/hello");
756 assert_eq!(req.query(), "world=1");
757
758 let uri = "http://localhost:3000/hello?world=1";
759 let req = Request::new(Method::Get, uri);
760 assert_eq!(req.uri(), uri);
761 assert_eq!(req.path(), "/hello");
762 assert_eq!(req.query(), "world=1");
763
764 let uri = "localhost:3000/hello?world=1";
765 let req = Request::new(Method::Get, uri);
766 assert_eq!(req.uri(), uri);
767 assert_eq!(req.path(), "/hello");
768 assert_eq!(req.query(), "world=1");
769 }
770}