1 unstable release

0.2.0 May 12, 2024

#772 in HTTP server


216 lines

Upstream Module for Pingora

This crate helps configure Pingora’s upstream functionality. It is most useful in combination with the virtual-hosts-module crate that allows applying multiple upstream configurations conditionally.

Currently only one configuration option is provided: upstream (--upstream as command line option). The value should be a URL like or https://example.com.

Supported URL schemes are http:// and https://. Other than the scheme, only host name and port are considered. Other parts of the URL are ignored if present.

The UpstreamHandler type has to be called in both request_filter and upstream_peer Pingora Proxy phases. The former selects an upstream peer and modifies the request by adding the appropriate Host header. The latter retrieves the previously selected upstream peer.

use async_trait::async_trait;
use upstream_module::UpstreamHandler;
use module_utils::RequestFilter;
use pingora_core::Error;
use pingora_core::upstreams::peer::HttpPeer;
use pingora_proxy::{ProxyHttp, Session};

pub struct MyServer {
    upstream_handler: UpstreamHandler,

impl ProxyHttp for MyServer {
    type CTX = <UpstreamHandler as RequestFilter>::CTX;
    fn new_ctx(&self) -> Self::CTX {

    async fn request_filter(
        session: &mut Session,
        ctx: &mut Self::CTX,
    ) -> Result<bool, Box<Error>> {
        // Select upstream peer according to configuration. This could be called based on some
        // conditions.
        self.upstream_handler.handle(session, ctx).await

    async fn upstream_peer(
        session: &mut Session,
        ctx: &mut Self::CTX,
    ) -> Result<Box<HttpPeer>, Box<Error>> {
        // Return previously selected peer if any
        UpstreamHandler::upstream_peer(session, ctx).await

To create a handler, you will typically read its configuration from a configuration file, optionally combined with command line options. The following code will extend Pingora's usual configuration file and command line options accordingly.

use upstream_module::{UpstreamConf, UpstreamHandler, UpstreamOpt};
use module_utils::{merge_conf, merge_opt, FromYaml};
use pingora_core::server::Server;
use pingora_core::server::configuration::{Opt as ServerOpt, ServerConf};
use structopt::StructOpt;

struct Opt {
    server: ServerOpt,
    upstream: UpstreamOpt,

struct Conf {
    server: ServerConf,
    upstream: UpstreamConf,

let opt = Opt::from_args();
let mut conf = opt
    .and_then(|path| Conf::load_from_yaml(path).ok())

let mut server = Server::new_with_opt_and_conf(opt.server, conf.server);

let upstream_handler: UpstreamHandler = conf.upstream.try_into().unwrap();

For complete and more realistic code see virtual-hosts example in the repository.


~1M SLoC