mirror of https://github.com/fafhrd91/actix-web
put all the items behind feature gate
This commit is contained in:
parent
9a0190a1cc
commit
1facfec04b
|
|
@ -165,10 +165,7 @@ impl AppService {
|
|||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let guard_names = guard_list
|
||||
.iter()
|
||||
.map(|g| g.name().to_string())
|
||||
.collect::<Vec<_>>();
|
||||
let guard_names = guard_list.iter().map(|g| g.name()).collect::<Vec<_>>();
|
||||
let guard_details = crate::introspection::guard_reports_from_iter(guard_list.iter());
|
||||
|
||||
let is_resource = nested.is_none();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use super::{Guard, GuardContext, GuardDetail};
|
||||
use super::{Guard, GuardContext};
|
||||
use crate::http::header::Accept;
|
||||
|
||||
/// A guard that verifies that an `Accept` header is present and it contains a compatible MIME type.
|
||||
|
|
@ -64,35 +64,25 @@ impl Guard for Acceptable {
|
|||
false
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn name(&self) -> String {
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
{
|
||||
if self.match_star_star {
|
||||
format!("Acceptable({}, match_star_star=true)", self.mime)
|
||||
} else {
|
||||
format!("Acceptable({})", self.mime)
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "experimental-introspection"))]
|
||||
{
|
||||
std::any::type_name::<Self>().to_string()
|
||||
if self.match_star_star {
|
||||
format!("Acceptable({}, match_star_star=true)", self.mime)
|
||||
} else {
|
||||
format!("Acceptable({})", self.mime)
|
||||
}
|
||||
}
|
||||
|
||||
fn details(&self) -> Option<Vec<GuardDetail>> {
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
{
|
||||
let mut details = Vec::new();
|
||||
details.push(GuardDetail::Generic(format!("mime={}", self.mime)));
|
||||
if self.match_star_star {
|
||||
details.push(GuardDetail::Generic("match_star_star=true".to_string()));
|
||||
}
|
||||
Some(details)
|
||||
}
|
||||
#[cfg(not(feature = "experimental-introspection"))]
|
||||
{
|
||||
None
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn details(&self) -> Option<Vec<super::GuardDetail>> {
|
||||
let mut details = Vec::new();
|
||||
details.push(super::GuardDetail::Generic(format!("mime={}", self.mime)));
|
||||
if self.match_star_star {
|
||||
details.push(super::GuardDetail::Generic(
|
||||
"match_star_star=true".to_string(),
|
||||
));
|
||||
}
|
||||
Some(details)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,12 +125,12 @@ mod tests {
|
|||
let details = guard.details().expect("missing guard details");
|
||||
|
||||
assert!(details.iter().any(|detail| match detail {
|
||||
GuardDetail::Generic(value) => value == "match_star_star=true",
|
||||
crate::guard::GuardDetail::Generic(value) => value == "match_star_star=true",
|
||||
_ => false,
|
||||
}));
|
||||
let expected = format!("mime={}", mime::APPLICATION_JSON);
|
||||
assert!(details.iter().any(|detail| match detail {
|
||||
GuardDetail::Generic(value) => value == &expected,
|
||||
crate::guard::GuardDetail::Generic(value) => value == &expected,
|
||||
_ => false,
|
||||
}));
|
||||
assert_eq!(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use actix_http::{header, uri::Uri, RequestHead, Version};
|
||||
|
||||
use super::{Guard, GuardContext, GuardDetail};
|
||||
use super::{Guard, GuardContext};
|
||||
|
||||
/// Creates a guard that matches requests targeting a specific host.
|
||||
///
|
||||
|
|
@ -118,39 +118,27 @@ impl Guard for HostGuard {
|
|||
true
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn name(&self) -> String {
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
{
|
||||
if let Some(ref scheme) = self.scheme {
|
||||
format!("Host({}, scheme={})", self.host, scheme)
|
||||
} else {
|
||||
format!("Host({})", self.host)
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "experimental-introspection"))]
|
||||
{
|
||||
std::any::type_name::<Self>().to_string()
|
||||
if let Some(ref scheme) = self.scheme {
|
||||
format!("Host({}, scheme={})", self.host, scheme)
|
||||
} else {
|
||||
format!("Host({})", self.host)
|
||||
}
|
||||
}
|
||||
|
||||
fn details(&self) -> Option<Vec<GuardDetail>> {
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
{
|
||||
let mut details = vec![GuardDetail::Headers(vec![(
|
||||
"host".to_string(),
|
||||
self.host.clone(),
|
||||
)])];
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn details(&self) -> Option<Vec<super::GuardDetail>> {
|
||||
let mut details = vec![super::GuardDetail::Headers(vec![(
|
||||
"host".to_string(),
|
||||
self.host.clone(),
|
||||
)])];
|
||||
|
||||
if let Some(ref scheme) = self.scheme {
|
||||
details.push(GuardDetail::Generic(format!("scheme={scheme}")));
|
||||
}
|
||||
if let Some(ref scheme) = self.scheme {
|
||||
details.push(super::GuardDetail::Generic(format!("scheme={scheme}")));
|
||||
}
|
||||
|
||||
Some(details)
|
||||
}
|
||||
#[cfg(not(feature = "experimental-introspection"))]
|
||||
{
|
||||
None
|
||||
}
|
||||
Some(details)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -282,13 +270,13 @@ mod tests {
|
|||
let details = host.details().expect("missing guard details");
|
||||
|
||||
assert!(details.iter().any(|detail| match detail {
|
||||
GuardDetail::Headers(headers) => headers
|
||||
crate::guard::GuardDetail::Headers(headers) => headers
|
||||
.iter()
|
||||
.any(|(name, value)| name == "host" && value == "example.com"),
|
||||
_ => false,
|
||||
}));
|
||||
assert!(details.iter().any(|detail| match detail {
|
||||
GuardDetail::Generic(value) => value == "scheme=https",
|
||||
crate::guard::GuardDetail::Generic(value) => value == "scheme=https",
|
||||
_ => false,
|
||||
}));
|
||||
assert_eq!(host.name(), "Host(example.com, scheme=https)");
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ pub use self::{
|
|||
};
|
||||
|
||||
/// Enum to encapsulate various introspection details of a guard.
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GuardDetail {
|
||||
/// Detail associated with explicit HTTP method guards.
|
||||
|
|
@ -137,6 +139,7 @@ pub trait Guard {
|
|||
fn check(&self, ctx: &GuardContext<'_>) -> bool;
|
||||
|
||||
/// Returns a nominal representation of the guard.
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn name(&self) -> String {
|
||||
std::any::type_name::<Self>().to_string()
|
||||
}
|
||||
|
|
@ -144,6 +147,7 @@ pub trait Guard {
|
|||
/// Returns detailed introspection information, when available.
|
||||
///
|
||||
/// This is best-effort and may omit complex guard logic.
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn details(&self) -> Option<Vec<GuardDetail>> {
|
||||
None
|
||||
}
|
||||
|
|
@ -153,9 +157,13 @@ impl Guard for Rc<dyn Guard> {
|
|||
fn check(&self, ctx: &GuardContext<'_>) -> bool {
|
||||
(**self).check(ctx)
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn name(&self) -> String {
|
||||
(**self).name()
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn details(&self) -> Option<Vec<GuardDetail>> {
|
||||
(**self).details()
|
||||
}
|
||||
|
|
@ -248,6 +256,8 @@ impl Guard for AnyGuard {
|
|||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn name(&self) -> String {
|
||||
format!(
|
||||
"AnyGuard({})",
|
||||
|
|
@ -258,6 +268,8 @@ impl Guard for AnyGuard {
|
|||
.join(", ")
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn details(&self) -> Option<Vec<GuardDetail>> {
|
||||
Some(
|
||||
self.guards
|
||||
|
|
@ -318,6 +330,8 @@ impl Guard for AllGuard {
|
|||
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn name(&self) -> String {
|
||||
format!(
|
||||
"AllGuard({})",
|
||||
|
|
@ -328,6 +342,8 @@ impl Guard for AllGuard {
|
|||
.join(", ")
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn details(&self) -> Option<Vec<GuardDetail>> {
|
||||
Some(
|
||||
self.guards
|
||||
|
|
@ -356,18 +372,15 @@ impl<G: Guard> Guard for Not<G> {
|
|||
fn check(&self, ctx: &GuardContext<'_>) -> bool {
|
||||
!self.0.check(ctx)
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn name(&self) -> String {
|
||||
format!("Not({})", self.0.name())
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn details(&self) -> Option<Vec<GuardDetail>> {
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
{
|
||||
Some(vec![GuardDetail::Generic(self.name())])
|
||||
}
|
||||
#[cfg(not(feature = "experimental-introspection"))]
|
||||
{
|
||||
None
|
||||
}
|
||||
Some(vec![GuardDetail::Generic(self.name())])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -398,9 +411,13 @@ impl Guard for MethodGuard {
|
|||
|
||||
ctx.head().method == self.0
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn name(&self) -> String {
|
||||
self.0.to_string()
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn details(&self) -> Option<Vec<GuardDetail>> {
|
||||
Some(vec![GuardDetail::HttpMethods(vec![self.0.to_string()])])
|
||||
}
|
||||
|
|
@ -466,9 +483,13 @@ impl Guard for HeaderGuard {
|
|||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn name(&self) -> String {
|
||||
format!("Header({}, {})", self.0, self.1.to_str().unwrap_or(""))
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-introspection")]
|
||||
fn details(&self) -> Option<Vec<GuardDetail>> {
|
||||
Some(vec![GuardDetail::Headers(vec![(
|
||||
self.0.to_string(),
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@
|
|||
//!
|
||||
//! Notes:
|
||||
//! - Method lists are best-effort and derived only from explicit method guards; an empty list means
|
||||
//! the route matches any method.
|
||||
//! no explicit method guards were observed for the node.
|
||||
//! - Guard and method lists are aggregated per `full_path` and do not preserve per-route
|
||||
//! correlations when multiple routes/services share the same path.
|
||||
//! - Reachability hints are best-effort and should be treated as diagnostics, not a hard guarantee.
|
||||
//!
|
||||
//! This feature is intended for local/non-production use. Avoid exposing introspection endpoints
|
||||
|
|
@ -32,7 +34,7 @@ use crate::{
|
|||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RouteDetail {
|
||||
struct RouteDetail {
|
||||
methods: Vec<Method>,
|
||||
guards: Vec<String>,
|
||||
guard_details: Vec<GuardReport>,
|
||||
|
|
@ -43,7 +45,7 @@ pub struct RouteDetail {
|
|||
|
||||
/// Input data for registering routes with the introspector.
|
||||
#[derive(Clone)]
|
||||
pub struct RouteInfo {
|
||||
pub(crate) struct RouteInfo {
|
||||
full_path: String,
|
||||
methods: Vec<Method>,
|
||||
guards: Vec<String>,
|
||||
|
|
@ -53,7 +55,7 @@ pub struct RouteInfo {
|
|||
}
|
||||
|
||||
impl RouteInfo {
|
||||
pub fn new(
|
||||
pub(crate) fn new(
|
||||
full_path: String,
|
||||
methods: Vec<Method>,
|
||||
guards: Vec<String>,
|
||||
|
|
@ -182,9 +184,12 @@ pub struct IntrospectionReportItem {
|
|||
pub full_path: String,
|
||||
/// Methods derived from explicit method guards.
|
||||
///
|
||||
/// An empty list indicates the route matches any method.
|
||||
/// An empty list indicates no explicit method guards were observed for the node.
|
||||
pub methods: Vec<String>,
|
||||
/// Guard names attached to the route.
|
||||
///
|
||||
/// This is aggregated per `full_path` and does not necessarily represent a single matching
|
||||
/// condition when multiple routes/services share the same path.
|
||||
pub guards: Vec<String>,
|
||||
/// Structured guard details, when available.
|
||||
///
|
||||
|
|
@ -201,7 +206,10 @@ pub struct IntrospectionReportItem {
|
|||
pub patterns: Vec<String>,
|
||||
/// The type of node represented by the report item.
|
||||
pub resource_type: String,
|
||||
/// Depth within the scope tree (root = 0).
|
||||
/// Depth within this report tree (root = 0).
|
||||
///
|
||||
/// This currently corresponds to the number of path segments (for example, `/foo` has depth 1
|
||||
/// and `/foo/bar` has depth 2).
|
||||
pub scope_depth: usize,
|
||||
/// True if the route might be unreachable at runtime.
|
||||
#[serde(skip_serializing_if = "is_false")]
|
||||
|
|
@ -276,7 +284,7 @@ impl From<&IntrospectionNode> for Vec<IntrospectionReportItem> {
|
|||
|
||||
/// Collects route details during app configuration.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct IntrospectionCollector {
|
||||
pub(crate) struct IntrospectionCollector {
|
||||
details: BTreeMap<String, RouteDetail>,
|
||||
registrations: Vec<Registration>,
|
||||
externals: Vec<ExternalResourceReportItem>,
|
||||
|
|
@ -286,7 +294,7 @@ pub struct IntrospectionCollector {
|
|||
|
||||
impl IntrospectionCollector {
|
||||
/// Creates a new, empty collector.
|
||||
pub fn new() -> Self {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
details: BTreeMap::new(),
|
||||
registrations: Vec::new(),
|
||||
|
|
@ -296,13 +304,13 @@ impl IntrospectionCollector {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn next_scope_id(&mut self) -> usize {
|
||||
pub(crate) fn next_scope_id(&mut self) -> usize {
|
||||
let scope_id = self.next_scope_id;
|
||||
self.next_scope_id += 1;
|
||||
scope_id
|
||||
}
|
||||
|
||||
pub fn register_service(
|
||||
pub(crate) fn register_service(
|
||||
&mut self,
|
||||
info: RouteInfo,
|
||||
is_resource: bool,
|
||||
|
|
@ -327,7 +335,7 @@ impl IntrospectionCollector {
|
|||
self.next_registration_order += 1;
|
||||
}
|
||||
|
||||
pub fn register_route(&mut self, info: RouteInfo, scope_id: Option<usize>) {
|
||||
pub(crate) fn register_route(&mut self, info: RouteInfo, scope_id: Option<usize>) {
|
||||
let full_path = normalize_path(&info.full_path);
|
||||
|
||||
self.register_pattern_detail(&full_path, &info, true);
|
||||
|
|
@ -345,7 +353,7 @@ impl IntrospectionCollector {
|
|||
self.next_registration_order += 1;
|
||||
}
|
||||
|
||||
pub fn register_external(&mut self, rdef: &ResourceDef, origin_scope: &str) {
|
||||
pub(crate) fn register_external(&mut self, rdef: &ResourceDef, origin_scope: &str) {
|
||||
let report = external_report_from_rdef(rdef, origin_scope);
|
||||
|
||||
if let Some(name) = report.name.as_deref() {
|
||||
|
|
@ -365,12 +373,7 @@ impl IntrospectionCollector {
|
|||
}
|
||||
|
||||
/// Registers details for a route pattern.
|
||||
pub fn register_pattern_detail(
|
||||
&mut self,
|
||||
full_path: &str,
|
||||
info: &RouteInfo,
|
||||
is_resource: bool,
|
||||
) {
|
||||
fn register_pattern_detail(&mut self, full_path: &str, info: &RouteInfo, is_resource: bool) {
|
||||
let full_path = normalize_path(full_path);
|
||||
|
||||
self.details
|
||||
|
|
@ -398,7 +401,7 @@ impl IntrospectionCollector {
|
|||
}
|
||||
|
||||
/// Produces the finalized introspection tree.
|
||||
pub fn finalize(&mut self) -> IntrospectionTree {
|
||||
pub(crate) fn finalize(&mut self) -> IntrospectionTree {
|
||||
let detail_registry = std::mem::take(&mut self.details);
|
||||
let registrations = std::mem::take(&mut self.registrations);
|
||||
let externals = std::mem::take(&mut self.externals);
|
||||
|
|
|
|||
Loading…
Reference in New Issue