# Virtual Hosts Module for Pingora This module simplifies dealing with virtual hosts. It wraps any handler implementing `module_utils::RequestFilter` and its configuration, allowing to supply a different configuration for that handler for each virtual host and subdirectories of that host. For example, if Static Files Module is the wrapped handler, the configuration file might look like this: ```yaml vhosts: localhost:8000: aliases: - 127.0.0.1:8000 - "[::1]:8000" root: ./local-debug-root example.com: aliases: - www.example.com default: true root: ./production-root subdirs: /metrics root: ./metrics /test: strip_prefix: true root: ./local-debug-root redirect_prefix: /test ``` A virtual host configuration adds three configuration settings to the configuration of the wrapped handler: * `aliases` lists additional host names that should share the same configuration. * `default` can be set to `true` to indicate that this configuration should apply to all host names not listed explicitly. * `subdirs` maps subdirectories to their respective configuration. The configuration is that of the wrapped handler with the added `strip_prefix` setting. If `true`, this setting will remove the subdirectory path from the URI before the request is passed on to the handler. If no default host entry is present and a request is made for an unknown host name, this handler will leave the request unhandled. Otherwise the handling is delegated to the wrapped handler. When selecting a subdirectory configuration, longer matching paths are preferred. Matching always happens against full file names, meaning that URI `/test/abc` matches the subdirectory `/test` whereas the URI `/test_abc` doesn’t. If no matching path is found, the host configuration will be used. *Note*: When the `strip_prefix` option is used, the subsequent handlers will receive a URI which doesn’t match the actual URI of the request. This might result in wrong links or redirects. When using Static Files Module you can set `redirect_prefix` setting like in the example above to compensate. Upstream responses might have to be corrected via Pingora’s `upstream_response_filter`. ## Code example Usually, the virtual hosts configuration will be read from a configuration file and used to instantiate the corresponding handler. This is how it would be done: ```rust use pingora_core::server::configuration::{Opt, ServerConf}; use module_utils::{FromYaml, merge_conf}; use static_files_module::{StaticFilesConf, StaticFilesHandler}; use structopt::StructOpt; use virtual_hosts_module::{VirtualHostsConf, VirtualHostsHandler}; // Combine Pingora server configuration with virtual hosts wrapping static files configuration. #[merge_conf] struct Conf { server: ServerConf, virtual_hosts: VirtualHostsConf, } // Read command line options and configuration file. let opt = Opt::from_args(); let conf = opt .conf .as_ref() .and_then(|path| Conf::load_from_yaml(path).ok()) .unwrap_or_else(Conf::default); // Create handler from configuration let handler: VirtualHostsHandler = conf.virtual_hosts.try_into().unwrap(); ``` You can then use that handler in your server implementation: ```rust use async_trait::async_trait; use pingora_core::upstreams::peer::HttpPeer; use pingora_core::Error; use pingora_proxy::{ProxyHttp, Session}; use module_utils::RequestFilter; use static_files_module::StaticFilesHandler; use virtual_hosts_module::VirtualHostsHandler; pub struct MyServer { handler: VirtualHostsHandler, } #[async_trait] impl ProxyHttp for MyServer { type CTX = as RequestFilter>::CTX; fn new_ctx(&self) -> Self::CTX { VirtualHostsHandler::::new_ctx() } async fn request_filter( &self, session: &mut Session, ctx: &mut Self::CTX, ) -> Result> { self.handler.handle(session, ctx).await } async fn upstream_peer( &self, _session: &mut Session, _ctx: &mut Self::CTX, ) -> Result, Box> { // Virtual hosts handler didn't handle the request, meaning no matching virtual host in // configuration. Delegate to upstream peer. Ok(Box::new(HttpPeer::new( "example.com:443", true, "example.com".to_owned(), ))) } } ``` For complete and more comprehensive code see [virtual-hosts example](https://github.com/palant/pingora-utils/tree/main/examples/virtual-hosts) in the repository.