diff --git a/docs/src/main/asciidoc/_configprops.adoc b/docs/src/main/asciidoc/_configprops.adoc index 90d82129..50923ed8 100644 --- a/docs/src/main/asciidoc/_configprops.adoc +++ b/docs/src/main/asciidoc/_configprops.adoc @@ -14,6 +14,7 @@ |spring.cloud.gateway.filter.add-request-header.enabled | `+++true+++` | Enables the add-request-header filter. |spring.cloud.gateway.filter.add-request-parameter.enabled | `+++true+++` | Enables the add-request-parameter filter. |spring.cloud.gateway.filter.add-response-header.enabled | `+++true+++` | Enables the add-response-header filter. +|spring.cloud.gateway.filter.json-to-grpc.enabled | `+++true+++` | Enables the JSON to gRPC filter. |spring.cloud.gateway.filter.circuit-breaker.enabled | `+++true+++` | Enables the circuit-breaker filter. |spring.cloud.gateway.filter.dedupe-response-header.enabled | `+++true+++` | Enables the dedupe-response-header filter. |spring.cloud.gateway.filter.fallback-headers.enabled | `+++true+++` | Enables the fallback-headers filter. @@ -26,7 +27,7 @@ |spring.cloud.gateway.filter.preserve-host-header.enabled | `+++true+++` | Enables the preserve-host-header filter. |spring.cloud.gateway.filter.redirect-to.enabled | `+++true+++` | Enables the redirect-to filter. |spring.cloud.gateway.filter.remove-hop-by-hop.headers | | -|spring.cloud.gateway.filter.remove-hop-by-hop.order | `+++0+++` | +|spring.cloud.gateway.filter.remove-hop-by-hop.order | `+++0+++` | |spring.cloud.gateway.filter.remove-request-header.enabled | `+++true+++` | Enables the remove-request-header filter. |spring.cloud.gateway.filter.remove-request-parameter.enabled | `+++true+++` | Enables the remove-request-parameter filter. |spring.cloud.gateway.filter.remove-response-header.enabled | `+++true+++` | Enables the remove-response-header filter. @@ -42,16 +43,16 @@ |spring.cloud.gateway.filter.rewrite-path.enabled | `+++true+++` | Enables the rewrite-path filter. |spring.cloud.gateway.filter.rewrite-response-header.enabled | `+++true+++` | Enables the rewrite-response-header filter. |spring.cloud.gateway.filter.save-session.enabled | `+++true+++` | Enables the save-session filter. -|spring.cloud.gateway.filter.secure-headers.content-security-policy | `+++default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'+++` | -|spring.cloud.gateway.filter.secure-headers.content-type-options | `+++nosniff+++` | +|spring.cloud.gateway.filter.secure-headers.content-security-policy | `+++default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'+++` | +|spring.cloud.gateway.filter.secure-headers.content-type-options | `+++nosniff+++` | |spring.cloud.gateway.filter.secure-headers.disable | | -|spring.cloud.gateway.filter.secure-headers.download-options | `+++noopen+++` | +|spring.cloud.gateway.filter.secure-headers.download-options | `+++noopen+++` | |spring.cloud.gateway.filter.secure-headers.enabled | `+++true+++` | Enables the secure-headers filter. -|spring.cloud.gateway.filter.secure-headers.frame-options | `+++DENY+++` | -|spring.cloud.gateway.filter.secure-headers.permitted-cross-domain-policies | `+++none+++` | -|spring.cloud.gateway.filter.secure-headers.referrer-policy | `+++no-referrer+++` | -|spring.cloud.gateway.filter.secure-headers.strict-transport-security | `+++max-age=631138519+++` | -|spring.cloud.gateway.filter.secure-headers.xss-protection-header | `+++1 ; mode=block+++` | +|spring.cloud.gateway.filter.secure-headers.frame-options | `+++DENY+++` | +|spring.cloud.gateway.filter.secure-headers.permitted-cross-domain-policies | `+++none+++` | +|spring.cloud.gateway.filter.secure-headers.referrer-policy | `+++no-referrer+++` | +|spring.cloud.gateway.filter.secure-headers.strict-transport-security | `+++max-age=631138519+++` | +|spring.cloud.gateway.filter.secure-headers.xss-protection-header | `+++1 ; mode=block+++` | |spring.cloud.gateway.filter.set-path.enabled | `+++true+++` | Enables the set-path filter. |spring.cloud.gateway.filter.set-request-header.enabled | `+++true+++` | Enables the set-request-header filter. |spring.cloud.gateway.filter.set-request-host-header.enabled | `+++true+++` | Enables the set-request-host-header filter. @@ -106,7 +107,7 @@ |spring.cloud.gateway.httpclient.websocket.proxy-ping | `+++true+++` | Proxy ping frames to downstream services, defaults to true. |spring.cloud.gateway.httpclient.wiretap | `+++false+++` | Enables wiretap debugging for Netty HttpClient. |spring.cloud.gateway.httpserver.wiretap | `+++false+++` | Enables wiretap debugging for Netty HttpServer. -|spring.cloud.gateway.loadbalancer.use404 | `+++false+++` | +|spring.cloud.gateway.loadbalancer.use404 | `+++false+++` | |spring.cloud.gateway.metrics.enabled | `+++false+++` | Enables the collection of metrics data. |spring.cloud.gateway.metrics.prefix | `+++spring.cloud.gateway+++` | The prefix of all metrics emitted by gateway. |spring.cloud.gateway.metrics.tags | | Tags map that added to metrics. diff --git a/pom.xml b/pom.xml index 98a25c8d..c1ee9ac8 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 1.0.0 2.1.4-SNAPSHOT 3.1.4-SNAPSHOT - 1.16.3 + 1.17.2 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 b2dc170c..a4204b68 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 @@ -18,17 +18,18 @@ package org.springframework.cloud.gateway.tests.grpc; import java.io.File; import java.io.IOException; -import java.security.cert.X509Certificate; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLException; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; 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.springframework.boot.ApplicationArguments; @@ -36,13 +37,8 @@ import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.cloud.gateway.config.GRPCSSLContext; -import org.springframework.cloud.gateway.route.RouteLocator; -import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; -import org.springframework.context.annotation.Bean; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Component; -import org.springframework.util.SocketUtils; /** * @author Alberto C. RĂ­os @@ -51,40 +47,42 @@ import org.springframework.util.SocketUtils; @EnableAutoConfiguration public class GRPCApplication { - private static final int GRPC_SERVER_PORT = SocketUtils.findAvailableTcpPort(); + 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]); - } + // @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 { @@ -100,8 +98,8 @@ public class GRPCApplication { private void start() throws Exception { /* The port on which the server should run */ ServerCredentials creds = createServerCredentials(); - server = Grpc.newServerBuilderForPort(GRPC_SERVER_PORT, creds).addService(new HelloService()).build() - .start(); + server = Grpc.newServerBuilderForPort(GRPC_SERVER_PORT, creds) + .addService(new HelloService()).build().start(); System.out.println("Starting gRPC server in port " + GRPC_SERVER_PORT); @@ -116,11 +114,31 @@ public class GRPCApplication { } private ServerCredentials createServerCredentials() throws IOException { + File certChain = new ClassPathResource("public.cert").getFile(); File privateKey = new ClassPathResource("private.key").getFile(); - File certChain = new ClassPathResource("certificate.pem").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); @@ -130,12 +148,15 @@ 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(); + String greeting = "Hello, " + request.getFirstName() + " " + + request.getLastName(); System.out.println("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 100644 new mode 100755 diff --git a/spring-cloud-gateway-integration-tests/grpc/src/main/resources/certificate.pem b/spring-cloud-gateway-integration-tests/grpc/src/main/resources/certificate.pem deleted file mode 100644 index 808fb06c..00000000 --- a/spring-cloud-gateway-integration-tests/grpc/src/main/resources/certificate.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIEIon96DANBgkqhkiG9w0BAQsFADBsMRAwDgYDVQQGEwdV -bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD -VQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3du -MB4XDTIxMDkyNzE2NDQwNVoXDTMxMDkyNTE2NDQwNVowbDEQMA4GA1UEBhMHVW5r -bm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UE -ChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCC -ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIwevGHY30YdoUdCSB/5q7/F -c0KHetdjb71G2u6vFdeNvSwMpCV8Z1JznOJ/1zuY+0Z105QCPM7fi1ACi+tqxDR+ -L7yjUHhUEMTiGgCHcYJIZZCYfWS3BQXVxgORXhDv7RduCUaCLnkaFY++iPMTUy0C -VxIkplIEhAmqcikIgWaa5ZjBkegKgahlPQLKlfD4Rz/kq2P+LLYFsHNNdKfWv6XQ -u4LMw7ZEAJtfdpaMTzmtQipbTt6Dh87vIa0CIVnCPdlQ3o/5WeaxEA1pnfOLas07 -1VdHih2nC5vHhQcTPQDfa+uwzQvzHrchjuvMUUZaCYJzuT0G6nbGBba54EBT7yUC -AwEAAaMhMB8wHQYDVR0OBBYEFKOHmfytP5ab2C4iFHlSklu6tCcuMA0GCSqGSIb3 -DQEBCwUAA4IBAQAdgWwdOtRbI796Z22weTBc0/tM8kLc6G0raNb08WyZMPZVki04 -jPh73pPQCgYeI/pq5JqH46KgvehmygTzpWDAFIllW0kgABVw3Nu6duV+blt1JG8T -lWP7t5A+qDXgPDm3I5diii7O1YlLB3I37XiBdEV/+2WmF1VGQ7uBWAv+uotQeuW2 -JvHOr4ICOiW45TzRYtAbzWukSYKg/A7lwBs7HE9KVomUxNrkD+7+ugRuy/31pyen -pHsEJQpx5juFRE222B6GXmX0w9xLIOapytl4EoPUx3K8Ecc+yI2q00UUC43x0v28 -c05YqwRZ5vp+jUnRxkxaz85YdArfR7QFYWtO ------END CERTIFICATE----- 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 b22b3349..45662014 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 100644 new mode 100755 index 1db2b3cb..9c1d11f1 --- 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: mykey - localKeyID: 54 69 6D 65 20 31 36 33 32 38 32 34 38 37 31 33 33 35 + friendlyName: bootapp + localKeyID: 54 69 6D 65 20 31 36 35 39 37 31 32 34 31 30 37 34 35 Key Attributes: -----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCMHrxh2N9GHaFH -Qkgf+au/xXNCh3rXY2+9RtrurxXXjb0sDKQlfGdSc5zif9c7mPtGddOUAjzO34tQ -AovrasQ0fi+8o1B4VBDE4hoAh3GCSGWQmH1ktwUF1cYDkV4Q7+0XbglGgi55GhWP -vojzE1MtAlcSJKZSBIQJqnIpCIFmmuWYwZHoCoGoZT0CypXw+Ec/5Ktj/iy2BbBz -TXSn1r+l0LuCzMO2RACbX3aWjE85rUIqW07eg4fO7yGtAiFZwj3ZUN6P+VnmsRAN -aZ3zi2rNO9VXR4odpwubx4UHEz0A32vrsM0L8x63IY7rzFFGWgmCc7k9Bup2xgW2 -ueBAU+8lAgMBAAECggEAGu2xQJDAYCZDn4FCgTqnYkSdIRUOa6SFjfe3DZYCeZmY -2IVZaobdCICFjxYIlECTUfhFADXp38wgZvEGWOj86iWyIOu2BFoLmvrlCmL9Uo99 -TWuw9ZEi2vs5gegHDvQ9OXqBN9a+/bEgoa55fVWib4z6lNcMS8joYz8pj28+ByzE -LW0/3T3p6beM2fUcCJWn3d2M3wUgSuXmcdjVXJSkhEwKTVcc4vTTcOeF6xb2VZ/g -Pozv/39G1qZ+QtM58yBiqnJ1Z2gtAk71l/1ztQa3uY22gzw8Kj5dmqcHgdiN5DWI -bNE+k5Q93FzUmZNYPzmY6YVkdEzaNMtmi96sdtEu5QKBgQD36YdGXUyoRXeNDRuc -yMXr6j/9/ewii+byHhFoUvjLuXWIQ06V+nOtYqohg/zgGIiC5LQ8EB0uFZDMhbDf -kSwtoXbpUDLYD2OPgIyLaPqHiYQ9BampbUz5vHlfYLr0vB+Xp5r22Eb6nDQRRtb5 -EXHoYgAokgpYdeTIcdKRcB/2DwKBgQCQsPePeu7PagM9vYEo4zfbFxfY3Qkr4lOQ -BCZ/tgsS0b0jAAxpfUH0/3O5oXizmB/5K7vgKqGnTuBWJJ/hdCWq27FkKxJ+8ejU -8V9TFd5VQ89VeB5OekZwPks8vftwzW4L82ZRW5hvyQB0jPR+lwqjrNqds+xxH7i+ -c+RFx15biwKBgQCgkGGK0zao7YUGl+zAWNDHgQo9KM5deZr0SUEg/kwhNlbHEEC/ -plxxeauS1XdcdMdFb3bER/N+O31y2Uu7IL0qOJ9ZcRXdFep3sNxWFoHcctZw51AB -accnIEjD21R62bTkditJoL4n5i9a2TS2T/QkfAR6QkvtCz5IDGBCzgoFRQKBgC51 -VBfy3gklPgMt/PHW+1FSuep9FnvLwQ8F9iKdnjKdu8AoPNQGTw5Ok6bwDOSFnQaR -n1Kb/anN7sRaICfw9kNFJVFHbznpjNwK4JO5+tif3EvSNND3+/QAXIIVck3G+GXH -8nt/EJQcExRZSgv3jYf+cXeflPTBvb0RUyOAn3B/AoGAM9aUi2dg3XjlDkZahQlY -P5QNIr9BY25Ordga3GLwfR6rE+jiWTeTmreXBSJ7nvcaEQUNyFMX9n3v/QI84etk -lMJkZ4o2TgzRnCsFJoBV36Ihsa6B5uXVWAvMRLKvwKpptKXfO0TnhUg7oKtN4t3a -/FzAOS2Eu1PFP27z74gAMKY= +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== -----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 new file mode 100755 index 00000000..e48355c8 --- /dev/null +++ b/spring-cloud-gateway-integration-tests/grpc/src/main/resources/public.cert @@ -0,0 +1,21 @@ +-----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== +-----END CERTIFICATE----- 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 f7ee4620..48af3d0b 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 @@ -58,6 +58,7 @@ public class JsonToGrpcApplicationTests { @BeforeEach void setUp() { restTemplate = createUnsecureClient(); +// restTemplate = new RestTemplate(); } @Test 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 7cc4ef2e..510c30ea 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 @@ -7,6 +7,15 @@ server: 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 + spring: cloud: gateway: @@ -20,6 +29,22 @@ spring: 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 logging: level: 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 27f21421..4a3f39a4 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 @@ -163,6 +163,50 @@ public class GatewayControllerEndpointTests { .expectStatus().isCreated(); } + @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.setUri(URI.create("https://localhost:8095")); + testRouteDefinition.setOrder(0); + testRouteDefinition.setMetadata(Collections.emptyMap()); + + FilterDefinition longFilterDefinition = new FilterDefinition(); + FilterDefinition stripPrefix = new FilterDefinition(); + stripPrefix.setName("StripPrefix"); + 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"); + testRouteDefinition.setFilters(Collections.singletonList(longFilterDefinition)); + + PredicateDefinition hostRoutePredicateDefinition = new PredicateDefinition(); + hostRoutePredicateDefinition.setName("Path"); + 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() + .expectStatus().isCreated(); + } + @Test public void testPostRouteWithNotExistingFilter() { 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 283c6322..3bdf16ff 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