diff --git a/CHANGES.md b/CHANGES.md index fa4aa8595..708d330b5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,12 +1,17 @@ # Changes ## Unreleased - 2021-xx-xx +### Added +* Add extractors for `Version`, `Uri` and `Method`. [#2263] +* Add extractor for `ConnectionInfo` and `PeerAddr`. [#2263] + ### Changed * Change compression algorithm features flags. [#2250] * Deprecate `App::data` and `App::data_factory`. [#2271] [#2250]: https://github.com/actix/actix-web/pull/2250 [#2271]: https://github.com/actix/actix-web/pull/2271 +[#2263]: https://github.com/actix/actix-web/pull/2263 ## 4.0.0-beta.7 - 2021-06-17 diff --git a/src/extract.rs b/src/extract.rs index e12461fa5..c5d1eb58f 100644 --- a/src/extract.rs +++ b/src/extract.rs @@ -2,21 +2,16 @@ use std::{ convert::Infallible, - fmt, future::Future, - net::SocketAddr, pin::Pin, task::{Context, Poll}, }; use actix_http::http::{Method, Uri, Version}; -use actix_utils::future::{err, ok, Ready}; +use actix_utils::future::{ok, Ready}; use futures_core::ready; -use crate::{ - dev::{ConnectionInfo, Payload}, - Error, HttpRequest, ResponseError, -}; +use crate::{dev::Payload, Error, HttpRequest}; /// Trait implemented by types that can be extracted from request. /// @@ -253,43 +248,6 @@ impl FromRequest for Method { } } -impl FromRequest for ConnectionInfo { - type Error = Infallible; - type Future = Ready>; - type Config = (); - - fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { - ok(req.connection_info().clone()) - } -} - -#[derive(Debug)] -struct MissingPeerAddr; - -impl fmt::Display for MissingPeerAddr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Missing peer address.") - } -} - -impl ResponseError for MissingPeerAddr {} - -impl FromRequest for SocketAddr { - type Error = Error; - type Future = Ready>; - type Config = (); - - fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { - match req.peer_addr() { - Some(addr) => ok(addr), - None => { - log::error!("Missing peer address."); - err(MissingPeerAddr.into()) - } - } - } -} - #[doc(hidden)] impl FromRequest for () { type Error = Infallible; @@ -508,25 +466,4 @@ mod tests { let method = Method::extract(&req).await.unwrap(); assert_eq!(method, Method::GET); } - - #[actix_rt::test] - async fn test_conn_info() { - let req = TestRequest::default() - .uri("http://actix.rs/") - .to_http_request(); - let conn_info = ConnectionInfo::extract(&req).await.unwrap(); - assert_eq!(conn_info.scheme(), "http"); - } - - #[actix_rt::test] - async fn test_peer_addr() { - let addr = "127.0.0.1:8080".parse().unwrap(); - let req = TestRequest::default().peer_addr(addr).to_http_request(); - let peer_addr = SocketAddr::extract(&req).await.unwrap(); - assert_eq!(peer_addr, addr); - - let req = TestRequest::default().to_http_request(); - let res = SocketAddr::extract(&req).await; - assert!(res.is_err()); - } } diff --git a/src/info.rs b/src/info.rs index c9ddf6ec4..49bc6ca12 100644 --- a/src/info.rs +++ b/src/info.rs @@ -1,7 +1,13 @@ -use std::cell::Ref; +use std::{cell::Ref, convert::Infallible, net::SocketAddr}; -use crate::dev::{AppConfig, RequestHead}; -use crate::http::header::{self, HeaderName}; +use actix_utils::future::{err, ok, Ready}; +use derive_more::{Display, Error}; + +use crate::{ + dev::{AppConfig, Payload, RequestHead}, + http::header::{self, HeaderName}, + Error, FromRequest, HttpRequest, ResponseError, +}; const X_FORWARDED_FOR: &[u8] = b"x-forwarded-for"; const X_FORWARDED_HOST: &[u8] = b"x-forwarded-host"; @@ -187,6 +193,63 @@ impl ConnectionInfo { } } +impl FromRequest for ConnectionInfo { + type Error = Infallible; + type Future = Ready>; + type Config = (); + + fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { + ok(req.connection_info().clone()) + } +} + +/// Extractor for peer's socket address. +/// +/// Also see [`HttpRequest::peer_addr`]. +/// +/// # Examples +/// ``` +/// # use actix_web::Responder; +/// use actix_web::dev::PeerAddr; +/// +/// async fn handler(peer_addr: PeerAddr) -> impl Responder { +/// let socket_addr = peer_addr.0; +/// socket_addr.to_string() +/// } +/// ``` +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display)] +#[display(fmt = "{}", _0)] +pub struct PeerAddr(pub SocketAddr); + +impl PeerAddr { + /// Unwrap into inner `SocketAddr` value. + pub fn into_inner(self) -> SocketAddr { + self.0 + } +} + +#[derive(Debug, Display, Error)] +#[display(fmt = "Missing peer address")] +struct MissingPeerAddr; + +impl ResponseError for MissingPeerAddr {} + +impl FromRequest for PeerAddr { + type Error = Error; + type Future = Ready>; + type Config = (); + + fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { + match req.peer_addr() { + Some(addr) => ok(PeerAddr(addr)), + None => { + log::error!("Missing peer address."); + err(MissingPeerAddr.into()) + } + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -239,4 +302,25 @@ mod tests { let info = req.connection_info(); assert_eq!(info.scheme(), "https"); } + + #[actix_rt::test] + async fn test_conn_info() { + let req = TestRequest::default() + .uri("http://actix.rs/") + .to_http_request(); + let conn_info = ConnectionInfo::extract(&req).await.unwrap(); + assert_eq!(conn_info.scheme(), "http"); + } + + #[actix_rt::test] + async fn test_peer_addr() { + let addr = "127.0.0.1:8080".parse().unwrap(); + let req = TestRequest::default().peer_addr(addr).to_http_request(); + let peer_addr = PeerAddr::extract(&req).await.unwrap(); + assert_eq!(peer_addr, PeerAddr(addr)); + + let req = TestRequest::default().to_http_request(); + let res = PeerAddr::extract(&req).await; + assert!(res.is_err()); + } } diff --git a/src/lib.rs b/src/lib.rs index 4bcef3988..9d8bf62e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,7 +131,7 @@ pub mod dev { pub use crate::config::{AppConfig, AppService}; #[doc(hidden)] pub use crate::handler::Handler; - pub use crate::info::ConnectionInfo; + pub use crate::info::{ConnectionInfo, PeerAddr}; pub use crate::rmap::ResourceMap; pub use crate::service::{HttpServiceFactory, ServiceRequest, ServiceResponse, WebService};