Skip to content

Commit 078faba

Browse files
committed
more test cases
1 parent 26487ea commit 078faba

1 file changed

Lines changed: 215 additions & 0 deletions

File tree

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
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+
package com.linecorp.armeria.xds.it.server;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
20+
21+
import java.nio.file.Path;
22+
import java.util.concurrent.atomic.AtomicInteger;
23+
24+
import javax.net.ssl.SSLHandshakeException;
25+
26+
import org.junit.jupiter.api.Order;
27+
import org.junit.jupiter.api.Test;
28+
import org.junit.jupiter.api.extension.RegisterExtension;
29+
30+
import com.linecorp.armeria.client.BlockingWebClient;
31+
import com.linecorp.armeria.client.ClientTlsSpec;
32+
import com.linecorp.armeria.client.RequestOptions;
33+
import com.linecorp.armeria.client.WebClient;
34+
import com.linecorp.armeria.common.AggregatedHttpResponse;
35+
import com.linecorp.armeria.common.HttpMethod;
36+
import com.linecorp.armeria.common.HttpRequest;
37+
import com.linecorp.armeria.common.HttpResponse;
38+
import com.linecorp.armeria.common.HttpStatus;
39+
import com.linecorp.armeria.common.SessionProtocol;
40+
import com.linecorp.armeria.server.ConnectionAcceptor;
41+
import com.linecorp.armeria.server.ServerBuilder;
42+
import com.linecorp.armeria.server.ServerPort;
43+
import com.linecorp.armeria.server.ServerTlsProvider;
44+
import com.linecorp.armeria.server.ServerTlsSpec;
45+
import com.linecorp.armeria.testing.junit5.server.SelfSignedCertificateExtension;
46+
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
47+
import com.linecorp.armeria.xds.it.XdsCertificateExtension;
48+
import com.linecorp.armeria.xds.it.XdsControlPlaneExtension;
49+
import com.linecorp.armeria.xds.it.XdsResourceReader;
50+
import com.linecorp.armeria.xds.server.XdsServerPlugin;
51+
52+
import io.envoyproxy.envoy.config.listener.v3.Listener;
53+
54+
/**
55+
* Verifies that {@link XdsServerPlugin} correctly composes with user-configured
56+
* {@link ConnectionAcceptor} and {@link ServerTlsProvider} on both xDS-managed
57+
* and unmanaged ports.
58+
*/
59+
class ServerFallbackTest {
60+
61+
private static final String LISTENER_NAME = "fallback-listener";
62+
private static final ServerPort xdsPort =
63+
new ServerPort(0, SessionProtocol.HTTP, SessionProtocol.HTTPS);
64+
65+
private static final AtomicInteger acceptorCallCount = new AtomicInteger();
66+
67+
@RegisterExtension
68+
@Order(0)
69+
static final XdsCertificateExtension xdsCert =
70+
new XdsCertificateExtension(new SelfSignedCertificateExtension("127.0.0.1"));
71+
72+
@RegisterExtension
73+
@Order(1)
74+
static final SelfSignedCertificateExtension userCert =
75+
new SelfSignedCertificateExtension("127.0.0.1");
76+
77+
@RegisterExtension
78+
@Order(2)
79+
static final XdsControlPlaneExtension controlPlane = new XdsControlPlaneExtension();
80+
81+
@RegisterExtension
82+
@Order(3)
83+
static final ServerExtension server = new ServerExtension() {
84+
@Override
85+
protected void configure(ServerBuilder sb) {
86+
final Path certPath = xdsCert.certificateFile().toPath();
87+
final Path keyPath = xdsCert.privateKeyFile().toPath();
88+
89+
//language=YAML
90+
final String yaml =
91+
"""
92+
name: %s
93+
default_filter_chain:
94+
filters:
95+
- name: envoy.filters.network.http_connection_manager
96+
typed_config:
97+
"@type": type.googleapis.com/envoy.extensions.filters\
98+
.network.http_connection_manager.v3.HttpConnectionManager
99+
stat_prefix: ingress_http
100+
route_config:
101+
name: local_route
102+
virtual_hosts:
103+
- name: local_service
104+
domains: ["*"]
105+
routes:
106+
- match:
107+
prefix: "/"
108+
non_forwarding_action: {}
109+
http_filters:
110+
- name: envoy.filters.http.router
111+
transport_socket:
112+
name: envoy.transport_sockets.downstream_tls
113+
typed_config:
114+
"@type": type.googleapis.com/envoy.extensions.transport_sockets\
115+
.tls.v3.DownstreamTlsContext
116+
common_tls_context:
117+
tls_certificates:
118+
- certificate_chain:
119+
filename: "%s"
120+
private_key:
121+
filename: "%s"
122+
""".formatted(LISTENER_NAME, certPath, keyPath);
123+
controlPlane.set(XdsResourceReader.fromYaml(yaml, Listener.class));
124+
125+
// User-configured ConnectionAcceptor — should be called on all ports.
126+
sb.connectionAcceptor(ConnectionAcceptor.of(ctx -> {
127+
acceptorCallCount.incrementAndGet();
128+
return true;
129+
}));
130+
131+
// User-configured ServerTlsProvider for the unmanaged HTTPS port.
132+
final ServerTlsSpec userTlsSpec = ServerTlsSpec.builder()
133+
.tlsKeyPair(userCert.tlsKeyPair())
134+
.build();
135+
sb.tlsProvider(ServerTlsProvider.of(ctx -> userTlsSpec));
136+
137+
// Add an explicit unmanaged HTTPS port (not controlled by xDS).
138+
sb.https(0);
139+
140+
sb.plugin(XdsServerPlugin.builder(controlPlane.bootstrap(), LISTENER_NAME)
141+
.port(xdsPort)
142+
.build());
143+
sb.service("/hello", (ctx, req) -> HttpResponse.of("hello"));
144+
}
145+
};
146+
147+
@Test
148+
void connectionAcceptorCalledOnManagedPort() {
149+
acceptorCallCount.set(0);
150+
final ClientTlsSpec tlsSpec = ClientTlsSpec.builder()
151+
.trustedCertificates(xdsCert.certificate())
152+
.build();
153+
final BlockingWebClient client =
154+
WebClient.of("https://127.0.0.1:" + xdsPort.actualPort()).blocking();
155+
final AggregatedHttpResponse res = client.execute(
156+
HttpRequest.of(HttpMethod.GET, "/hello"),
157+
RequestOptions.builder().clientTlsSpec(tlsSpec).build());
158+
assertThat(res.status()).isEqualTo(HttpStatus.OK);
159+
assertThat(res.contentUtf8()).isEqualTo("hello");
160+
assertThat(acceptorCallCount.get()).isGreaterThanOrEqualTo(1);
161+
}
162+
163+
@Test
164+
void connectionAcceptorCalledOnUnmanagedPort() {
165+
acceptorCallCount.set(0);
166+
final AggregatedHttpResponse res =
167+
WebClient.of(server.httpUri()).blocking().get("/hello");
168+
assertThat(res.status()).isEqualTo(HttpStatus.OK);
169+
assertThat(res.contentUtf8()).isEqualTo("hello");
170+
assertThat(acceptorCallCount.get()).isGreaterThanOrEqualTo(1);
171+
}
172+
173+
@Test
174+
void tlsProviderFallbackOnUnmanagedPort() {
175+
// The unmanaged HTTPS port should use the user-configured TLS provider (userCert),
176+
// not the xDS certificate.
177+
final int unmanagedHttpsPort = server.server().activePorts().values().stream()
178+
.filter(ServerPort::hasHttps)
179+
.map(p -> p.localAddress().getPort())
180+
.filter(p -> p != xdsPort.actualPort())
181+
.findFirst()
182+
.orElseThrow();
183+
final ClientTlsSpec userTlsSpec = ClientTlsSpec.builder()
184+
.trustedCertificates(userCert.certificate())
185+
.build();
186+
final BlockingWebClient client =
187+
WebClient.of("https://127.0.0.1:" + unmanagedHttpsPort).blocking();
188+
final AggregatedHttpResponse res = client.execute(
189+
HttpRequest.of(HttpMethod.GET, "/hello"),
190+
RequestOptions.builder().clientTlsSpec(userTlsSpec).build());
191+
assertThat(res.status()).isEqualTo(HttpStatus.OK);
192+
assertThat(res.contentUtf8()).isEqualTo("hello");
193+
}
194+
195+
@Test
196+
void xdsCertNotTrustedOnUnmanagedPort() {
197+
// The xDS certificate should NOT work on the unmanaged port — that port uses
198+
// userCert, so trusting xdsCert should fail the TLS handshake.
199+
final int unmanagedHttpsPort = server.server().activePorts().values().stream()
200+
.filter(ServerPort::hasHttps)
201+
.map(p -> p.localAddress().getPort())
202+
.filter(p -> p != xdsPort.actualPort())
203+
.findFirst()
204+
.orElseThrow();
205+
final ClientTlsSpec xdsTlsSpec = ClientTlsSpec.builder()
206+
.trustedCertificates(xdsCert.certificate())
207+
.build();
208+
final BlockingWebClient client =
209+
WebClient.of("https://127.0.0.1:" + unmanagedHttpsPort).blocking();
210+
assertThatThrownBy(() -> client.execute(
211+
HttpRequest.of(HttpMethod.GET, "/hello"),
212+
RequestOptions.builder().clientTlsSpec(xdsTlsSpec).build()))
213+
.hasCauseInstanceOf(SSLHandshakeException.class);
214+
}
215+
}

0 commit comments

Comments
 (0)