diff --git a/Cargo.toml b/Cargo.toml index 1c19ca9d9..5933f6321 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,12 +32,11 @@ unstable = [] __internal__bench = [] [dependencies] -async-h1 = { version = "1.1.2", optional = true } +async-h1 = { version = "2.0.0", optional = true } async-sse = "2.1.0" -async-std = { version = "1.4.0", features = ["unstable"] } -cookie = { version = "0.12.0", features = ["percent-encode"]} +async-std = { version = "1.6.0", features = ["unstable"] } femme = "2.0.1" -http-types = "1.0.1" +http-types = "2.0.0" kv-log-macro = "1.0.4" mime = "0.3.14" mime_guess = "2.0.3" @@ -47,12 +46,12 @@ serde_json = "1.0.41" serde_qs = "0.5.0" [dev-dependencies] -async-std = { version = "1.4.0", features = ["unstable", "attributes"] } +async-std = { version = "1.6.0", features = ["unstable", "attributes"] } juniper = "0.14.1" portpicker = "0.1.0" serde = { version = "1.0.102", features = ["derive"] } -surf = "2.0.0-alpha.1" criterion = "0.3.1" +surf = { version = "2.0.0-alpha.2", default-features = false, features = ["h1-client"] } [[test]] name = "nested" diff --git a/examples/graphql.rs b/examples/graphql.rs index dfd16486f..1c3dad798 100644 --- a/examples/graphql.rs +++ b/examples/graphql.rs @@ -95,7 +95,7 @@ async fn handle_graphql(mut cx: Request) -> tide::Result { async fn handle_graphiql(_: Request) -> tide::Result { let res = Response::new(StatusCode::Ok) .body_string(juniper::http::graphiql::graphiql_source("/graphql")) - .set_header("content-type".parse().unwrap(), "text/html;charset=utf-8"); + .set_mime(mime::TEXT_HTML_UTF_8); Ok(res) } diff --git a/src/cookies/middleware.rs b/src/cookies/middleware.rs index e20d4c0cb..949bc7809 100644 --- a/src/cookies/middleware.rs +++ b/src/cookies/middleware.rs @@ -13,11 +13,12 @@ use std::sync::{Arc, RwLock}; /// /// ``` /// # use tide::{Request, Response, StatusCode}; +/// # use tide::http::cookies::Cookie; /// let mut app = tide::Server::new(); /// app.at("/get").get(|cx: Request<()>| async move { Ok(cx.cookie("testCookie").unwrap().value().to_string()) }); /// app.at("/set").get(|_| async { /// let mut res = Response::new(StatusCode::Ok); -/// res.set_cookie(cookie::Cookie::new("testCookie", "NewCookieValue")); +/// res.set_cookie(Cookie::new("testCookie", "NewCookieValue")); /// Ok(res) /// }); /// ``` @@ -38,13 +39,13 @@ impl Middleware for CookiesMiddleware { next: Next<'a, State>, ) -> BoxFuture<'a, crate::Result> { Box::pin(async move { - let cookie_jar = if let Some(cookie_data) = ctx.local::() { + let cookie_jar = if let Some(cookie_data) = ctx.ext::() { cookie_data.content.clone() } else { let cookie_data = CookieData::from_request(&ctx); - // no cookie data in local context, so we try to create it + // no cookie data in ext context, so we try to create it let content = cookie_data.content.clone(); - ctx = ctx.set_local(cookie_data); + ctx = ctx.set_ext(cookie_data); content }; diff --git a/src/redirect.rs b/src/redirect.rs index 92b1f186c..0e615cb1a 100644 --- a/src/redirect.rs +++ b/src/redirect.rs @@ -16,6 +16,7 @@ //! # Ok(()) }) } //! ``` +use crate::http::headers::LOCATION; use crate::utils::BoxFuture; use crate::StatusCode; use crate::{Endpoint, Request, Response}; @@ -103,6 +104,6 @@ impl> Into for Redirect { impl> Into for &Redirect { fn into(self) -> Response { - Response::new(self.status).set_header("location".parse().unwrap(), &self.location) + Response::new(self.status).set_header(LOCATION, self.location.as_ref()) } } diff --git a/src/request.rs b/src/request.rs index f73d97c91..a262e3372 100644 --- a/src/request.rs +++ b/src/request.rs @@ -9,7 +9,7 @@ use std::{str::FromStr, sync::Arc}; use crate::cookies::CookieData; use crate::http::cookies::Cookie; -use crate::http::headers::{HeaderName, HeaderValue}; +use crate::http::headers::{HeaderName, HeaderValues}; use crate::http::{self, Method, StatusCode, Url, Version}; use crate::Response; @@ -124,7 +124,7 @@ impl Request { /// /// let mut app = tide::new(); /// app.at("/").get(|req: Request<()>| async move { - /// assert_eq!(req.header(&"X-Forwarded-For".parse().unwrap()), Some(&vec!["127.0.0.1".parse().unwrap()])); + /// assert_eq!(req.header("X-Forwarded-For").unwrap(), "127.0.0.1"); /// Ok("") /// }); /// app.listen("127.0.0.1:8080").await?; @@ -134,20 +134,20 @@ impl Request { #[must_use] pub fn header( &self, - key: &http_types::headers::HeaderName, - ) -> Option<&Vec> { + key: impl Into, + ) -> Option<&http_types::headers::HeaderValues> { self.request.header(key) } - /// Get a local value. + /// Get a request extension value. #[must_use] - pub fn local(&self) -> Option<&T> { - self.request.local().get() + pub fn ext(&self) -> Option<&T> { + self.request.ext().get() } - /// Set a local value. - pub fn set_local(mut self, val: T) -> Self { - self.request.local_mut().insert(val); + /// Set a request extension value. + pub fn set_ext(mut self, val: T) -> Self { + self.request.ext_mut().insert(val); self } @@ -294,10 +294,8 @@ impl Request { /// returns a `Cookie` by name of the cookie. #[must_use] pub fn cookie(&self, name: &str) -> Option> { - if let Some(cookie_data) = self.local::() { - return cookie_data.content.read().unwrap().get(name).cloned(); - } - None + self.ext::() + .and_then(|cookie_data| cookie_data.content.read().unwrap().get(name).cloned()) } /// Get the length of the body. @@ -349,7 +347,7 @@ impl Into for Request { } impl IntoIterator for Request { - type Item = (HeaderName, Vec); + type Item = (HeaderName, HeaderValues); type IntoIter = http_types::headers::IntoIter; /// Returns a iterator of references over the remaining items. @@ -360,7 +358,7 @@ impl IntoIterator for Request { } impl<'a, State> IntoIterator for &'a Request { - type Item = (&'a HeaderName, &'a Vec); + type Item = (&'a HeaderName, &'a HeaderValues); type IntoIter = http_types::headers::Iter<'a>; #[inline] @@ -370,7 +368,7 @@ impl<'a, State> IntoIterator for &'a Request { } impl<'a, State> IntoIterator for &'a mut Request { - type Item = (&'a HeaderName, &'a mut Vec); + type Item = (&'a HeaderName, &'a mut HeaderValues); type IntoIter = http_types::headers::IterMut<'a>; #[inline] diff --git a/src/response.rs b/src/response.rs index 994b8a410..620ac2fba 100644 --- a/src/response.rs +++ b/src/response.rs @@ -5,9 +5,9 @@ use mime::Mime; use serde::Serialize; use crate::http::cookies::Cookie; -use crate::http::headers::{HeaderName, HeaderValue}; +use crate::http::headers::{HeaderName, HeaderValues, ToHeaderValues, CONTENT_TYPE}; use crate::http::{self, Body, StatusCode}; -use crate::Redirect; +use crate::redirect::Redirect; #[derive(Debug)] pub(crate) enum CookieEvent { @@ -99,38 +99,24 @@ impl Response { /// Get an HTTP header. #[must_use] - pub fn header(&self, name: &HeaderName) -> Option<&Vec> { + pub fn header(&self, name: impl Into) -> Option<&HeaderValues> { self.res.header(name) } /// Remove a header. - pub fn remove_header(&mut self, name: &HeaderName) -> Option> { + pub fn remove_header(&mut self, name: impl Into) -> Option { self.res.remove_header(name) } /// Insert an HTTP header. - pub fn set_header( - mut self, - key: http_types::headers::HeaderName, - value: impl AsRef, - ) -> Self { - let value = value.as_ref().to_owned(); - self.res - .insert_header(key, &[value.parse().unwrap()][..]) - .expect("invalid header"); + pub fn set_header(mut self, key: impl Into, value: impl ToHeaderValues) -> Self { + self.res.insert_header(key, value); self } /// Append an HTTP header. - pub fn append_header( - mut self, - key: http_types::headers::HeaderName, - value: impl AsRef, - ) -> Self { - let value = value.as_ref().to_owned(); - self.res - .append_header(key, &[value.parse().unwrap()][..]) - .expect("invalid header"); + pub fn append_header(mut self, key: impl Into, value: impl ToHeaderValues) -> Self { + self.res.append_header(key, value); self } @@ -139,7 +125,7 @@ impl Response { /// [Read more on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types) #[must_use] pub fn set_mime(self, mime: Mime) -> Self { - self.set_header(http_types::headers::CONTENT_TYPE, format!("{}", mime)) + self.set_header(CONTENT_TYPE, mime.to_string()) } /// Pass a string as the request body. @@ -233,15 +219,15 @@ impl Response { self.cookie_events.push(CookieEvent::Removed(cookie)); } - /// Get a local value. + /// Get a response extension value. #[must_use] - pub fn local(&self) -> Option<&T> { - self.res.local().get() + pub fn ext(&self) -> Option<&T> { + self.res.ext().get() } /// Set a local value. - pub fn set_local(mut self, val: T) -> Self { - self.res.local_mut().insert(val); + pub fn set_ext(mut self, val: T) -> Self { + self.res.ext_mut().insert(val); self } @@ -309,7 +295,7 @@ impl<'a> From<&'a str> for Response { } impl IntoIterator for Response { - type Item = (HeaderName, Vec); + type Item = (HeaderName, HeaderValues); type IntoIter = http_types::headers::IntoIter; /// Returns a iterator of references over the remaining items. @@ -320,7 +306,7 @@ impl IntoIterator for Response { } impl<'a> IntoIterator for &'a Response { - type Item = (&'a HeaderName, &'a Vec); + type Item = (&'a HeaderName, &'a HeaderValues); type IntoIter = http_types::headers::Iter<'a>; #[inline] @@ -330,7 +316,7 @@ impl<'a> IntoIterator for &'a Response { } impl<'a> IntoIterator for &'a mut Response { - type Item = (&'a HeaderName, &'a mut Vec); + type Item = (&'a HeaderName, &'a mut HeaderValues); type IntoIter = http_types::headers::IterMut<'a>; #[inline] diff --git a/src/route.rs b/src/route.rs index acce2fde0..c4642e347 100644 --- a/src/route.rs +++ b/src/route.rs @@ -153,7 +153,7 @@ impl<'a, State: 'static> Route<'a, State> { )); (ep.clone(), ep) }; - self.router.add(&self.path, method.clone(), ep1); + self.router.add(&self.path, method, ep1); let wildcard = self.at("*--tide-path-rest"); wildcard.router.add(&wildcard.path, method, ep2); } else { diff --git a/src/security/cors.rs b/src/security/cors.rs index 7edf74344..f6fd16463 100644 --- a/src/security/cors.rs +++ b/src/security/cors.rs @@ -1,8 +1,7 @@ use crate::utils::BoxFuture; -use http_types::headers::HeaderValue; +use http_types::headers::{HeaderValue, HeaderValues}; use http_types::{headers, Method, StatusCode}; -use crate::http; use crate::middleware::{Middleware, Next}; use crate::{Request, Result}; @@ -87,51 +86,38 @@ impl CorsMiddleware { self } - fn build_preflight_response(&self, origin: &[HeaderValue]) -> http_types::Response { + fn build_preflight_response(&self, origins: &HeaderValues) -> http_types::Response { let mut response = http_types::Response::new(StatusCode::Ok); - response - .insert_header(headers::ACCESS_CONTROL_ALLOW_ORIGIN, origin) - .unwrap(); - response - .insert_header( - headers::ACCESS_CONTROL_ALLOW_METHODS, - self.allow_methods.clone(), - ) - .unwrap(); - response - .insert_header( - headers::ACCESS_CONTROL_ALLOW_HEADERS, - self.allow_headers.clone(), - ) - .unwrap(); - response - .insert_header(headers::ACCESS_CONTROL_MAX_AGE, self.max_age.clone()) - .unwrap(); + response.insert_header(headers::ACCESS_CONTROL_ALLOW_ORIGIN, origins); + + response.insert_header( + headers::ACCESS_CONTROL_ALLOW_METHODS, + self.allow_methods.clone(), + ); + + response.insert_header( + headers::ACCESS_CONTROL_ALLOW_HEADERS, + self.allow_headers.clone(), + ); + + response.insert_header(headers::ACCESS_CONTROL_MAX_AGE, self.max_age.clone()); if let Some(allow_credentials) = self.allow_credentials.clone() { - response - .insert_header(headers::ACCESS_CONTROL_ALLOW_CREDENTIALS, allow_credentials) - .unwrap(); + response.insert_header(headers::ACCESS_CONTROL_ALLOW_CREDENTIALS, allow_credentials); } if let Some(expose_headers) = self.expose_headers.clone() { - response - .insert_header(headers::ACCESS_CONTROL_EXPOSE_HEADERS, expose_headers) - .unwrap(); + response.insert_header(headers::ACCESS_CONTROL_EXPOSE_HEADERS, expose_headers); } response } /// Look at origin of request and determine `allow_origin` - fn response_origin(&self, origin: &HeaderValue) -> Option { - if !self.is_valid_origin(origin) { - return None; - } - + fn response_origin(&self, origin: &HeaderValue) -> HeaderValue { match self.allow_origin { - Origin::Any => Some(WILDCARD.parse().unwrap()), - _ => Some(origin.clone()), + Origin::Any => WILDCARD.parse().unwrap(), + _ => origin.clone(), } } @@ -150,17 +136,18 @@ impl CorsMiddleware { impl Middleware for CorsMiddleware { fn handle<'a>(&'a self, req: Request, next: Next<'a, State>) -> BoxFuture<'a, Result> { Box::pin(async move { - let origins = req.header(&headers::ORIGIN).cloned().unwrap_or_default(); - // TODO: how should multiple origin values be handled? - let origin = if let Some(origin) = origins.first() { - origin - } else { + let origins = req.header(&headers::ORIGIN).cloned(); + + if origins.is_none() { // This is not a CORS request if there is no Origin header return next.run(req).await; - }; + } + + let origins = origins.unwrap(); + let origin = origins.last(); - if !self.is_valid_origin(origin) { + if !self.is_valid_origin(&origin) { return Ok(http_types::Response::new(StatusCode::Unauthorized).into()); } @@ -169,25 +156,27 @@ impl Middleware for CorsMiddleware { return Ok(self.build_preflight_response(&origins).into()); } - let mut response: http::Response = next.run(req).await?.into(); - response - .insert_header( - headers::ACCESS_CONTROL_ALLOW_ORIGIN, - self.response_origin(origin).unwrap(), - ) - .unwrap(); - - if let Some(allow_credentials) = self.allow_credentials.clone() { - response - .insert_header(headers::ACCESS_CONTROL_ALLOW_CREDENTIALS, allow_credentials) - .unwrap(); + let mut response: http_types::Response = next.run(req).await?.into(); + + response.insert_header( + headers::ACCESS_CONTROL_ALLOW_ORIGIN, + self.response_origin(&origin), + ); + + if let Some(allow_credentials) = &self.allow_credentials { + response.insert_header( + headers::ACCESS_CONTROL_ALLOW_CREDENTIALS, + allow_credentials.clone(), + ); } - if let Some(expose_headers) = self.expose_headers.clone() { - response - .insert_header(headers::ACCESS_CONTROL_EXPOSE_HEADERS, expose_headers) - .unwrap(); + if let Some(expose_headers) = &self.expose_headers { + response.insert_header( + headers::ACCESS_CONTROL_EXPOSE_HEADERS, + expose_headers.clone(), + ); } + Ok(response.into()) }) } @@ -271,8 +260,7 @@ mod test { fn request() -> http_types::Request { let mut req = http_types::Request::new(http_types::Method::Get, endpoint_url()); - req.insert_header(http_types::headers::ORIGIN, ALLOW_ORIGIN) - .unwrap(); + req.insert_header(http_types::headers::ORIGIN, ALLOW_ORIGIN); req } @@ -288,36 +276,18 @@ mod test { ); let mut req = http_types::Request::new(http_types::Method::Options, endpoint_url()); - req.insert_header(http_types::headers::ORIGIN, ALLOW_ORIGIN) - .unwrap(); + req.insert_header(http_types::headers::ORIGIN, ALLOW_ORIGIN); let res: crate::http::Response = app.respond(req).await.unwrap(); assert_eq!(res.status(), 200); - assert_eq!( - res.header(&headers::ACCESS_CONTROL_ALLOW_ORIGIN).unwrap()[0].as_str(), - ALLOW_ORIGIN - ); - assert_eq!( - res.header(&headers::ACCESS_CONTROL_ALLOW_METHODS).unwrap()[0].as_str(), - ALLOW_METHODS - ); - assert_eq!( - res.header(&headers::ACCESS_CONTROL_ALLOW_HEADERS).unwrap()[0].as_str(), - WILDCARD - ); - assert_eq!( - res.header(&headers::ACCESS_CONTROL_MAX_AGE).unwrap()[0].as_str(), - DEFAULT_MAX_AGE - ); + assert_eq!(res[headers::ACCESS_CONTROL_ALLOW_ORIGIN], ALLOW_ORIGIN); + assert_eq!(res[headers::ACCESS_CONTROL_ALLOW_METHODS], ALLOW_METHODS); + assert_eq!(res[headers::ACCESS_CONTROL_ALLOW_HEADERS], WILDCARD); + assert_eq!(res[headers::ACCESS_CONTROL_MAX_AGE], DEFAULT_MAX_AGE); - assert_eq!( - res.header(&headers::ACCESS_CONTROL_ALLOW_CREDENTIALS) - .unwrap()[0] - .as_str(), - "true" - ); + assert_eq!(res[headers::ACCESS_CONTROL_ALLOW_CREDENTIALS], "true"); } #[async_std::test] async fn default_cors_middleware() { @@ -326,11 +296,7 @@ mod test { let res: crate::http::Response = app.respond(request()).await.unwrap(); assert_eq!(res.status(), 200); - - assert_eq!( - res.header(&headers::ACCESS_CONTROL_ALLOW_ORIGIN).unwrap()[0].as_str(), - "*" - ); + assert_eq!(res[headers::ACCESS_CONTROL_ALLOW_ORIGIN], "*"); } #[async_std::test] @@ -346,10 +312,7 @@ mod test { let res: crate::http::Response = app.respond(request()).await.unwrap(); assert_eq!(res.status(), 200); - assert_eq!( - res.header(&headers::ACCESS_CONTROL_ALLOW_ORIGIN).unwrap()[0].as_str(), - ALLOW_ORIGIN - ); + assert_eq!(res[headers::ACCESS_CONTROL_ALLOW_ORIGIN], ALLOW_ORIGIN); } #[async_std::test] @@ -359,12 +322,7 @@ mod test { let res: crate::http::Response = app.respond(request()).await.unwrap(); assert_eq!(res.status(), 200); - assert_eq!( - res.header(&headers::ACCESS_CONTROL_ALLOW_CREDENTIALS) - .unwrap()[0] - .as_str(), - "true" - ); + assert_eq!(res[headers::ACCESS_CONTROL_ALLOW_CREDENTIALS], "true"); } #[async_std::test] @@ -375,16 +333,12 @@ mod test { for origin in origins { let mut req = http_types::Request::new(http_types::Method::Get, endpoint_url()); - req.insert_header(http_types::headers::ORIGIN, origin) - .unwrap(); + req.insert_header(http_types::headers::ORIGIN, origin); let res: crate::http::Response = app.respond(req).await.unwrap(); assert_eq!(res.status(), 200); - assert_eq!( - res.header(&headers::ACCESS_CONTROL_ALLOW_ORIGIN), - Some(&vec![origin.parse().unwrap()]) - ); + assert_eq!(res[headers::ACCESS_CONTROL_ALLOW_ORIGIN][0], origin); } } @@ -405,8 +359,7 @@ mod test { app.middleware(CorsMiddleware::new().allow_origin(ALLOW_ORIGIN)); let mut req = http_types::Request::new(http_types::Method::Get, endpoint_url()); - req.insert_header(http_types::headers::ORIGIN, "unauthorize-origin.net") - .unwrap(); + req.insert_header(http_types::headers::ORIGIN, "unauthorize-origin.net"); let res: crate::http::Response = app.respond(req).await.unwrap(); assert_eq!(res.status(), 401); diff --git a/src/server.rs b/src/server.rs index 19e7a4de2..428568072 100644 --- a/src/server.rs +++ b/src/server.rs @@ -295,10 +295,9 @@ impl Server { let mut incoming = listener.incoming(); while let Some(stream) = incoming.next().await { let stream = stream?; - let addr = addr.clone(); let this = self.clone(); task::spawn(async move { - let res = async_h1::accept(&addr, stream, |req| async { + let res = async_h1::accept(stream, |req| async { let res = this.respond(req).await; let res = res.map_err(|_| io::Error::from(io::ErrorKind::Other))?; Ok(res) diff --git a/tests/chunked-encode-large.rs b/tests/chunked-encode-large.rs index 1f31f23ef..e9d4bb039 100644 --- a/tests/chunked-encode-large.rs +++ b/tests/chunked-encode-large.rs @@ -2,7 +2,7 @@ mod test_utils; use async_std::io::Cursor; use async_std::prelude::*; use async_std::task; -use http_types::{headers, StatusCode}; +use http_types::StatusCode; use std::time::Duration; use tide::Response; @@ -75,7 +75,7 @@ async fn chunked_large() -> Result<(), http_types::Error> { let body = Cursor::new(TEXT.to_owned()); let res = Response::new(StatusCode::Ok) .body(body) - .set_header(headers::CONTENT_TYPE, "text/plain; charset=utf-8"); + .set_mime(mime::TEXT_PLAIN_UTF_8); Ok(res) }); app.listen(("localhost", port)).await?; @@ -84,17 +84,21 @@ async fn chunked_large() -> Result<(), http_types::Error> { let client = task::spawn(async move { task::sleep(Duration::from_millis(100)).await; - let mut res = surf::get(format!("http://localhost:{}", port)).await?; + let mut res = surf::get(format!("http://localhost:{}", port)) + .await + .unwrap(); assert_eq!(res.status(), 200); assert_eq!( - res.header(&"transfer-encoding".parse().unwrap()), - Some(&vec![http_types::headers::HeaderValue::from_ascii( - b"chunked" - ) - .unwrap()]) + // this is awkward and should be revisited when surf is on newer http-types + res.header(&"transfer-encoding".parse().unwrap()) + .unwrap() + .last() + .unwrap() + .as_str(), + "chunked" ); assert_eq!(res.header(&"content-length".parse().unwrap()), None); - let string = res.body_string().await?; + let string = res.body_string().await.unwrap(); assert_eq!(string, TEXT.to_string()); Ok(()) }); diff --git a/tests/chunked-encode-small.rs b/tests/chunked-encode-small.rs index f8ff4c529..14ecae1e2 100644 --- a/tests/chunked-encode-small.rs +++ b/tests/chunked-encode-small.rs @@ -2,7 +2,7 @@ mod test_utils; use async_std::io::Cursor; use async_std::prelude::*; use async_std::task; -use http_types::{headers, StatusCode}; +use http_types::StatusCode; use std::time::Duration; use tide::Response; @@ -20,11 +20,11 @@ async fn chunked_large() -> Result<(), http_types::Error> { let port = test_utils::find_port().await; let server = task::spawn(async move { let mut app = tide::new(); - app.at("/").get(|mut _req: tide::Request<()>| async move { + app.at("/").get(|_| async move { let body = Cursor::new(TEXT.to_owned()); let res = Response::new(StatusCode::Ok) .body(body) - .set_header(headers::CONTENT_TYPE, "text/plain; charset=utf-8"); + .set_mime(mime::TEXT_PLAIN_UTF_8); Ok(res) }); app.listen(("localhost", port)).await?; @@ -33,17 +33,21 @@ async fn chunked_large() -> Result<(), http_types::Error> { let client = task::spawn(async move { task::sleep(Duration::from_millis(100)).await; - let mut res = surf::get(format!("http://localhost:{}", port)).await?; + let mut res = surf::get(format!("http://localhost:{}", port)) + .await + .unwrap(); assert_eq!(res.status(), 200); assert_eq!( - res.header(&"transfer-encoding".parse().unwrap()), - Some(&vec![http_types::headers::HeaderValue::from_ascii( - b"chunked" - ) - .unwrap()]) + // this is awkward and should be revisited when surf is on newer http-types + res.header(&"transfer-encoding".parse().unwrap()) + .unwrap() + .last() + .unwrap() + .as_str(), + "chunked" ); assert_eq!(res.header(&"content-length".parse().unwrap()), None); - let string = res.body_string().await?; + let string = res.body_string().await.unwrap(); assert_eq!(string, TEXT.to_string()); Ok(()) }); diff --git a/tests/cookies.rs b/tests/cookies.rs index fa95750ae..630e5647d 100644 --- a/tests/cookies.rs +++ b/tests/cookies.rs @@ -1,5 +1,6 @@ use async_std::prelude::*; use tide::http::cookies::Cookie; +use tide::http::headers::{COOKIE, SET_COOKIE}; use tide::{Request, Response, Server, StatusCode}; @@ -46,13 +47,16 @@ async fn make_request(endpoint: &str) -> http_types::Response { let app = app(); let mut req = http_types::Request::new( http_types::Method::Get, - format!("http://example.com{}", endpoint).parse().unwrap(), + http_types::url::Url::parse("http://example.com") + .unwrap() + .join(endpoint) + .unwrap(), ); + req.insert_header( - http_types::headers::COOKIE, + COOKIE, "testCookie=RequestCookieValue; secondTestCookie=Other%3BCookie%20Value", - ) - .unwrap(); + ); let res: tide::http::Response = app.respond(req).await.unwrap(); res @@ -74,15 +78,14 @@ async fn successfully_retrieve_request_cookie() { async fn successfully_set_cookie() { let res = make_request("/set").await; assert_eq!(res.status(), StatusCode::Ok); - let test_cookie_header = res.header(&http_types::headers::SET_COOKIE).unwrap()[0].as_str(); - assert_eq!(test_cookie_header, "testCookie=NewCookieValue"); + assert_eq!(res[SET_COOKIE], "testCookie=NewCookieValue"); } #[async_std::test] async fn successfully_remove_cookie() { let res = make_request("/remove").await; assert_eq!(res.status(), StatusCode::Ok); - let test_cookie_header = res.header(&http_types::headers::SET_COOKIE).unwrap()[0].as_str(); + let test_cookie_header = res[SET_COOKIE].last().as_str(); assert!(test_cookie_header.starts_with("testCookie=;")); let cookie = Cookie::parse_encoded(test_cookie_header).unwrap(); assert_eq!(cookie.name(), COOKIE_NAME); @@ -95,8 +98,8 @@ async fn successfully_remove_cookie() { async fn successfully_set_multiple_cookies() { let res = make_request("/multi").await; assert_eq!(res.status(), StatusCode::Ok); - let cookie_header = res.header(&http_types::headers::SET_COOKIE); - let cookies = cookie_header.unwrap().iter().collect::>(); + let cookie_header = res[SET_COOKIE].clone(); + let cookies = cookie_header.iter().collect::>(); assert_eq!(cookies.len(), 2, "{:?}", &cookies); let cookie1 = cookies[0].as_str(); let cookie2 = cookies[1].as_str(); diff --git a/tests/nested.rs b/tests/nested.rs index a58a1acf4..bdd81c9e8 100644 --- a/tests/nested.rs +++ b/tests/nested.rs @@ -1,6 +1,5 @@ -use http_types::headers::{HeaderName, HeaderValue}; +use http_types::headers::HeaderName; use http_types::{Method, Request, Response, Url}; -use std::str::FromStr; use test_utils::BoxFuture; use tide::{Middleware, Next}; @@ -55,7 +54,7 @@ async fn nested_middleware() { Box::pin(async move { let res = next.run(req).await?; let res = res.set_header( - HeaderName::from_ascii("X-Tide-Test".to_owned().into_bytes()).unwrap(), + HeaderName::from_bytes("X-Tide-Test".to_owned().into_bytes()).unwrap(), "1", ); Ok(res) @@ -78,7 +77,7 @@ async fn nested_middleware() { Url::parse("http://example.com/foo/echo").unwrap(), ); let res: Response = app.respond(req).await.unwrap(); - assert_header(&res, "X-Tide-Test", Some("1")); + assert_eq!(res["X-Tide-Test"], "1"); assert_eq!(res.status(), 200); assert_eq!(res.body_string().await.unwrap(), "/echo"); @@ -87,13 +86,13 @@ async fn nested_middleware() { Url::parse("http://example.com/foo/x/bar").unwrap(), ); let res: Response = app.respond(req).await.unwrap(); - assert_header(&res, "X-Tide-Test", Some("1")); + assert_eq!(res["X-Tide-Test"], "1"); assert_eq!(res.status(), 200); assert_eq!(res.body_string().await.unwrap(), "/"); let req = Request::new(Method::Get, Url::parse("http://example.com/bar").unwrap()); let res: Response = app.respond(req).await.unwrap(); - assert_header(&res, "X-Tide-Test", None); + assert!(res.header("X-Tide-Test").is_none()); assert_eq!(res.status(), 200); assert_eq!(res.body_string().await.unwrap(), "/bar"); } @@ -119,27 +118,3 @@ async fn nested_with_different_state() { assert_eq!(res.status(), 200); assert_eq!(res.body_string().await.unwrap(), "Hello, world!"); } - -// See https://github.com/http-rs/http-types/issues/89 for a proposed fix to this boilerplate. -fn assert_header(headers: impl AsRef, lhs: &str, rhs: Option<&str>) { - match rhs { - Some(s) => { - let header = headers - .as_ref() - .get( - &http_types::headers::HeaderName::from_ascii(lhs.to_owned().into_bytes()) - .unwrap(), - ) - .unwrap() - .iter() - .next(); - assert_eq!(header, Some(&HeaderValue::from_str(s).unwrap())); - } - None => { - let header = headers.as_ref().get( - &http_types::headers::HeaderName::from_ascii(lhs.to_owned().into_bytes()).unwrap(), - ); - assert_eq!(header, None); - } - } -} diff --git a/tests/querystring.rs b/tests/querystring.rs index d145363ae..7aaee67b3 100644 --- a/tests/querystring.rs +++ b/tests/querystring.rs @@ -1,4 +1,5 @@ use async_std::prelude::*; +use http_types::{url::Url, Method}; use serde::Deserialize; use tide::{http, Request, Response, Server, StatusCode}; @@ -40,8 +41,8 @@ fn get_server() -> Server<()> { async fn successfully_deserialize_query() { let app = get_server(); let req = http_types::Request::new( - http_types::Method::Get, - "http://example.com/?msg=Hello".parse().unwrap(), + Method::Get, + Url::parse("http://example.com/?msg=Hello").unwrap(), ); let mut res: http::Response = app.respond(req).await.unwrap(); @@ -54,10 +55,7 @@ async fn successfully_deserialize_query() { #[async_std::test] async fn unsuccessfully_deserialize_query() { let app = get_server(); - let req = http_types::Request::new( - http_types::Method::Get, - "http://example.com/".parse().unwrap(), - ); + let req = http_types::Request::new(Method::Get, Url::parse("http://example.com/").unwrap()); let mut res: http::Response = app.respond(req).await.unwrap(); assert_eq!(res.status(), 400); @@ -70,8 +68,8 @@ async fn unsuccessfully_deserialize_query() { async fn malformatted_query() { let app = get_server(); let req = http_types::Request::new( - http_types::Method::Get, - "http://example.com/?error=should_fail".parse().unwrap(), + Method::Get, + Url::parse("http://example.com/?error=should_fail").unwrap(), ); let mut res: http::Response = app.respond(req).await.unwrap(); assert_eq!(res.status(), 400); @@ -85,8 +83,8 @@ async fn malformatted_query() { async fn empty_query_string_for_struct_with_no_required_fields() { let app = get_server(); let req = http_types::Request::new( - http_types::Method::Get, - "http://example.com/optional".parse().unwrap(), + Method::Get, + Url::parse("http://example.com/optional").unwrap(), ); let res: http::Response = app.respond(req).await.unwrap(); assert_eq!(res.status(), StatusCode::Ok); diff --git a/tests/route_middleware.rs b/tests/route_middleware.rs index 402f79082..e99a2d9bb 100644 --- a/tests/route_middleware.rs +++ b/tests/route_middleware.rs @@ -1,6 +1,6 @@ use http_types::headers::HeaderName; use std::convert::TryInto; -use tide::http::{self, Method}; +use tide::http::{self, url::Url, Method}; use tide::Middleware; use test_utils::BoxFuture; @@ -49,34 +49,22 @@ async fn route_middleware() { .reset_middleware() .put(echo_path); - let req = http::Request::new(Method::Get, "http://localhost/foo".parse().unwrap()); + let req = http::Request::new(Method::Get, Url::parse("http://localhost/foo").unwrap()); let res: http::Response = app.respond(req).await.unwrap(); - assert_eq!( - res.header(&"X-Foo".try_into().unwrap()), - Some(&vec!["foo".parse().unwrap()]) - ); + assert_eq!(res["X-Foo"], "foo"); - let req = http::Request::new(Method::Post, "http://localhost/foo".parse().unwrap()); + let req = http::Request::new(Method::Post, Url::parse("http://localhost/foo").unwrap()); let res: http::Response = app.respond(req).await.unwrap(); - assert_eq!( - res.header(&"X-Foo".parse().unwrap()), - Some(&vec!["foo".parse().unwrap()]) - ); + assert_eq!(res["X-Foo"], "foo"); - let req = http::Request::new(Method::Put, "http://localhost/foo".parse().unwrap()); + let req = http::Request::new(Method::Put, Url::parse("http://localhost/foo").unwrap()); let res: http::Response = app.respond(req).await.unwrap(); - assert_eq!(res.header(&"X-Foo".try_into().unwrap()), None); + assert!(res.header("X-Foo").is_none()); - let req = http::Request::new(Method::Get, "http://localhost/foo/bar".parse().unwrap()); + let req = http::Request::new(Method::Get, Url::parse("http://localhost/foo/bar").unwrap()); let res: http::Response = app.respond(req).await.unwrap(); - assert_eq!( - res.header(&"X-Foo".try_into().unwrap()), - Some(&vec!["foo".parse().unwrap()]) - ); - assert_eq!( - res.header(&"X-Bar".try_into().unwrap()), - Some(&vec!["bar".parse().unwrap()]) - ); + assert_eq!(res["X-Foo"], "foo"); + assert_eq!(res["x-bar"], "bar"); } #[async_std::test] @@ -90,29 +78,18 @@ async fn app_and_route_middleware() { .middleware(TestMiddleware::with_header_name("X-Bar", "bar")) .get(echo_path); - let req = http::Request::new(Method::Get, "http://localhost/foo".parse().unwrap()); + let req = http::Request::new(Method::Get, Url::parse("http://localhost/foo").unwrap()); let res: http::Response = app.respond(req).await.unwrap(); - assert_eq!( - res.header(&"X-Root".try_into().unwrap()), - Some(&vec!["root".parse().unwrap()]) - ); - assert_eq!( - res.header(&"X-Foo".try_into().unwrap()), - Some(&vec!["foo".parse().unwrap()]) - ); - assert_eq!(res.header(&"X-Bar".try_into().unwrap()), None); + assert_eq!(res["X-Root"], "root"); + assert_eq!(res["x-foo"], "foo"); - let req = http::Request::new(Method::Get, "http://localhost/bar".parse().unwrap()); + assert!(res.header("x-bar").is_none()); + + let req = http::Request::new(Method::Get, Url::parse("http://localhost/bar").unwrap()); let res: http::Response = app.respond(req).await.unwrap(); - assert_eq!( - res.header(&"X-Root".try_into().unwrap()), - Some(&vec!["root".parse().unwrap()]) - ); - assert_eq!(res.header(&"X-Foo".try_into().unwrap()), None); - assert_eq!( - res.header(&"X-Bar".try_into().unwrap()), - Some(&vec!["bar".parse().unwrap()]) - ); + assert_eq!(res["X-Root"], "root"); + assert!(res.header("x-foo").is_none()); + assert_eq!(res["X-Bar"], "bar"); } #[async_std::test] @@ -133,39 +110,21 @@ async fn nested_app_with_route_middleware() { .middleware(TestMiddleware::with_header_name("X-Bar", "bar")) .nest(inner); - let req = http::Request::new(Method::Get, "http://localhost/foo".parse().unwrap()); + let req = http::Request::new(Method::Get, Url::parse("http://localhost/foo").unwrap()); let res: http::Response = app.respond(req).await.unwrap(); - assert_eq!( - res.header(&"X-Root".try_into().unwrap()), - Some(&vec!["root".parse().unwrap()]) - ); - assert_eq!(res.header(&"X-Inner".try_into().unwrap()), None); - assert_eq!( - res.header(&"X-Foo".try_into().unwrap()), - Some(&vec!["foo".parse().unwrap()]) - ); - assert_eq!(res.header(&"X-Bar".try_into().unwrap()), None); - assert_eq!(res.header(&"X-Baz".try_into().unwrap()), None); + assert_eq!(res["X-Root"], "root"); + assert!(res.header("X-Inner").is_none()); + assert_eq!(res["X-Foo"], "foo"); + assert!(res.header("X-Bar").is_none()); + assert!(res.header("X-Baz").is_none()); - let req = http::Request::new(Method::Get, "http://localhost/bar/baz".parse().unwrap()); + let req = http::Request::new(Method::Get, Url::parse("http://localhost/bar/baz").unwrap()); let res: http::Response = app.respond(req).await.unwrap(); - assert_eq!( - res.header(&"X-Root".try_into().unwrap()), - Some(&vec!["root".parse().unwrap()]) - ); - assert_eq!( - res.header(&"X-Inner".try_into().unwrap()), - Some(&vec!["inner".parse().unwrap()]) - ); - assert_eq!(res.header(&"X-Foo".try_into().unwrap()), None); - assert_eq!( - res.header(&"X-Bar".try_into().unwrap()), - Some(&vec!["bar".parse().unwrap()]) - ); - assert_eq!( - res.header(&"X-Baz".try_into().unwrap()), - Some(&vec!["baz".parse().unwrap()]) - ); + assert_eq!(res["X-Root"], "root"); + assert_eq!(res["X-Inner"], "inner"); + assert!(res.header("X-Foo").is_none()); + assert_eq!(res["X-Bar"], "bar"); + assert_eq!(res["X-Baz"], "baz"); } #[async_std::test] @@ -180,12 +139,9 @@ async fn subroute_not_nested() { let req = http::Request::new( Method::Get, - "http://localhost/parent/child".parse().unwrap(), + Url::parse("http://localhost/parent/child").unwrap(), ); let res: http::Response = app.respond(req).await.unwrap(); - assert_eq!(res.header(&"X-Parent".try_into().unwrap()), None); - assert_eq!( - res.header(&"X-Child".try_into().unwrap()), - Some(&vec!["child".parse().unwrap()]) - ); + assert!(res.header("X-Parent").is_none()); + assert_eq!(res["x-child"], "child"); } diff --git a/tests/server.rs b/tests/server.rs index 52153eb6c..8419c71b8 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -26,7 +26,8 @@ fn hello_world() -> Result<(), http_types::Error> { let string = surf::get(format!("http://localhost:{}", port)) .body_string("nori".to_string()) .recv_string() - .await?; + .await + .unwrap(); assert_eq!(string, "says hello".to_string()); Ok(()) }); @@ -52,7 +53,8 @@ fn echo_server() -> Result<(), http_types::Error> { let string = surf::get(format!("http://localhost:{}", port)) .body_string("chashu".to_string()) .recv_string() - .await?; + .await + .unwrap(); assert_eq!(string, "chashu".to_string()); Ok(()) }); @@ -88,7 +90,8 @@ fn json() -> Result<(), http_types::Error> { let counter: Counter = surf::get(format!("http://localhost:{}", &port)) .body_json(&Counter { count: 0 })? .recv_json() - .await?; + .await + .unwrap(); assert_eq!(counter.count, 1); Ok(()) });