mirror of
https://github.com/actix/actix-web.git
synced 2024-11-22 17:41:11 +00:00
add connection level data container (#2491)
This commit is contained in:
parent
069cf2da07
commit
d35b7644dc
18 changed files with 152 additions and 125 deletions
|
@ -7,6 +7,7 @@
|
|||
* `Range` typed header. [#2485]
|
||||
* `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]
|
||||
* 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
|
||||
* Rename `Accept::{mime_precedence => ranked}`. [#2480]
|
||||
|
@ -27,6 +28,7 @@
|
|||
[#2482]: https://github.com/actix/actix-web/pull/2482
|
||||
[#2484]: https://github.com/actix/actix-web/pull/2484
|
||||
[#2485]: https://github.com/actix/actix-web/pull/2485
|
||||
[#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
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
* `header::QualityItem::{max, min}`. [#2486]
|
||||
* `header::Quality::{MAX, MIN}`. [#2486]
|
||||
* `impl Display` for `header::Quality`. [#2486]
|
||||
* Connection data set through the `on_connect_ext` callbacks is now accessible only from the new `Request::conn_data()` method. [#2491]
|
||||
* `Request::take_conn_data()`. [#2491]
|
||||
|
||||
### Changed
|
||||
* Rename `body::BoxBody::{from_body => new}`. [#2468]
|
||||
|
@ -39,6 +41,7 @@
|
|||
[#1920]: https://github.com/actix/actix-web/pull/1920
|
||||
[#2486]: https://github.com/actix/actix-web/pull/2486
|
||||
[#2488]: https://github.com/actix/actix-web/pull/2488
|
||||
[#2491]: https://github.com/actix/actix-web/pull/2491
|
||||
|
||||
|
||||
## 3.0.0-beta.14 - 2021-11-30
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{
|
||||
any::{Any, TypeId},
|
||||
fmt, mem,
|
||||
fmt,
|
||||
};
|
||||
|
||||
use ahash::AHashMap;
|
||||
|
@ -10,8 +10,7 @@ use ahash::AHashMap;
|
|||
/// All entries into this map must be owned types (or static references).
|
||||
#[derive(Default)]
|
||||
pub struct Extensions {
|
||||
/// Use FxHasher with a std HashMap with for faster
|
||||
/// lookups on the small `TypeId` (u64 equivalent) keys.
|
||||
/// Use AHasher with a std HashMap with for faster lookups on the small `TypeId` keys.
|
||||
map: AHashMap<TypeId, Box<dyn Any>>,
|
||||
}
|
||||
|
||||
|
@ -123,11 +122,6 @@ impl Extensions {
|
|||
pub fn extend(&mut self, other: Extensions) {
|
||||
self.map.extend(other.map);
|
||||
}
|
||||
|
||||
/// Sets (or overrides) items from `other` into this map.
|
||||
pub(crate) fn drain_from(&mut self, other: &mut Self) {
|
||||
self.map.extend(mem::take(&mut other.map));
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Extensions {
|
||||
|
@ -179,6 +173,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_integers() {
|
||||
static A: u32 = 8;
|
||||
|
||||
let mut map = Extensions::new();
|
||||
|
||||
map.insert::<i8>(8);
|
||||
|
@ -191,6 +187,7 @@ mod tests {
|
|||
map.insert::<u32>(32);
|
||||
map.insert::<u64>(64);
|
||||
map.insert::<u128>(128);
|
||||
map.insert::<&'static u32>(&A);
|
||||
assert!(map.get::<i8>().is_some());
|
||||
assert!(map.get::<i16>().is_some());
|
||||
assert!(map.get::<i32>().is_some());
|
||||
|
@ -201,6 +198,7 @@ mod tests {
|
|||
assert!(map.get::<u32>().is_some());
|
||||
assert!(map.get::<u64>().is_some());
|
||||
assert!(map.get::<u128>().is_some());
|
||||
assert!(map.get::<&'static u32>().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -279,27 +277,4 @@ mod tests {
|
|||
assert_eq!(extensions.get(), Some(&20u8));
|
||||
assert_eq!(extensions.get_mut(), Some(&mut 20u8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drain_from() {
|
||||
let mut ext = Extensions::new();
|
||||
ext.insert(2isize);
|
||||
|
||||
let mut more_ext = Extensions::new();
|
||||
|
||||
more_ext.insert(5isize);
|
||||
more_ext.insert(5usize);
|
||||
|
||||
assert_eq!(ext.get::<isize>(), Some(&2isize));
|
||||
assert_eq!(ext.get::<usize>(), None);
|
||||
assert_eq!(more_ext.get::<isize>(), Some(&5isize));
|
||||
assert_eq!(more_ext.get::<usize>(), Some(&5usize));
|
||||
|
||||
ext.drain_from(&mut more_ext);
|
||||
|
||||
assert_eq!(ext.get::<isize>(), Some(&5isize));
|
||||
assert_eq!(ext.get::<usize>(), Some(&5usize));
|
||||
assert_eq!(more_ext.get::<isize>(), None);
|
||||
assert_eq!(more_ext.get::<usize>(), None);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ use crate::{
|
|||
config::ServiceConfig,
|
||||
error::{DispatchError, ParseError, PayloadError},
|
||||
service::HttpFlow,
|
||||
OnConnectData, Request, Response, StatusCode,
|
||||
Extensions, OnConnectData, Request, Response, StatusCode,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
@ -100,9 +100,9 @@ where
|
|||
U::Error: fmt::Display,
|
||||
{
|
||||
flow: Rc<HttpFlow<S, X, U>>,
|
||||
on_connect_data: OnConnectData,
|
||||
flags: Flags,
|
||||
peer_addr: Option<net::SocketAddr>,
|
||||
conn_data: Option<Rc<Extensions>>,
|
||||
error: Option<DispatchError>,
|
||||
|
||||
#[pin]
|
||||
|
@ -179,10 +179,10 @@ where
|
|||
/// Create HTTP/1 dispatcher.
|
||||
pub(crate) fn new(
|
||||
io: T,
|
||||
config: ServiceConfig,
|
||||
flow: Rc<HttpFlow<S, X, U>>,
|
||||
on_connect_data: OnConnectData,
|
||||
config: ServiceConfig,
|
||||
peer_addr: Option<net::SocketAddr>,
|
||||
conn_data: OnConnectData,
|
||||
) -> Self {
|
||||
let flags = if config.keep_alive_enabled() {
|
||||
Flags::KEEPALIVE
|
||||
|
@ -198,20 +198,23 @@ where
|
|||
|
||||
Dispatcher {
|
||||
inner: DispatcherState::Normal(InnerDispatcher {
|
||||
read_buf: BytesMut::with_capacity(HW_BUFFER_SIZE),
|
||||
write_buf: BytesMut::with_capacity(HW_BUFFER_SIZE),
|
||||
payload: None,
|
||||
state: State::None,
|
||||
error: None,
|
||||
messages: VecDeque::new(),
|
||||
io: Some(io),
|
||||
codec: Codec::new(config),
|
||||
flow,
|
||||
on_connect_data,
|
||||
flags,
|
||||
peer_addr,
|
||||
conn_data: conn_data.0.map(Rc::new),
|
||||
error: None,
|
||||
|
||||
state: State::None,
|
||||
payload: None,
|
||||
messages: VecDeque::new(),
|
||||
|
||||
ka_expire,
|
||||
ka_timer,
|
||||
|
||||
io: Some(io),
|
||||
read_buf: BytesMut::with_capacity(HW_BUFFER_SIZE),
|
||||
write_buf: BytesMut::with_capacity(HW_BUFFER_SIZE),
|
||||
codec: Codec::new(config),
|
||||
}),
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -593,8 +596,7 @@ where
|
|||
Message::Item(mut req) => {
|
||||
req.head_mut().peer_addr = *this.peer_addr;
|
||||
|
||||
// merge on_connect_ext data into request extensions
|
||||
this.on_connect_data.merge_into(&mut req);
|
||||
req.conn_data = this.conn_data.as_ref().map(Rc::clone);
|
||||
|
||||
match this.codec.message_type() {
|
||||
// Request is upgradable. add upgrade message and break.
|
||||
|
@ -1100,10 +1102,10 @@ mod tests {
|
|||
|
||||
let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new(
|
||||
buf,
|
||||
ServiceConfig::default(),
|
||||
services,
|
||||
OnConnectData::default(),
|
||||
ServiceConfig::default(),
|
||||
None,
|
||||
OnConnectData::default(),
|
||||
);
|
||||
|
||||
actix_rt::pin!(h1);
|
||||
|
@ -1140,10 +1142,10 @@ mod tests {
|
|||
|
||||
let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new(
|
||||
buf,
|
||||
cfg,
|
||||
services,
|
||||
OnConnectData::default(),
|
||||
cfg,
|
||||
None,
|
||||
OnConnectData::default(),
|
||||
);
|
||||
|
||||
actix_rt::pin!(h1);
|
||||
|
@ -1194,10 +1196,10 @@ mod tests {
|
|||
|
||||
let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new(
|
||||
buf,
|
||||
cfg,
|
||||
services,
|
||||
OnConnectData::default(),
|
||||
cfg,
|
||||
None,
|
||||
OnConnectData::default(),
|
||||
);
|
||||
|
||||
actix_rt::pin!(h1);
|
||||
|
@ -1244,10 +1246,10 @@ mod tests {
|
|||
|
||||
let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new(
|
||||
buf.clone(),
|
||||
cfg,
|
||||
services,
|
||||
OnConnectData::default(),
|
||||
cfg,
|
||||
None,
|
||||
OnConnectData::default(),
|
||||
);
|
||||
|
||||
buf.extend_read_buf(
|
||||
|
@ -1316,10 +1318,10 @@ mod tests {
|
|||
|
||||
let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new(
|
||||
buf.clone(),
|
||||
cfg,
|
||||
services,
|
||||
OnConnectData::default(),
|
||||
cfg,
|
||||
None,
|
||||
OnConnectData::default(),
|
||||
);
|
||||
|
||||
buf.extend_read_buf(
|
||||
|
@ -1393,10 +1395,10 @@ mod tests {
|
|||
|
||||
let h1 = Dispatcher::<_, _, _, _, TestUpgrade>::new(
|
||||
buf.clone(),
|
||||
cfg,
|
||||
services,
|
||||
OnConnectData::default(),
|
||||
cfg,
|
||||
None,
|
||||
OnConnectData::default(),
|
||||
);
|
||||
|
||||
buf.extend_read_buf(
|
||||
|
|
|
@ -365,15 +365,7 @@ where
|
|||
}
|
||||
|
||||
fn call(&self, (io, addr): (T, Option<net::SocketAddr>)) -> Self::Future {
|
||||
let on_connect_data =
|
||||
OnConnectData::from_io(&io, self.on_connect_ext.as_deref());
|
||||
|
||||
Dispatcher::new(
|
||||
io,
|
||||
self.cfg.clone(),
|
||||
self.flow.clone(),
|
||||
on_connect_data,
|
||||
addr,
|
||||
)
|
||||
let conn_data = OnConnectData::from_io(&io, self.on_connect_ext.as_deref());
|
||||
Dispatcher::new(io, self.flow.clone(), self.cfg.clone(), addr, conn_data)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ use crate::{
|
|||
body::{BodySize, BoxBody, MessageBody},
|
||||
config::ServiceConfig,
|
||||
service::HttpFlow,
|
||||
OnConnectData, Payload, Request, Response, ResponseHead,
|
||||
Extensions, OnConnectData, Payload, Request, Response, ResponseHead,
|
||||
};
|
||||
|
||||
const CHUNK_SIZE: usize = 16_384;
|
||||
|
@ -37,7 +37,7 @@ pin_project! {
|
|||
pub struct Dispatcher<T, S, B, X, U> {
|
||||
flow: Rc<HttpFlow<S, X, U>>,
|
||||
connection: Connection<T, Bytes>,
|
||||
on_connect_data: OnConnectData,
|
||||
conn_data: Option<Rc<Extensions>>,
|
||||
config: ServiceConfig,
|
||||
peer_addr: Option<net::SocketAddr>,
|
||||
ping_pong: Option<H2PingPong>,
|
||||
|
@ -50,11 +50,11 @@ where
|
|||
T: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
pub(crate) fn new(
|
||||
flow: Rc<HttpFlow<S, X, U>>,
|
||||
mut conn: Connection<T, Bytes>,
|
||||
on_connect_data: OnConnectData,
|
||||
flow: Rc<HttpFlow<S, X, U>>,
|
||||
config: ServiceConfig,
|
||||
peer_addr: Option<net::SocketAddr>,
|
||||
conn_data: OnConnectData,
|
||||
timer: Option<Pin<Box<Sleep>>>,
|
||||
) -> Self {
|
||||
let ping_pong = config.keep_alive().map(|dur| H2PingPong {
|
||||
|
@ -74,7 +74,7 @@ where
|
|||
config,
|
||||
peer_addr,
|
||||
connection: conn,
|
||||
on_connect_data,
|
||||
conn_data: conn_data.0.map(Rc::new),
|
||||
ping_pong,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
|
@ -119,8 +119,7 @@ where
|
|||
head.headers = parts.headers.into();
|
||||
head.peer_addr = this.peer_addr;
|
||||
|
||||
// merge on_connect_ext data into request extensions
|
||||
this.on_connect_data.merge_into(&mut req);
|
||||
req.conn_data = this.conn_data.as_ref().map(Rc::clone);
|
||||
|
||||
let fut = this.flow.service.call(req);
|
||||
let config = this.config.clone();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::{
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
net,
|
||||
mem, net,
|
||||
pin::Pin,
|
||||
rc::Rc,
|
||||
task::{Context, Poll},
|
||||
|
@ -339,21 +339,24 @@ where
|
|||
ref mut srv,
|
||||
ref mut config,
|
||||
ref peer_addr,
|
||||
ref mut on_connect_data,
|
||||
ref mut conn_data,
|
||||
ref mut handshake,
|
||||
) => match ready!(Pin::new(handshake).poll(cx)) {
|
||||
Ok((conn, timer)) => {
|
||||
let on_connect_data = std::mem::take(on_connect_data);
|
||||
let on_connect_data = mem::take(conn_data);
|
||||
|
||||
self.state = State::Incoming(Dispatcher::new(
|
||||
srv.take().unwrap(),
|
||||
conn,
|
||||
on_connect_data,
|
||||
srv.take().unwrap(),
|
||||
config.take().unwrap(),
|
||||
*peer_addr,
|
||||
on_connect_data,
|
||||
timer,
|
||||
));
|
||||
|
||||
self.poll(cx)
|
||||
}
|
||||
|
||||
Err(err) => {
|
||||
trace!("H2 handshake error: {}", err);
|
||||
Poll::Ready(Err(err))
|
||||
|
|
|
@ -92,19 +92,11 @@ impl OnConnectData {
|
|||
on_connect_ext: Option<&ConnectCallback<T>>,
|
||||
) -> Self {
|
||||
let ext = on_connect_ext.map(|handler| {
|
||||
let mut extensions = Extensions::new();
|
||||
let mut extensions = Extensions::default();
|
||||
handler(io, &mut extensions);
|
||||
extensions
|
||||
});
|
||||
|
||||
Self(ext)
|
||||
}
|
||||
|
||||
/// Merge self into given request's extensions.
|
||||
#[inline]
|
||||
pub(crate) fn merge_into(&mut self, req: &mut Request) {
|
||||
if let Some(ref mut ext) = self.0 {
|
||||
req.head.extensions.get_mut().drain_from(ext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
use std::{
|
||||
cell::{Ref, RefMut},
|
||||
fmt, net, str,
|
||||
fmt, net,
|
||||
rc::Rc,
|
||||
str,
|
||||
};
|
||||
|
||||
use http::{header, Method, Uri, Version};
|
||||
|
@ -19,6 +21,7 @@ use crate::{
|
|||
pub struct Request<P = PayloadStream> {
|
||||
pub(crate) payload: Payload<P>,
|
||||
pub(crate) head: Message<RequestHead>,
|
||||
pub(crate) conn_data: Option<Rc<Extensions>>,
|
||||
}
|
||||
|
||||
impl<P> HttpMessage for Request<P> {
|
||||
|
@ -51,6 +54,7 @@ impl From<Message<RequestHead>> for Request<PayloadStream> {
|
|||
Request {
|
||||
head,
|
||||
payload: Payload::None,
|
||||
conn_data: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +65,7 @@ impl Request<PayloadStream> {
|
|||
Request {
|
||||
head: Message::new(),
|
||||
payload: Payload::None,
|
||||
conn_data: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,16 +76,19 @@ impl<P> Request<P> {
|
|||
Request {
|
||||
payload,
|
||||
head: Message::new(),
|
||||
conn_data: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create new Request instance
|
||||
pub fn replace_payload<P1>(self, payload: Payload<P1>) -> (Request<P1>, Payload<P>) {
|
||||
let pl = self.payload;
|
||||
|
||||
(
|
||||
Request {
|
||||
payload,
|
||||
head: self.head,
|
||||
conn_data: self.conn_data,
|
||||
},
|
||||
pl,
|
||||
)
|
||||
|
@ -170,6 +178,26 @@ impl<P> Request<P> {
|
|||
pub fn peer_addr(&self) -> Option<net::SocketAddr> {
|
||||
self.head().peer_addr
|
||||
}
|
||||
|
||||
/// Returns a reference a piece of connection data set in an [on-connect] callback.
|
||||
///
|
||||
/// ```ignore
|
||||
/// let opt_t = req.conn_data::<PeerCertificate>();
|
||||
/// ```
|
||||
///
|
||||
/// [on-connect]: crate::HttpServiceBuilder::on_connect_ext
|
||||
pub fn conn_data<T: 'static>(&self) -> Option<&T> {
|
||||
self.conn_data
|
||||
.as_deref()
|
||||
.and_then(|container| container.get::<T>())
|
||||
}
|
||||
|
||||
/// Returns the connection data container if an [on-connect] callback was registered.
|
||||
///
|
||||
/// [on-connect]: crate::HttpServiceBuilder::on_connect_ext
|
||||
pub fn take_conn_data(&mut self) -> Option<Rc<Extensions>> {
|
||||
self.conn_data.take()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> fmt::Debug for Request<P> {
|
||||
|
|
|
@ -507,8 +507,7 @@ where
|
|||
&self,
|
||||
(io, proto, peer_addr): (T, Protocol, Option<net::SocketAddr>),
|
||||
) -> Self::Future {
|
||||
let on_connect_data =
|
||||
OnConnectData::from_io(&io, self.on_connect_ext.as_deref());
|
||||
let conn_data = OnConnectData::from_io(&io, self.on_connect_ext.as_deref());
|
||||
|
||||
match proto {
|
||||
Protocol::Http2 => HttpServiceHandlerResponse {
|
||||
|
@ -517,7 +516,7 @@ where
|
|||
h2::handshake_with_timeout(io, &self.cfg),
|
||||
self.cfg.clone(),
|
||||
self.flow.clone(),
|
||||
on_connect_data,
|
||||
conn_data,
|
||||
peer_addr,
|
||||
)),
|
||||
},
|
||||
|
@ -527,10 +526,10 @@ where
|
|||
state: State::H1 {
|
||||
dispatcher: h1::Dispatcher::new(
|
||||
io,
|
||||
self.cfg.clone(),
|
||||
self.flow.clone(),
|
||||
on_connect_data,
|
||||
self.cfg.clone(),
|
||||
peer_addr,
|
||||
conn_data,
|
||||
),
|
||||
},
|
||||
},
|
||||
|
@ -627,17 +626,12 @@ where
|
|||
StateProj::H2Handshake { handshake: data } => {
|
||||
match ready!(Pin::new(&mut data.as_mut().unwrap().0).poll(cx)) {
|
||||
Ok((conn, timer)) => {
|
||||
let (_, config, flow, on_connect_data, peer_addr) =
|
||||
let (_, config, flow, conn_data, peer_addr) =
|
||||
data.take().unwrap();
|
||||
|
||||
self.as_mut().project().state.set(State::H2 {
|
||||
dispatcher: h2::Dispatcher::new(
|
||||
flow,
|
||||
conn,
|
||||
on_connect_data,
|
||||
config,
|
||||
peer_addr,
|
||||
timer,
|
||||
conn, flow, config, peer_addr, conn_data, timer,
|
||||
),
|
||||
});
|
||||
self.poll(cx)
|
||||
|
|
|
@ -8,7 +8,7 @@ use actix_http::{
|
|||
body::{BodyStream, BoxBody, SizedStream},
|
||||
error::PayloadError,
|
||||
header::{self, HeaderValue},
|
||||
Error, HttpMessage, HttpService, Method, Request, Response, StatusCode, Version,
|
||||
Error, HttpService, Method, Request, Response, StatusCode, Version,
|
||||
};
|
||||
use actix_http_test::test_server;
|
||||
use actix_service::{fn_service, ServiceFactoryExt};
|
||||
|
@ -430,7 +430,7 @@ async fn test_h2_on_connect() {
|
|||
data.insert(20isize);
|
||||
})
|
||||
.h2(|req: Request| {
|
||||
assert!(req.extensions().contains::<isize>());
|
||||
assert!(req.conn_data::<isize>().is_some());
|
||||
ok::<_, Infallible>(Response::ok())
|
||||
})
|
||||
.openssl(tls_config())
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::{
|
|||
|
||||
use actix_http::{
|
||||
body::{self, BodyStream, BoxBody, SizedStream},
|
||||
header, Error, HttpMessage, HttpService, KeepAlive, Request, Response, StatusCode,
|
||||
header, Error, HttpService, KeepAlive, Request, Response, StatusCode,
|
||||
};
|
||||
use actix_http_test::test_server;
|
||||
use actix_rt::time::sleep;
|
||||
|
@ -748,7 +748,7 @@ async fn test_h1_on_connect() {
|
|||
data.insert(20isize);
|
||||
})
|
||||
.h1(|req: Request| {
|
||||
assert!(req.extensions().contains::<isize>());
|
||||
assert!(req.conn_data::<isize>().is_some());
|
||||
ok::<_, Infallible>(Response::ok())
|
||||
})
|
||||
.tcp()
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
|
||||
use std::{any::Any, io, net::SocketAddr};
|
||||
|
||||
use actix_web::{dev::Extensions, rt::net::TcpStream, web, App, HttpServer};
|
||||
use actix_web::{
|
||||
dev::Extensions, rt::net::TcpStream, web, App, HttpRequest, HttpResponse, HttpServer,
|
||||
Responder,
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -16,11 +19,16 @@ struct ConnectionInfo {
|
|||
ttl: Option<u32>,
|
||||
}
|
||||
|
||||
async fn route_whoami(conn_info: web::ReqData<ConnectionInfo>) -> String {
|
||||
format!(
|
||||
"Here is some info about your connection:\n\n{:#?}",
|
||||
conn_info
|
||||
)
|
||||
async fn route_whoami(req: HttpRequest) -> impl Responder {
|
||||
match req.conn_data::<ConnectionInfo>() {
|
||||
Some(info) => HttpResponse::Ok().body(format!(
|
||||
"Here is some info about your connection:\n\n{:#?}",
|
||||
info
|
||||
)),
|
||||
None => {
|
||||
HttpResponse::InternalServerError().body("Missing expected request extension data")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_conn_info(connection: &dyn Any, data: &mut Extensions) {
|
||||
|
@ -39,9 +47,12 @@ fn get_conn_info(connection: &dyn Any, data: &mut Extensions) {
|
|||
async fn main() -> io::Result<()> {
|
||||
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
||||
|
||||
let bind = ("127.0.0.1", 8080);
|
||||
log::info!("staring server at http://{}:{}", &bind.0, &bind.1);
|
||||
|
||||
HttpServer::new(|| App::new().default_service(web::to(route_whoami)))
|
||||
.on_connect(get_conn_info)
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.bind(bind)?
|
||||
.workers(1)
|
||||
.run()
|
||||
.await
|
||||
|
|
|
@ -197,7 +197,8 @@ where
|
|||
|
||||
actix_service::forward_ready!(service);
|
||||
|
||||
fn call(&self, req: Request) -> Self::Future {
|
||||
fn call(&self, mut req: Request) -> Self::Future {
|
||||
let conn_data = req.take_conn_data();
|
||||
let (head, payload) = req.into_parts();
|
||||
|
||||
let req = if let Some(mut req) = self.app_state.pool().pop() {
|
||||
|
@ -205,6 +206,7 @@ where
|
|||
inner.path.get_mut().update(&head.uri);
|
||||
inner.path.reset();
|
||||
inner.head = head;
|
||||
inner.conn_data = conn_data;
|
||||
req
|
||||
} else {
|
||||
HttpRequest::new(
|
||||
|
@ -212,6 +214,7 @@ where
|
|||
head,
|
||||
self.app_state.clone(),
|
||||
self.app_data.clone(),
|
||||
conn_data,
|
||||
)
|
||||
};
|
||||
self.service.call(ServiceRequest::new(req, payload))
|
||||
|
|
|
@ -37,6 +37,7 @@ pub(crate) struct HttpRequestInner {
|
|||
pub(crate) head: Message<RequestHead>,
|
||||
pub(crate) path: Path<Url>,
|
||||
pub(crate) app_data: SmallVec<[Rc<Extensions>; 4]>,
|
||||
pub(crate) conn_data: Option<Rc<Extensions>>,
|
||||
app_state: Rc<AppInitServiceState>,
|
||||
}
|
||||
|
||||
|
@ -47,6 +48,7 @@ impl HttpRequest {
|
|||
head: Message<RequestHead>,
|
||||
app_state: Rc<AppInitServiceState>,
|
||||
app_data: Rc<Extensions>,
|
||||
conn_data: Option<Rc<Extensions>>,
|
||||
) -> HttpRequest {
|
||||
let mut data = SmallVec::<[Rc<Extensions>; 4]>::new();
|
||||
data.push(app_data);
|
||||
|
@ -57,6 +59,7 @@ impl HttpRequest {
|
|||
path,
|
||||
app_state,
|
||||
app_data: data,
|
||||
conn_data,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -165,6 +168,20 @@ impl HttpRequest {
|
|||
self.head().extensions_mut()
|
||||
}
|
||||
|
||||
/// Returns a reference a piece of connection data set in an [on-connect] callback.
|
||||
///
|
||||
/// ```ignore
|
||||
/// let opt_t = req.conn_data::<PeerCertificate>();
|
||||
/// ```
|
||||
///
|
||||
/// [on-connect]: crate::HttpServiceBuilder::on_connect_ext
|
||||
pub fn conn_data<T: 'static>(&self) -> Option<&T> {
|
||||
self.inner
|
||||
.conn_data
|
||||
.as_deref()
|
||||
.and_then(|container| container.get::<T>())
|
||||
}
|
||||
|
||||
/// Generates URL for a named resource.
|
||||
///
|
||||
/// This substitutes in sequence all URL parameters that appear in the resource itself and in
|
||||
|
|
|
@ -101,9 +101,9 @@ where
|
|||
|
||||
/// Sets function that will be called once before each connection is handled.
|
||||
/// It will receive a `&std::any::Any`, which contains underlying connection type and an
|
||||
/// [Extensions] container so that request-local data can be passed to middleware and handlers.
|
||||
/// [Extensions] container so that connection data can be accessed in middleware and handlers.
|
||||
///
|
||||
/// For example:
|
||||
/// # Connection Types
|
||||
/// - `actix_tls::accept::openssl::TlsStream<actix_web::rt::net::TcpStream>` when using openssl.
|
||||
/// - `actix_tls::accept::rustls::TlsStream<actix_web::rt::net::TcpStream>` when using rustls.
|
||||
/// - `actix_web::rt::net::TcpStream` when no encryption is used.
|
||||
|
|
|
@ -172,12 +172,10 @@ impl ServiceRequest {
|
|||
self.head().uri.path()
|
||||
}
|
||||
|
||||
/// The query string in the URL.
|
||||
///
|
||||
/// E.g., id=10
|
||||
/// Counterpart to [`HttpRequest::query_string`](super::HttpRequest::query_string()).
|
||||
#[inline]
|
||||
pub fn query_string(&self) -> &str {
|
||||
self.uri().query().unwrap_or_default()
|
||||
self.req.query_string()
|
||||
}
|
||||
|
||||
/// Peer socket address.
|
||||
|
@ -241,6 +239,7 @@ impl ServiceRequest {
|
|||
}
|
||||
|
||||
/// Counterpart to [`HttpRequest::app_data`](super::HttpRequest::app_data()).
|
||||
#[inline]
|
||||
pub fn app_data<T: 'static>(&self) -> Option<&T> {
|
||||
for container in self.req.inner.app_data.iter().rev() {
|
||||
if let Some(data) = container.get::<T>() {
|
||||
|
@ -251,6 +250,12 @@ impl ServiceRequest {
|
|||
None
|
||||
}
|
||||
|
||||
/// Counterpart to [`HttpRequest::conn_data`](super::HttpRequest::conn_data()).
|
||||
#[inline]
|
||||
pub fn conn_data<T: 'static>(&self) -> Option<&T> {
|
||||
self.req.conn_data()
|
||||
}
|
||||
|
||||
#[cfg(feature = "cookies")]
|
||||
pub fn cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {
|
||||
self.req.cookies()
|
||||
|
@ -263,6 +268,7 @@ impl ServiceRequest {
|
|||
}
|
||||
|
||||
/// Set request payload.
|
||||
#[inline]
|
||||
pub fn set_payload(&mut self, payload: Payload) {
|
||||
self.payload = payload;
|
||||
}
|
||||
|
@ -280,6 +286,7 @@ impl ServiceRequest {
|
|||
}
|
||||
|
||||
impl Resource<Url> for ServiceRequest {
|
||||
#[inline]
|
||||
fn resource_path(&mut self) -> &mut Path<Url> {
|
||||
self.match_info_mut()
|
||||
}
|
||||
|
@ -404,12 +411,11 @@ impl<B> ServiceResponse<B> {
|
|||
}
|
||||
|
||||
/// Extract response body
|
||||
#[inline]
|
||||
pub fn into_body(self) -> B {
|
||||
self.response.into_body()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> ServiceResponse<B> {
|
||||
/// Set a new body
|
||||
#[inline]
|
||||
pub fn map_body<F, B2>(self, f: F) -> ServiceResponse<B2>
|
||||
|
|
|
@ -581,7 +581,7 @@ impl TestRequest {
|
|||
let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());
|
||||
|
||||
ServiceRequest::new(
|
||||
HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data)),
|
||||
HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data), None),
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
@ -599,7 +599,7 @@ impl TestRequest {
|
|||
|
||||
let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());
|
||||
|
||||
HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data))
|
||||
HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data), None)
|
||||
}
|
||||
|
||||
/// Complete request creation and generate `HttpRequest` and `Payload` instances
|
||||
|
@ -610,7 +610,7 @@ impl TestRequest {
|
|||
|
||||
let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());
|
||||
|
||||
let req = HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data));
|
||||
let req = HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data), None);
|
||||
|
||||
(req, payload)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue