diff --git a/docs/src/main/asciidoc/spring-cloud-gateway.adoc b/docs/src/main/asciidoc/spring-cloud-gateway.adoc
index 0285d51a..df165283 100644
--- a/docs/src/main/asciidoc/spring-cloud-gateway.adoc
+++ b/docs/src/main/asciidoc/spring-cloud-gateway.adoc
@@ -1894,7 +1894,7 @@ spring:
When a request is made through the gateway to `/json/hello` the request will be transformed using the definition provided in `hello.proto`, sent to `com.example.grpcserver.hello.HelloService/hello`, and transform the response back to JSON.
-By default, it will create a `NettyChannel` using the default `TrustManagerFactory`. However, this `TrustManager` can be customized by creating a bean of type `GRPCSSLContext`:
+By default, it will create a `NettyChannel` using the default `TrustManagerFactory`. However, this `TrustManager` can be customized by creating a bean of type `GrpcSslConfigurer`:
[source,java]
----
diff --git a/spring-cloud-gateway-integration-tests/grpc/pom.xml b/spring-cloud-gateway-integration-tests/grpc/pom.xml
index 29bc5339..3c89d222 100644
--- a/spring-cloud-gateway-integration-tests/grpc/pom.xml
+++ b/spring-cloud-gateway-integration-tests/grpc/pom.xml
@@ -30,9 +30,13 @@
org.springframework.cloud
spring-cloud-starter-gateway
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
io.grpc
- grpc-netty-shaded
+ grpc-netty
${grpc.version}
diff --git a/spring-cloud-gateway-integration-tests/grpc/src/main/java/org/springframework/cloud/gateway/tests/grpc/GRPCApplication.java b/spring-cloud-gateway-integration-tests/grpc/src/main/java/org/springframework/cloud/gateway/tests/grpc/GRPCApplication.java
index a4204b68..a417fa3e 100644
--- a/spring-cloud-gateway-integration-tests/grpc/src/main/java/org/springframework/cloud/gateway/tests/grpc/GRPCApplication.java
+++ b/spring-cloud-gateway-integration-tests/grpc/src/main/java/org/springframework/cloud/gateway/tests/grpc/GRPCApplication.java
@@ -20,23 +20,20 @@ import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
-import javax.net.ssl.SSLException;
-
import io.grpc.Grpc;
import io.grpc.Server;
import io.grpc.ServerCredentials;
import io.grpc.TlsServerCredentials;
-import io.grpc.netty.shaded.io.grpc.netty.NettySslContextServerCredentials;
-import io.grpc.netty.shaded.io.netty.handler.ssl.ApplicationProtocolConfig;
-import io.grpc.netty.shaded.io.netty.handler.ssl.ApplicationProtocolNames;
-import io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder;
import io.grpc.stub.StreamObserver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
@@ -47,61 +44,39 @@ import org.springframework.stereotype.Component;
@EnableAutoConfiguration
public class GRPCApplication {
- private static final int GRPC_SERVER_PORT = 8095;
-
public static void main(String[] args) {
SpringApplication.run(GRPCApplication.class, args);
}
- // @Bean
- // public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
- // return builder.routes().route("json-grpc", r -> r.path("/json/hello").filters(f ->
- // {
- // String protoDescriptor = "file:src/main/proto/hello.pb";
- // String protoFile = "file:src/main/proto/hello.proto";
- // String service = "HelloService";
- // String method = "hello";
- // return f.jsonToGRPC(protoDescriptor, protoFile, service, method);
- // }).uri("https://localhost:" + GRPC_SERVER_PORT))
- // .route("grpc", r -> r.predicate(p -> true).uri("https://localhost:" +
- // GRPC_SERVER_PORT)).build();
- // }
- //
- // @Bean
- // public GRPCSSLContext sslContext() throws SSLException {
- // TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
- // public X509Certificate[] getAcceptedIssuers() {
- // return new X509Certificate[0];
- // }
- //
- // public void checkClientTrusted(X509Certificate[] certs, String authType) {
- // }
- //
- // public void checkServerTrusted(X509Certificate[] certs, String authType) {
- // }
- // } };
- //
- // return new GRPCSSLContext(trustAllCerts[0]);
- // }
-
@Component
static class GRPCServer implements ApplicationRunner {
+ private static final Logger log = LoggerFactory.getLogger(GRPCServer.class);
+
+ private final Environment environment;
+
private Server server;
+ GRPCServer(Environment environment) {
+ this.environment = environment;
+ }
+
@Override
public void run(ApplicationArguments args) throws Exception {
- final GRPCServer server = new GRPCServer();
+ final GRPCServer server = new GRPCServer(environment);
server.start();
}
- private void start() throws Exception {
- /* The port on which the server should run */
+ private void start() throws IOException {
+ Integer serverPort = environment.getProperty("local.server.port", Integer.class);
+ int grpcPort = serverPort + 1;
+ /*
+ * The port on which the server should run. We run
+ */
ServerCredentials creds = createServerCredentials();
- server = Grpc.newServerBuilderForPort(GRPC_SERVER_PORT, creds)
- .addService(new HelloService()).build().start();
+ server = Grpc.newServerBuilderForPort(grpcPort, creds).addService(new HelloService()).build().start();
- System.out.println("Starting gRPC server in port " + GRPC_SERVER_PORT);
+ log.info("Starting gRPC server in port " + grpcPort);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
@@ -116,29 +91,10 @@ public class GRPCApplication {
private ServerCredentials createServerCredentials() throws IOException {
File certChain = new ClassPathResource("public.cert").getFile();
File privateKey = new ClassPathResource("private.key").getFile();
- File keystore = new ClassPathResource("keystore.p12").getFile();
- // return nettyCredentials(certChain, privateKey);
+
return TlsServerCredentials.create(certChain, privateKey);
}
- private ServerCredentials nettyCredentials(File certChain, File privateKey)
- throws SSLException {
- ApplicationProtocolConfig apn = new ApplicationProtocolConfig(
- ApplicationProtocolConfig.Protocol.ALPN,
- // NO_ADVERTISE is currently the only mode supported by both OpenSsl
- // and JDK providers.
- ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
- // ACCEPT is currently the only mode supported by both OpenSsl and JDK
- // providers.
- ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
- ApplicationProtocolNames.HTTP_2, ApplicationProtocolNames.HTTP_1_1);
-
- ServerCredentials serverCredentials = NettySslContextServerCredentials
- .create(SslContextBuilder.forServer(certChain, privateKey)
- .applicationProtocolConfig(apn).build());
- return serverCredentials;
- }
-
private void stop() throws InterruptedException {
if (server != null) {
server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
@@ -148,15 +104,12 @@ public class GRPCApplication {
static class HelloService extends HelloServiceGrpc.HelloServiceImplBase {
@Override
- public void hello(HelloRequest request,
- StreamObserver responseObserver) {
+ public void hello(HelloRequest request, StreamObserver responseObserver) {
- String greeting = "Hello, " + request.getFirstName() + " "
- + request.getLastName();
- System.out.println("Sending response: " + greeting);
+ String greeting = String.format("Hello, %s %s", request.getFirstName(), request.getLastName());
+ log.info("Sending response: " + greeting);
- HelloResponse response = HelloResponse.newBuilder().setGreeting(greeting)
- .build();
+ HelloResponse response = HelloResponse.newBuilder().setGreeting(greeting).build();
responseObserver.onNext(response);
responseObserver.onCompleted();
diff --git a/spring-cloud-gateway-integration-tests/grpc/src/main/resources/application.yml b/spring-cloud-gateway-integration-tests/grpc/src/main/resources/application.yml
old mode 100755
new mode 100644
diff --git a/spring-cloud-gateway-integration-tests/grpc/src/main/resources/keystore.p12 b/spring-cloud-gateway-integration-tests/grpc/src/main/resources/keystore.p12
index 45662014..991ecbcf 100644
Binary files a/spring-cloud-gateway-integration-tests/grpc/src/main/resources/keystore.p12 and b/spring-cloud-gateway-integration-tests/grpc/src/main/resources/keystore.p12 differ
diff --git a/spring-cloud-gateway-integration-tests/grpc/src/main/resources/private.key b/spring-cloud-gateway-integration-tests/grpc/src/main/resources/private.key
old mode 100755
new mode 100644
index 9c1d11f1..d9952a36
--- a/spring-cloud-gateway-integration-tests/grpc/src/main/resources/private.key
+++ b/spring-cloud-gateway-integration-tests/grpc/src/main/resources/private.key
@@ -1,32 +1,32 @@
Bag Attributes
friendlyName: bootapp
- localKeyID: 54 69 6D 65 20 31 36 35 39 37 31 32 34 31 30 37 34 35
+ localKeyID: 54 69 6D 65 20 31 36 35 39 39 37 34 34 38 34 30 33 30
Key Attributes:
-----BEGIN PRIVATE KEY-----
-MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCr+JyQOvGQcuSm
-t1BN615YtrI0IoRFxfWHcbvU26A3TiGQtlnvXUIUTZApm1uSLR0o2VeS6ph+i3Ku
-WFZTjF9wTem4IY8U22f4SymD7tFU/X4//YL0GgcyJCT/gGaYY58EVEH8+K3Pz1Hy
-z/UnNuRrttxo+u94tB3OgTAYBB1KqY7Bqe7iZGleNz5baatOzYfEXmW4owHmqnfE
-LZAmXdFj55Rbndkr0LBiKkBHM8aDC1l0OSJBzKt6RF07+we8Nte1Oynd2F3lZRHa
-D8Pk7leDuV+KjC4W0X6kkwvZftmT/Y9C18+t2+J1BAz0kMsDjH1P87BconqVEhcM
-PehXGgXFAgMBAAECggEAIzrxSBLrPf5rnUPcrbnUQDRdWZTgqDKf1DmWk0rTDcFx
-2uWgkwr16JbjO8LaBZ48ZQvxhuWMjBAhVFpAhSkyvB0aDmDBoTI5oII1ZRPdyp2L
-6awT0dIrOzhwY+94FSwDfa2NPzfq07HTRf0YagoyzWZOzSrrOD0eBhotMh5Vqd+w
-UJ3VJCAWC0/Nz/4N5Ia2Hkyn2fz4fXdzux+/s5UKqaEHCXQWYmHj0XFLYSUb9rrO
-qMYKM8sJjma4R3XthNalZEd6TD+07jNl6BwFmMBMj6D0VBWxV02woKkoWltmydUD
-XhkdikCAB33CvHoj56PqCAFHrI6tnSRs9UqF/HPGQQKBgQDlfTJ3l2Orq1hy/D3d
-8Z3ndOthwjm7QLOX8dJi4CHtymX8Lg4qfHcrs5ltoazXMYgdFHLX1rNs+gJg9I5p
-vQS2jpRJ3BMzddrHaXjqhHv8WY1VYkPGML2tPol51LU7fx+2g+YnMzWNB7AIUz9K
-OZkvlPWRO6jOcjvGgLjKARQUzQKBgQC/1mgGpKKBnAfEoDGZ1SWpI21BFKL3DWMx
-YaOeFx+DETfAoUeIu1Ge2u8AIhrNE4dT8xOcG0vwGzHhKV63pabbMZ4+2pE92SEw
-U6VsGvd/lcKPJvxgLS3xFQEzEmgL4v1mSBsxqaGZf4XHpdb32bQeybAQwUm25MCw
-sEq/ZkX02QKBgAJOvvoq4IqyX2JQnQKlUlQofdFu1YvHe8bUXKw32r98YIgnombU
-95HN7YYHsSg8zESWlw0KkKVQ4kM9Uk1H5Es9pcoUV3EWB0woCFo7WM/RyrUIFuxg
-QxgB3/oEpInjnlsEeoT8Y9Z2NFPxGlrRt3OeCNYcBneE+IrncnQ9jIklAoGACUF0
-/20OEibPESORgRSRnpmA+fTe8ACLWqSVWllPQemgwQCHDQfMpld11JHQrThV/Szr
-M0r13P3S6EQwt7ecV1MLiYjOHSfOvCAtCQw8CMHXA0UkRBep8cnLpwUqU/h9tWOh
-PEIs89T9RWaw+oBcemfMwOIyhkp/KYc39AghM/ECgYAdTIT7J613mg8Ck/eUOBwx
-udT1wbpcrCqHJX8TKWmBOLh9teZvmq7OIuXGudf1tDRjw85p/7bYBcG+gvaiH0Yf
-qz5Npr4/+knxI2CBF2m81ln4Otq3X5siDL1wPCswawpnfdH+0qyfH6nxeGy0XSj2
-IQMa/LX1XqbUCry4sFl0yQ==
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQClCZtSgRjM2uYM
+O70+acD4rhwcc8ND23SwoKTNLPH6s1K2Pi5MZqF4Rnx+REJwerQrkgfMnsQ0QHcb
+NjI9dhSwHw6aPigAGJQEzR8whDu2PkR57iBmySr3GLtpR+WkVGo/xAK92ozKt9aW
+Saxm0miygYI8mgzrNpbaQAu6sZcS3RHkPYWw/izYVZLBB6IEpAJJrUqjzlu53TyN
+X4VC4FWddItjOCiyrHe2+A+tFLNK3IZnFZBqoMcYR6/Rq+zsXx8ynCggkUwTFaPd
+IVyzSYtooE2oq+aSjkJzKvoPA7BObLnrW+5Dklwj1iPtLhfuCflz2oa54/Hpl60R
+Ke6y6/t5AgMBAAECggEAQvJ/sFswDUGq2kGNhfjuT7KJMr1+81Ldphy0XYqi6li9
+77GPpGxpidnF/I6CCRCtb5NAWK/61VtlNYOpo6b3w24FxWn7XfaabMwsn8i0VDw0
+GOYQ/MEUDcJZm96PeDbKFu3TUuKKBF3IzZQ7PEaUM/03MJApN20gio46c9RAfWTK
+hN9jX9YkBpkSWPfx9RA9BOIXRrRMTXhidsVPnNxhhM7OAGZ1LhggDPaeiCIbVx6l
+yMsyCkNvqdQbKu0hFcVyeQsGjovCC1Ye71F3em3ACguqDu+VAy3flpTbcDVf2D7T
+zP2T5e5OEJ2hRTGdnEVSsXxWOWnq6Gt6ydNEe2AWVQKBgQC5HcQAUTnvIMaTez44
+7SBfELNY1AzF24dha24JFIGY78QNRZi+4Agsvxjkx4fKSd4DfmPsCqentmEkEcDr
+w6fH0CAFm/rrkzLHmesBCC8byl4WtfMW5i8h1yzOeQbYpx3MGIu5xhkE92gbNj+o
+ioM7nkoBvloU0odiYTVnoNYnFwKBgQDkO5ldA8Vl3+BWhmhOtPu+1EKssWry5rdO
+m6IiZ1uB6uG4bhAj5dSVVgDRX6pcuppMVZqIbPpdkS7/i0LVwohT7EmpMMJ5bezz
+EhG1z6AvZDcGMDC/PPMp7SLjJviYZDaTWLekKwY6yV1T9gtOY70jjWkUcfN1IHpA
+renHNNyL7wKBgQCEYvIqW+y3xFPfY1MzePoeop3wl+3ujjo6hI7z9XNdgZNO/ofn
+cebGwX+3Fa9aDwu0qe4h/9i4y2ibWAsFUS6ran+MI2oGkYXOU5hKa6TtFgPF8CfC
+J6prZCxKGSm5RYK81I0Qtchs0dblJx3NlgmWWHSK3Kwlmg5yYBzGWLLuzwKBgDzg
+rNrDq76txcAun9oGqnPPWG2J8XYTFmgQWWIF4cG4rjasnP+GSXr/8r3mX6HWYFvm
+JY8oSmv00u107wHnnseL6mYHzIfpS1/WvQSa+iZJ++dZqVcJYe8YAstGVN8JNAl/
+i5RtqX66wXso0QE613OJP7MlZgQjApkICqiJMB7fAoGAVFdv/Nphcju7MCkXGuQe
+amk7Z6Q2PjMWvLwiyB39PrIzvTOdaQyQk7oYdnAGFJbkmi7mmGiet5uvcE9OjNRb
++XeXo2LEZ2QWJEnHi9uCXSQF3eeBlJ2wGUsELPTHglWIUTpi5iDB2hHzczPzO8GY
+qPb+ckCl8mTsHRKQQse4Bm0=
-----END PRIVATE KEY-----
diff --git a/spring-cloud-gateway-integration-tests/grpc/src/main/resources/public.cert b/spring-cloud-gateway-integration-tests/grpc/src/main/resources/public.cert
old mode 100755
new mode 100644
index e48355c8..ecb750c6
--- a/spring-cloud-gateway-integration-tests/grpc/src/main/resources/public.cert
+++ b/spring-cloud-gateway-integration-tests/grpc/src/main/resources/public.cert
@@ -1,21 +1,18 @@
-----BEGIN CERTIFICATE-----
-MIIDezCCAmOgAwIBAgIIAZ+HRnyzONYwDQYJKoZIhvcNAQELBQAwbDEQMA4GA1UE
-BhMHVW5rbm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQ
-MA4GA1UEChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5r
-bm93bjAeFw0yMjA4MDUxNTEzMzBaFw0zMjA4MDIxNTEzMzBaMGwxEDAOBgNVBAYT
-B1Vua25vd24xEDAOBgNVBAgTB1Vua25vd24xEDAOBgNVBAcTB1Vua25vd24xEDAO
-BgNVBAoTB1Vua25vd24xEDAOBgNVBAsTB1Vua25vd24xEDAOBgNVBAMTB1Vua25v
-d24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCr+JyQOvGQcuSmt1BN
-615YtrI0IoRFxfWHcbvU26A3TiGQtlnvXUIUTZApm1uSLR0o2VeS6ph+i3KuWFZT
-jF9wTem4IY8U22f4SymD7tFU/X4//YL0GgcyJCT/gGaYY58EVEH8+K3Pz1Hyz/Un
-NuRrttxo+u94tB3OgTAYBB1KqY7Bqe7iZGleNz5baatOzYfEXmW4owHmqnfELZAm
-XdFj55Rbndkr0LBiKkBHM8aDC1l0OSJBzKt6RF07+we8Nte1Oynd2F3lZRHaD8Pk
-7leDuV+KjC4W0X6kkwvZftmT/Y9C18+t2+J1BAz0kMsDjH1P87BconqVEhcMPehX
-GgXFAgMBAAGjITAfMB0GA1UdDgQWBBTC2DG76TuGW8VAEJawy96C2VPBpDANBgkq
-hkiG9w0BAQsFAAOCAQEAduMMKQvGMMbmN6OdnrV9oBzpphKs1BWvbDBPsSrFm1zX
-d1yRLSn2GyICQIA7SNX6Z59rWHe2HSXtzW6gbXm0yvAcPBI1DcRbamO12VF75Xjl
-yg9oGuwckIcK2YtqYw7iTjFHyDFShTdWpYePxmyZ4HyZKWX3gKn1hGHA0SWYUJY+
-HkfPat0iwuwXDfAoyMchZrd7mOsXAhlD6+39gw9Vr+WhRFhv29hU8uuzcY+ahZfQ
-9H8OF2nW0ePcc41909uorWfa3hWAVA2xfvzoBunFVMJt6mzT5M2Es5I/0wSXflbU
-e4NjO4usVx7aU3jPkHEPGlDrwFJ8olDpKPutr2dGVA==
+MIIC1zCCAb+gAwIBAgIIEsL9TgSEch8wDQYJKoZIhvcNAQELBQAwGjEYMBYGA1UE
+AxMPbXktYm9vdC1hcHAub3JnMB4XDTIyMDgwODE2MDEyNFoXDTMyMDgwNTE2MDEy
+NFowGjEYMBYGA1UEAxMPbXktYm9vdC1hcHAub3JnMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEApQmbUoEYzNrmDDu9PmnA+K4cHHPDQ9t0sKCkzSzx+rNS
+tj4uTGaheEZ8fkRCcHq0K5IHzJ7ENEB3GzYyPXYUsB8Omj4oABiUBM0fMIQ7tj5E
+ee4gZskq9xi7aUflpFRqP8QCvdqMyrfWlkmsZtJosoGCPJoM6zaW2kALurGXEt0R
+5D2FsP4s2FWSwQeiBKQCSa1Ko85bud08jV+FQuBVnXSLYzgosqx3tvgPrRSzStyG
+ZxWQaqDHGEev0avs7F8fMpwoIJFMExWj3SFcs0mLaKBNqKvmko5Ccyr6DwOwTmy5
+61vuQ5JcI9Yj7S4X7gn5c9qGuePx6ZetESnusuv7eQIDAQABoyEwHzAdBgNVHQ4E
+FgQUf72EgsQASMCzSGwrFDaKnBoufm8wDQYJKoZIhvcNAQELBQADggEBAAHzJkp/
+q+U0ki+JaFaUBYg85h+yJbi262hmFjLYDQEcz2tvAw93X9ytOzVtmo2H9AgsTJYA
+z4UbJHfnTTa1KAHDHhzL/Adh/s2OZ7Y2kazLrHArFteKtGUo815JDL935JpiRBWc
+Qn1WJJ6EFb+ZRMG7oROQ3LgvrradH8G51qxvSbH/fpmEFDTSHJNIG8tXcIA3fJkI
+dsVJLygrXLzZQo5xI8rwMDX9aujRFU4Dgit58xquCkuzob/BonN7Es1Hp3beQjro
+gddts4x5wWyiFun7uYrbPVhGMJNgwRKB5e3QQA6vYDyeQqvIy6n1lnDNIxvaOfPW
+Je5gI59jCbxHs5g=
-----END CERTIFICATE-----
diff --git a/spring-cloud-gateway-integration-tests/grpc/src/test/java/org/springframework/cloud/gateway/tests/grpc/GRPCApplicationTests.java b/spring-cloud-gateway-integration-tests/grpc/src/test/java/org/springframework/cloud/gateway/tests/grpc/GRPCApplicationTests.java
index 0c6289ed..f540862a 100644
--- a/spring-cloud-gateway-integration-tests/grpc/src/test/java/org/springframework/cloud/gateway/tests/grpc/GRPCApplicationTests.java
+++ b/spring-cloud-gateway-integration-tests/grpc/src/test/java/org/springframework/cloud/gateway/tests/grpc/GRPCApplicationTests.java
@@ -23,8 +23,8 @@ import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import io.grpc.ManagedChannel;
-import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
-import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
+import io.grpc.netty.GrpcSslContexts;
+import io.grpc.netty.NettyChannelBuilder;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -32,7 +32,7 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.test.annotation.DirtiesContext;
-import static io.grpc.netty.shaded.io.grpc.netty.NegotiationType.TLS;
+import static io.grpc.netty.NegotiationType.TLS;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
/**
@@ -47,7 +47,7 @@ public class GRPCApplicationTests {
@Test
public void gRPCUnaryCalShouldReturnResponse() throws SSLException {
- ManagedChannel channel = createSecuredChannel(port);
+ ManagedChannel channel = createSecuredChannel(port + 1);
final HelloResponse response = HelloServiceGrpc.newBlockingStub(channel)
.hello(HelloRequest.newBuilder().setFirstName("Sir").setLastName("FromClient").build());
diff --git a/spring-cloud-gateway-integration-tests/grpc/src/test/java/org/springframework/cloud/gateway/tests/grpc/JsonToGrpcApplicationTests.java b/spring-cloud-gateway-integration-tests/grpc/src/test/java/org/springframework/cloud/gateway/tests/grpc/JsonToGrpcApplicationTests.java
index 48af3d0b..b0ec1e62 100644
--- a/spring-cloud-gateway-integration-tests/grpc/src/test/java/org/springframework/cloud/gateway/tests/grpc/JsonToGrpcApplicationTests.java
+++ b/spring-cloud-gateway-integration-tests/grpc/src/test/java/org/springframework/cloud/gateway/tests/grpc/JsonToGrpcApplicationTests.java
@@ -19,6 +19,11 @@ package org.springframework.cloud.gateway.tests.grpc;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
import javax.net.ssl.SSLContext;
@@ -39,13 +44,19 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.client.RestTemplate;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
/**
* @author Alberto C. Ríos
+ * @author Abel Salgado Romero
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class JsonToGrpcApplicationTests {
@@ -58,12 +69,18 @@ public class JsonToGrpcApplicationTests {
@BeforeEach
void setUp() {
restTemplate = createUnsecureClient();
-// restTemplate = new RestTemplate();
}
@Test
public void shouldConvertFromJSONToGRPC() {
- String response = restTemplate.postForEntity("https://localhost:" + port + "/json/hello",
+ // Since GRPC server and GW run in same instance and don't know server port until
+ // test starts,
+ // we need to configure route dynamically using the actuator endpoint.
+ final RouteConfigurer configurer = new RouteConfigurer(port);
+ configurer.addRoute(port + 1, "/json/hello",
+ "JsonToGrpc=file:src/main/proto/hello.pb,file:src/main/proto/hello.proto,HelloService,hello");
+
+ String response = restTemplate.postForEntity("https://localhost:" + this.port + "/json/hello",
"{\"firstName\":\"Duff\", \"lastName\":\"McKagan\"}", String.class).getBody();
Assertions.assertThat(response).isNotNull();
@@ -95,4 +112,46 @@ public class JsonToGrpcApplicationTests {
return new RestTemplate(requestFactory);
}
+ class RouteConfigurer {
+
+ private final WebTestClient actuatorWebClient;
+
+ private final int actuatorPort;
+
+ RouteConfigurer(int actuatorPort) {
+ this.actuatorPort = actuatorPort;
+ this.actuatorWebClient = WebTestClient.bindToServer().baseUrl("http://localhost:" + actuatorPort).build();
+
+ }
+
+ public void addRoute(int uriPort, String path, String filter) {
+ final String routeId = "test-route-" + UUID.randomUUID();
+
+ Map route = new HashMap<>();
+ route.put("id", routeId);
+ route.put("uri", "http://localhost:" + uriPort);
+ route.put("predicates", Collections.singletonList("Path=" + path));
+ route.put("filters", Arrays.asList(filter));
+
+ ResponseEntity exchange = restTemplate.exchange(url("/actuator/gateway/routes/" + routeId),
+ HttpMethod.POST, new HttpEntity<>(route), String.class);
+
+ assert exchange.getStatusCode() == HttpStatus.CREATED;
+
+ refreshRoutes();
+ }
+
+ private void refreshRoutes() {
+ ResponseEntity exchange = restTemplate.exchange(url("/actuator/gateway/refresh"), HttpMethod.POST,
+ new HttpEntity<>(""), String.class);
+
+ assert exchange.getStatusCode() == HttpStatus.OK;
+ }
+
+ private String url(String context) {
+ return String.format("https://localhost:%s%s", this.actuatorPort, context);
+ }
+
+ }
+
}
diff --git a/spring-cloud-gateway-integration-tests/grpc/src/test/resources/application.yml b/spring-cloud-gateway-integration-tests/grpc/src/test/resources/application.yml
index 510c30ea..97c1b950 100644
--- a/spring-cloud-gateway-integration-tests/grpc/src/test/resources/application.yml
+++ b/spring-cloud-gateway-integration-tests/grpc/src/test/resources/application.yml
@@ -2,19 +2,21 @@ server:
http2:
enabled: true
ssl:
- key-store-type: PKCS12
key-store: classpath:keystore.p12
+ key-store-type: pkcs12
key-store-password: password
- key-password: password
- enabled: true
key-alias: bootapp
-# key-store-provider: SUN
-# trust-certificate: classpath:certificate.pem
-# trust-certificate-private-key: classpath:private.key
-# trust-store: classpath:keystore.jks
-# trust-store-password: password
-# trust-certificate-private-key: classpath:private.key
-# trust-store-provider: classpath:keystore.jks
+
+management:
+ endpoint:
+ health:
+ show-details: when_authorized
+ gateway:
+ enabled: true
+ endpoints:
+ web:
+ exposure:
+ include: "*"
spring:
cloud:
@@ -24,27 +26,10 @@ spring:
httpclient:
wiretap: true
ssl:
- key-store-type: PKCS12
- key-store: classpath:keystore.p12
- key-store-password: password
- key-password: password
use-insecure-trust-manager: true
-# key-store-type: JKS
-# key-store: classpath:keystore.jks
-# key-store-password: password
-# trusted-x509-certificates: classpath:certificate.pem
-# trust-store: classpath:keystore.jks
-# trust-store-password: password
-# trust-certificate-private-key: classpath:private.key
-# key-store-provider: classpath:keystore.jks
-# key-password: password
-
- routes:
- - uri: https://localhost:8095
- predicates:
- - Path=/json/hello
- filters:
- - JsonToGrpc=file:src/main/proto/hello.pb,file:src/main/proto/hello.proto,HelloService,hello
+# Requires faking domain name (modifying /etc/hosts)
+# trustedX509Certificates:
+# - classpath:public.cert
logging:
level:
diff --git a/spring-cloud-gateway-server/pom.xml b/spring-cloud-gateway-server/pom.xml
index d3756764..1c422fbe 100644
--- a/spring-cloud-gateway-server/pom.xml
+++ b/spring-cloud-gateway-server/pom.xml
@@ -94,7 +94,7 @@
io.grpc
- grpc-netty-shaded
+ grpc-netty
true
${grpc.version}
diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/AbstractSslConfigurer.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/AbstractSslConfigurer.java
new file mode 100644
index 00000000..5f310d3f
--- /dev/null
+++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/AbstractSslConfigurer.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2013-2020 the original author or 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
+ *
+ * https://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 org.springframework.cloud.gateway.config;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchProviderException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.TrustManagerFactory;
+
+import io.netty.handler.ssl.SslContextBuilder;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.util.ResourceUtils;
+
+/**
+ * Base class to configure SSL for component T. Returns an instance S with the resulting
+ * configuration (can be the same as T).
+ *
+ * @author Abel Salgado Romero
+ */
+public abstract class AbstractSslConfigurer {
+
+ protected final Log logger = LogFactory.getLog(this.getClass());
+
+ private final HttpClientProperties.Ssl ssl;
+
+ protected AbstractSslConfigurer(HttpClientProperties.Ssl sslProperties) {
+ this.ssl = sslProperties;
+ }
+
+ abstract public S configureSsl(T client) throws SSLException;
+
+ protected HttpClientProperties.Ssl getSslProperties() {
+ return ssl;
+ }
+
+ protected X509Certificate[] getTrustedX509CertificatesForTrustManager() {
+
+ try {
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+ ArrayList allCerts = new ArrayList<>();
+ for (String trustedCert : ssl.getTrustedX509Certificates()) {
+ try {
+ URL url = ResourceUtils.getURL(trustedCert);
+ Collection extends Certificate> certs = certificateFactory.generateCertificates(url.openStream());
+ allCerts.addAll(certs);
+ }
+ catch (IOException e) {
+ throw new RuntimeException("Could not load certificate '" + trustedCert + "'", e);
+ }
+ }
+ return allCerts.toArray(new X509Certificate[allCerts.size()]);
+ }
+ catch (CertificateException e1) {
+ throw new RuntimeException("Could not load CertificateFactory X.509", e1);
+ }
+ }
+
+ protected KeyManagerFactory getKeyManagerFactory() {
+
+ try {
+ if (ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0) {
+ KeyManagerFactory keyManagerFactory = KeyManagerFactory
+ .getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ char[] keyPassword = ssl.getKeyPassword() != null ? ssl.getKeyPassword().toCharArray() : null;
+
+ if (keyPassword == null && ssl.getKeyStorePassword() != null) {
+ keyPassword = ssl.getKeyStorePassword().toCharArray();
+ }
+
+ keyManagerFactory.init(this.createKeyStore(), keyPassword);
+
+ return keyManagerFactory;
+ }
+
+ return null;
+ }
+ catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ protected KeyStore createKeyStore() {
+
+ try {
+ KeyStore store = ssl.getKeyStoreProvider() != null
+ ? KeyStore.getInstance(ssl.getKeyStoreType(), ssl.getKeyStoreProvider())
+ : KeyStore.getInstance(ssl.getKeyStoreType());
+ try {
+ URL url = ResourceUtils.getURL(ssl.getKeyStore());
+ store.load(url.openStream(),
+ ssl.getKeyStorePassword() != null ? ssl.getKeyStorePassword().toCharArray() : null);
+ }
+ catch (Exception e) {
+ throw new RuntimeException("Could not load key store ' " + ssl.getKeyStore() + "'", e);
+ }
+
+ return store;
+ }
+ catch (KeyStoreException | NoSuchProviderException e) {
+ throw new RuntimeException("Could not load KeyStore for given type and provider", e);
+ }
+ }
+
+ protected void setTrustManager(SslContextBuilder sslContextBuilder, X509Certificate... trustedX509Certificates) {
+ sslContextBuilder.trustManager(trustedX509Certificates);
+ }
+
+ protected void setTrustManager(SslContextBuilder sslContextBuilder, TrustManagerFactory factory) {
+ sslContextBuilder.trustManager(factory);
+ }
+
+}
diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GRPCSSLContext.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GRPCSSLContext.java
deleted file mode 100644
index bbdb34a6..00000000
--- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GRPCSSLContext.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2013-2020 the original author or 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
- *
- * https://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 org.springframework.cloud.gateway.config;
-
-import javax.net.ssl.TrustManager;
-
-/**
- * @author Alberto C. Ríos
- */
-public class GRPCSSLContext {
-
- private final TrustManager trustManager;
-
- public GRPCSSLContext(TrustManager trustManager) {
- this.trustManager = trustManager;
- }
-
- public TrustManager getTrustManager() {
- return trustManager;
- }
-
-}
diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java
index 99a5f54d..80717349 100644
--- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java
+++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java
@@ -25,6 +25,7 @@ import java.util.function.Supplier;
import javax.net.ssl.TrustManagerFactory;
+import io.grpc.Channel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Flux;
@@ -302,22 +303,23 @@ public class GatewayAutoConfiguration {
@Bean
@ConditionalOnEnabledFilter
@ConditionalOnProperty(name = "server.http2.enabled", matchIfMissing = true)
- @ConditionalOnClass(name = "io.grpc.Channel")
- public JsonToGrpcGatewayFilterFactory jsonToGRPCFilterFactory(GRPCSSLContext gRPCSSLContext,
+ @ConditionalOnClass(Channel.class)
+ public JsonToGrpcGatewayFilterFactory jsonToGRPCFilterFactory(GrpcSslConfigurer gRPCSSLContext,
ResourceLoader resourceLoader) {
return new JsonToGrpcGatewayFilterFactory(gRPCSSLContext, resourceLoader);
}
@Bean
@ConditionalOnEnabledFilter(JsonToGrpcGatewayFilterFactory.class)
- @ConditionalOnMissingBean(GRPCSSLContext.class)
+ @ConditionalOnMissingBean(GrpcSslConfigurer.class)
@ConditionalOnClass(name = "io.grpc.Channel")
- public GRPCSSLContext gRPCSSLContext() throws KeyStoreException, NoSuchAlgorithmException {
+ public GrpcSslConfigurer grpcSslConfigurer(HttpClientProperties properties)
+ throws KeyStoreException, NoSuchAlgorithmException {
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(KeyStore.getInstance(KeyStore.getDefaultType()));
- return new GRPCSSLContext(trustManagerFactory.getTrustManagers()[0]);
+ return new GrpcSslConfigurer(properties.getSsl());
}
@Bean
@@ -683,11 +685,19 @@ public class GatewayAutoConfiguration {
};
}
+ @Bean
+ public HttpClientSslConfigurer httpClientSslConfigurer(ServerProperties serverProperties,
+ HttpClientProperties httpClientProperties) {
+ return new HttpClientSslConfigurer(httpClientProperties.getSsl(), serverProperties) {
+ };
+ }
+
@Bean
@ConditionalOnMissingBean({ HttpClient.class, HttpClientFactory.class })
public HttpClientFactory gatewayHttpClientFactory(HttpClientProperties properties,
- ServerProperties serverProperties, List customizers) {
- return new HttpClientFactory(properties, serverProperties, customizers);
+ ServerProperties serverProperties, List customizers,
+ HttpClientSslConfigurer sslConfigurer) {
+ return new HttpClientFactory(properties, serverProperties, sslConfigurer, customizers);
}
@Bean
diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GrpcSslConfigurer.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GrpcSslConfigurer.java
new file mode 100644
index 00000000..f3b6907e
--- /dev/null
+++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GrpcSslConfigurer.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013-2020 the original author or 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
+ *
+ * https://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 org.springframework.cloud.gateway.config;
+
+import javax.net.ssl.SSLException;
+
+import io.grpc.ManagedChannel;
+import io.grpc.netty.GrpcSslContexts;
+import io.grpc.netty.NettyChannelBuilder;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
+
+/**
+ * @author Alberto C. Ríos
+ */
+public class GrpcSslConfigurer extends AbstractSslConfigurer {
+
+ public GrpcSslConfigurer(HttpClientProperties.Ssl sslProperties) {
+ super(sslProperties);
+ }
+
+ @Override
+ public ManagedChannel configureSsl(NettyChannelBuilder NettyChannelBuilder) throws SSLException {
+ return NettyChannelBuilder.useTransportSecurity().sslContext(getSslContext()).build();
+ }
+
+ private SslContext getSslContext() throws SSLException {
+
+ final SslContextBuilder sslContextBuilder = GrpcSslContexts.forClient();
+
+ final HttpClientProperties.Ssl ssl = getSslProperties();
+ boolean useInsecureTrustManager = ssl.isUseInsecureTrustManager();
+ if (useInsecureTrustManager) {
+ sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE.getTrustManagers()[0]);
+ }
+
+ if (!useInsecureTrustManager && ssl.getTrustedX509Certificates().size() > 0) {
+ sslContextBuilder.trustManager(getTrustedX509CertificatesForTrustManager());
+ }
+
+ return sslContextBuilder.keyManager(getKeyManagerFactory()).build();
+ }
+
+}
diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/HttpClientFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/HttpClientFactory.java
index 4d033750..f2b66576 100644
--- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/HttpClientFactory.java
+++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/HttpClientFactory.java
@@ -16,18 +16,9 @@
package org.springframework.cloud.gateway.config;
-import java.io.IOException;
-import java.net.URL;
import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchProviderException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
import javax.net.ssl.KeyManagerFactory;
@@ -35,9 +26,6 @@ import javax.net.ssl.TrustManagerFactory;
import io.netty.channel.ChannelOption;
import io.netty.handler.ssl.SslContextBuilder;
-import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
-import reactor.netty.http.Http11SslContextSpec;
-import reactor.netty.http.Http2SslContextSpec;
import reactor.netty.http.HttpProtocol;
import reactor.netty.http.client.HttpClient;
import reactor.netty.http.client.HttpResponseDecoderSpec;
@@ -50,7 +38,6 @@ import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.util.CollectionUtils;
-import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import static org.springframework.cloud.gateway.config.HttpClientProperties.Pool.PoolType.DISABLED;
@@ -69,12 +56,23 @@ public class HttpClientFactory extends AbstractFactoryBean {
protected final ServerProperties serverProperties;
+ protected final HttpClientSslConfigurer sslConfigurer;
+
protected final List customizers;
public HttpClientFactory(HttpClientProperties properties, ServerProperties serverProperties,
List customizers) {
this.properties = properties;
this.serverProperties = serverProperties;
+ this.sslConfigurer = new HttpClientSslConfigurer(properties.getSsl(), serverProperties);
+ this.customizers = customizers;
+ }
+
+ public HttpClientFactory(HttpClientProperties properties, ServerProperties serverProperties,
+ HttpClientSslConfigurer sslConfigurer, List customizers) {
+ this.properties = properties;
+ this.serverProperties = serverProperties;
+ this.sslConfigurer = sslConfigurer;
this.customizers = customizers;
}
@@ -117,6 +115,34 @@ public class HttpClientFactory extends AbstractFactoryBean {
return httpClient;
}
+ protected HttpClient configureSsl(HttpClient httpClient) {
+ return sslConfigurer.configureSsl(httpClient);
+ }
+
+ protected void configureSslContext(HttpClientProperties.Ssl ssl, SslProvider.SslContextSpec sslContextSpec) {
+ sslConfigurer.configureSslContext(ssl, sslContextSpec);
+ }
+
+ protected X509Certificate[] getTrustedX509CertificatesForTrustManager() {
+ return sslConfigurer.getTrustedX509CertificatesForTrustManager();
+ }
+
+ protected KeyManagerFactory getKeyManagerFactory() {
+ return sslConfigurer.getKeyManagerFactory();
+ }
+
+ protected KeyStore createKeyStore() {
+ return sslConfigurer.createKeyStore();
+ }
+
+ protected void setTrustManager(SslContextBuilder sslContextBuilder, X509Certificate... trustedX509Certificates) {
+ sslConfigurer.setTrustManager(sslContextBuilder, trustedX509Certificates);
+ }
+
+ protected void setTrustManager(SslContextBuilder sslContextBuilder, TrustManagerFactory factory) {
+ sslConfigurer.setTrustManager(sslContextBuilder, factory);
+ }
+
private HttpClient applyCustomizers(HttpClient httpClient) {
if (!CollectionUtils.isEmpty(customizers)) {
customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
@@ -127,43 +153,6 @@ public class HttpClientFactory extends AbstractFactoryBean {
return httpClient;
}
- protected HttpClient configureSsl(HttpClient httpClient) {
- HttpClientProperties.Ssl ssl = properties.getSsl();
- if ((ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0)
- || getTrustedX509CertificatesForTrustManager().length > 0 || ssl.isUseInsecureTrustManager()) {
- httpClient = httpClient.secure(sslContextSpec -> {
- // configure ssl
- configureSslContext(ssl, sslContextSpec);
- });
- }
- return httpClient;
- }
-
- protected void configureSslContext(HttpClientProperties.Ssl ssl, SslProvider.SslContextSpec sslContextSpec) {
- SslProvider.ProtocolSslContextSpec clientSslContext = (serverProperties.getHttp2().isEnabled())
- ? Http2SslContextSpec.forClient() : Http11SslContextSpec.forClient();
- clientSslContext.configure(sslContextBuilder -> {
- X509Certificate[] trustedX509Certificates = getTrustedX509CertificatesForTrustManager();
- if (trustedX509Certificates.length > 0) {
- setTrustManager(sslContextBuilder, trustedX509Certificates);
- }
- else if (ssl.isUseInsecureTrustManager()) {
- setTrustManager(sslContextBuilder, InsecureTrustManagerFactory.INSTANCE);
- }
-
- try {
- sslContextBuilder.keyManager(getKeyManagerFactory());
- }
- catch (Exception e) {
- logger.error(e);
- }
- });
-
- sslContextSpec.sslContext(clientSslContext).handshakeTimeout(ssl.getHandshakeTimeout())
- .closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout())
- .closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
- }
-
protected HttpClient configureProxy(HttpClient httpClient) {
// configure proxy if proxy host is set.
if (StringUtils.hasText(properties.getProxy().getHost())) {
@@ -201,83 +190,6 @@ public class HttpClientFactory extends AbstractFactoryBean {
return spec;
}
- protected X509Certificate[] getTrustedX509CertificatesForTrustManager() {
- HttpClientProperties.Ssl ssl = properties.getSsl();
-
- try {
- CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
- ArrayList allCerts = new ArrayList<>();
- for (String trustedCert : ssl.getTrustedX509Certificates()) {
- try {
- URL url = ResourceUtils.getURL(trustedCert);
- Collection extends Certificate> certs = certificateFactory.generateCertificates(url.openStream());
- allCerts.addAll(certs);
- }
- catch (IOException e) {
- throw new RuntimeException("Could not load certificate '" + trustedCert + "'", e);
- }
- }
- return allCerts.toArray(new X509Certificate[allCerts.size()]);
- }
- catch (CertificateException e1) {
- throw new RuntimeException("Could not load CertificateFactory X.509", e1);
- }
- }
-
- protected KeyManagerFactory getKeyManagerFactory() {
- HttpClientProperties.Ssl ssl = properties.getSsl();
- try {
- if (ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0) {
- KeyManagerFactory keyManagerFactory = KeyManagerFactory
- .getInstance(KeyManagerFactory.getDefaultAlgorithm());
- char[] keyPassword = ssl.getKeyPassword() != null ? ssl.getKeyPassword().toCharArray() : null;
-
- if (keyPassword == null && ssl.getKeyStorePassword() != null) {
- keyPassword = ssl.getKeyStorePassword().toCharArray();
- }
-
- keyManagerFactory.init(this.createKeyStore(), keyPassword);
-
- return keyManagerFactory;
- }
-
- return null;
- }
- catch (Exception e) {
- throw new IllegalStateException(e);
- }
- }
-
- protected KeyStore createKeyStore() {
- HttpClientProperties.Ssl ssl = properties.getSsl();
- try {
- KeyStore store = ssl.getKeyStoreProvider() != null
- ? KeyStore.getInstance(ssl.getKeyStoreType(), ssl.getKeyStoreProvider())
- : KeyStore.getInstance(ssl.getKeyStoreType());
- try {
- URL url = ResourceUtils.getURL(ssl.getKeyStore());
- store.load(url.openStream(),
- ssl.getKeyStorePassword() != null ? ssl.getKeyStorePassword().toCharArray() : null);
- }
- catch (Exception e) {
- throw new RuntimeException("Could not load key store ' " + ssl.getKeyStore() + "'", e);
- }
-
- return store;
- }
- catch (KeyStoreException | NoSuchProviderException e) {
- throw new RuntimeException("Could not load KeyStore for given type and provider", e);
- }
- }
-
- protected void setTrustManager(SslContextBuilder sslContextBuilder, X509Certificate... trustedX509Certificates) {
- sslContextBuilder.trustManager(trustedX509Certificates);
- }
-
- protected void setTrustManager(SslContextBuilder sslContextBuilder, TrustManagerFactory factory) {
- sslContextBuilder.trustManager(factory);
- }
-
protected ConnectionProvider buildConnectionProvider(HttpClientProperties properties) {
HttpClientProperties.Pool pool = properties.getPool();
diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/HttpClientSslConfigurer.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/HttpClientSslConfigurer.java
new file mode 100644
index 00000000..0938a891
--- /dev/null
+++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/HttpClientSslConfigurer.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2013-2020 the original author or 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
+ *
+ * https://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 org.springframework.cloud.gateway.config;
+
+import java.security.cert.X509Certificate;
+
+import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
+import reactor.netty.http.Http11SslContextSpec;
+import reactor.netty.http.Http2SslContextSpec;
+import reactor.netty.http.client.HttpClient;
+import reactor.netty.tcp.SslProvider;
+
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+
+public class HttpClientSslConfigurer extends AbstractSslConfigurer {
+
+ private final ServerProperties serverProperties;
+
+ public HttpClientSslConfigurer(HttpClientProperties.Ssl sslProperties, ServerProperties serverProperties) {
+ super(sslProperties);
+ this.serverProperties = serverProperties;
+ }
+
+ public HttpClient configureSsl(HttpClient client) {
+ final HttpClientProperties.Ssl ssl = getSslProperties();
+
+ if ((ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0)
+ || getTrustedX509CertificatesForTrustManager().length > 0 || ssl.isUseInsecureTrustManager()) {
+ client = client.secure(sslContextSpec -> {
+ // configure ssl
+ configureSslContext(ssl, sslContextSpec);
+ });
+ }
+ return client;
+ }
+
+ protected void configureSslContext(HttpClientProperties.Ssl ssl, SslProvider.SslContextSpec sslContextSpec) {
+ SslProvider.ProtocolSslContextSpec clientSslContext = (serverProperties.getHttp2().isEnabled())
+ ? Http2SslContextSpec.forClient() : Http11SslContextSpec.forClient();
+ clientSslContext.configure(sslContextBuilder -> {
+ X509Certificate[] trustedX509Certificates = getTrustedX509CertificatesForTrustManager();
+ if (trustedX509Certificates.length > 0) {
+ setTrustManager(sslContextBuilder, trustedX509Certificates);
+ }
+ else if (ssl.isUseInsecureTrustManager()) {
+ setTrustManager(sslContextBuilder, InsecureTrustManagerFactory.INSTANCE);
+ }
+
+ try {
+ sslContextBuilder.keyManager(getKeyManagerFactory());
+ }
+ catch (Exception e) {
+ logger.error(e);
+ }
+ });
+
+ sslContextSpec.sslContext(clientSslContext).handshakeTimeout(ssl.getHandshakeTimeout())
+ .closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout())
+ .closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
+ }
+
+}
diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/JsonToGrpcGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/JsonToGrpcGatewayFilterFactory.java
index adec2f18..aed1911e 100644
--- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/JsonToGrpcGatewayFilterFactory.java
+++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/JsonToGrpcGatewayFilterFactory.java
@@ -45,8 +45,7 @@ import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ManagedChannel;
import io.grpc.MethodDescriptor;
-import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
-import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
+import io.grpc.netty.NettyChannelBuilder;
import io.grpc.protobuf.ProtoUtils;
import io.grpc.stub.ClientCalls;
import io.netty.buffer.PooledByteBufAllocator;
@@ -54,7 +53,7 @@ import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
-import org.springframework.cloud.gateway.config.GRPCSSLContext;
+import org.springframework.cloud.gateway.config.GrpcSslConfigurer;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
@@ -70,7 +69,6 @@ import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.web.server.ServerWebExchange;
-import static io.grpc.netty.shaded.io.grpc.netty.NegotiationType.TLS;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
/**
@@ -85,13 +83,13 @@ import static org.springframework.cloud.gateway.support.GatewayToStringStyler.fi
public class JsonToGrpcGatewayFilterFactory
extends AbstractGatewayFilterFactory {
- private final GRPCSSLContext sslContext;
+ private final GrpcSslConfigurer grpcSslConfigurer;
private final ResourceLoader resourceLoader;
- public JsonToGrpcGatewayFilterFactory(GRPCSSLContext sslContext, ResourceLoader resourceLoader) {
+ public JsonToGrpcGatewayFilterFactory(GrpcSslConfigurer grpcSslConfigurer, ResourceLoader resourceLoader) {
super(Config.class);
- this.sslContext = sslContext;
+ this.grpcSslConfigurer = grpcSslConfigurer;
this.resourceLoader = resourceLoader;
}
@@ -298,11 +296,11 @@ public class JsonToGrpcGatewayFilterFactory
};
}
+ // We are creating this on every call, should optimize?
private ManagedChannel createChannelChannel(String host, int port) {
+ NettyChannelBuilder nettyChannelBuilder = NettyChannelBuilder.forAddress(host, port);
try {
- return NettyChannelBuilder.forAddress(host, port).useTransportSecurity()
- .sslContext(GrpcSslContexts.forClient().trustManager(sslContext.getTrustManager()).build())
- .negotiationType(TLS).build();
+ return grpcSslConfigurer.configureSsl(nettyChannelBuilder);
}
catch (SSLException e) {
throw new RuntimeException(e);
diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java
index 4a3f39a4..0a1094b9 100644
--- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java
+++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java
@@ -165,21 +165,9 @@ public class GatewayControllerEndpointTests {
@Test
public void testPostValidShortcutRouteDefinition() {
-// 04-08-2022 08:41:31.866 [reactor-http-epoll-1] TRACE org.springframework.web.HttpLogging.trace - [0a3d20a5-43] Decoded [RouteDefinition{id='gatewaywithgrpcfiltertest-0-104014-8916263311295787431172436062-test-gateway-tls-client-mapping-0', predicates=[PredicateDefinition{name='Path', args={_genkey_0=/json/hello}}], filters=[FilterDefinition{name='StripPrefix', args={_genkey_0=1}}, FilterDefinition{name='JsonToGrpcGatewayFilterFactory', args={_genkey_0=file:src/main/proto/hello.pb, _genkey_1=file:src/main/proto/hello.proto, _genkey_2=HelloService, _genkey_3=hello}}], uri=https://localhost:8095, order=0, metadata={}}]
-// 04-08-2022 08:41:31.870 [reactor-http-epoll-1] WARN o.s.c.g.a.GatewayControllerEndpoint.handleUnavailableDefinition - Invalid FilterDefinition: [JsonToGrpcGatewayFilterFactory]
-// 04-08-2022 08:41:31.882 [reactor-http-epoll-1] DEBUG o.s.w.s.h.ResponseStatusExceptionHandler.handle - [0a3d20a5-43] Resolved [ResponseStatusException: "400 BAD_REQUEST "Invalid FilterDefinition: [JsonToGrpcGatewayFilterFactory]""] for HTTP POST /actuator/gateway/routes/gatewaywithgrpcfiltertest-0-104014-8916263311295787431172436062-test-gateway-tls-client-mapping-0
-// 04-08-2022 08:41:31.883 [reactor-http-epoll-1] TRACE o.s.w.s.a.HttpWebHandlerAdapter.traceDebug - [0a3d20a5-43] Completed 400 BAD_REQUEST, headers={masked}
-//
-//[RouteDefinition{id='gatewaywithgrpcfiltertest-0-104014-8916263311295787431172436062-test-gateway-tls-client-mapping-0',
- // predicates=[PredicateDefinition{name='Path', args={_genkey_0=/json/hello}}],
- // filters=[FilterDefinition{name='StripPrefix', args={_genkey_0=1}},
- // FilterDefinition{name='JsonToGrpcGatewayFilterFactory',
- // args={_genkey_0=file:src/main/proto/hello.pb, _genkey_1=file:src/main/proto/hello.proto, _genkey_2=HelloService, _genkey_3=hello}}],
-
- // uri=https://localhost:8095, order=0, metadata={}}]
-
RouteDefinition testRouteDefinition = new RouteDefinition();
- testRouteDefinition.setId("gatewaywithgrpcfiltertest-0-104014-8916263311295787431172436062-test-gateway-tls-client-mapping-0");
+ testRouteDefinition.setId(
+ "gatewaywithgrpcfiltertest-0-104014-8916263311295787431172436062-test-gateway-tls-client-mapping-0");
testRouteDefinition.setUri(URI.create("https://localhost:8095"));
testRouteDefinition.setOrder(0);
testRouteDefinition.setMetadata(Collections.emptyMap());
@@ -187,20 +175,19 @@ public class GatewayControllerEndpointTests {
FilterDefinition longFilterDefinition = new FilterDefinition();
FilterDefinition stripPrefix = new FilterDefinition();
stripPrefix.setName("StripPrefix");
- stripPrefix.addArg("_genkey_0","1");
+ stripPrefix.addArg("_genkey_0", "1");
longFilterDefinition.setName("JsonToGrpc");
- longFilterDefinition.addArg("_genkey_0","file:src/main/proto/hello.pb");
- longFilterDefinition.addArg("_genkey_1","file:src/main/proto/hello.proto");
- longFilterDefinition.addArg("_genkey_2","HelloService");
- longFilterDefinition.addArg("_genkey_3","hello");
+ longFilterDefinition.addArg("_genkey_0", "file:src/main/proto/hello.pb");
+ longFilterDefinition.addArg("_genkey_1", "file:src/main/proto/hello.proto");
+ longFilterDefinition.addArg("_genkey_2", "HelloService");
+ longFilterDefinition.addArg("_genkey_3", "hello");
testRouteDefinition.setFilters(Collections.singletonList(longFilterDefinition));
PredicateDefinition hostRoutePredicateDefinition = new PredicateDefinition();
hostRoutePredicateDefinition.setName("Path");
- hostRoutePredicateDefinition.addArg("_genkey_0","/json/hello");
- testRouteDefinition.setPredicates(
- Arrays.asList(hostRoutePredicateDefinition));
+ hostRoutePredicateDefinition.addArg("_genkey_0", "/json/hello");
+ testRouteDefinition.setPredicates(Arrays.asList(hostRoutePredicateDefinition));
testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/test-route")
.accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition)).exchange()
diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/GatewayAutoConfigurationTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/GatewayAutoConfigurationTests.java
index 6c46143c..dbb4bd14 100644
--- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/GatewayAutoConfigurationTests.java
+++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/GatewayAutoConfigurationTests.java
@@ -51,6 +51,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint;
import org.springframework.cloud.gateway.actuate.GatewayLegacyControllerEndpoint;
+import org.springframework.cloud.gateway.config.GatewayAutoConfigurationTests.CustomHttpClientFactory.CustomSslConfigurer;
import org.springframework.cloud.gateway.filter.factory.TokenRelayGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.headers.GRPCRequestHeadersFilter;
import org.springframework.cloud.gateway.filter.headers.GRPCResponseHeadersFilter;
@@ -96,7 +97,7 @@ public class GatewayAutoConfigurationTests {
assertThat(factory.connectionProvider.maxConnections()).isEqualTo(Integer.MAX_VALUE); // elastic
assertThat(factory.proxyProvider).isNull();
- assertThat(factory.sslConfigured).isFalse();
+ assertThat(factory.isSslConfigured()).isFalse();
assertThat(httpClient.configuration().isAcceptGzip()).isFalse();
assertThat(httpClient.configuration().loggingHandler()).isNull();
@@ -147,8 +148,8 @@ public class GatewayAutoConfigurationTests {
assertThat(factory.proxyProvider).isNotNull();
assertThat(factory.proxyProvider.build().getAddress().get().getHostName()).isEqualTo("myhost");
- assertThat(factory.sslConfigured).isTrue();
- assertThat(factory.insecureTrustManagerSet).isTrue();
+ assertThat(factory.isSslConfigured()).isTrue();
+ assertThat(factory.isInsecureTrustManagerSet()).isTrue();
assertThat(context).hasSingleBean(ReactorNettyRequestUpgradeStrategy.class);
ReactorNettyRequestUpgradeStrategy upgradeStrategy = context
@@ -285,7 +286,7 @@ public class GatewayAutoConfigurationTests {
.withPropertyValues("server.http2.enabled=true").run(context -> {
assertThat(context).hasSingleBean(HttpClient.class);
CustomHttpClientFactory factory = context.getBean(CustomHttpClientFactory.class);
- assertThat(factory.insecureTrustManagerSet).isFalse();
+ assertThat(factory.isInsecureTrustManagerSet()).isFalse();
});
}
@@ -310,25 +311,32 @@ public class GatewayAutoConfigurationTests {
@Bean
@Primary
CustomHttpClientFactory customHttpClientFactory(HttpClientProperties properties,
- ServerProperties serverProperties, List customizers) {
- return new CustomHttpClientFactory(properties, serverProperties, customizers);
+ ServerProperties serverProperties, List customizers,
+ HttpClientSslConfigurer sslConfigurer) {
+ return new CustomHttpClientFactory(properties, serverProperties, sslConfigurer, customizers);
+ }
+
+ @Bean
+ @Primary
+ CustomSslConfigurer customSslContextFactory(ServerProperties serverProperties,
+ HttpClientProperties httpClientProperties) {
+ return new CustomSslConfigurer(httpClientProperties.getSsl(), serverProperties);
}
}
protected static class CustomHttpClientFactory extends HttpClientFactory {
- boolean insecureTrustManagerSet;
-
- boolean sslConfigured;
-
private ConnectionProvider connectionProvider;
private ProxyProvider.Builder proxyProvider;
+ private CustomSslConfigurer customSslContextFactory;
+
public CustomHttpClientFactory(HttpClientProperties properties, ServerProperties serverProperties,
- List customizers) {
- super(properties, serverProperties, customizers);
+ HttpClientSslConfigurer sslConfigurer, List customizers) {
+ super(properties, serverProperties, sslConfigurer, customizers);
+ this.customSslContextFactory = (CustomSslConfigurer) sslConfigurer;
}
@Override
@@ -344,16 +352,37 @@ public class GatewayAutoConfigurationTests {
return proxyProvider;
}
- @Override
- protected void configureSslContext(HttpClientProperties.Ssl ssl, SslProvider.SslContextSpec sslContextSpec) {
- sslConfigured = true;
- super.configureSslContext(ssl, sslContextSpec);
+ public boolean isSslConfigured() {
+ return customSslContextFactory.sslConfigured;
}
- @Override
- protected void setTrustManager(SslContextBuilder sslContextBuilder, TrustManagerFactory factory) {
- insecureTrustManagerSet = factory == InsecureTrustManagerFactory.INSTANCE;
- super.setTrustManager(sslContextBuilder, factory);
+ public boolean isInsecureTrustManagerSet() {
+ return customSslContextFactory.insecureTrustManagerSet;
+ }
+
+ protected static class CustomSslConfigurer extends HttpClientSslConfigurer {
+
+ boolean sslConfigured;
+
+ boolean insecureTrustManagerSet;
+
+ protected CustomSslConfigurer(HttpClientProperties.Ssl sslProperties, ServerProperties serverProperties) {
+ super(sslProperties, serverProperties);
+ }
+
+ @Override
+ protected void configureSslContext(HttpClientProperties.Ssl ssl,
+ SslProvider.SslContextSpec sslContextSpec) {
+ sslConfigured = true;
+ super.configureSslContext(getSslProperties(), sslContextSpec);
+ }
+
+ @Override
+ protected void setTrustManager(SslContextBuilder sslContextBuilder, TrustManagerFactory factory) {
+ insecureTrustManagerSet = factory == InsecureTrustManagerFactory.INSTANCE;
+ super.setTrustManager(sslContextBuilder, factory);
+ }
+
}
}
diff --git a/spring-cloud-gateway-server/src/test/kotlin/org/springframework/cloud/gateway/route/builder/RouteDslTests.kt b/spring-cloud-gateway-server/src/test/kotlin/org/springframework/cloud/gateway/route/builder/RouteDslTests.kt
index 3bdf16ff..283c6322 100644
--- a/spring-cloud-gateway-server/src/test/kotlin/org/springframework/cloud/gateway/route/builder/RouteDslTests.kt
+++ b/spring-cloud-gateway-server/src/test/kotlin/org/springframework/cloud/gateway/route/builder/RouteDslTests.kt
@@ -1,127 +1,127 @@
-///*
-// * Copyright 2013-2020 the original author or 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
-// *
-// * https://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 org.springframework.cloud.gateway.route.builder
-//
-//import org.junit.Test
-//import org.junit.runner.RunWith
-//import org.springframework.beans.factory.annotation.Autowired
-//import org.springframework.boot.autoconfigure.EnableAutoConfiguration
-//import org.springframework.boot.test.context.SpringBootTest
-//import org.springframework.cloud.gateway.support.ServerWebExchangeUtils
-//import org.springframework.context.annotation.Configuration
-//import org.springframework.mock.http.server.reactive.MockServerHttpRequest
-//import org.springframework.mock.web.server.MockServerWebExchange
-//import org.springframework.test.context.junit4.SpringRunner
-//import org.springframework.web.server.ServerWebExchange
-//import reactor.core.publisher.toMono
-//import reactor.test.StepVerifier
-//import java.net.URI
-//
-//@RunWith(SpringRunner::class)
-//@SpringBootTest(classes = arrayOf(Config::class))
-//class RouteDslTests {
-//
-// @Autowired
-// lateinit var builder: RouteLocatorBuilder
-//
-// @Test
-// fun sampleRouteDsl() {
-// val routeLocator = builder.routes {
-// route(id = "test") {
-// host("**.abc.org") and path("/image/png")
-// filters {
-// addResponseHeader("X-TestHeader", "foobar")
-// }
-// uri("http://httpbin.org:80")
-// }
-//
-// route(id = "test2") {
-// path("/image/webp") or path("/image/anotherone")
-// filters {
-// addResponseHeader("X-AnotherHeader", "baz")
-// addResponseHeader("X-AnotherHeader-2", "baz-2")
-// }
-// uri("https://httpbin.org:443")
-// }
-// }
-//
-// StepVerifier
-// .create(routeLocator.routes)
-// .expectNextMatches({
-// it.id == "test" && it.filters.size == 1 && it.uri == URI.create("http://httpbin.org:80")
-// })
-// .expectNextMatches({
-// it.id == "test2" && it.filters.size == 2 && it.uri == URI.create("https://httpbin.org:443")
-// })
-// .expectComplete()
-// .verify()
-//
-// val sampleExchange: ServerWebExchange = MockServerWebExchange.from(MockServerHttpRequest.get("/image/webp")
-// .header("Host", "test.abc.org").build())
-//
-// val filteredRoutes = routeLocator.routes.filter({
-// sampleExchange.attributes.put(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR, it.id)
-// it.predicate.apply(sampleExchange).toMono().block()
-// })
-//
-// StepVerifier.create(filteredRoutes)
-// .expectNextMatches({
-// it.id == "test2" && it.filters.size == 2 && it.uri == URI.create("https://httpbin.org:443")
-// })
-// .expectComplete()
-// .verify()
-// }
-//
-// @Test
-// fun dslWithFunctionParameters() {
-// val routerLocator = builder.routes {
-// route(id = "test1", order = 10, uri = "http://httpbin.org") {
-// host("**.abc.org")
-// }
-// route(id = "test2", order = 10, uri = "http://someurl") {
-// host("**.abc.org")
-// uri("http://override-url")
-// }
-// }
-//
-// StepVerifier.create(routerLocator.routes)
-// .expectNextMatches({
-// it.id == "test1" &&
-// it.uri == URI.create("http://httpbin.org:80") &&
-// it.order == 10 &&
-// it.predicate.apply(MockServerWebExchange
-// .from(MockServerHttpRequest
-// .get("/someuri").header("Host", "test.abc.org")))
-// .toMono().block()
-// })
-// .expectNextMatches({
-// it.id == "test2" &&
-// it.uri == URI.create("http://override-url:80") &&
-// it.order == 10 &&
-// it.predicate.apply(MockServerWebExchange
-// .from(MockServerHttpRequest
-// .get("/someuri").header("Host", "test.abc.org")))
-// .toMono().block()
-// })
-// .expectComplete()
-// .verify()
-// }
-//}
-//
-//@Configuration(proxyBeanMethods = false)
-//@EnableAutoConfiguration
-//open class Config {}
\ No newline at end of file
+/*
+ * Copyright 2013-2020 the original author or 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
+ *
+ * https://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 org.springframework.cloud.gateway.route.builder
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.cloud.gateway.support.ServerWebExchangeUtils
+import org.springframework.context.annotation.Configuration
+import org.springframework.mock.http.server.reactive.MockServerHttpRequest
+import org.springframework.mock.web.server.MockServerWebExchange
+import org.springframework.test.context.junit4.SpringRunner
+import org.springframework.web.server.ServerWebExchange
+import reactor.core.publisher.toMono
+import reactor.test.StepVerifier
+import java.net.URI
+
+@RunWith(SpringRunner::class)
+@SpringBootTest(classes = arrayOf(Config::class))
+class RouteDslTests {
+
+ @Autowired
+ lateinit var builder: RouteLocatorBuilder
+
+ @Test
+ fun sampleRouteDsl() {
+ val routeLocator = builder.routes {
+ route(id = "test") {
+ host("**.abc.org") and path("/image/png")
+ filters {
+ addResponseHeader("X-TestHeader", "foobar")
+ }
+ uri("http://httpbin.org:80")
+ }
+
+ route(id = "test2") {
+ path("/image/webp") or path("/image/anotherone")
+ filters {
+ addResponseHeader("X-AnotherHeader", "baz")
+ addResponseHeader("X-AnotherHeader-2", "baz-2")
+ }
+ uri("https://httpbin.org:443")
+ }
+ }
+
+ StepVerifier
+ .create(routeLocator.routes)
+ .expectNextMatches({
+ it.id == "test" && it.filters.size == 1 && it.uri == URI.create("http://httpbin.org:80")
+ })
+ .expectNextMatches({
+ it.id == "test2" && it.filters.size == 2 && it.uri == URI.create("https://httpbin.org:443")
+ })
+ .expectComplete()
+ .verify()
+
+ val sampleExchange: ServerWebExchange = MockServerWebExchange.from(MockServerHttpRequest.get("/image/webp")
+ .header("Host", "test.abc.org").build())
+
+ val filteredRoutes = routeLocator.routes.filter({
+ sampleExchange.attributes.put(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR, it.id)
+ it.predicate.apply(sampleExchange).toMono().block()
+ })
+
+ StepVerifier.create(filteredRoutes)
+ .expectNextMatches({
+ it.id == "test2" && it.filters.size == 2 && it.uri == URI.create("https://httpbin.org:443")
+ })
+ .expectComplete()
+ .verify()
+ }
+
+ @Test
+ fun dslWithFunctionParameters() {
+ val routerLocator = builder.routes {
+ route(id = "test1", order = 10, uri = "http://httpbin.org") {
+ host("**.abc.org")
+ }
+ route(id = "test2", order = 10, uri = "http://someurl") {
+ host("**.abc.org")
+ uri("http://override-url")
+ }
+ }
+
+ StepVerifier.create(routerLocator.routes)
+ .expectNextMatches({
+ it.id == "test1" &&
+ it.uri == URI.create("http://httpbin.org:80") &&
+ it.order == 10 &&
+ it.predicate.apply(MockServerWebExchange
+ .from(MockServerHttpRequest
+ .get("/someuri").header("Host", "test.abc.org")))
+ .toMono().block()
+ })
+ .expectNextMatches({
+ it.id == "test2" &&
+ it.uri == URI.create("http://override-url:80") &&
+ it.order == 10 &&
+ it.predicate.apply(MockServerWebExchange
+ .from(MockServerHttpRequest
+ .get("/someuri").header("Host", "test.abc.org")))
+ .toMono().block()
+ })
+ .expectComplete()
+ .verify()
+ }
+}
+
+@Configuration(proxyBeanMethods = false)
+@EnableAutoConfiguration
+open class Config {}
\ No newline at end of file