use std::sync::Arc;
use std::sync::atomic::Ordering;
use std::time::Duration;
use super::Route;
use crate::extractors::json::SimdJsonMode;
use crate::middleware::Next;
#[cfg(feature = "plugins")]
use crate::plugins::TakoPlugin;
use crate::responder::Responder;
#[cfg(feature = "signals")]
use crate::signals::Signal;
#[cfg(feature = "signals")]
use crate::signals::SignalArbiter;
use crate::types::BoxMiddleware;
use crate::types::Request;
impl Route {
pub fn middleware<F, Fut, R>(&self, f: F) -> &Self
where
F: Fn(Request, Next) -> Fut + Clone + Send + Sync + 'static,
Fut: std::future::Future<Output = R> + Send + 'static,
R: Responder + Send + 'static,
{
let mw: BoxMiddleware = Arc::new(move |req, next| {
let fut = f(req, next);
Box::pin(async move { fut.await.into_response() })
});
self.middlewares.rcu(move |current| {
let mut next = Vec::with_capacity(current.len() + 1);
next.extend(current.iter().cloned());
next.push(mw.clone());
Arc::new(next)
});
self.has_middleware.store(true, Ordering::Release);
self
}
#[cfg(feature = "plugins")]
#[cfg_attr(docsrs, doc(cfg(feature = "plugins")))]
pub fn plugin<P>(&self, plugin: P) -> &Self
where
P: TakoPlugin + Clone + Send + Sync + 'static,
{
self.plugins.write().push(Box::new(plugin));
self
}
#[cfg(feature = "plugins")]
#[cfg_attr(docsrs, doc(cfg(feature = "plugins")))]
pub(crate) fn setup_plugins_once(&self) {
if self.plugins_initialized.load(Ordering::Acquire) {
return;
}
if !self.plugins_initialized.swap(true, Ordering::SeqCst) {
let mini_router = crate::router::Router::new();
let plugins = self.plugins.read();
for plugin in plugins.iter() {
if let Err(e) = plugin.setup(&mini_router) {
tracing::error!(
plugin = plugin.name(),
error = %e,
"route-level TakoPlugin::setup failed; plugin not active"
);
}
}
let plugin_middlewares = mini_router.middlewares.load();
let existing = self.middlewares.load_full();
let mut merged = Vec::with_capacity(plugin_middlewares.len() + existing.len());
merged.extend(plugin_middlewares.iter().cloned());
merged.extend(existing.iter().cloned());
if !merged.is_empty() {
self.has_middleware.store(true, Ordering::Release);
}
self.middlewares.store(Arc::new(merged));
}
}
pub fn version(&self, version: http::Version) -> &Self {
if let Err(_existing) = self.http_protocol.set(version) {
tracing::warn!(
path = %self.path,
method = ?self.method,
existing = ?self.http_protocol.get().copied(),
requested = ?version,
"Route::version called twice; subsequent calls are ignored (OnceLock first-wins)",
);
}
self
}
pub fn h09(&self) -> &Self {
self.version(http::Version::HTTP_09)
}
pub fn h10(&self) -> &Self {
self.version(http::Version::HTTP_10)
}
pub fn h11(&self) -> &Self {
self.version(http::Version::HTTP_11)
}
pub fn h2(&self) -> &Self {
self.version(http::Version::HTTP_2)
}
#[inline]
pub(crate) fn protocol_guard(&self) -> Option<http::Version> {
self.http_protocol.get().copied()
}
#[cfg(feature = "signals")]
pub fn signals(&self) -> &SignalArbiter {
&self.signals
}
#[cfg(feature = "signals")]
pub fn signal_arbiter(&self) -> SignalArbiter {
self.signals.clone()
}
#[cfg(feature = "signals")]
pub fn on_signal<F, Fut>(&self, id: impl Into<String>, handler: F)
where
F: Fn(Signal) -> Fut + Send + Sync + 'static,
Fut: std::future::Future<Output = ()> + Send + 'static,
{
self.signals.on(id, handler);
}
#[cfg(feature = "signals")]
pub async fn emit_signal(&self, signal: Signal) {
self.signals.emit(signal).await;
}
pub fn timeout(&self, duration: Duration) -> &Self {
if let Err(_existing) = self.timeout.set(duration) {
tracing::warn!(
path = %self.path,
method = ?self.method,
existing_ms = self.timeout.get().copied().unwrap_or_default().as_millis() as u64,
requested_ms = duration.as_millis() as u64,
"Route::timeout called twice; subsequent calls are ignored (OnceLock first-wins)",
);
}
self
}
#[inline]
pub(crate) fn get_timeout(&self) -> Option<Duration> {
self.timeout.get().copied()
}
pub fn simd_json(&self, mode: SimdJsonMode) -> &Self {
if let Err(_existing) = self.simd_json_mode.set(mode) {
tracing::warn!(
path = %self.path,
method = ?self.method,
existing = ?self.simd_json_mode.get().copied(),
requested = ?mode,
"Route::simd_json called twice; subsequent calls are ignored (OnceLock first-wins)",
);
}
self
}
#[inline]
pub(crate) fn get_simd_json_mode(&self) -> Option<SimdJsonMode> {
self.simd_json_mode.get().copied()
}
}