Skip to content

Commit a357f5b

Browse files
committed
minimal impl
1 parent eb8520c commit a357f5b

69 files changed

Lines changed: 3151 additions & 954 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/skills/xds-dev/SKILL.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
name: xds-dev
3+
description: Build and test all xDS-related modules. Useful during xDS development to verify changes compile and pass tests across the full xDS module set.
4+
---
5+
6+
# xDS Development
7+
8+
Build and test commands for all xDS-related modules.
9+
10+
## Commands
11+
12+
### Test all xDS modules
13+
14+
```
15+
./gradlew --parallel -Pretry=true :xds:test :xds-api:test :xds-validator:test :it:xds-client:test :it:xds-controlplane-api:test :it:xds-no-validation:test :it:xds-istio:test
16+
```
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright 2026 LY Corporation
3+
*
4+
* LY Corporation licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package com.linecorp.armeria.xds.client.endpoint;
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
21+
import java.util.concurrent.atomic.AtomicReference;
22+
import java.util.stream.Stream;
23+
24+
import org.junit.jupiter.api.extension.RegisterExtension;
25+
import org.junit.jupiter.params.ParameterizedTest;
26+
import org.junit.jupiter.params.provider.Arguments;
27+
import org.junit.jupiter.params.provider.MethodSource;
28+
29+
import com.linecorp.armeria.client.BlockingWebClient;
30+
import com.linecorp.armeria.client.WebClient;
31+
import com.linecorp.armeria.common.HttpResponse;
32+
import com.linecorp.armeria.common.SessionProtocol;
33+
import com.linecorp.armeria.server.ServerBuilder;
34+
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
35+
import com.linecorp.armeria.xds.XdsBootstrap;
36+
import com.linecorp.armeria.xds.it.XdsResourceReader;
37+
38+
import io.envoyproxy.envoy.config.bootstrap.v3.Bootstrap;
39+
40+
class HttpProtocolOptionsTest {
41+
42+
@RegisterExtension
43+
static ServerExtension server = new ServerExtension() {
44+
@Override
45+
protected void configure(ServerBuilder sb) {
46+
sb.http(0);
47+
sb.service("/", (ctx, req) -> HttpResponse.of("OK"));
48+
}
49+
};
50+
51+
// language=YAML
52+
private static final String BOOTSTRAP_TEMPLATE =
53+
"""
54+
static_resources:
55+
listeners:
56+
- name: my-listener
57+
api_listener:
58+
api_listener:
59+
"@type": type.googleapis.com/envoy.extensions.filters.network.\
60+
http_connection_manager.v3.HttpConnectionManager
61+
stat_prefix: http
62+
route_config:
63+
name: local_route
64+
virtual_hosts:
65+
- name: local_service
66+
domains: [ "*" ]
67+
routes:
68+
- match:
69+
prefix: /
70+
route:
71+
cluster: my-cluster
72+
http_filters:
73+
- name: envoy.filters.http.router
74+
typed_config:
75+
"@type": type.googleapis.com/envoy.extensions.filters.\
76+
http.router.v3.Router
77+
clusters:
78+
- name: my-cluster
79+
type: STATIC
80+
load_assignment:
81+
cluster_name: my-cluster
82+
endpoints:
83+
- lb_endpoints:
84+
- endpoint:
85+
address:
86+
socket_address:
87+
address: 127.0.0.1
88+
port_value: %d
89+
typed_extension_protocol_options:
90+
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
91+
"@type": type.googleapis.com/envoy.extensions.upstreams.\
92+
http.v3.HttpProtocolOptions
93+
%s
94+
""";
95+
96+
static Stream<Arguments> sessionProtocolSelection() {
97+
return Stream.of(
98+
Arguments.of("explicit_http_config:\n http2_protocol_options: {}",
99+
SessionProtocol.H2C),
100+
Arguments.of("explicit_http_config:\n http_protocol_options: {}",
101+
SessionProtocol.H1C),
102+
Arguments.of("auto_config: {}", SessionProtocol.HTTP)
103+
);
104+
}
105+
106+
@ParameterizedTest
107+
@MethodSource
108+
void sessionProtocolSelection(String protocolConfig,
109+
SessionProtocol expectedProtocol) {
110+
final String yaml = BOOTSTRAP_TEMPLATE.formatted(
111+
server.httpPort(), protocolConfig.indent(20).strip());
112+
final Bootstrap bootstrap = XdsResourceReader.fromYaml(yaml, Bootstrap.class);
113+
final AtomicReference<SessionProtocol> protocolRef = new AtomicReference<>();
114+
try (XdsBootstrap xdsBootstrap = XdsBootstrap.of(bootstrap);
115+
XdsHttpPreprocessor preprocessor =
116+
XdsHttpPreprocessor.ofListener("my-listener", xdsBootstrap)) {
117+
final BlockingWebClient client =
118+
WebClient.builder(preprocessor)
119+
.decorator((delegate, ctx, req) -> {
120+
protocolRef.set(ctx.sessionProtocol());
121+
return delegate.execute(ctx, req);
122+
})
123+
.build()
124+
.blocking();
125+
client.get("/");
126+
assertThat(protocolRef.get()).isEqualTo(expectedProtocol);
127+
}
128+
}
129+
}

0 commit comments

Comments
 (0)