fix feature resolving

This commit is contained in:
Rob Ede 2021-04-08 22:45:46 +01:00
parent adc94f505d
commit 3f6350a4b6
No known key found for this signature in database
GPG Key ID: 97C636207D3EF933
16 changed files with 149 additions and 101 deletions

View File

@ -1,3 +1,7 @@
[alias]
chk = "hack check --workspace --all-features --tests --examples"
lint = "hack --clean-per-run clippy --workspace --tests --examples"
ci-min = "hack check --workspace --no-default-features"
ci-min-test = "hack check --workspace --no-default-features --tests --examples"
ci-default = "hack check --workspace"
ci-full = "check --workspace --bins --examples --tests"

View File

@ -16,7 +16,7 @@ edition = "2018"
[package.metadata.docs.rs]
# features that docs.rs will build with
features = ["openssl", "rustls", "compress", "secure-cookies"]
features = ["openssl", "rustls", "compress", "cookies", "secure-cookies"]
[lib]
name = "actix_web"
@ -34,6 +34,7 @@ members = [
"actix-http-test",
"actix-test",
]
resolver = "2"
[features]
default = ["compress", "cookies"]
@ -53,22 +54,6 @@ openssl = ["actix-http/openssl", "actix-tls/accept", "actix-tls/openssl"]
# rustls
rustls = ["actix-http/rustls", "actix-tls/accept", "actix-tls/rustls"]
[[example]]
name = "basic"
required-features = ["compress"]
[[example]]
name = "uds"
required-features = ["compress"]
[[test]]
name = "test_server"
required-features = ["compress", "cookies"]
[[example]]
name = "on_connect"
required-features = []
[dependencies]
actix-codec = "0.4.0-beta.1"
actix-macros = "0.2.0"
@ -135,6 +120,22 @@ actix-web-actors = { path = "actix-web-actors" }
actix-web-codegen = { path = "actix-web-codegen" }
awc = { path = "awc" }
[[test]]
name = "test_server"
required-features = ["compress", "cookies"]
[[example]]
name = "basic"
required-features = ["compress"]
[[example]]
name = "uds"
required-features = ["compress"]
[[example]]
name = "on_connect"
required-features = []
[[bench]]
name = "server"
harness = false

View File

@ -2,8 +2,13 @@
## Unreleased - 2021-xx-xx
### Removed
* `HttpMessage::cookies` and `HttpMessage::cookie`. [#????]
* `impl ResponseError for CookieParseError`. [#????]
* `cookies` feature flag. [#2065]
* Top-level `cookies` mod (re-export). [#2065]
* `HttpMessage` trait loses the `cookies` and `cookie` methods. [#2065]
* `impl ResponseError for CookieParseError`. [#2065]
[#2065]: https://github.com/actix/actix-web/pull/2065
## 3.0.0-beta.5 - 2021-04-02
### Added

View File

@ -36,12 +36,12 @@ async fn main() -> io::Result<()> {
async fn handler(req: Request) -> Result<Response<BodyStream<Heartbeat>>, Error> {
log::info!("handshaking");
let res = ws::handshake(req.head())?;
let mut res = ws::handshake(req.head())?;
// handshake will always fail under HTTP/2
log::info!("responding");
Ok(res.set_body(BodyStream::new(Heartbeat::new(ws::Codec::new()))))
Ok(res.message_body(BodyStream::new(Heartbeat::new(ws::Codec::new()))))
}
struct Heartbeat {

View File

@ -14,9 +14,7 @@ use serde::de::value::Error as DeError;
use serde_json::error::Error as JsonError;
use serde_urlencoded::ser::Error as FormError;
use crate::body::Body;
use crate::helpers::Writer;
use crate::response::Response;
use crate::{body::Body, helpers::Writer, Response, ResponseBuilder};
/// A specialized [`std::result::Result`]
/// for actix web operations
@ -135,12 +133,12 @@ impl From<Response> for Error {
}
}
// /// Convert ResponseBuilder to a Error
// impl From<ResponseBuilder> for Error {
// fn from(mut res: ResponseBuilder) -> Error {
// InternalError::from_response("", res.finish()).into()
// }
// }
/// Convert ResponseBuilder to a Error
impl From<ResponseBuilder> for Error {
fn from(mut res: ResponseBuilder) -> Error {
InternalError::from_response("", res.finish()).into()
}
}
#[derive(Debug, Display)]
#[display(fmt = "UnknownError")]

View File

@ -340,14 +340,12 @@ where
StateProj::ServiceCall(fut) => match fut.poll(cx) {
// service call resolved. send response.
Poll::Ready(Ok(res)) => {
eprintln!("dispatcher ok!");
let (res, body) = res.into().replace_body(());
self.as_mut().send_response(res, body)?;
}
// send service call error as response
Poll::Ready(Err(err)) => {
eprintln!("dispatcher err");
let res = Response::from_error(err.into());
let (res, body) = res.replace_body(());
self.as_mut().send_response(res, body.into_body())?;

View File

@ -34,6 +34,18 @@ pub struct Response<B = Body> {
}
impl Response<Body> {
/// Create HTTP response builder with specific status.
#[inline]
pub fn build(status: StatusCode) -> ResponseBuilder {
ResponseBuilder::new(status)
}
/// Create HTTP response builder
#[inline]
pub fn build_from<T: Into<ResponseBuilder>>(source: T) -> ResponseBuilder {
source.into()
}
/// Constructs a response
#[inline]
pub fn new(status: StatusCode) -> Response {
@ -444,6 +456,32 @@ impl ResponseBuilder {
self
}
/// This method calls provided closure with builder reference if value is `true`.
#[doc(hidden)]
#[deprecated = "Use an if statement."]
pub fn if_true<F>(&mut self, value: bool, f: F) -> &mut Self
where
F: FnOnce(&mut ResponseBuilder),
{
if value {
f(self);
}
self
}
/// This method calls provided closure with builder reference if value is `Some`.
#[doc(hidden)]
#[deprecated = "Use an if-let construction."]
pub fn if_some<T, F>(&mut self, value: Option<T>, f: F) -> &mut Self
where
F: FnOnce(T, &mut ResponseBuilder),
{
if let Some(val) = value {
f(val, self);
}
self
}
/// Responses extensions
#[inline]
pub fn extensions(&self) -> Ref<'_, Extensions> {
@ -692,7 +730,7 @@ mod tests {
#[test]
fn test_upgrade() {
let resp = ResponseBuilder::new(StatusCode::OK)
let resp = Response::build(StatusCode::OK)
.upgrade("websocket")
.finish();
assert!(resp.upgrade());
@ -704,13 +742,13 @@ mod tests {
#[test]
fn test_force_close() {
let resp = ResponseBuilder::new(StatusCode::OK).force_close().finish();
let resp = Response::build(StatusCode::OK).force_close().finish();
assert!(!resp.keep_alive())
}
#[test]
fn test_content_type() {
let resp = ResponseBuilder::new(StatusCode::OK)
let resp = Response::build(StatusCode::OK)
.content_type("text/plain")
.body(Body::Empty);
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "text/plain")
@ -731,7 +769,7 @@ mod tests {
#[test]
fn test_json_ct() {
let resp = ResponseBuilder::new(StatusCode::OK)
let resp = Response::build(StatusCode::OK)
.insert_header((CONTENT_TYPE, "text/json"))
.json(&vec!["v1", "v2", "v3"]);
let ct = resp.headers().get(CONTENT_TYPE).unwrap();
@ -744,7 +782,7 @@ mod tests {
use serde_json::json;
let resp =
ResponseBuilder::new(StatusCode::OK).body(json!({"test-key":"test-value"}));
Response::build(StatusCode::OK).body(json!({"test-key":"test-value"}));
assert_eq!(resp.body().get_ref(), br#"{"test-key":"test-value"}"#);
}
@ -818,6 +856,24 @@ mod tests {
assert_eq!(resp.body().get_ref(), b"test");
}
#[test]
fn test_into_builder() {
let mut resp: Response = "test".into();
assert_eq!(resp.status(), StatusCode::OK);
resp.headers_mut().insert(
HeaderName::from_static("cookie"),
HeaderValue::from_static("cookie1=val100"),
);
let mut builder: ResponseBuilder = resp.into();
let resp = builder.status(StatusCode::BAD_REQUEST).finish();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let cookie = resp.headers().get_all("Cookie").next().unwrap();
assert_eq!(cookie.to_str().unwrap(), "cookie1=val100");
}
#[test]
fn response_builder_header_insert_kv() {
let mut res = Response::Ok();

View File

@ -9,10 +9,8 @@ use derive_more::{Display, Error, From};
use http::{header, Method, StatusCode};
use crate::{
error::ResponseError,
header::HeaderValue,
message::{ConnectionType, RequestHead},
response::Response,
error::ResponseError, header::HeaderValue, message::RequestHead, response::Response,
ResponseBuilder,
};
mod codec;
@ -131,7 +129,7 @@ impl ResponseError for HandshakeError {
}
/// Verify WebSocket handshake request and create handshake response.
pub fn handshake(req: &RequestHead) -> Result<Response, HandshakeError> {
pub fn handshake(req: &RequestHead) -> Result<ResponseBuilder, HandshakeError> {
verify_handshake(req)?;
Ok(handshake_response(req))
}
@ -187,39 +185,21 @@ pub fn verify_handshake(req: &RequestHead) -> Result<(), HandshakeError> {
/// Create WebSocket handshake response.
///
/// This function returns handshake `Response`, ready to send to peer.
pub fn handshake_response(req: &RequestHead) -> Response {
pub fn handshake_response(req: &RequestHead) -> ResponseBuilder {
let key = {
let key = req.headers().get(header::SEC_WEBSOCKET_KEY).unwrap();
proto::hash_key(key.as_ref())
};
let mut res = Response::new(StatusCode::SWITCHING_PROTOCOLS);
res.head_mut().set_connection_type(ConnectionType::Upgrade);
let headers = res.headers_mut();
headers.insert(header::UPGRADE, HeaderValue::from_static("websocket"));
headers.insert(
header::TRANSFER_ENCODING,
HeaderValue::from_static("chunked"),
);
headers.insert(
Response::build(StatusCode::SWITCHING_PROTOCOLS)
.upgrade("websocket")
.insert_header((header::TRANSFER_ENCODING, "chunked"))
.insert_header((
header::SEC_WEBSOCKET_ACCEPT,
// key is known to be header value safe ascii
HeaderValue::from_bytes(&key).unwrap(),
);
res
// Response::build(StatusCode::SWITCHING_PROTOCOLS)
// .upgrade("websocket")
// .insert_header((header::TRANSFER_ENCODING, "chunked"))
// .insert_header((
// header::SEC_WEBSOCKET_ACCEPT,
// // key is known to be header value safe ascii
// HeaderValue::from_bytes(&key).unwrap(),
// ))
))
.take()
}
#[cfg(test)]
@ -334,7 +314,7 @@ mod tests {
.finish();
assert_eq!(
StatusCode::SWITCHING_PROTOCOLS,
handshake_response(req.head()).status()
handshake_response(req.head()).finish().status()
);
}

View File

@ -52,7 +52,7 @@ where
fn call(&self, (req, mut framed): (Request, Framed<T, h1::Codec>)) -> Self::Future {
let fut = async move {
let res = ws::handshake(req.head()).unwrap().set_body(());
let res = ws::handshake(req.head()).unwrap().message_body(());
framed
.send((res, body::BodySize::None).into())

View File

@ -163,7 +163,7 @@ pub fn handshake_with_protocols(
.find(|req_p| protocols.iter().any(|p| p == req_p))
});
let mut response = HttpResponseBuilder::new(StatusCode::SWITCHING_PROTOCOLS)
let mut response = HttpResponse::build(StatusCode::SWITCHING_PROTOCOLS)
.upgrade("websocket")
.insert_header((
header::SEC_WEBSOCKET_ACCEPT,

View File

@ -8,13 +8,13 @@ use futures_core::Stream;
use serde::Serialize;
use actix_http::body::Body;
#[cfg(feature = "cookies")]
use actix_http::http::header::{self, IntoHeaderPair};
use actix_http::http::{
uri, ConnectionType, Error as HttpError, HeaderMap, HeaderValue, Method, Uri, Version,
};
use actix_http::{Error, RequestHead};
#[cfg(feature = "cookies")]
use crate::cookie::{Cookie, CookieJar};
use crate::error::{FreezeRequestError, InvalidUrl};
use crate::frozen::FrozenClientRequest;

View File

@ -1,6 +1,5 @@
//! Test helpers for actix http client to use during testing.
use actix_http::http::header::IntoHeaderPair;
use actix_http::http::header::{self, HeaderValue};
use actix_http::http::{StatusCode, Version};
use actix_http::{h1, Payload, ResponseHead};
use bytes::Bytes;
@ -90,6 +89,8 @@ impl TestResponse {
#[cfg(feature = "cookies")]
for cookie in self.cookies.delta() {
use actix_http::http::header::{self, HeaderValue};
head.headers.insert(
header::SET_COOKIE,
HeaderValue::from_str(&cookie.encoded().to_string()).unwrap(),

View File

@ -25,7 +25,7 @@ async fn test_simple() {
HttpService::build()
.upgrade(|(req, mut framed): (Request, Framed<_, _>)| {
async move {
let res = ws::handshake_response(req.head());
let res = ws::handshake_response(req.head()).finish();
// send handshake response
framed
.send(h1::Message::Item((res.drop_body(), BodySize::None)))

View File

@ -17,7 +17,7 @@ use smallvec::SmallVec;
use crate::{
app_service::AppInitServiceState, config::AppConfig, error::UrlGenerationError,
extract::FromRequest, http::header, info::ConnectionInfo, rmap::ResourceMap,
extract::FromRequest, info::ConnectionInfo, rmap::ResourceMap,
};
#[cfg(feature = "cookies")]
@ -272,9 +272,11 @@ impl HttpRequest {
/// Load request cookies.
#[cfg(feature = "cookies")]
pub fn cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {
use actix_http::http::header::COOKIE;
if self.extensions().get::<Cookies>().is_none() {
let mut cookies = Vec::new();
for hdr in self.headers().get_all(header::COOKIE) {
for hdr in self.headers().get_all(COOKIE) {
let s = str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?;
for cookie_str in s.split(';').map(|s| s.trim()) {
if !cookie_str.is_empty() {

View File

@ -3,7 +3,6 @@ use std::fmt;
use actix_http::{
error::InternalError,
http::{header::IntoHeaderPair, Error as HttpError, HeaderMap, StatusCode},
ResponseBuilder,
};
use bytes::{Bytes, BytesMut};
@ -73,11 +72,25 @@ impl Responder for actix_http::Response {
}
}
impl Responder for HttpResponseBuilder {
#[inline]
fn respond_to(mut self, _: &HttpRequest) -> HttpResponse {
self.finish()
}
}
impl Responder for actix_http::ResponseBuilder {
#[inline]
fn respond_to(mut self, _: &HttpRequest) -> HttpResponse {
HttpResponse::from(self.finish())
}
}
impl<T: Responder> Responder for Option<T> {
fn respond_to(self, req: &HttpRequest) -> HttpResponse {
match self {
Some(t) => t.respond_to(req),
None => HttpResponseBuilder::new(StatusCode::NOT_FOUND).finish(),
Some(val) => val.respond_to(req),
None => HttpResponse::new(StatusCode::NOT_FOUND),
}
}
}
@ -95,20 +108,6 @@ where
}
}
impl Responder for HttpResponseBuilder {
#[inline]
fn respond_to(mut self, _: &HttpRequest) -> HttpResponse {
self.finish()
}
}
impl Responder for ResponseBuilder {
#[inline]
fn respond_to(mut self, _: &HttpRequest) -> HttpResponse {
HttpResponse::from(self.finish())
}
}
impl<T: Responder> Responder for (T, StatusCode) {
fn respond_to(self, req: &HttpRequest) -> HttpResponse {
let mut res = self.0.respond_to(req);

View File

@ -4,21 +4,19 @@ use std::{net::SocketAddr, rc::Rc};
pub use actix_http::test::TestBuffer;
use actix_http::{
http::{
header::{self, HeaderValue, IntoHeaderPair},
Method, StatusCode, Uri, Version,
},
http::{header::IntoHeaderPair, Method, StatusCode, Uri, Version},
test::TestRequest as HttpTestRequest,
Extensions, Request,
};
use actix_router::{Path, ResourceDef, Url};
use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory};
use actix_utils::future::ok;
use cookie::{Cookie, CookieJar};
use futures_core::Stream;
use futures_util::StreamExt as _;
use serde::{de::DeserializeOwned, Serialize};
#[cfg(feature = "cookies")]
use crate::cookie::{Cookie, CookieJar};
use crate::{
app_service::AppInitServiceState,
config::AppConfig,
@ -361,6 +359,7 @@ pub struct TestRequest {
path: Path<Url>,
peer_addr: Option<SocketAddr>,
app_data: Extensions,
#[cfg(feature = "cookies")]
cookies: CookieJar,
}
@ -373,6 +372,7 @@ impl Default for TestRequest {
path: Path::new(Url::new(Uri::default())),
peer_addr: None,
app_data: Extensions::new(),
#[cfg(feature = "cookies")]
cookies: CookieJar::new(),
}
}
@ -512,10 +512,14 @@ impl TestRequest {
}
fn finish(&mut self) -> Request {
// mut used when cookie feature is enabled
#[allow(unused_mut)]
let mut req = self.req.finish();
#[cfg(feature = "cookies")]
{
use actix_http::http::header::{HeaderValue, COOKIE};
let cookie: String = self
.cookies
.delta()
@ -526,7 +530,7 @@ impl TestRequest {
if !cookie.is_empty() {
req.headers_mut()
.insert(header::COOKIE, HeaderValue::from_str(&cookie).unwrap());
.insert(COOKIE, HeaderValue::from_str(&cookie).unwrap());
}
}