@@ -15,13 +15,132 @@ pub use super::wit::wasi::http::types::*;
1515/// is no need for streaming bodies.
1616pub struct Request {
1717 /// The method of the request
18- pub method : Method ,
19- /// The path together with the query string
20- pub path_and_query : String ,
18+ method : Method ,
19+ /// The uri for the request
20+ ///
21+ /// The first item is set to `None` if the supplied uri is malformed
22+ uri : ( Option < hyperium:: Uri > , String ) ,
2123 /// The request headers
22- pub headers : Vec < ( String , Vec < u8 > ) > ,
24+ headers : Vec < ( String , String ) > ,
2325 /// The request body as bytes
24- pub body : Vec < u8 > ,
26+ body : Vec < u8 > ,
27+ }
28+
29+ impl Request {
30+ fn new ( method : Method , uri : impl Into < String > ) -> Self {
31+ Self {
32+ method,
33+ uri : Self :: parse_uri ( uri. into ( ) ) ,
34+ headers : Vec :: new ( ) ,
35+ body : Vec :: new ( ) ,
36+ }
37+ }
38+
39+ /// The request method
40+ pub fn method ( & self ) -> & Method {
41+ & self . method
42+ }
43+
44+ /// The request uri
45+ pub fn uri ( & self ) -> & str {
46+ & self . uri . 1
47+ }
48+
49+ /// The request uri path
50+ pub fn path ( & self ) -> & str {
51+ self . uri . 0 . as_ref ( ) . map ( |u| u. path ( ) ) . unwrap_or_default ( )
52+ }
53+
54+ /// The request uri query
55+ pub fn query ( & self ) -> & str {
56+ self . uri
57+ . 0
58+ . as_ref ( )
59+ . and_then ( |u| u. query ( ) )
60+ . unwrap_or_default ( )
61+ }
62+
63+ /// The request headers
64+ pub fn headers ( & self ) -> & [ ( String , String ) ] {
65+ & self . headers
66+ }
67+
68+ /// The request headers
69+ pub fn headers_mut ( & mut self ) -> & mut Vec < ( String , String ) > {
70+ & mut self . headers
71+ }
72+
73+ /// The request body
74+ pub fn body ( & self ) -> & [ u8 ] {
75+ & self . body
76+ }
77+
78+ /// The request body
79+ pub fn body_mut ( & mut self ) -> & mut Vec < u8 > {
80+ & mut self . body
81+ }
82+
83+ /// Consume this type and return its body
84+ pub fn into_body ( self ) -> Vec < u8 > {
85+ self . body
86+ }
87+
88+ /// Create a request builder
89+ pub fn builder ( ) -> RequestBuilder {
90+ RequestBuilder :: new ( Method :: Get , "/" )
91+ }
92+
93+ fn parse_uri ( uri : String ) -> ( Option < hyperium:: Uri > , String ) {
94+ (
95+ hyperium:: Uri :: try_from ( & uri)
96+ . or_else ( |_| hyperium:: Uri :: try_from ( & format ! ( "http://{uri}" ) ) )
97+ . ok ( ) ,
98+ uri,
99+ )
100+ }
101+ }
102+
103+ /// A request builder
104+ pub struct RequestBuilder {
105+ request : Request ,
106+ }
107+
108+ impl RequestBuilder {
109+ /// Create a new `RequestBuilder`
110+ pub fn new ( method : Method , uri : impl Into < String > ) -> Self {
111+ Self {
112+ request : Request :: new ( method, uri. into ( ) ) ,
113+ }
114+ }
115+
116+ /// Set the method
117+ pub fn method ( & mut self , method : Method ) -> & mut Self {
118+ self . request . method = method;
119+ self
120+ }
121+
122+ /// Set the uri
123+ pub fn uri ( & mut self , uri : impl Into < String > ) -> & mut Self {
124+ self . request . uri = Request :: parse_uri ( uri. into ( ) ) ;
125+ self
126+ }
127+
128+ /// Set the headers
129+ pub fn headers ( & mut self , headers : impl conversions:: IntoHeaders ) -> & mut Self {
130+ self . request . headers = headers. into_headers ( ) ;
131+ self
132+ }
133+
134+ /// Set the body
135+ pub fn body ( & mut self , body : impl conversions:: IntoBody ) -> & mut Self {
136+ self . request . body = body. into_body ( ) ;
137+ self
138+ }
139+
140+ /// Build the `Request`
141+ pub fn build ( & mut self ) -> Request {
142+ std:: mem:: replace ( & mut self . request , Request :: new ( Method :: Get , "/" ) )
143+ }
25144}
26145
27146/// A unified response object that can represent both outgoing and incoming responses.
@@ -30,38 +149,94 @@ pub struct Request {
30149/// is no need for streaming bodies.
31150pub struct Response {
32151 /// The status of the response
33- pub status : StatusCode ,
152+ status : StatusCode ,
34153 /// The response headers
35- pub headers : Vec < ( String , Vec < u8 > ) > ,
154+ headers : Vec < ( String , String ) > ,
36155 /// The body of the response as bytes
37- pub body : Vec < u8 > ,
156+ body : Vec < u8 > ,
38157}
39158
40159impl Response {
41160 /// Create a new response from a status and optional headers and body
42- pub fn new < S : conversions:: IntoStatusCode , B : conversions:: IntoBody > (
43- status : S ,
44- body : B ,
45- ) -> Self {
161+ pub fn new ( status : impl conversions:: IntoStatusCode , body : impl conversions:: IntoBody ) -> Self {
46162 Self {
47163 status : status. into_status_code ( ) ,
48- headers : Default :: default ( ) ,
164+ headers : Vec :: new ( ) ,
49165 body : body. into_body ( ) ,
50166 }
51167 }
52168
53- /// Create a new response from a status and optional headers and body
54- pub fn new_with_headers < S : conversions:: IntoStatusCode , B : conversions:: IntoBody > (
55- status : S ,
56- headers : Vec < ( String , Vec < u8 > ) > ,
57- body : B ,
58- ) -> Self {
59- Self {
60- status : status. into_status_code ( ) ,
61- headers,
62- body : body. into_body ( ) ,
169+ /// The response status
170+ pub fn status ( & self ) -> & StatusCode {
171+ & self . status
172+ }
173+
174+ /// The response headers
175+ ///
176+ /// Technically headers do not have to be utf8 encoded but this type assumes they are.
177+ /// If you know you'll be dealing with non-utf8 encoded headers, reach more a more powerful
178+ /// response type like that found in the `http` crate.
179+ pub fn headers ( & self ) -> & [ ( String , String ) ] {
180+ & self . headers
181+ }
182+
183+ /// The response headers
184+ pub fn headers_mut ( & mut self ) -> & mut Vec < ( String , String ) > {
185+ & mut self . headers
186+ }
187+
188+ /// The response body
189+ pub fn body ( & self ) -> & [ u8 ] {
190+ & self . body
191+ }
192+
193+ /// The response body
194+ pub fn body_mut ( & mut self ) -> & mut Vec < u8 > {
195+ & mut self . body
196+ }
197+
198+ /// Consume this type and return its body
199+ pub fn into_body ( self ) -> Vec < u8 > {
200+ self . body
201+ }
202+
203+ fn builder ( ) -> ResponseBuilder {
204+ ResponseBuilder :: new ( 200 )
205+ }
206+ }
207+
208+ struct ResponseBuilder {
209+ response : Response ,
210+ }
211+
212+ impl ResponseBuilder {
213+ pub fn new ( status : impl conversions:: IntoStatusCode ) -> Self {
214+ ResponseBuilder {
215+ response : Response :: new ( status, Vec :: new ( ) ) ,
63216 }
64217 }
218+
219+ /// Set the status
220+ pub fn status ( & mut self , status : impl conversions:: IntoStatusCode ) -> & mut Self {
221+ self . response . status = status. into_status_code ( ) ;
222+ self
223+ }
224+
225+ /// Set the headers
226+ pub fn headers ( & mut self , headers : impl conversions:: IntoHeaders ) -> & mut Self {
227+ self . response . headers = headers. into_headers ( ) ;
228+ self
229+ }
230+
231+ /// Set the body
232+ pub fn body ( & mut self , body : impl conversions:: IntoBody ) -> & mut Self {
233+ self . response . body = body. into_body ( ) ;
234+ self
235+ }
236+
237+ pub fn build ( & mut self ) -> Response {
238+ std:: mem:: replace ( & mut self . response , Response :: new ( 200 , Vec :: new ( ) ) )
239+ }
65240}
66241
67242impl std:: hash:: Hash for Method {
@@ -99,6 +274,23 @@ impl std::fmt::Display for Method {
99274}
100275
101276impl IncomingRequest {
277+ /// The incoming request Uri
278+ pub fn uri ( & self ) -> String {
279+ let scheme_and_authority =
280+ if let ( Some ( scheme) , Some ( authority) ) = ( self . scheme ( ) , self . authority ( ) ) {
281+ let scheme = match & scheme {
282+ Scheme :: Http => "http://" ,
283+ Scheme :: Https => "https://" ,
284+ Scheme :: Other ( s) => s. as_str ( ) ,
285+ } ;
286+ format ! ( "{scheme}{authority}" )
287+ } else {
288+ String :: new ( )
289+ } ;
290+ let path_and_query = self . path_with_query ( ) . unwrap_or_default ( ) ;
291+ format ! ( "{scheme_and_authority}{path_and_query}" )
292+ }
293+
102294 /// Return a `Stream` from which the body of the specified request may be read.
103295 ///
104296 /// # Panics
@@ -287,3 +479,29 @@ pub mod responses {
287479 Response :: new ( 400 , msg. map ( |m| m. into_bytes ( ) ) )
288480 }
289481}
482+
483+ #[ cfg( test) ]
484+ mod tests {
485+ use super :: * ;
486+
487+ #[ test]
488+ fn request_uri_parses ( ) {
489+ let uri = "/hello?world=1" ;
490+ let req = Request :: new ( Method :: Get , uri) ;
491+ assert_eq ! ( req. uri( ) , uri) ;
492+ assert_eq ! ( req. path( ) , "/hello" ) ;
493+ assert_eq ! ( req. query( ) , "world=1" ) ;
494+
495+ let uri = "http://localhost:3000/hello?world=1" ;
496+ let req = Request :: new ( Method :: Get , uri) ;
497+ assert_eq ! ( req. uri( ) , uri) ;
498+ assert_eq ! ( req. path( ) , "/hello" ) ;
499+ assert_eq ! ( req. query( ) , "world=1" ) ;
500+
501+ let uri = "localhost:3000/hello?world=1" ;
502+ let req = Request :: new ( Method :: Get , uri) ;
503+ assert_eq ! ( req. uri( ) , uri) ;
504+ assert_eq ! ( req. path( ) , "/hello" ) ;
505+ assert_eq ! ( req. query( ) , "world=1" ) ;
506+ }
507+ }
0 commit comments