Skip to main content

Extensions

Envoy's architecture is built around extensions — pluggable components that handle specific concerns like HTTP filtering, transport sockets, and config sources. Each extension is identified by a name (e.g. envoy.filters.http.router) and/or a type URL (e.g. type.googleapis.com/envoy.extensions.filters.http.router.v3.Router).

Armeria follows the same model. Extensions are resolved from a registry by type URL (primary) or name (fallback). Built-in extensions cover the core xDS functionality, and custom extensions can be added via the Java ServiceLoader mechanism.

HTTP filter extensions

HTTP filters are the most common extension point. They intercept requests on both the client side (see Client — Request flow) and the server side (see Server).

Built-in filters

The following HTTP filters are bundled with Armeria:

NameDescription
envoy.filters.http.routerRoute matching and endpoint selection (client), route gating (server). Always required as the last filter in http_filters.
envoy.filters.http.credential_injectorInjects credentials (e.g. from SDS secrets) into outgoing requests.

Any other filter name referenced in http_filters or upstream_http_filters must be registered as a custom extension (see below), or marked as optional in the xDS config to be silently skipped. An unregistered, non-optional filter causes configuration resolution to fail.

Custom filters

To create a custom HTTP filter, implement HttpFilterFactory and XdsHttpFilter.

HttpFilterFactory

A factory that creates an XdsHttpFilter from an xDS HttpFilter config. Factories are responsible for all config parsing, including unwrapping per-route overrides. Returning null from create causes the filter to be silently skipped.

import com.linecorp.armeria.xds.filter.FactoryContext;
import com.linecorp.armeria.xds.filter.HttpFilterFactory;
import com.linecorp.armeria.xds.filter.XdsHttpFilter;

public class MyFilterFactory implements HttpFilterFactory {

@Override
public String name() {
return "my.custom.filter";
}

@Override
public List<String> typeUrls() {
return List.of("type.googleapis.com/my.custom.filter.v1.Config");
}

@Override
@Nullable
public XdsHttpFilter create(HttpFilter httpFilter, Any config,
FactoryContext context) {
MyConfig myConfig = context.validator().unpack(config, MyConfig.class);
return new MyFilter(myConfig);
}
}

XdsHttpFilter

The resolved filter returned by the factory. Override the methods relevant to your use case:

MethodUsed for
httpDecorator()Client-side filtering (both downstream http_filters and upstream upstream_http_filters)
rpcDecorator()Client-side RPC filtering (Thrift)
serviceDecorator()Server-side filtering
import com.linecorp.armeria.xds.filter.XdsHttpFilter;

public class MyFilter implements XdsHttpFilter {

@Override
public DecoratingHttpClientFunction httpDecorator() {
return (delegate, ctx, req) -> {
// client-side filter logic
return delegate.execute(ctx, req);
};
}

@Override
public DecoratingHttpServiceFunction serviceDecorator() {
return (delegate, ctx, req) -> {
// server-side filter logic
return delegate.serve(ctx, req);
};
}
}

Registration

Register your factory using Java's ServiceLoader via XdsExtensionFactoryProvider. Create a provider class:

package com.example;

import com.linecorp.armeria.xds.XdsExtensionFactory;
import com.linecorp.armeria.xds.XdsExtensionFactoryProvider;

public class MyFilterFactoryProvider implements XdsExtensionFactoryProvider {

@Override
public XdsExtensionFactory newFactory() {
return new MyFilterFactory();
}
}

Then create the file:

META-INF/services/com.linecorp.armeria.xds.XdsExtensionFactoryProvider

With the fully qualified class name of your provider:

com.example.MyFilterFactoryProvider

The factory will be discovered automatically when XdsBootstrap is created.

Usage

Once registered, reference your filter by name and type URL in either the http_filters list (downstream) or the Router's upstream_http_filters list (upstream):

listeners:
- name: my-listener
api_listener:
api_listener:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
http_filters:
- name: my.custom.filter # ← downstream filter
typed_config:
"@type": type.googleapis.com/my.custom.filter.v1.Config
some_field: some_value
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
upstream_http_filters:
- name: my.custom.filter # ← upstream filter
typed_config:
"@type": type.googleapis.com/my.custom.filter.v1.Config
some_field: some_value
route_config:
name: route
virtual_hosts:
- name: my-vhost
domains: ["*"]
routes:
- match: { prefix: "/" }
route: { cluster: my-cluster }

Downstream filters wrap the entire retry loop and are invoked once per request. Upstream filters run inside each retry attempt. Both are executed in the order they are declared.

Other extension points

The same XdsExtensionFactoryProvider mechanism supports other extension types:

Like Armeria?
Star us ⭐️

×