Merge remote-tracking branch 'origin/master' into on-connect-fix-2

This commit is contained in:
Rob Ede 2021-12-07 17:15:57 +00:00
commit d4a04b20c6
No known key found for this signature in database
GPG Key ID: 97C636207D3EF933
7 changed files with 115 additions and 74 deletions

View File

@ -7,13 +7,15 @@
* `Range` typed header. [#2485] * `Range` typed header. [#2485]
* `HttpResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] * `HttpResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468]
* `ServiceResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] * `ServiceResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468]
* Connection data set through the `HttpServer::on_connect` callback is now accessible only from the new `HttpRequest::conn_data()` method. [#2491] * Connection data set through the `HttpServer::on_connect` callback is now accessible only from the new `HttpRequest::conn_data()` and `ServiceRequest::conn_data()` methods. [#2491]
### Changed ### Changed
* Rename `Accept::{mime_precedence => ranked}`. [#2480] * Rename `Accept::{mime_precedence => ranked}`. [#2480]
* Rename `Accept::{mime_preference => preference}`. [#2480] * Rename `Accept::{mime_preference => preference}`. [#2480]
* Un-deprecate `App::data_factory`. [#2484] * Un-deprecate `App::data_factory`. [#2484]
* `HttpRequest::url_for` no longer constructs URLs with query or fragment components. [#2430] * `HttpRequest::url_for` no longer constructs URLs with query or fragment components. [#2430]
* Remove `B` (body) type parameter on `App`. [#2493]
* Add `B` (body) type parameter on `Scope`. [#2492]
### Fixed ### Fixed
* Accept wildcard `*` items in `AcceptLanguage`. [#2480] * Accept wildcard `*` items in `AcceptLanguage`. [#2480]
@ -27,6 +29,8 @@
[#2484]: https://github.com/actix/actix-web/pull/2484 [#2484]: https://github.com/actix/actix-web/pull/2484
[#2485]: https://github.com/actix/actix-web/pull/2485 [#2485]: https://github.com/actix/actix-web/pull/2485
[#2491]: https://github.com/actix/actix-web/pull/2491 [#2491]: https://github.com/actix/actix-web/pull/2491
[#2492]: https://github.com/actix/actix-web/pull/2492
[#2493]: https://github.com/actix/actix-web/pull/2493
## 4.0.0-beta.13 - 2021-11-30 ## 4.0.0-beta.13 - 2021-11-30

View File

@ -84,6 +84,7 @@ zstd = { version = "0.9", optional = true }
actix-server = "2.0.0-rc.1" actix-server = "2.0.0-rc.1"
actix-http-test = { version = "3.0.0-beta.7", features = ["openssl"] } actix-http-test = { version = "3.0.0-beta.7", features = ["openssl"] }
actix-tls = { version = "3.0.0-rc.1", features = ["openssl"] } actix-tls = { version = "3.0.0-rc.1", features = ["openssl"] }
actix-web = "4.0.0-beta.13"
async-stream = "0.3" async-stream = "0.3"
criterion = { version = "0.3", features = ["html_reports"] } criterion = { version = "0.3", features = ["html_reports"] }
env_logger = "0.9" env_logger = "0.9"
@ -95,7 +96,7 @@ serde_json = "1.0"
static_assertions = "1" static_assertions = "1"
tls-openssl = { package = "openssl", version = "0.10.9" } tls-openssl = { package = "openssl", version = "0.10.9" }
tls-rustls = { package = "rustls", version = "0.20.0" } tls-rustls = { package = "rustls", version = "0.20.0" }
tokio = { version = "1.2", features = ["net", "rt"] } tokio = { version = "1.2", features = ["net", "rt", "macros"] }
[[example]] [[example]]
name = "ws" name = "ws"

View File

@ -0,0 +1,26 @@
use actix_http::HttpService;
use actix_server::Server;
use actix_service::map_config;
use actix_web::{dev::AppConfig, get, App};
#[get("/")]
async fn index() -> &'static str {
"Hello, world. From Actix Web!"
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> std::io::Result<()> {
Server::build()
.bind("hello-world", "127.0.0.1:8080", || {
// construct actix-web app
let app = App::new().service(index);
HttpService::build()
// pass the app to service builder
// map_config is used to map App's configuration to ServiceBuilder
.finish(map_config(app, |_| AppConfig::default()))
.tcp()
})?
.run()
.await
}

View File

@ -1,9 +1,6 @@
use std::{cell::RefCell, fmt, future::Future, marker::PhantomData, rc::Rc}; use std::{cell::RefCell, fmt, future::Future, rc::Rc};
use actix_http::{ use actix_http::{body::MessageBody, Extensions, Request};
body::{BoxBody, MessageBody},
Extensions, Request,
};
use actix_service::{ use actix_service::{
apply, apply_fn_factory, boxed, IntoServiceFactory, ServiceFactory, ServiceFactoryExt, apply, apply_fn_factory, boxed, IntoServiceFactory, ServiceFactory, ServiceFactoryExt,
Transform, Transform,
@ -26,7 +23,7 @@ use crate::{
/// Application builder - structure that follows the builder pattern /// Application builder - structure that follows the builder pattern
/// for building application instances. /// for building application instances.
pub struct App<T, B> { pub struct App<T> {
endpoint: T, endpoint: T,
services: Vec<Box<dyn AppServiceFactory>>, services: Vec<Box<dyn AppServiceFactory>>,
default: Option<Rc<BoxedHttpServiceFactory>>, default: Option<Rc<BoxedHttpServiceFactory>>,
@ -34,10 +31,9 @@ pub struct App<T, B> {
data_factories: Vec<FnDataFactory>, data_factories: Vec<FnDataFactory>,
external: Vec<ResourceDef>, external: Vec<ResourceDef>,
extensions: Extensions, extensions: Extensions,
_phantom: PhantomData<B>,
} }
impl App<AppEntry, BoxBody> { impl App<AppEntry> {
/// Create application builder. Application can be configured with a builder-like pattern. /// Create application builder. Application can be configured with a builder-like pattern.
#[allow(clippy::new_without_default)] #[allow(clippy::new_without_default)]
pub fn new() -> Self { pub fn new() -> Self {
@ -51,22 +47,11 @@ impl App<AppEntry, BoxBody> {
factory_ref, factory_ref,
external: Vec::new(), external: Vec::new(),
extensions: Extensions::new(), extensions: Extensions::new(),
_phantom: PhantomData,
} }
} }
} }
impl<T, B> App<T, B> impl<T> App<T> {
where
B: MessageBody,
T: ServiceFactory<
ServiceRequest,
Config = (),
Response = ServiceResponse<B>,
Error = Error,
InitError = (),
>,
{
/// Set application (root level) data. /// Set application (root level) data.
/// ///
/// Application data stored with `App::app_data()` method is available through the /// Application data stored with `App::app_data()` method is available through the
@ -365,7 +350,7 @@ where
/// .route("/index.html", web::get().to(index)); /// .route("/index.html", web::get().to(index));
/// } /// }
/// ``` /// ```
pub fn wrap<M, B1>( pub fn wrap<M, B, B1>(
self, self,
mw: M, mw: M,
) -> App< ) -> App<
@ -376,9 +361,16 @@ where
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
B1,
> >
where where
T: ServiceFactory<
ServiceRequest,
Response = ServiceResponse<B>,
Error = Error,
Config = (),
InitError = (),
>,
B: MessageBody,
M: Transform< M: Transform<
T::Service, T::Service,
ServiceRequest, ServiceRequest,
@ -396,7 +388,6 @@ where
factory_ref: self.factory_ref, factory_ref: self.factory_ref,
external: self.external, external: self.external,
extensions: self.extensions, extensions: self.extensions,
_phantom: PhantomData,
} }
} }
@ -431,7 +422,7 @@ where
/// .route("/index.html", web::get().to(index)); /// .route("/index.html", web::get().to(index));
/// } /// }
/// ``` /// ```
pub fn wrap_fn<B1, F, R>( pub fn wrap_fn<F, R, B, B1>(
self, self,
mw: F, mw: F,
) -> App< ) -> App<
@ -442,12 +433,19 @@ where
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
B1,
> >
where where
B1: MessageBody, T: ServiceFactory<
ServiceRequest,
Response = ServiceResponse<B>,
Error = Error,
Config = (),
InitError = (),
>,
B: MessageBody,
F: Fn(ServiceRequest, &T::Service) -> R + Clone, F: Fn(ServiceRequest, &T::Service) -> R + Clone,
R: Future<Output = Result<ServiceResponse<B1>, Error>>, R: Future<Output = Result<ServiceResponse<B1>, Error>>,
B1: MessageBody,
{ {
App { App {
endpoint: apply_fn_factory(self.endpoint, mw), endpoint: apply_fn_factory(self.endpoint, mw),
@ -457,12 +455,11 @@ where
factory_ref: self.factory_ref, factory_ref: self.factory_ref,
external: self.external, external: self.external,
extensions: self.extensions, extensions: self.extensions,
_phantom: PhantomData,
} }
} }
} }
impl<T, B> IntoServiceFactory<AppInit<T, B>, Request> for App<T, B> impl<T, B> IntoServiceFactory<AppInit<T, B>, Request> for App<T>
where where
B: MessageBody, B: MessageBody,
T: ServiceFactory< T: ServiceFactory<

View File

@ -31,41 +31,53 @@ pub(crate) type FnDataFactory =
/// server constructs an application instance for each thread, thus application data must be /// server constructs an application instance for each thread, thus application data must be
/// constructed multiple times. If you want to share data between different threads, a shareable /// constructed multiple times. If you want to share data between different threads, a shareable
/// object should be used, e.g. `Send + Sync`. Application data does not need to be `Send` /// object should be used, e.g. `Send + Sync`. Application data does not need to be `Send`
/// or `Sync`. Internally `Data` uses `Arc`. /// or `Sync`. Internally `Data` contains an `Arc`.
/// ///
/// If route data is not set for a handler, using `Data<T>` extractor would cause *Internal /// If route data is not set for a handler, using `Data<T>` extractor would cause a `500 Internal
/// Server Error* response. /// Server Error` response.
/// ///
// TODO: document `dyn T` functionality through converting an Arc /// # Unsized Data
// TODO: note equivalence of req.app_data<Data<T>> and Data<T> extractor /// For types that are unsized, most commonly `dyn T`, `Data` can wrap these types by first
// TODO: note that data must be inserted using Data<T> in order to extract it /// constructing an `Arc<dyn T>` and using the `From` implementation to convert it.
///
/// ```
/// # use std::{fmt::Display, sync::Arc};
/// # use actix_web::web::Data;
/// let displayable_arc: Arc<dyn Display> = Arc::new(42usize);
/// let displayable_data: Data<dyn Display> = Data::from(displayable_arc);
/// ```
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use std::sync::Mutex; /// use std::sync::Mutex;
/// use actix_web::{web, App, HttpResponse, Responder}; /// use actix_web::{App, HttpRequest, HttpResponse, Responder, web::{self, Data}};
/// ///
/// struct MyData { /// struct MyData {
/// counter: usize, /// counter: usize,
/// } /// }
/// ///
/// /// Use the `Data<T>` extractor to access data in a handler. /// /// Use the `Data<T>` extractor to access data in a handler.
/// async fn index(data: web::Data<Mutex<MyData>>) -> impl Responder { /// async fn index(data: Data<Mutex<MyData>>) -> impl Responder {
/// let mut data = data.lock().unwrap(); /// let mut my_data = data.lock().unwrap();
/// data.counter += 1; /// my_data.counter += 1;
/// HttpResponse::Ok() /// HttpResponse::Ok()
/// } /// }
/// ///
/// fn main() { /// /// Alteratively, use the `HttpRequest::app_data` method to access data in a handler.
/// let data = web::Data::new(Mutex::new(MyData{ counter: 0 })); /// async fn index_alt(req: HttpRequest) -> impl Responder {
/// /// let data = req.app_data::<Data<Mutex<MyData>>>().unwrap();
/// let app = App::new() /// let mut my_data = data.lock().unwrap();
/// // Store `MyData` in application storage. /// my_data.counter += 1;
/// .app_data(data.clone()) /// HttpResponse::Ok()
/// .service(
/// web::resource("/index.html").route(
/// web::get().to(index)));
/// } /// }
///
/// let data = Data::new(Mutex::new(MyData { counter: 0 }));
///
/// let app = App::new()
/// // Store `MyData` in application storage.
/// .app_data(Data::clone(&data))
/// .route("/index.html", web::get().to(index))
/// .route("/index-alt.html", web::get().to(index_alt));
/// ``` /// ```
#[derive(Debug)] #[derive(Debug)]
pub struct Data<T: ?Sized>(Arc<T>); pub struct Data<T: ?Sized>(Arc<T>);

View File

@ -154,7 +154,7 @@ mod tests {
let srv = init_service( let srv = init_service(
App::new().service( App::new().service(
web::scope("app") web::scope("app")
.wrap(Compat::new(logger)) .wrap(logger)
.wrap(Compat::new(compress)) .wrap(Compat::new(compress))
.service(web::resource("/test").route(web::get().to(HttpResponse::Ok))), .service(web::resource("/test").route(web::get().to(HttpResponse::Ok))),
), ),

View File

@ -1,6 +1,6 @@
use std::{cell::RefCell, fmt, future::Future, mem, rc::Rc}; use std::{cell::RefCell, fmt, future::Future, marker::PhantomData, mem, rc::Rc};
use actix_http::Extensions; use actix_http::{body::BoxBody, Extensions};
use actix_router::{ResourceDef, Router}; use actix_router::{ResourceDef, Router};
use actix_service::{ use actix_service::{
apply, apply_fn_factory, boxed, IntoServiceFactory, Service, ServiceFactory, apply, apply_fn_factory, boxed, IntoServiceFactory, Service, ServiceFactory,
@ -52,7 +52,7 @@ type Guards = Vec<Box<dyn Guard>>;
/// * /{project_id}/path1 - responds to all http method /// * /{project_id}/path1 - responds to all http method
/// * /{project_id}/path2 - `GET` requests /// * /{project_id}/path2 - `GET` requests
/// * /{project_id}/path3 - `HEAD` requests /// * /{project_id}/path3 - `HEAD` requests
pub struct Scope<T = ScopeEndpoint> { pub struct Scope<T = ScopeEndpoint, B = BoxBody> {
endpoint: T, endpoint: T,
rdef: String, rdef: String,
app_data: Option<Extensions>, app_data: Option<Extensions>,
@ -61,6 +61,7 @@ pub struct Scope<T = ScopeEndpoint> {
default: Option<Rc<BoxedHttpServiceFactory>>, default: Option<Rc<BoxedHttpServiceFactory>>,
external: Vec<ResourceDef>, external: Vec<ResourceDef>,
factory_ref: Rc<RefCell<Option<ScopeFactory>>>, factory_ref: Rc<RefCell<Option<ScopeFactory>>>,
_phantom: PhantomData<B>,
} }
impl Scope { impl Scope {
@ -77,19 +78,21 @@ impl Scope {
default: None, default: None,
external: Vec::new(), external: Vec::new(),
factory_ref, factory_ref,
_phantom: Default::default(),
} }
} }
} }
impl<T> Scope<T> impl<T, B> Scope<T, B>
where where
T: ServiceFactory< T: ServiceFactory<
ServiceRequest, ServiceRequest,
Config = (), Config = (),
Response = ServiceResponse, Response = ServiceResponse<B>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
B: 'static,
{ {
/// Add match guard to a scope. /// Add match guard to a scope.
/// ///
@ -295,32 +298,29 @@ where
self self
} }
/// Registers middleware, in the form of a middleware component (type), /// Registers middleware, in the form of a middleware component (type), that runs during inbound
/// that runs during inbound processing in the request /// processing in the request life-cycle (request -> response), modifying request as necessary,
/// life-cycle (request -> response), modifying request as /// across all requests managed by the *Scope*.
/// necessary, across all requests managed by the *Scope*. Scope-level
/// middleware is more limited in what it can modify, relative to Route or
/// Application level middleware, in that Scope-level middleware can not modify
/// ServiceResponse.
/// ///
/// Use middleware when you need to read or modify *every* request in some way. /// Use middleware when you need to read or modify *every* request in some way.
pub fn wrap<M>( pub fn wrap<M, B1>(
self, self,
mw: M, mw: M,
) -> Scope< ) -> Scope<
impl ServiceFactory< impl ServiceFactory<
ServiceRequest, ServiceRequest,
Config = (), Config = (),
Response = ServiceResponse, Response = ServiceResponse<B1>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
B1,
> >
where where
M: Transform< M: Transform<
T::Service, T::Service,
ServiceRequest, ServiceRequest,
Response = ServiceResponse, Response = ServiceResponse<B1>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
@ -334,16 +334,15 @@ where
default: self.default, default: self.default,
external: self.external, external: self.external,
factory_ref: self.factory_ref, factory_ref: self.factory_ref,
_phantom: PhantomData,
} }
} }
/// Registers middleware, in the form of a closure, that runs during inbound /// Registers middleware, in the form of a closure, that runs during inbound processing in the
/// processing in the request life-cycle (request -> response), modifying /// request life-cycle (request -> response), modifying request as necessary, across all
/// request as necessary, across all requests managed by the *Scope*. /// requests managed by the *Scope*.
/// Scope-level middleware is more limited in what it can modify, relative
/// to Route or Application level middleware, in that Scope-level middleware
/// can not modify ServiceResponse.
/// ///
/// # Examples
/// ``` /// ```
/// use actix_service::Service; /// use actix_service::Service;
/// use actix_web::{web, App}; /// use actix_web::{web, App};
@ -369,21 +368,22 @@ where
/// .route("/index.html", web::get().to(index))); /// .route("/index.html", web::get().to(index)));
/// } /// }
/// ``` /// ```
pub fn wrap_fn<F, R>( pub fn wrap_fn<F, R, B1>(
self, self,
mw: F, mw: F,
) -> Scope< ) -> Scope<
impl ServiceFactory< impl ServiceFactory<
ServiceRequest, ServiceRequest,
Config = (), Config = (),
Response = ServiceResponse, Response = ServiceResponse<B1>,
Error = Error, Error = Error,
InitError = (), InitError = (),
>, >,
B1,
> >
where where
F: Fn(ServiceRequest, &T::Service) -> R + Clone, F: Fn(ServiceRequest, &T::Service) -> R + Clone,
R: Future<Output = Result<ServiceResponse, Error>>, R: Future<Output = Result<ServiceResponse<B1>, Error>>,
{ {
Scope { Scope {
endpoint: apply_fn_factory(self.endpoint, mw), endpoint: apply_fn_factory(self.endpoint, mw),
@ -394,6 +394,7 @@ where
default: self.default, default: self.default,
external: self.external, external: self.external,
factory_ref: self.factory_ref, factory_ref: self.factory_ref,
_phantom: PhantomData,
} }
} }
} }