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). Also applies to RPC clients (Thrift, gRPC) at the HTTP layer.
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.

Cluster type extensions​

Custom cluster types allow xDS clusters to resolve endpoints from sources other than static addresses, DNS, or EDS. Implement ClusterTypeFactory and register it via XdsExtensionFactoryProvider.

Kubernetes endpoint discovery​

The armeria-xds-kubernetes module provides a built-in cluster type that resolves endpoints from Kubernetes services. Add the dependency:

dependencies {
implementation 'com.linecorp.armeria:armeria-xds-kubernetes'
}

Then reference it in your cluster configuration:

clusters:
- name: my-service
cluster_type:
name: armeria.cluster.kubernetes
typed_config:
"@type": type.googleapis.com/armeria.xds.kubernetes.KubernetesClusterConfig
service_name: my-service
namespace: production
port_name: http
mode: POD
FieldDescription
service_nameThe Kubernetes Service to watch (required)
namespaceKubernetes namespace; defaults to the client's default namespace
port_namePort name to select; uses the first port if empty
modePOD (default) for direct pod IPs, or NODE_PORT for NodePort/LoadBalancer access
api_server_urlCustom Kubernetes API server URL; uses kubeconfig/in-cluster default if unset
credentialSDS secret config for bearer token authentication

Config source extensions​

Custom config sources allow xDS resources to be loaded from sources other than ADS or file paths. Implement SotwConfigSourceSubscriptionFactory and register it via XdsExtensionFactoryProvider.

Spring Boot integration​

The armeria-spring-boot4-xds module uses Spring properties as a control plane for xDS resources. Instead of connecting to an external control plane, listeners and clusters are defined as Spring Environment properties and served to the xDS module via a built-in custom_config_source. Add the dependency:

dependencies {
implementation 'com.linecorp.armeria:armeria-spring-boot4-xds'
}

Defining resources​

Spring properties can act as a config source for any xDS resource type. The SpringConfigSourceFactory reads properties matching a configured prefix and parses each value as the corresponding xDS resource. For example, the default bootstrap fetches listeners from armeria.xds.listener.* and clusters from armeria.xds.cluster.*:

armeria:
xds:
listener:
my-listener: |
name: my-listener
api_listener:
api_listener:
"@type": ...HttpConnectionManager
# ...
cluster:
my-cluster: |
name: my-cluster
type: STATIC
# ...

These are ordinary Spring properties — they can come from application.yml, environment variables, Spring Cloud Config Server, or any other property source. When the Spring environment changes (e.g. via EnvironmentChangeEvent), resources are automatically refreshed.

Customizing the bootstrap​

The bootstrap itself is read from the armeria.xds.bootstrap property. A default bootstrap is provided that configures both LDS and CDS to use SpringConfigSourceFactory. Override it to mix config sources — for example, defining listeners in Spring properties while fetching clusters and endpoints from a remote control plane:

armeria:
xds:
bootstrap: |
dynamic_resources:
lds_config:
custom_config_source:
name: armeria.config_source.spring
typed_config:
"@type": type.googleapis.com/armeria.xds.spring.SpringConfigSource
prefix: "armeria.xds.listener."
cds_config:
ads: {}
ads_config:
api_type: GRPC
grpc_services:
- envoy_grpc:
cluster_name: xds-cluster
static_resources:
clusters:
- name: xds-cluster
type: STATIC
load_assignment:
cluster_name: xds-cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: control-plane.example.com
port_value: 15010

In this setup, listeners are managed as Spring properties and can be updated via Spring Cloud Config Server, while clusters and endpoints are discovered dynamically from the control plane.

Like Armeria?
Star us ⭐️

×