Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ IO_GRPC_GRPC_JAVA_ARTIFACTS = [
"com.google.re2j:re2j:1.8",
"com.google.s2a.proto.v2:s2a-proto:0.1.3",
"com.google.truth:truth:1.4.5",
"dev.cel:runtime:0.12.0",
"dev.cel:protobuf:0.12.0",
"dev.cel:common:0.12.0",
"com.squareup.okhttp:okhttp:2.7.5",
"com.squareup.okio:okio:2.10.0", # 3.0+ needs swapping to -jvm; need work to avoid flag-day
"io.netty:netty-buffer:4.1.132.Final",
Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ subprojects {
ignoreGradleMetadataRedirection()
}
}
maven { url 'https://central.sonatype.com/repository/maven-snapshots/' }
}

tasks.withType(JavaCompile).configureEach {
Expand Down
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ checkstyle = "com.puppycrawl.tools:checkstyle:10.26.1"
# checkstyle 10.0+ requires Java 11+
# See https://checkstyle.sourceforge.io/releasenotes_old_8-35_10-26.html#Release_10.0
# checkForUpdates: checkstylejava8:9.+
cel-runtime = "dev.cel:runtime:0.12.0"
cel-protobuf = "dev.cel:protobuf:0.12.0"
cel-compiler = "dev.cel:compiler:0.12.0"
checkstylejava8 = "com.puppycrawl.tools:checkstyle:9.3"
commons-math3 = "org.apache.commons:commons-math3:3.6.1"
conscrypt = "org.conscrypt:conscrypt-openjdk-uber:2.5.2"
Expand Down
3 changes: 3 additions & 0 deletions repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ IO_GRPC_GRPC_JAVA_ARTIFACTS = [
"com.google.re2j:re2j:1.8",
"com.google.s2a.proto.v2:s2a-proto:0.1.3",
"com.google.truth:truth:1.4.5",
"dev.cel:runtime:0.12.0",
"dev.cel:protobuf:0.12.0",
"dev.cel:common:0.12.0",
"com.squareup.okhttp:okhttp:2.7.5",
"com.squareup.okio:okio:2.10.0", # 3.0+ needs swapping to -jvm; need work to avoid flag-day
"io.netty:netty-buffer:4.1.132.Final",
Expand Down
5 changes: 5 additions & 0 deletions xds/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ java_library(
artifact("com.google.errorprone:error_prone_annotations"),
artifact("com.google.guava:guava"),
artifact("com.google.re2j:re2j"),
artifact("dev.cel:runtime"),
artifact("dev.cel:protobuf"),
artifact("dev.cel:common"),
artifact("io.netty:netty-buffer"),
artifact("io.netty:netty-codec"),
artifact("io.netty:netty-common"),
Expand Down Expand Up @@ -97,6 +100,8 @@ JAR_JAR_RULES = [
"rule com.google.api.expr.** io.grpc.xds.shaded.com.google.api.expr.@1",
"rule com.google.security.** io.grpc.xds.shaded.com.google.security.@1",
"rule dev.cel.expr.** io.grpc.xds.shaded.dev.cel.expr.@1",
"rule dev.cel.** io.grpc.xds.shaded.dev.cel.@1",
"rule cel.** io.grpc.xds.shaded.cel.@1",
"rule envoy.annotations.** io.grpc.xds.shaded.envoy.annotations.@1",
"rule io.envoyproxy.** io.grpc.xds.shaded.io.envoyproxy.@1",
"rule udpa.annotations.** io.grpc.xds.shaded.udpa.annotations.@1",
Expand Down
11 changes: 11 additions & 0 deletions xds/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,18 @@ dependencies {
libraries.re2j,
libraries.auto.value.annotations,
libraries.protobuf.java.util
implementation(libraries.cel.runtime) {
exclude group: 'com.google.protobuf', module: 'protobuf-java'
}
implementation(libraries.cel.protobuf) {
exclude group: 'com.google.protobuf', module: 'protobuf-java'
}
def nettyDependency = implementation project(':grpc-netty')

testImplementation project(':grpc-api')
testImplementation project(':grpc-rls')
testImplementation project(':grpc-inprocess')
testImplementation libraries.cel.compiler
testImplementation testFixtures(project(':grpc-core')),
testFixtures(project(':grpc-api')),
testFixtures(project(':grpc-util'))
Expand Down Expand Up @@ -175,13 +182,15 @@ tasks.named("javadoc").configure {
exclude 'io/grpc/xds/XdsNameResolverProvider.java'
exclude 'io/grpc/xds/internal/**'
exclude 'io/grpc/xds/Internal*'
exclude 'dev/cel/**'
}

def prefixName = 'io.grpc.xds'
tasks.named("shadowJar").configure {
archiveClassifier = null
dependencies {
include(project(':grpc-xds'))
include(dependency('dev.cel:.*'))
}
// Relocated packages commonly need exclusions in jacocoTestReport and javadoc
// Keep in sync with BUILD.bazel's JAR_JAR_RULES
Expand All @@ -198,6 +207,8 @@ tasks.named("shadowJar").configure {
// TODO: missing java_package option in .proto
relocate 'udpa.annotations', "${prefixName}.shaded.udpa.annotations"
relocate 'xds.annotations', "${prefixName}.shaded.xds.annotations"
relocate 'dev.cel', "${prefixName}.shaded.dev.cel"
relocate 'cel', "${prefixName}.shaded.cel"
exclude "**/*.proto"
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2026 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.grpc.xds.internal.matcher;

/**
* Interface for resolving attributes from {@link MatchContext}.
*/
public interface AttributeProvider {
/**
* Returns the names of the attributes this provider handles.
*/
Iterable<String> names();

/**
* Resolves the attribute value from the context.
*/
Object get(MatchContext context);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
/*
* Copyright 2026 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.grpc.xds.internal.matcher;

import com.google.common.collect.ImmutableSet;
import io.grpc.Metadata;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nullable;

/**
* Registry for {@link AttributeProvider}s.
*/
public final class AttributeProviderRegistry {
private static final AttributeProviderRegistry DEFAULT_INSTANCE = new AttributeProviderRegistry();

private final Map<String, AttributeProvider> providers = new ConcurrentHashMap<>();

private AttributeProviderRegistry() {
register(new HeadersProvider());
register(new HostProvider());
register(new IdProvider());
register(new MethodProvider());
register(new PathProvider());
register(new QueryProvider());
register(new SchemeProvider());
register(new ProtocolProvider());
register(new TimeProvider());
register(new RefererProvider());
register(new UserAgentProvider());
}

public static AttributeProviderRegistry getDefaultRegistry() {
return DEFAULT_INSTANCE;
}

public void register(AttributeProvider provider) {
for (String name : provider.names()) {
providers.put(name, provider);
}
}

@Nullable
public AttributeProvider get(String name) {
return providers.get(name);
}

public Set<String> getRegisteredNames() {
return Collections.unmodifiableSet(providers.keySet());
}

private static String orEmpty(@Nullable Object s) {
return s == null ? "" : s.toString();
}

private static String or(@Nullable Object s, String def) {
return s == null ? def : s.toString();
}

private static String getHeader(MatchContext context, String key) {
Metadata metadata = context.getMetadata();
Iterable<String> values = metadata.getAll(
Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER));
if (values == null) {
return "";
}
return String.join(",", values);
}

private static final class HeadersProvider implements AttributeProvider {
private static final Set<String> NAMES = Collections.singleton("headers");

@Override
public Iterable<String> names() {
return NAMES;
}

@Override
public Object get(MatchContext context) {
return new HeadersWrapper(context);
}
}

private static final class HostProvider implements AttributeProvider {
private static final Set<String> NAMES = Collections.singleton("host");

@Override
public Iterable<String> names() {
return NAMES;
}

@Override
public Object get(MatchContext context) {
return orEmpty(context.getRawAttribute("host"));
}
}

private static final class IdProvider implements AttributeProvider {
private static final Set<String> NAMES = Collections.singleton("id");

@Override
public Iterable<String> names() {
return NAMES;
}

@Override
public Object get(MatchContext context) {
return orEmpty(context.getRawAttribute("id"));
}
}

private static final class MethodProvider implements AttributeProvider {
private static final Set<String> NAMES = Collections.singleton("method");

@Override
public Iterable<String> names() {
return NAMES;
}

@Override
public Object get(MatchContext context) {
return or(context.getRawAttribute("method"), "POST");
}
}

private static final class PathProvider implements AttributeProvider {
private static final Set<String> NAMES = ImmutableSet.of("path", "url_path");

@Override
public Iterable<String> names() {
return NAMES;
}

@Override
public Object get(MatchContext context) {
return orEmpty(context.getRawAttribute("path"));
}
}

private static final class QueryProvider implements AttributeProvider {
private static final Set<String> NAMES = Collections.singleton("query");

@Override
public Iterable<String> names() {
return NAMES;
}

@Override
public Object get(MatchContext context) {
return "";
}
}

private static final class SchemeProvider implements AttributeProvider {
private static final Set<String> NAMES = Collections.singleton("scheme");

@Override
public Iterable<String> names() {
return NAMES;
}

@Override
public Object get(MatchContext context) {
return "";
}
}

private static final class ProtocolProvider implements AttributeProvider {
private static final Set<String> NAMES = Collections.singleton("protocol");

@Override
public Iterable<String> names() {
return NAMES;
}

@Override
public Object get(MatchContext context) {
return "";
}
}

private static final class TimeProvider implements AttributeProvider {
private static final Set<String> NAMES = Collections.singleton("time");

@Override
public Iterable<String> names() {
return NAMES;
}

@Override
@Nullable
public Object get(MatchContext context) {
return null;
}
}

private static final class RefererProvider implements AttributeProvider {
private static final Set<String> NAMES = Collections.singleton("referer");

@Override
public Iterable<String> names() {
return NAMES;
}

@Override
public Object get(MatchContext context) {
return getHeader(context, "referer");
}
}

private static final class UserAgentProvider implements AttributeProvider {
private static final Set<String> NAMES = Collections.singleton("useragent");

@Override
public Iterable<String> names() {
return NAMES;
}

@Override
public Object get(MatchContext context) {
return getHeader(context, "user-agent");
}
}
}
Loading
Loading