Module API
This page summarizes the traits you implement when you write a Ferron module and points to the source for full details.
ModuleLoader#
Trait: ferron_core::loader::ModuleLoader (file: core/src/loader.rs).
ModuleLoader is the registration entry point. You derive Default and override only the methods your module needs. All methods have no-op defaults.
Typical overrides:
register_stages: callregistry.with_stage::<C, _>(|| Arc::new(MyStage)).register_providers: callregistry.with_provider::<C, _>(|| Arc::new(MyProvider)).register_directives: callregistry.register(Directive { ... }, subblock).register_*_validators: push validators or insert scoped validators.register_configuration_adapters: insertBox<dyn ConfigurationAdapter>.register_modules: readServerConfiguration, createArc<dyn Module>, push intomodules. ReturnErrto abort start-up.
Stage<C>#
Trait: ferron_core::pipeline::Stage<C> (file: core/src/pipeline.rs).
Stages form the request pipeline. C is the context type, e.g. HttpContext, HttpFileContext, or HttpErrorContext (types in types/http).
name() -> &str: unique withinC.constraints() -> Vec<StageConstraint>:Before/Afterordering.is_applicable(config: Option<&ServerConfigurationBlock>) -> bool: called when building withStageRegistry::build_with_config. Returnfalseto omit the stage when its directive is absent.run(&self, ctx: &mut C) -> Result<bool, PipelineError>: forward pass.Ok(true)continues,Ok(false)stops gracefully,Errstops with error.run_inverse(&self, ctx: &mut C) -> Result<(), PipelineError>: reverse pass for cleanup and response modification.
Per-request state goes into ctx.extensions (TypeMap). Stages are shared across threads, so do not store request state in self.
Provider<C> and DnsClient, TlsResolver, EventSink#
Traits: ferron_core::providers::Provider<C> (core/src/providers.rs), plus domain types in types/tls, types/dns, types/observability.
Providers are discovered by type and name at runtime:
registry.with_provider::<TlsContext, _>(|| Arc::new(MyTlsProvider));
let provider = registry.get_provider_registry::<TlsContext>().unwrap().get("my_tls").unwrap();Common families:
Provider<TlsContext>whereexecutesetsctx.resolver: Option<Arc<dyn TlsResolver>>.Provider<DnsContext>whereexecutesetsctx.client: Option<Arc<dyn DnsClient>>.Provider<ObservabilityContext>whereexecutesetsctx.sink: Option<Arc<dyn EventSink>>.Provider<LogFormatterContext>/Provider<ApplicationLogFormatterContext>whereexecutesetsctx.output: Option<String>.
Scoped validators use config_validator_scoped_key!(ns, name) with namespaces tls, dns, observability, logformat, etc. The key must match Provider::name().
Module#
Trait: ferron_core::Module (core/src/lib.rs).
Long-lived components. Module::start(&self, runtime: &mut Runtime) spawns tasks. Use:
runtime.spawn_primary_task(|| Box::pin(async { ... }))for per-CPU zincio tasks.runtime.spawn_secondary_task(async { ... })for tokio tasks.runtime.spawn_primary_task_on(idx, ...)to pin to a specific CPU.
Read SHUTDOWN_TOKEN / RELOAD_TOKEN (from core/src/shutdown.rs) to handle graceful stop.
ConfigurationAdapter / ConfigurationWatcher#
Traits: ferron_core::config::adapter::{ConfigurationAdapter, ConfigurationWatcher} (core/src/config/adapter.rs).
adapt(&self, params: &HashMapreturns) -> AdaptResult (ServerConfiguration, Box<dyn ConfigurationWatcher>, ConfigurationMetadata).file_extension() -> Vec<&'static str>selects the adapter by file suffix.- The watcher implements
watch(&mut self) -> Futureandcheck_drift.
Drift detection uses ConfigurationMetadata (config_hash, config_mtime, config_files) and the ADMIN_METRICS.config_drift gauge.
Configuration helpers#
core/src/config/mod.rs:ServerConfiguration,ServerConfigurationBlock,ServerConfigurationValue,LayeredConfiguration,Variables.core/src/config/validator.rs:ConfigurationValidator,ConfigurationValidatorContext,validate_scoped_block!,entry_span.core/src/config/macros:validate_directive!,validate_nested!,config_validator_scoped_key!.types/http:HttpContext,HttpRequest,HttpResponse,HttpFileContext.
Observability#
Ferron has two observability channels. Use the right one for each situation.
Application logging macros#
ferron_core::log_info!, log_warn!, log_error!, and log_debug! write to stdout (or Windows Event Log). These are synchronous and unstructured. Use them for server-infrastructure events: startup, shutdown, TLS configuration, and file rotation errors.
ferron_core::log_info!("Listening on port {}", port);
ferron_core::log_error!("TLS handshake failed: {}", err);The macros check a global level guard. The lowest level is Debug.
Structured event system#
Request processing uses Event values emitted through ctx.events (CompositeEventSink) on HttpContext.
A CompositeEventSink wraps multiple EventSink implementations. It applies trace sampling before dispatch and optimizes the one-sink and zero-sink cases. Use CompositeEventSink::default() for tests and no-op sinks.
The Event enum has these variants:
Event::Log: a log event.Event::Metric: a metric event.Event::Trace: a trace event (can be start or end of span).Event::Access: an access log event.
Where to read more#
- Source files listed above (each has module-level docs and examples).
- Generated Rust docs via
cargo doc --no-deps(see Obtaining API docs). - Runnable examples in
https://github.com/ferronweb/ferron3-example-modules(each crate is ~150 lines and has inline comments).