mirror of https://github.com/fafhrd91/actix-web
Merge branch 'master' into fix/actix-files-doc
This commit is contained in:
commit
fda2334734
|
@ -8,6 +8,8 @@
|
||||||
### Changed
|
### Changed
|
||||||
* Rework `Responder` trait to be sync and returns `Response`/`HttpResponse` directly.
|
* Rework `Responder` trait to be sync and returns `Response`/`HttpResponse` directly.
|
||||||
Making it more simple and performant. [#1891]
|
Making it more simple and performant. [#1891]
|
||||||
|
* `ServiceRequest::into_parts` and `ServiceRequest::from_parts` would not fail.
|
||||||
|
`ServiceRequest::from_request` would not fail and no payload would be generated [#1893]
|
||||||
* Our `Either` type now uses `Left`/`Right` variants (instead of `A`/`B`) [#1894]
|
* Our `Either` type now uses `Left`/`Right` variants (instead of `A`/`B`) [#1894]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
@ -15,8 +17,10 @@
|
||||||
* Public field of `web::Query` has been made private. [#1894]
|
* Public field of `web::Query` has been made private. [#1894]
|
||||||
|
|
||||||
[#1891]: https://github.com/actix/actix-web/pull/1891
|
[#1891]: https://github.com/actix/actix-web/pull/1891
|
||||||
|
[#1893]: https://github.com/actix/actix-web/pull/1893
|
||||||
[#1894]: https://github.com/actix/actix-web/pull/1894
|
[#1894]: https://github.com/actix/actix-web/pull/1894
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.1 - 2021-01-07
|
## 4.0.0-beta.1 - 2021-01-07
|
||||||
### Added
|
### Added
|
||||||
* `Compat` middleware enabling generic response body/error type of middlewares like `Logger` and
|
* `Compat` middleware enabling generic response body/error type of middlewares like `Logger` and
|
||||||
|
|
|
@ -287,42 +287,35 @@ where
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
) -> Result<bool, DispatchError> {
|
) -> Result<bool, DispatchError> {
|
||||||
if self.write_buf.is_empty() {
|
let len = self.write_buf.len();
|
||||||
|
if len == 0 {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let len = self.write_buf.len();
|
|
||||||
let mut written = 0;
|
|
||||||
let InnerDispatcherProj { io, write_buf, .. } = self.project();
|
let InnerDispatcherProj { io, write_buf, .. } = self.project();
|
||||||
let mut io = Pin::new(io.as_mut().unwrap());
|
let mut io = Pin::new(io.as_mut().unwrap());
|
||||||
|
|
||||||
|
let mut written = 0;
|
||||||
while written < len {
|
while written < len {
|
||||||
match io.as_mut().poll_write(cx, &write_buf[written..]) {
|
match io.as_mut().poll_write(cx, &write_buf[written..]) {
|
||||||
Poll::Ready(Ok(0)) => {
|
Poll::Ready(Ok(0)) => {
|
||||||
return Err(DispatchError::Io(io::Error::new(
|
return Err(DispatchError::Io(io::Error::new(
|
||||||
io::ErrorKind::WriteZero,
|
io::ErrorKind::WriteZero,
|
||||||
"",
|
"",
|
||||||
)));
|
)))
|
||||||
}
|
|
||||||
Poll::Ready(Ok(n)) => {
|
|
||||||
written += n;
|
|
||||||
}
|
}
|
||||||
|
Poll::Ready(Ok(n)) => written += n,
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
if written > 0 {
|
write_buf.advance(written);
|
||||||
write_buf.advance(written);
|
|
||||||
}
|
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
Poll::Ready(Err(err)) => return Err(DispatchError::Io(err)),
|
Poll::Ready(Err(err)) => return Err(DispatchError::Io(err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if written == write_buf.len() {
|
// SAFETY: setting length to 0 is safe
|
||||||
// SAFETY: setting length to 0 is safe
|
// skips one length check vs truncate
|
||||||
// skips one length check vs truncate
|
unsafe { write_buf.set_len(0) }
|
||||||
unsafe { write_buf.set_len(0) }
|
|
||||||
} else {
|
|
||||||
write_buf.advance(written);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
@ -766,19 +759,12 @@ where
|
||||||
} else {
|
} else {
|
||||||
// flush buffer
|
// flush buffer
|
||||||
inner.as_mut().poll_flush(cx)?;
|
inner.as_mut().poll_flush(cx)?;
|
||||||
if !inner.write_buf.is_empty() || inner.io.is_none() {
|
if !inner.write_buf.is_empty() {
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
} else {
|
} else {
|
||||||
match Pin::new(inner.project().io)
|
Pin::new(inner.project().io.as_mut().unwrap())
|
||||||
.as_pin_mut()
|
|
||||||
.unwrap()
|
|
||||||
.poll_shutdown(cx)
|
.poll_shutdown(cx)
|
||||||
{
|
.map_err(DispatchError::from)
|
||||||
Poll::Ready(res) => {
|
|
||||||
Poll::Ready(res.map_err(DispatchError::from))
|
|
||||||
}
|
|
||||||
Poll::Pending => Poll::Pending,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -920,7 +906,7 @@ where
|
||||||
buf.reserve(HW_BUFFER_SIZE - remaining);
|
buf.reserve(HW_BUFFER_SIZE - remaining);
|
||||||
}
|
}
|
||||||
|
|
||||||
match read(cx, io, buf) {
|
match actix_codec::poll_read_buf(Pin::new(io), cx, buf) {
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
return if read_some { Ok(Some(false)) } else { Ok(None) };
|
return if read_some { Ok(Some(false)) } else { Ok(None) };
|
||||||
}
|
}
|
||||||
|
@ -948,17 +934,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read<T>(
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
io: &mut T,
|
|
||||||
buf: &mut BytesMut,
|
|
||||||
) -> Poll<Result<usize, io::Error>>
|
|
||||||
where
|
|
||||||
T: AsyncRead + Unpin,
|
|
||||||
{
|
|
||||||
actix_codec::poll_read_buf(Pin::new(io), cx, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::task::{Context, Poll};
|
use std::task::Poll;
|
||||||
|
|
||||||
use actix_http::{Extensions, Request, Response};
|
use actix_http::{Extensions, Request, Response};
|
||||||
use actix_router::{Path, ResourceDef, Router, Url};
|
use actix_router::{Path, ResourceDef, Router, Url};
|
||||||
|
@ -62,7 +62,8 @@ where
|
||||||
type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
|
||||||
|
|
||||||
fn new_service(&self, config: AppConfig) -> Self::Future {
|
fn new_service(&self, config: AppConfig) -> Self::Future {
|
||||||
// update resource default service
|
// set AppService's default service to 404 NotFound
|
||||||
|
// if no user defined default service exists.
|
||||||
let default = self.default.clone().unwrap_or_else(|| {
|
let default = self.default.clone().unwrap_or_else(|| {
|
||||||
Rc::new(boxed::factory(fn_service(|req: ServiceRequest| async {
|
Rc::new(boxed::factory(fn_service(|req: ServiceRequest| async {
|
||||||
Ok(req.into_response(Response::NotFound().finish()))
|
Ok(req.into_response(Response::NotFound().finish()))
|
||||||
|
@ -141,10 +142,8 @@ where
|
||||||
|
|
||||||
Ok(AppInitService {
|
Ok(AppInitService {
|
||||||
service,
|
service,
|
||||||
rmap,
|
|
||||||
config,
|
|
||||||
app_data: Rc::new(app_data),
|
app_data: Rc::new(app_data),
|
||||||
pool: HttpRequestPool::create(),
|
app_state: AppInitServiceState::new(rmap, config),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -156,10 +155,42 @@ where
|
||||||
T: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
T: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
||||||
{
|
{
|
||||||
service: T,
|
service: T,
|
||||||
|
app_data: Rc<Extensions>,
|
||||||
|
app_state: Rc<AppInitServiceState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// a collection of AppInitService state that shared between HttpRequests.
|
||||||
|
pub(crate) struct AppInitServiceState {
|
||||||
rmap: Rc<ResourceMap>,
|
rmap: Rc<ResourceMap>,
|
||||||
config: AppConfig,
|
config: AppConfig,
|
||||||
app_data: Rc<Extensions>,
|
pool: HttpRequestPool,
|
||||||
pool: &'static HttpRequestPool,
|
}
|
||||||
|
|
||||||
|
impl AppInitServiceState {
|
||||||
|
pub(crate) fn new(rmap: Rc<ResourceMap>, config: AppConfig) -> Rc<Self> {
|
||||||
|
Rc::new(AppInitServiceState {
|
||||||
|
rmap,
|
||||||
|
config,
|
||||||
|
// TODO: AppConfig can be used to pass user defined HttpRequestPool
|
||||||
|
// capacity.
|
||||||
|
pool: HttpRequestPool::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn rmap(&self) -> &ResourceMap {
|
||||||
|
&*self.rmap
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn config(&self) -> &AppConfig {
|
||||||
|
&self.config
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn pool(&self) -> &HttpRequestPool {
|
||||||
|
&self.pool
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, B> Service<Request> for AppInitService<T, B>
|
impl<T, B> Service<Request> for AppInitService<T, B>
|
||||||
|
@ -170,32 +201,26 @@ where
|
||||||
type Error = T::Error;
|
type Error = T::Error;
|
||||||
type Future = T::Future;
|
type Future = T::Future;
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
actix_service::forward_ready!(service);
|
||||||
self.service.poll_ready(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, req: Request) -> Self::Future {
|
fn call(&mut self, req: Request) -> Self::Future {
|
||||||
let (head, payload) = req.into_parts();
|
let (head, payload) = req.into_parts();
|
||||||
|
|
||||||
let req = if let Some(mut req) = self.pool.get_request() {
|
let req = if let Some(mut req) = self.app_state.pool().pop() {
|
||||||
let inner = Rc::get_mut(&mut req.inner).unwrap();
|
let inner = Rc::get_mut(&mut req.inner).unwrap();
|
||||||
inner.path.get_mut().update(&head.uri);
|
inner.path.get_mut().update(&head.uri);
|
||||||
inner.path.reset();
|
inner.path.reset();
|
||||||
inner.head = head;
|
inner.head = head;
|
||||||
inner.payload = payload;
|
|
||||||
req
|
req
|
||||||
} else {
|
} else {
|
||||||
HttpRequest::new(
|
HttpRequest::new(
|
||||||
Path::new(Url::new(head.uri.clone())),
|
Path::new(Url::new(head.uri.clone())),
|
||||||
head,
|
head,
|
||||||
payload,
|
self.app_state.clone(),
|
||||||
self.rmap.clone(),
|
|
||||||
self.config.clone(),
|
|
||||||
self.app_data.clone(),
|
self.app_data.clone(),
|
||||||
self.pool,
|
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
self.service.call(ServiceRequest::new(req))
|
self.service.call(ServiceRequest::new(req, payload))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +229,7 @@ where
|
||||||
T: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
T: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
||||||
{
|
{
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.pool.clear();
|
self.app_state.pool().clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -125,9 +125,7 @@ impl AppService {
|
||||||
|
|
||||||
/// Application connection config
|
/// Application connection config
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AppConfig(Rc<AppConfigInner>);
|
pub struct AppConfig {
|
||||||
|
|
||||||
struct AppConfigInner {
|
|
||||||
secure: bool,
|
secure: bool,
|
||||||
host: String,
|
host: String,
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
|
@ -135,7 +133,7 @@ struct AppConfigInner {
|
||||||
|
|
||||||
impl AppConfig {
|
impl AppConfig {
|
||||||
pub(crate) fn new(secure: bool, addr: SocketAddr, host: String) -> Self {
|
pub(crate) fn new(secure: bool, addr: SocketAddr, host: String) -> Self {
|
||||||
AppConfig(Rc::new(AppConfigInner { secure, addr, host }))
|
AppConfig { secure, addr, host }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Server host name.
|
/// Server host name.
|
||||||
|
@ -146,17 +144,17 @@ impl AppConfig {
|
||||||
///
|
///
|
||||||
/// By default host name is set to a "localhost" value.
|
/// By default host name is set to a "localhost" value.
|
||||||
pub fn host(&self) -> &str {
|
pub fn host(&self) -> &str {
|
||||||
&self.0.host
|
&self.host
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if connection is secure(https)
|
/// Returns true if connection is secure(https)
|
||||||
pub fn secure(&self) -> bool {
|
pub fn secure(&self) -> bool {
|
||||||
self.0.secure
|
self.secure
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the socket address of the local half of this TCP connection
|
/// Returns the socket address of the local half of this TCP connection
|
||||||
pub fn local_addr(&self) -> SocketAddr {
|
pub fn local_addr(&self) -> SocketAddr {
|
||||||
self.0.addr
|
self.addr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
103
src/request.rs
103
src/request.rs
|
@ -8,6 +8,7 @@ use actix_router::{Path, Url};
|
||||||
use futures_util::future::{ok, Ready};
|
use futures_util::future::{ok, Ready};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
use crate::app_service::AppInitServiceState;
|
||||||
use crate::config::AppConfig;
|
use crate::config::AppConfig;
|
||||||
use crate::error::UrlGenerationError;
|
use crate::error::UrlGenerationError;
|
||||||
use crate::extract::FromRequest;
|
use crate::extract::FromRequest;
|
||||||
|
@ -27,11 +28,8 @@ pub struct HttpRequest {
|
||||||
pub(crate) struct HttpRequestInner {
|
pub(crate) struct HttpRequestInner {
|
||||||
pub(crate) head: Message<RequestHead>,
|
pub(crate) head: Message<RequestHead>,
|
||||||
pub(crate) path: Path<Url>,
|
pub(crate) path: Path<Url>,
|
||||||
pub(crate) payload: Payload,
|
|
||||||
pub(crate) app_data: SmallVec<[Rc<Extensions>; 4]>,
|
pub(crate) app_data: SmallVec<[Rc<Extensions>; 4]>,
|
||||||
rmap: Rc<ResourceMap>,
|
app_state: Rc<AppInitServiceState>,
|
||||||
config: AppConfig,
|
|
||||||
pool: &'static HttpRequestPool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HttpRequest {
|
impl HttpRequest {
|
||||||
|
@ -39,11 +37,8 @@ impl HttpRequest {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
path: Path<Url>,
|
path: Path<Url>,
|
||||||
head: Message<RequestHead>,
|
head: Message<RequestHead>,
|
||||||
payload: Payload,
|
app_state: Rc<AppInitServiceState>,
|
||||||
rmap: Rc<ResourceMap>,
|
|
||||||
config: AppConfig,
|
|
||||||
app_data: Rc<Extensions>,
|
app_data: Rc<Extensions>,
|
||||||
pool: &'static HttpRequestPool,
|
|
||||||
) -> HttpRequest {
|
) -> HttpRequest {
|
||||||
let mut data = SmallVec::<[Rc<Extensions>; 4]>::new();
|
let mut data = SmallVec::<[Rc<Extensions>; 4]>::new();
|
||||||
data.push(app_data);
|
data.push(app_data);
|
||||||
|
@ -52,11 +47,8 @@ impl HttpRequest {
|
||||||
inner: Rc::new(HttpRequestInner {
|
inner: Rc::new(HttpRequestInner {
|
||||||
head,
|
head,
|
||||||
path,
|
path,
|
||||||
payload,
|
app_state,
|
||||||
rmap,
|
|
||||||
config,
|
|
||||||
app_data: data,
|
app_data: data,
|
||||||
pool,
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,7 +134,7 @@ impl HttpRequest {
|
||||||
/// Returns a None when no resource is fully matched, including default services.
|
/// Returns a None when no resource is fully matched, including default services.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn match_pattern(&self) -> Option<String> {
|
pub fn match_pattern(&self) -> Option<String> {
|
||||||
self.inner.rmap.match_pattern(self.path())
|
self.resource_map().match_pattern(self.path())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The resource name that matched the path. Useful for logging and metrics.
|
/// The resource name that matched the path. Useful for logging and metrics.
|
||||||
|
@ -150,7 +142,7 @@ impl HttpRequest {
|
||||||
/// Returns a None when no resource is fully matched, including default services.
|
/// Returns a None when no resource is fully matched, including default services.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn match_name(&self) -> Option<&str> {
|
pub fn match_name(&self) -> Option<&str> {
|
||||||
self.inner.rmap.match_name(self.path())
|
self.resource_map().match_name(self.path())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request extensions
|
/// Request extensions
|
||||||
|
@ -192,7 +184,7 @@ impl HttpRequest {
|
||||||
U: IntoIterator<Item = I>,
|
U: IntoIterator<Item = I>,
|
||||||
I: AsRef<str>,
|
I: AsRef<str>,
|
||||||
{
|
{
|
||||||
self.inner.rmap.url_for(&self, name, elements)
|
self.resource_map().url_for(&self, name, elements)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate url for named resource
|
/// Generate url for named resource
|
||||||
|
@ -207,7 +199,7 @@ impl HttpRequest {
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Get a reference to a `ResourceMap` of current application.
|
/// Get a reference to a `ResourceMap` of current application.
|
||||||
pub fn resource_map(&self) -> &ResourceMap {
|
pub fn resource_map(&self) -> &ResourceMap {
|
||||||
&self.inner.rmap
|
&self.app_state().rmap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Peer socket address
|
/// Peer socket address
|
||||||
|
@ -227,13 +219,13 @@ impl HttpRequest {
|
||||||
/// borrowed.
|
/// borrowed.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> {
|
pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> {
|
||||||
ConnectionInfo::get(self.head(), &*self.app_config())
|
ConnectionInfo::get(self.head(), self.app_config())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// App config
|
/// App config
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn app_config(&self) -> &AppConfig {
|
pub fn app_config(&self) -> &AppConfig {
|
||||||
&self.inner.config
|
self.app_state().config()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an application data object stored with `App::data` or `App::app_data`
|
/// Get an application data object stored with `App::data` or `App::app_data`
|
||||||
|
@ -253,6 +245,11 @@ impl HttpRequest {
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn app_state(&self) -> &AppInitServiceState {
|
||||||
|
&*self.inner.app_state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HttpMessage for HttpRequest {
|
impl HttpMessage for HttpRequest {
|
||||||
|
@ -288,14 +285,16 @@ impl Drop for HttpRequest {
|
||||||
|
|
||||||
// This relies on no Weak<HttpRequestInner> exists anywhere.(There is none)
|
// This relies on no Weak<HttpRequestInner> exists anywhere.(There is none)
|
||||||
if let Some(inner) = Rc::get_mut(&mut self.inner) {
|
if let Some(inner) = Rc::get_mut(&mut self.inner) {
|
||||||
let v = &mut inner.pool.0.borrow_mut();
|
if inner.app_state.pool().is_available() {
|
||||||
if v.len() < 128 {
|
|
||||||
// clear additional app_data and keep the root one for reuse.
|
// clear additional app_data and keep the root one for reuse.
|
||||||
inner.app_data.truncate(1);
|
inner.app_data.truncate(1);
|
||||||
// inner is borrowed mut here. get head's Extension mutably
|
// inner is borrowed mut here. get head's Extension mutably
|
||||||
// to reduce borrow check
|
// to reduce borrow check
|
||||||
inner.head.extensions.get_mut().clear();
|
inner.head.extensions.get_mut().clear();
|
||||||
v.push(self.inner.clone());
|
|
||||||
|
// a re-borrow of pool is necessary here.
|
||||||
|
let req = self.inner.clone();
|
||||||
|
self.app_state().pool().push(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -364,25 +363,50 @@ impl fmt::Debug for HttpRequest {
|
||||||
/// Request objects are added when they are dropped (see `<HttpRequest as Drop>::drop`) and re-used
|
/// Request objects are added when they are dropped (see `<HttpRequest as Drop>::drop`) and re-used
|
||||||
/// in `<AppInitService as Service>::call` when there are available objects in the list.
|
/// in `<AppInitService as Service>::call` when there are available objects in the list.
|
||||||
///
|
///
|
||||||
/// The pool's initial capacity is 128 items.
|
/// The pool's default capacity is 128 items.
|
||||||
pub(crate) struct HttpRequestPool(RefCell<Vec<Rc<HttpRequestInner>>>);
|
pub(crate) struct HttpRequestPool {
|
||||||
|
inner: RefCell<Vec<Rc<HttpRequestInner>>>,
|
||||||
|
cap: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for HttpRequestPool {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::with_capacity(128)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl HttpRequestPool {
|
impl HttpRequestPool {
|
||||||
/// Allocates a slab of memory for pool use.
|
pub(crate) fn with_capacity(cap: usize) -> Self {
|
||||||
pub(crate) fn create() -> &'static HttpRequestPool {
|
HttpRequestPool {
|
||||||
let pool = HttpRequestPool(RefCell::new(Vec::with_capacity(128)));
|
inner: RefCell::new(Vec::with_capacity(cap)),
|
||||||
Box::leak(Box::new(pool))
|
cap,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Re-use a previously allocated (but now completed/discarded) HttpRequest object.
|
/// Re-use a previously allocated (but now completed/discarded) HttpRequest object.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn get_request(&self) -> Option<HttpRequest> {
|
pub(crate) fn pop(&self) -> Option<HttpRequest> {
|
||||||
self.0.borrow_mut().pop().map(|inner| HttpRequest { inner })
|
self.inner
|
||||||
|
.borrow_mut()
|
||||||
|
.pop()
|
||||||
|
.map(|inner| HttpRequest { inner })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the pool still has capacity for request storage.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn is_available(&self) -> bool {
|
||||||
|
self.inner.borrow_mut().len() < self.cap
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a request to pool.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn push(&self, req: Rc<HttpRequestInner>) {
|
||||||
|
self.inner.borrow_mut().push(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears all allocated HttpRequest objects.
|
/// Clears all allocated HttpRequest objects.
|
||||||
pub(crate) fn clear(&self) {
|
pub(crate) fn clear(&self) {
|
||||||
self.0.borrow_mut().clear()
|
self.inner.borrow_mut().clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -528,6 +552,25 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_drop_http_request_pool() {
|
||||||
|
let mut srv = init_service(App::new().service(web::resource("/").to(
|
||||||
|
|req: HttpRequest| {
|
||||||
|
HttpResponse::Ok()
|
||||||
|
.set_header("pool_cap", req.app_state().pool().cap)
|
||||||
|
.finish()
|
||||||
|
},
|
||||||
|
)))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let req = TestRequest::default().to_request();
|
||||||
|
let resp = call_service(&mut srv, req).await;
|
||||||
|
|
||||||
|
drop(srv);
|
||||||
|
|
||||||
|
assert_eq!(resp.headers().get("pool_cap").unwrap(), "128");
|
||||||
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_data() {
|
async fn test_data() {
|
||||||
let mut srv = init_service(App::new().app_data(10usize).service(
|
let mut srv = init_service(App::new().app_data(10usize).service(
|
||||||
|
|
215
src/scope.rs
215
src/scope.rs
|
@ -1,18 +1,18 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::task::{Context, Poll};
|
use std::task::Poll;
|
||||||
|
|
||||||
use actix_http::{Extensions, Response};
|
use actix_http::Extensions;
|
||||||
use actix_router::{ResourceDef, ResourceInfo, Router};
|
use actix_router::{ResourceDef, Router};
|
||||||
use actix_service::boxed::{self, BoxService, BoxServiceFactory};
|
use actix_service::boxed::{self, BoxService, BoxServiceFactory};
|
||||||
use actix_service::{
|
use actix_service::{
|
||||||
apply, apply_fn_factory, IntoServiceFactory, Service, ServiceFactory,
|
apply, apply_fn_factory, IntoServiceFactory, Service, ServiceFactory,
|
||||||
ServiceFactoryExt, Transform,
|
ServiceFactoryExt, Transform,
|
||||||
};
|
};
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
|
use futures_util::future::join_all;
|
||||||
|
|
||||||
use crate::config::ServiceConfig;
|
use crate::config::ServiceConfig;
|
||||||
use crate::data::Data;
|
use crate::data::Data;
|
||||||
|
@ -61,10 +61,10 @@ type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Err
|
||||||
pub struct Scope<T = ScopeEndpoint> {
|
pub struct Scope<T = ScopeEndpoint> {
|
||||||
endpoint: T,
|
endpoint: T,
|
||||||
rdef: String,
|
rdef: String,
|
||||||
data: Option<Extensions>,
|
app_data: Option<Extensions>,
|
||||||
services: Vec<Box<dyn AppServiceFactory>>,
|
services: Vec<Box<dyn AppServiceFactory>>,
|
||||||
guards: Vec<Box<dyn Guard>>,
|
guards: Vec<Box<dyn Guard>>,
|
||||||
default: Rc<RefCell<Option<Rc<HttpNewService>>>>,
|
default: Option<Rc<HttpNewService>>,
|
||||||
external: Vec<ResourceDef>,
|
external: Vec<ResourceDef>,
|
||||||
factory_ref: Rc<RefCell<Option<ScopeFactory>>>,
|
factory_ref: Rc<RefCell<Option<ScopeFactory>>>,
|
||||||
}
|
}
|
||||||
|
@ -76,10 +76,10 @@ impl Scope {
|
||||||
Scope {
|
Scope {
|
||||||
endpoint: ScopeEndpoint::new(fref.clone()),
|
endpoint: ScopeEndpoint::new(fref.clone()),
|
||||||
rdef: path.to_string(),
|
rdef: path.to_string(),
|
||||||
data: None,
|
app_data: None,
|
||||||
guards: Vec::new(),
|
guards: Vec::new(),
|
||||||
services: Vec::new(),
|
services: Vec::new(),
|
||||||
default: Rc::new(RefCell::new(None)),
|
default: None,
|
||||||
external: Vec::new(),
|
external: Vec::new(),
|
||||||
factory_ref: fref,
|
factory_ref: fref,
|
||||||
}
|
}
|
||||||
|
@ -155,10 +155,10 @@ where
|
||||||
///
|
///
|
||||||
/// Data of different types from parent contexts will still be accessible.
|
/// Data of different types from parent contexts will still be accessible.
|
||||||
pub fn app_data<U: 'static>(mut self, data: U) -> Self {
|
pub fn app_data<U: 'static>(mut self, data: U) -> Self {
|
||||||
if self.data.is_none() {
|
if self.app_data.is_none() {
|
||||||
self.data = Some(Extensions::new());
|
self.app_data = Some(Extensions::new());
|
||||||
}
|
}
|
||||||
self.data.as_mut().unwrap().insert(data);
|
self.app_data.as_mut().unwrap().insert(data);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,15 +201,15 @@ where
|
||||||
self.external.extend(cfg.external);
|
self.external.extend(cfg.external);
|
||||||
|
|
||||||
if !cfg.data.is_empty() {
|
if !cfg.data.is_empty() {
|
||||||
let mut data = self.data.unwrap_or_else(Extensions::new);
|
let mut data = self.app_data.unwrap_or_else(Extensions::new);
|
||||||
|
|
||||||
for value in cfg.data.iter() {
|
for value in cfg.data.iter() {
|
||||||
value.create(&mut data);
|
value.create(&mut data);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.data = Some(data);
|
self.app_data = Some(data);
|
||||||
}
|
}
|
||||||
self.data
|
self.app_data
|
||||||
.get_or_insert_with(Extensions::new)
|
.get_or_insert_with(Extensions::new)
|
||||||
.extend(cfg.extensions);
|
.extend(cfg.extensions);
|
||||||
self
|
self
|
||||||
|
@ -295,11 +295,9 @@ where
|
||||||
U::InitError: fmt::Debug,
|
U::InitError: fmt::Debug,
|
||||||
{
|
{
|
||||||
// create and configure default resource
|
// create and configure default resource
|
||||||
self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::factory(
|
self.default = Some(Rc::new(boxed::factory(f.into_factory().map_init_err(
|
||||||
f.into_factory().map_init_err(|e| {
|
|e| log::error!("Can not construct default service: {:?}", e),
|
||||||
log::error!("Can not construct default service: {:?}", e)
|
))));
|
||||||
}),
|
|
||||||
)))));
|
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -337,7 +335,7 @@ where
|
||||||
Scope {
|
Scope {
|
||||||
endpoint: apply(mw, self.endpoint),
|
endpoint: apply(mw, self.endpoint),
|
||||||
rdef: self.rdef,
|
rdef: self.rdef,
|
||||||
data: self.data,
|
app_data: self.app_data,
|
||||||
guards: self.guards,
|
guards: self.guards,
|
||||||
services: self.services,
|
services: self.services,
|
||||||
default: self.default,
|
default: self.default,
|
||||||
|
@ -397,7 +395,7 @@ where
|
||||||
Scope {
|
Scope {
|
||||||
endpoint: apply_fn_factory(self.endpoint, mw),
|
endpoint: apply_fn_factory(self.endpoint, mw),
|
||||||
rdef: self.rdef,
|
rdef: self.rdef,
|
||||||
data: self.data,
|
app_data: self.app_data,
|
||||||
guards: self.guards,
|
guards: self.guards,
|
||||||
services: self.services,
|
services: self.services,
|
||||||
default: self.default,
|
default: self.default,
|
||||||
|
@ -419,9 +417,7 @@ where
|
||||||
{
|
{
|
||||||
fn register(mut self, config: &mut AppService) {
|
fn register(mut self, config: &mut AppService) {
|
||||||
// update default resource if needed
|
// update default resource if needed
|
||||||
if self.default.borrow().is_none() {
|
let default = self.default.unwrap_or_else(|| config.default_service());
|
||||||
*self.default.borrow_mut() = Some(config.default_service());
|
|
||||||
}
|
|
||||||
|
|
||||||
// register nested services
|
// register nested services
|
||||||
let mut cfg = config.clone_config();
|
let mut cfg = config.clone_config();
|
||||||
|
@ -437,14 +433,14 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom app data storage
|
// custom app data storage
|
||||||
if let Some(ref mut ext) = self.data {
|
if let Some(ref mut ext) = self.app_data {
|
||||||
config.set_service_data(ext);
|
config.set_service_data(ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
// complete scope pipeline creation
|
// complete scope pipeline creation
|
||||||
*self.factory_ref.borrow_mut() = Some(ScopeFactory {
|
*self.factory_ref.borrow_mut() = Some(ScopeFactory {
|
||||||
data: self.data.take().map(Rc::new),
|
app_data: self.app_data.take().map(Rc::new),
|
||||||
default: self.default.clone(),
|
default,
|
||||||
services: cfg
|
services: cfg
|
||||||
.into_services()
|
.into_services()
|
||||||
.1
|
.1
|
||||||
|
@ -476,129 +472,65 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ScopeFactory {
|
pub struct ScopeFactory {
|
||||||
data: Option<Rc<Extensions>>,
|
app_data: Option<Rc<Extensions>>,
|
||||||
services: Rc<[(ResourceDef, HttpNewService, RefCell<Option<Guards>>)]>,
|
services: Rc<[(ResourceDef, HttpNewService, RefCell<Option<Guards>>)]>,
|
||||||
default: Rc<RefCell<Option<Rc<HttpNewService>>>>,
|
default: Rc<HttpNewService>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServiceFactory<ServiceRequest> for ScopeFactory {
|
impl ServiceFactory<ServiceRequest> for ScopeFactory {
|
||||||
type Config = ();
|
|
||||||
type Response = ServiceResponse;
|
type Response = ServiceResponse;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type InitError = ();
|
type Config = ();
|
||||||
type Service = ScopeService;
|
type Service = ScopeService;
|
||||||
type Future = ScopeFactoryResponse;
|
type InitError = ();
|
||||||
|
type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
|
||||||
|
|
||||||
fn new_service(&self, _: ()) -> Self::Future {
|
fn new_service(&self, _: ()) -> Self::Future {
|
||||||
let default_fut = if let Some(ref default) = *self.default.borrow() {
|
// construct default service factory future
|
||||||
Some(default.new_service(()))
|
let default_fut = self.default.new_service(());
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeFactoryResponse {
|
// construct all services factory future with it's resource def and guards.
|
||||||
fut: self
|
let factory_fut =
|
||||||
.services
|
join_all(self.services.iter().map(|(path, factory, guards)| {
|
||||||
.iter()
|
let path = path.clone();
|
||||||
.map(|(path, service, guards)| {
|
let guards = guards.borrow_mut().take();
|
||||||
CreateScopeServiceItem::Future(
|
let factory_fut = factory.new_service(());
|
||||||
Some(path.clone()),
|
async move {
|
||||||
guards.borrow_mut().take(),
|
let service = factory_fut.await?;
|
||||||
service.new_service(()),
|
Ok((path, guards, service))
|
||||||
)
|
}
|
||||||
})
|
}));
|
||||||
.collect(),
|
|
||||||
default: None,
|
|
||||||
data: self.data.clone(),
|
|
||||||
default_fut,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create scope service
|
let app_data = self.app_data.clone();
|
||||||
#[doc(hidden)]
|
|
||||||
#[pin_project::pin_project]
|
|
||||||
pub struct ScopeFactoryResponse {
|
|
||||||
fut: Vec<CreateScopeServiceItem>,
|
|
||||||
data: Option<Rc<Extensions>>,
|
|
||||||
default: Option<HttpService>,
|
|
||||||
default_fut: Option<LocalBoxFuture<'static, Result<HttpService, ()>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
type HttpServiceFut = LocalBoxFuture<'static, Result<HttpService, ()>>;
|
Box::pin(async move {
|
||||||
|
let default = default_fut.await?;
|
||||||
|
|
||||||
enum CreateScopeServiceItem {
|
// build router from the factory future result.
|
||||||
Future(Option<ResourceDef>, Option<Guards>, HttpServiceFut),
|
let router = factory_fut
|
||||||
Service(ResourceDef, Option<Guards>, HttpService),
|
.await
|
||||||
}
|
.into_iter()
|
||||||
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
impl Future for ScopeFactoryResponse {
|
|
||||||
type Output = Result<ScopeService, ()>;
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let mut done = true;
|
|
||||||
|
|
||||||
if let Some(ref mut fut) = self.default_fut {
|
|
||||||
match Pin::new(fut).poll(cx)? {
|
|
||||||
Poll::Ready(default) => self.default = Some(default),
|
|
||||||
Poll::Pending => done = false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// poll http services
|
|
||||||
for item in &mut self.fut {
|
|
||||||
let res = match item {
|
|
||||||
CreateScopeServiceItem::Future(
|
|
||||||
ref mut path,
|
|
||||||
ref mut guards,
|
|
||||||
ref mut fut,
|
|
||||||
) => match Pin::new(fut).poll(cx)? {
|
|
||||||
Poll::Ready(service) => {
|
|
||||||
Some((path.take().unwrap(), guards.take(), service))
|
|
||||||
}
|
|
||||||
Poll::Pending => {
|
|
||||||
done = false;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
CreateScopeServiceItem::Service(_, _, _) => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some((path, guards, service)) = res {
|
|
||||||
*item = CreateScopeServiceItem::Service(path, guards, service);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if done {
|
|
||||||
let router = self
|
|
||||||
.fut
|
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.fold(Router::build(), |mut router, item| {
|
.fold(Router::build(), |mut router, (path, guards, service)| {
|
||||||
match item {
|
router.rdef(path, service).2 = guards;
|
||||||
CreateScopeServiceItem::Service(path, guards, service) => {
|
|
||||||
router.rdef(path, service).2 = guards;
|
|
||||||
}
|
|
||||||
CreateScopeServiceItem::Future(_, _, _) => unreachable!(),
|
|
||||||
}
|
|
||||||
router
|
router
|
||||||
});
|
})
|
||||||
Poll::Ready(Ok(ScopeService {
|
.finish();
|
||||||
data: self.data.clone(),
|
|
||||||
router: router.finish(),
|
Ok(ScopeService {
|
||||||
default: self.default.take(),
|
app_data,
|
||||||
_ready: None,
|
router,
|
||||||
}))
|
default,
|
||||||
} else {
|
})
|
||||||
Poll::Pending
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ScopeService {
|
pub struct ScopeService {
|
||||||
data: Option<Rc<Extensions>>,
|
app_data: Option<Rc<Extensions>>,
|
||||||
router: Router<HttpService, Vec<Box<dyn Guard>>>,
|
router: Router<HttpService, Vec<Box<dyn Guard>>>,
|
||||||
default: Option<HttpService>,
|
default: HttpService,
|
||||||
_ready: Option<(ServiceRequest, ResourceInfo)>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Service<ServiceRequest> for ScopeService {
|
impl Service<ServiceRequest> for ScopeService {
|
||||||
|
@ -606,9 +538,7 @@ impl Service<ServiceRequest> for ScopeService {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
actix_service::always_ready!();
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
|
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
|
||||||
let res = self.router.recognize_mut_checked(&mut req, |req, guards| {
|
let res = self.router.recognize_mut_checked(&mut req, |req, guards| {
|
||||||
|
@ -622,21 +552,14 @@ impl Service<ServiceRequest> for ScopeService {
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if let Some(ref app_data) = self.app_data {
|
||||||
|
req.add_data_container(app_data.clone());
|
||||||
|
}
|
||||||
|
|
||||||
if let Some((srv, _info)) = res {
|
if let Some((srv, _info)) = res {
|
||||||
if let Some(ref data) = self.data {
|
|
||||||
req.add_data_container(data.clone());
|
|
||||||
}
|
|
||||||
srv.call(req)
|
srv.call(req)
|
||||||
} else if let Some(ref mut default) = self.default {
|
|
||||||
if let Some(ref data) = self.data {
|
|
||||||
req.add_data_container(data.clone());
|
|
||||||
}
|
|
||||||
default.call(req)
|
|
||||||
} else {
|
} else {
|
||||||
let req = req.into_parts().0;
|
self.default.call(req)
|
||||||
Box::pin(async {
|
|
||||||
Ok(ServiceResponse::new(req, Response::NotFound().finish()))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -658,7 +581,7 @@ impl ServiceFactory<ServiceRequest> for ScopeEndpoint {
|
||||||
type Config = ();
|
type Config = ();
|
||||||
type Service = ScopeService;
|
type Service = ScopeService;
|
||||||
type InitError = ();
|
type InitError = ();
|
||||||
type Future = ScopeFactoryResponse;
|
type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
|
||||||
|
|
||||||
fn new_service(&self, _: ()) -> Self::Future {
|
fn new_service(&self, _: ()) -> Self::Future {
|
||||||
self.factory.borrow_mut().as_mut().unwrap().new_service(())
|
self.factory.borrow_mut().as_mut().unwrap().new_service(())
|
||||||
|
|
|
@ -283,11 +283,7 @@ where
|
||||||
lst,
|
lst,
|
||||||
move || {
|
move || {
|
||||||
let c = cfg.lock().unwrap();
|
let c = cfg.lock().unwrap();
|
||||||
let cfg = AppConfig::new(
|
let host = c.host.clone().unwrap_or_else(|| format!("{}", addr));
|
||||||
false,
|
|
||||||
addr,
|
|
||||||
c.host.clone().unwrap_or_else(|| format!("{}", addr)),
|
|
||||||
);
|
|
||||||
|
|
||||||
let svc = HttpService::build()
|
let svc = HttpService::build()
|
||||||
.keep_alive(c.keep_alive)
|
.keep_alive(c.keep_alive)
|
||||||
|
@ -302,8 +298,10 @@ where
|
||||||
svc
|
svc
|
||||||
};
|
};
|
||||||
|
|
||||||
svc.finish(map_config(factory(), move |_| cfg.clone()))
|
svc.finish(map_config(factory(), move |_| {
|
||||||
.tcp()
|
AppConfig::new(false, addr, host.clone())
|
||||||
|
}))
|
||||||
|
.tcp()
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
|
@ -342,11 +340,7 @@ where
|
||||||
lst,
|
lst,
|
||||||
move || {
|
move || {
|
||||||
let c = cfg.lock().unwrap();
|
let c = cfg.lock().unwrap();
|
||||||
let cfg = AppConfig::new(
|
let host = c.host.clone().unwrap_or_else(|| format!("{}", addr));
|
||||||
true,
|
|
||||||
addr,
|
|
||||||
c.host.clone().unwrap_or_else(|| format!("{}", addr)),
|
|
||||||
);
|
|
||||||
|
|
||||||
let svc = HttpService::build()
|
let svc = HttpService::build()
|
||||||
.keep_alive(c.keep_alive)
|
.keep_alive(c.keep_alive)
|
||||||
|
@ -361,8 +355,10 @@ where
|
||||||
svc
|
svc
|
||||||
};
|
};
|
||||||
|
|
||||||
svc.finish(map_config(factory(), move |_| cfg.clone()))
|
svc.finish(map_config(factory(), move |_| {
|
||||||
.openssl(acceptor.clone())
|
AppConfig::new(true, addr, host.clone())
|
||||||
|
}))
|
||||||
|
.openssl(acceptor.clone())
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
|
@ -401,11 +397,7 @@ where
|
||||||
lst,
|
lst,
|
||||||
move || {
|
move || {
|
||||||
let c = cfg.lock().unwrap();
|
let c = cfg.lock().unwrap();
|
||||||
let cfg = AppConfig::new(
|
let host = c.host.clone().unwrap_or_else(|| format!("{}", addr));
|
||||||
true,
|
|
||||||
addr,
|
|
||||||
c.host.clone().unwrap_or_else(|| format!("{}", addr)),
|
|
||||||
);
|
|
||||||
|
|
||||||
let svc = HttpService::build()
|
let svc = HttpService::build()
|
||||||
.keep_alive(c.keep_alive)
|
.keep_alive(c.keep_alive)
|
||||||
|
@ -420,8 +412,10 @@ where
|
||||||
svc
|
svc
|
||||||
};
|
};
|
||||||
|
|
||||||
svc.finish(map_config(factory(), move |_| cfg.clone()))
|
svc.finish(map_config(factory(), move |_| {
|
||||||
.rustls(config.clone())
|
AppConfig::new(true, addr, host.clone())
|
||||||
|
}))
|
||||||
|
.rustls(config.clone())
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
Ok(self)
|
Ok(self)
|
||||||
|
|
|
@ -52,75 +52,61 @@ where
|
||||||
/// An service http request
|
/// An service http request
|
||||||
///
|
///
|
||||||
/// ServiceRequest allows mutable access to request's internal structures
|
/// ServiceRequest allows mutable access to request's internal structures
|
||||||
pub struct ServiceRequest(HttpRequest);
|
pub struct ServiceRequest {
|
||||||
|
req: HttpRequest,
|
||||||
|
payload: Payload,
|
||||||
|
}
|
||||||
|
|
||||||
impl ServiceRequest {
|
impl ServiceRequest {
|
||||||
/// Construct service request
|
/// Construct service request
|
||||||
pub(crate) fn new(req: HttpRequest) -> Self {
|
pub(crate) fn new(req: HttpRequest, payload: Payload) -> Self {
|
||||||
ServiceRequest(req)
|
Self { req, payload }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deconstruct request into parts
|
/// Deconstruct request into parts
|
||||||
pub fn into_parts(mut self) -> (HttpRequest, Payload) {
|
#[inline]
|
||||||
let pl = Rc::get_mut(&mut (self.0).inner).unwrap().payload.take();
|
pub fn into_parts(self) -> (HttpRequest, Payload) {
|
||||||
(self.0, pl)
|
(self.req, self.payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct request from parts.
|
/// Construct request from parts.
|
||||||
///
|
pub fn from_parts(req: HttpRequest, payload: Payload) -> Self {
|
||||||
/// `ServiceRequest` can be re-constructed only if `req` hasn't been cloned.
|
Self { req, payload }
|
||||||
pub fn from_parts(
|
|
||||||
mut req: HttpRequest,
|
|
||||||
pl: Payload,
|
|
||||||
) -> Result<Self, (HttpRequest, Payload)> {
|
|
||||||
match Rc::get_mut(&mut req.inner) {
|
|
||||||
Some(p) => {
|
|
||||||
p.payload = pl;
|
|
||||||
Ok(ServiceRequest(req))
|
|
||||||
}
|
|
||||||
None => Err((req, pl)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct request from request.
|
/// Construct request from request.
|
||||||
///
|
///
|
||||||
/// `HttpRequest` implements `Clone` trait via `Rc` type. `ServiceRequest`
|
/// The returned `ServiceRequest` would have no payload.
|
||||||
/// can be re-constructed only if rc's strong pointers count eq 1 and
|
pub fn from_request(req: HttpRequest) -> Self {
|
||||||
/// weak pointers count is 0.
|
ServiceRequest {
|
||||||
pub fn from_request(req: HttpRequest) -> Result<Self, HttpRequest> {
|
req,
|
||||||
// There is no weak pointer used on HttpRequest so intentionally
|
payload: Payload::None,
|
||||||
// ignore the check.
|
|
||||||
if Rc::strong_count(&req.inner) == 1 {
|
|
||||||
debug_assert!(Rc::weak_count(&req.inner) == 0);
|
|
||||||
Ok(ServiceRequest(req))
|
|
||||||
} else {
|
|
||||||
Err(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create service response
|
/// Create service response
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_response<B, R: Into<Response<B>>>(self, res: R) -> ServiceResponse<B> {
|
pub fn into_response<B, R: Into<Response<B>>>(self, res: R) -> ServiceResponse<B> {
|
||||||
ServiceResponse::new(self.0, res.into())
|
ServiceResponse::new(self.req, res.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create service response for error
|
/// Create service response for error
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn error_response<B, E: Into<Error>>(self, err: E) -> ServiceResponse<B> {
|
pub fn error_response<B, E: Into<Error>>(self, err: E) -> ServiceResponse<B> {
|
||||||
let res: Response = err.into().into();
|
let res: Response = err.into().into();
|
||||||
ServiceResponse::new(self.0, res.into_body())
|
ServiceResponse::new(self.req, res.into_body())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method returns reference to the request head
|
/// This method returns reference to the request head
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn head(&self) -> &RequestHead {
|
pub fn head(&self) -> &RequestHead {
|
||||||
&self.0.head()
|
&self.req.head()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method returns reference to the request head
|
/// This method returns reference to the request head
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn head_mut(&mut self) -> &mut RequestHead {
|
pub fn head_mut(&mut self) -> &mut RequestHead {
|
||||||
self.0.head_mut()
|
self.req.head_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request's uri.
|
/// Request's uri.
|
||||||
|
@ -196,42 +182,42 @@ impl ServiceRequest {
|
||||||
/// access the matched value for that segment.
|
/// access the matched value for that segment.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn match_info(&self) -> &Path<Url> {
|
pub fn match_info(&self) -> &Path<Url> {
|
||||||
self.0.match_info()
|
self.req.match_info()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Counterpart to [`HttpRequest::match_name`](super::HttpRequest::match_name()).
|
/// Counterpart to [`HttpRequest::match_name`](super::HttpRequest::match_name()).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn match_name(&self) -> Option<&str> {
|
pub fn match_name(&self) -> Option<&str> {
|
||||||
self.0.match_name()
|
self.req.match_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Counterpart to [`HttpRequest::match_pattern`](super::HttpRequest::match_pattern()).
|
/// Counterpart to [`HttpRequest::match_pattern`](super::HttpRequest::match_pattern()).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn match_pattern(&self) -> Option<String> {
|
pub fn match_pattern(&self) -> Option<String> {
|
||||||
self.0.match_pattern()
|
self.req.match_pattern()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Get a mutable reference to the Path parameters.
|
/// Get a mutable reference to the Path parameters.
|
||||||
pub fn match_info_mut(&mut self) -> &mut Path<Url> {
|
pub fn match_info_mut(&mut self) -> &mut Path<Url> {
|
||||||
self.0.match_info_mut()
|
self.req.match_info_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Get a reference to a `ResourceMap` of current application.
|
/// Get a reference to a `ResourceMap` of current application.
|
||||||
pub fn resource_map(&self) -> &ResourceMap {
|
pub fn resource_map(&self) -> &ResourceMap {
|
||||||
self.0.resource_map()
|
self.req.resource_map()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Service configuration
|
/// Service configuration
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn app_config(&self) -> &AppConfig {
|
pub fn app_config(&self) -> &AppConfig {
|
||||||
self.0.app_config()
|
self.req.app_config()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Counterpart to [`HttpRequest::app_data`](super::HttpRequest::app_data()).
|
/// Counterpart to [`HttpRequest::app_data`](super::HttpRequest::app_data()).
|
||||||
pub fn app_data<T: 'static>(&self) -> Option<&T> {
|
pub fn app_data<T: 'static>(&self) -> Option<&T> {
|
||||||
for container in (self.0).inner.app_data.iter().rev() {
|
for container in self.req.inner.app_data.iter().rev() {
|
||||||
if let Some(data) = container.get::<T>() {
|
if let Some(data) = container.get::<T>() {
|
||||||
return Some(data);
|
return Some(data);
|
||||||
}
|
}
|
||||||
|
@ -242,13 +228,13 @@ impl ServiceRequest {
|
||||||
|
|
||||||
/// Set request payload.
|
/// Set request payload.
|
||||||
pub fn set_payload(&mut self, payload: Payload) {
|
pub fn set_payload(&mut self, payload: Payload) {
|
||||||
Rc::get_mut(&mut (self.0).inner).unwrap().payload = payload;
|
self.payload = payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
/// Add app data container to request's resolution set.
|
/// Add app data container to request's resolution set.
|
||||||
pub fn add_data_container(&mut self, extensions: Rc<Extensions>) {
|
pub fn add_data_container(&mut self, extensions: Rc<Extensions>) {
|
||||||
Rc::get_mut(&mut (self.0).inner)
|
Rc::get_mut(&mut (self.req).inner)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.app_data
|
.app_data
|
||||||
.push(extensions);
|
.push(extensions);
|
||||||
|
@ -273,18 +259,18 @@ impl HttpMessage for ServiceRequest {
|
||||||
/// Request extensions
|
/// Request extensions
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extensions(&self) -> Ref<'_, Extensions> {
|
fn extensions(&self) -> Ref<'_, Extensions> {
|
||||||
self.0.extensions()
|
self.req.extensions()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutable reference to a the request's extensions
|
/// Mutable reference to a the request's extensions
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extensions_mut(&self) -> RefMut<'_, Extensions> {
|
fn extensions_mut(&self) -> RefMut<'_, Extensions> {
|
||||||
self.0.extensions_mut()
|
self.req.extensions_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn take_payload(&mut self) -> Payload<Self::Stream> {
|
fn take_payload(&mut self) -> Payload<Self::Stream> {
|
||||||
Rc::get_mut(&mut (self.0).inner).unwrap().payload.take()
|
self.payload.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,27 +538,6 @@ mod tests {
|
||||||
use actix_service::Service;
|
use actix_service::Service;
|
||||||
use futures_util::future::ok;
|
use futures_util::future::ok;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_service_request() {
|
|
||||||
let req = TestRequest::default().to_srv_request();
|
|
||||||
let (r, pl) = req.into_parts();
|
|
||||||
assert!(ServiceRequest::from_parts(r, pl).is_ok());
|
|
||||||
|
|
||||||
let req = TestRequest::default().to_srv_request();
|
|
||||||
let (r, pl) = req.into_parts();
|
|
||||||
let _r2 = r.clone();
|
|
||||||
assert!(ServiceRequest::from_parts(r, pl).is_err());
|
|
||||||
|
|
||||||
let req = TestRequest::default().to_srv_request();
|
|
||||||
let (r, _pl) = req.into_parts();
|
|
||||||
assert!(ServiceRequest::from_request(r).is_ok());
|
|
||||||
|
|
||||||
let req = TestRequest::default().to_srv_request();
|
|
||||||
let (r, _pl) = req.into_parts();
|
|
||||||
let _r2 = r.clone();
|
|
||||||
assert!(ServiceRequest::from_request(r).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_service() {
|
async fn test_service() {
|
||||||
let mut srv = init_service(
|
let mut srv = init_service(
|
||||||
|
|
44
src/test.rs
44
src/test.rs
|
@ -27,10 +27,10 @@ use socket2::{Domain, Protocol, Socket, Type};
|
||||||
|
|
||||||
pub use actix_http::test::TestBuffer;
|
pub use actix_http::test::TestBuffer;
|
||||||
|
|
||||||
|
use crate::app_service::AppInitServiceState;
|
||||||
use crate::config::AppConfig;
|
use crate::config::AppConfig;
|
||||||
use crate::data::Data;
|
use crate::data::Data;
|
||||||
use crate::dev::{Body, MessageBody, Payload, Server};
|
use crate::dev::{Body, MessageBody, Payload, Server};
|
||||||
use crate::request::HttpRequestPool;
|
|
||||||
use crate::rmap::ResourceMap;
|
use crate::rmap::ResourceMap;
|
||||||
use crate::service::{ServiceRequest, ServiceResponse};
|
use crate::service::{ServiceRequest, ServiceResponse};
|
||||||
use crate::{Error, HttpRequest, HttpResponse};
|
use crate::{Error, HttpRequest, HttpResponse};
|
||||||
|
@ -542,15 +542,13 @@ impl TestRequest {
|
||||||
head.peer_addr = self.peer_addr;
|
head.peer_addr = self.peer_addr;
|
||||||
self.path.get_mut().update(&head.uri);
|
self.path.get_mut().update(&head.uri);
|
||||||
|
|
||||||
ServiceRequest::new(HttpRequest::new(
|
let app_state =
|
||||||
self.path,
|
AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());
|
||||||
head,
|
|
||||||
|
ServiceRequest::new(
|
||||||
|
HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data)),
|
||||||
payload,
|
payload,
|
||||||
Rc::new(self.rmap),
|
)
|
||||||
self.config.clone(),
|
|
||||||
Rc::new(self.app_data),
|
|
||||||
HttpRequestPool::create(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete request creation and generate `ServiceResponse` instance
|
/// Complete request creation and generate `ServiceResponse` instance
|
||||||
|
@ -560,19 +558,14 @@ impl TestRequest {
|
||||||
|
|
||||||
/// Complete request creation and generate `HttpRequest` instance
|
/// Complete request creation and generate `HttpRequest` instance
|
||||||
pub fn to_http_request(mut self) -> HttpRequest {
|
pub fn to_http_request(mut self) -> HttpRequest {
|
||||||
let (mut head, payload) = self.req.finish().into_parts();
|
let (mut head, _) = self.req.finish().into_parts();
|
||||||
head.peer_addr = self.peer_addr;
|
head.peer_addr = self.peer_addr;
|
||||||
self.path.get_mut().update(&head.uri);
|
self.path.get_mut().update(&head.uri);
|
||||||
|
|
||||||
HttpRequest::new(
|
let app_state =
|
||||||
self.path,
|
AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());
|
||||||
head,
|
|
||||||
payload,
|
HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data))
|
||||||
Rc::new(self.rmap),
|
|
||||||
self.config.clone(),
|
|
||||||
Rc::new(self.app_data),
|
|
||||||
HttpRequestPool::create(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete request creation and generate `HttpRequest` and `Payload` instances
|
/// Complete request creation and generate `HttpRequest` and `Payload` instances
|
||||||
|
@ -581,15 +574,10 @@ impl TestRequest {
|
||||||
head.peer_addr = self.peer_addr;
|
head.peer_addr = self.peer_addr;
|
||||||
self.path.get_mut().update(&head.uri);
|
self.path.get_mut().update(&head.uri);
|
||||||
|
|
||||||
let req = HttpRequest::new(
|
let app_state =
|
||||||
self.path,
|
AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());
|
||||||
head,
|
|
||||||
Payload::None,
|
let req = HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data));
|
||||||
Rc::new(self.rmap),
|
|
||||||
self.config.clone(),
|
|
||||||
Rc::new(self.app_data),
|
|
||||||
HttpRequestPool::create(),
|
|
||||||
);
|
|
||||||
|
|
||||||
(req, payload)
|
(req, payload)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue