1
+ /*
2
+ * Copyright 2013-2024 the original author or authors.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * https://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ package org .springframework .cloud .gateway .actuate ;
18
+
19
+ import java .net .URI ;
20
+ import java .time .Duration ;
21
+ import java .util .HashMap ;
22
+ import java .util .List ;
23
+ import java .util .Map ;
24
+
25
+ import org .awaitility .Awaitility ;
26
+ import org .junit .jupiter .api .BeforeAll ;
27
+ import org .junit .jupiter .api .Tag ;
28
+ import org .junit .jupiter .api .Test ;
29
+ import org .testcontainers .containers .GenericContainer ;
30
+ import org .testcontainers .junit .jupiter .Container ;
31
+ import org .testcontainers .junit .jupiter .Testcontainers ;
32
+
33
+ import org .springframework .beans .factory .annotation .Autowired ;
34
+ import org .springframework .boot .test .context .SpringBootTest ;
35
+ import org .springframework .boot .test .web .server .LocalServerPort ;
36
+ import org .springframework .cloud .gateway .handler .predicate .PredicateDefinition ;
37
+ import org .springframework .cloud .gateway .route .RouteDefinition ;
38
+ import org .springframework .http .MediaType ;
39
+ import org .springframework .test .annotation .DirtiesContext ;
40
+ import org .springframework .test .context .ActiveProfiles ;
41
+ import org .springframework .test .context .DynamicPropertyRegistry ;
42
+ import org .springframework .test .context .DynamicPropertySource ;
43
+ import org .springframework .test .web .reactive .server .WebTestClient ;
44
+ import org .springframework .web .reactive .function .BodyInserters ;
45
+
46
+ import static org .assertj .core .api .Assertions .assertThat ;
47
+ import static org .springframework .boot .test .context .SpringBootTest .WebEnvironment .RANDOM_PORT ;
48
+
49
+ /**
50
+ * @author Peter Müller
51
+ */
52
+ @ SpringBootTest (properties = {"management.endpoint.gateway.enabled=true" ,
53
+ "management.endpoints.web.exposure.include=*" , "spring.cloud.gateway.actuator.verbose.enabled=true" },
54
+ webEnvironment = RANDOM_PORT )
55
+ @ DirtiesContext (classMode = DirtiesContext .ClassMode .AFTER_EACH_TEST_METHOD )
56
+ @ ActiveProfiles ("redis-route-repository" )
57
+ @ Testcontainers
58
+ @ Tag ("DockerRequired" )
59
+ public class GatewayControllerEndpointRedisRefreshTest {
60
+
61
+ @ Container
62
+ public static GenericContainer redis = new GenericContainer <>("redis:5.0.14-alpine" ).withExposedPorts (6379 );
63
+
64
+ @ BeforeAll
65
+ public static void startRedisContainer () {
66
+ redis .start ();
67
+ }
68
+
69
+ @ DynamicPropertySource
70
+ static void containerProperties (DynamicPropertyRegistry registry ) {
71
+ registry .add ("spring.data.redis.host" , redis ::getHost );
72
+ registry .add ("spring.data.redis.port" , redis ::getFirstMappedPort );
73
+ }
74
+
75
+ @ Autowired
76
+ WebTestClient testClient ;
77
+
78
+ @ LocalServerPort
79
+ int port ;
80
+
81
+ @ Test
82
+ public void testCorsConfigurationAfterReload () {
83
+ Map <String , Object > cors = new HashMap <>();
84
+ cors .put ("allowCredentials" , false );
85
+ cors .put ("allowedOrigins" , "*" );
86
+ cors .put ("allowedMethods" , "GET" );
87
+
88
+ createOrUpdateRouteWithCors (cors );
89
+
90
+ Awaitility .await ().atMost (Duration .ofSeconds (3 )).untilAsserted (() -> assertRouteHasCorsConfig (cors ));
91
+ Awaitility .await ().atMost (Duration .ofSeconds (3 )).untilAsserted (() -> assertPreflightAllowOrigin ("*" ));
92
+
93
+ cors .put ("allowedOrigins" , "http://example.org" );
94
+ createOrUpdateRouteWithCors (cors );
95
+
96
+ Awaitility .await ().atMost (Duration .ofSeconds (3 )).untilAsserted (() -> assertRouteHasCorsConfig (cors ));
97
+ Awaitility .await ().atMost (Duration .ofSeconds (3 ))
98
+ .untilAsserted (() -> assertPreflightAllowOrigin ("http://example.org" ));
99
+ }
100
+
101
+ void createOrUpdateRouteWithCors (Map <String , Object > cors ) {
102
+ RouteDefinition testRouteDefinition = new RouteDefinition ();
103
+ testRouteDefinition .setUri (URI .create ("http://example.org" ));
104
+
105
+ PredicateDefinition methodRoutePredicateDefinition = new PredicateDefinition ("Method=GET" );
106
+ testRouteDefinition .setPredicates (List .of (methodRoutePredicateDefinition ));
107
+
108
+ testRouteDefinition .setMetadata (Map .of ("cors" , cors ));
109
+
110
+ testClient .post ()
111
+ .uri ("http://localhost:" + port + "/actuator/gateway/routes/cors-test-route" )
112
+ .accept (MediaType .APPLICATION_JSON )
113
+ .body (BodyInserters .fromValue (testRouteDefinition ))
114
+ .exchange ()
115
+ .expectStatus ()
116
+ .isCreated ();
117
+
118
+ testClient .post ()
119
+ .uri ("http://localhost:" + port + "/actuator/gateway/refresh" )
120
+ .exchange ()
121
+ .expectStatus ()
122
+ .isOk ();
123
+ }
124
+
125
+ void assertRouteHasCorsConfig (Map <String , Object > cors ) {
126
+ testClient .get ()
127
+ .uri ("http://localhost:" + port + "/actuator/gateway/routes/cors-test-route" )
128
+ .exchange ()
129
+ .expectStatus ()
130
+ .isOk ()
131
+ .expectBody ()
132
+ .jsonPath ("$.metadata" )
133
+ .value (map -> assertThat ((Map <String , Object >) map ).hasSize (1 )
134
+ .containsEntry ("cors" , cors ));
135
+ }
136
+
137
+ void assertPreflightAllowOrigin (String origin ) {
138
+ testClient .options ()
139
+ .uri ("http://localhost:" + port + "/" )
140
+ .header ("Origin" , "http://example.org" )
141
+ .header ("Access-Control-Request-Method" , "GET" )
142
+ .exchange ()
143
+ .expectStatus ()
144
+ .isOk ()
145
+ .expectHeader ()
146
+ .valueEquals ("Access-Control-Allow-Origin" , origin );
147
+ }
148
+ }
0 commit comments