23
23
import java .lang .annotation .Target ;
24
24
import java .net .URI ;
25
25
import java .nio .charset .StandardCharsets ;
26
+ import java .time .Duration ;
27
+ import java .time .ZonedDateTime ;
28
+ import java .time .format .DateTimeFormatter ;
26
29
import java .util .ArrayList ;
27
30
import java .util .Arrays ;
28
31
import java .util .List ;
48
51
import org .springframework .core .io .buffer .DataBuffer ;
49
52
import org .springframework .core .io .buffer .DataBufferUtils ;
50
53
import org .springframework .core .io .buffer .DefaultDataBufferFactory ;
54
+ import org .springframework .http .HttpCookie ;
51
55
import org .springframework .http .HttpMethod ;
52
56
import org .springframework .http .HttpStatus ;
53
57
import org .springframework .http .ReactiveHttpOutputMessage ;
58
+ import org .springframework .http .ResponseCookie ;
54
59
55
60
import static org .assertj .core .api .Assertions .assertThat ;
56
61
import static org .assertj .core .api .Assertions .fail ;
@@ -65,6 +70,9 @@ class ClientHttpConnectorTests {
65
70
66
71
private static final Set <HttpMethod > METHODS_WITH_BODY =
67
72
Set .of (HttpMethod .PUT , HttpMethod .POST , HttpMethod .PATCH );
73
+ public static final String MAX_AGE_AND_EXPIRES = "Max-Age-And-Expires" ;
74
+ public static final String MAX_AGE_ONLY = "Max-Age-Only" ;
75
+ public static final String EXPIRES_ONLY = "Expires-Only" ;
68
76
69
77
private final MockWebServer server = new MockWebServer ();
70
78
@@ -172,6 +180,70 @@ void cancelResponseBody(ClientHttpConnector connector) {
172
180
.verify ();
173
181
}
174
182
183
+ @ ParameterizedConnectorTest
184
+ void testExpiresMaxAgeAttributes (ClientHttpConnector connector ) {
185
+ // maxAge is set to 12 days from system time.
186
+ long maxAge = 1036800L ;
187
+
188
+ // expires date is set to 10 days from system time.
189
+ long expires = 864000L ;
190
+ ZonedDateTime currentDateTime = ZonedDateTime .now (java .time .ZoneOffset .UTC );
191
+ ZonedDateTime futureDateTime = currentDateTime .plusSeconds (expires );
192
+
193
+ // processing time may affect the calculation of ZonedDateTime.now during merge of expires and max age
194
+ // therefore we check range with buffer of 2 seconds
195
+ long maxAgeLowerLimit = maxAge - 2 ;
196
+ long maxAgeUpperLimit = maxAge + 2 ;
197
+ long expiresLowerLimit = expires - 2 ;
198
+ long expiresUpperLimit = expires + 2 ;
199
+
200
+ List <HttpCookie > httpCookies = getHttpCookies (maxAge , futureDateTime );
201
+ prepareResponse (response -> {
202
+ response .setResponseCode (200 );
203
+ httpCookies .forEach (httpCookie -> response .addHeader ("Set-Cookie" , httpCookie .toString ()));
204
+ });
205
+
206
+ ClientHttpResponse response = connector .connect (HttpMethod .POST , this .server .url ("/" ).uri (),
207
+ ReactiveHttpOutputMessage ::setComplete ).block ();
208
+ assertThat (response ).isNotNull ();
209
+ assertThat (response .getCookies ()).isNotEmpty ();
210
+
211
+ List <ResponseCookie > maxAgeAndExpiresCookies = response .getCookies ().get (MAX_AGE_AND_EXPIRES );
212
+ assertThat (maxAgeAndExpiresCookies ).size ().isEqualTo (1 );
213
+ Duration maxAgeAndExpires = maxAgeAndExpiresCookies .get (0 ).getMaxAge ();
214
+ assertThat (maxAgeAndExpires .getSeconds ()).isGreaterThanOrEqualTo (maxAgeLowerLimit );
215
+ assertThat (maxAgeAndExpires .getSeconds ()).isLessThanOrEqualTo (maxAgeUpperLimit );
216
+
217
+ List <ResponseCookie > maxAgeOnlyCookies = response .getCookies ().get (MAX_AGE_ONLY );
218
+ assertThat (maxAgeOnlyCookies ).size ().isEqualTo (1 );
219
+ Duration maxAgeOnly = maxAgeOnlyCookies .get (0 ).getMaxAge ();
220
+ assertThat (maxAgeOnly .getSeconds ()).isGreaterThanOrEqualTo (maxAgeLowerLimit );
221
+ assertThat (maxAgeOnly .getSeconds ()).isLessThanOrEqualTo (maxAgeUpperLimit );
222
+
223
+ List <ResponseCookie > expiresOnlyCookies = response .getCookies ().get (EXPIRES_ONLY );
224
+ assertThat (expiresOnlyCookies ).size ().isEqualTo (1 );
225
+ Duration expiresOnly = expiresOnlyCookies .get (0 ).getMaxAge ();
226
+ assertThat (expiresOnly .getSeconds ()).isGreaterThanOrEqualTo (expiresLowerLimit );
227
+ assertThat (expiresOnly .getSeconds ()).isLessThanOrEqualTo (expiresUpperLimit );
228
+ }
229
+
230
+ private List <HttpCookie > getHttpCookies (long maxAge , ZonedDateTime futureDateTime ) {
231
+ String expires = futureDateTime .format (DateTimeFormatter .RFC_1123_DATE_TIME );
232
+
233
+ List <HttpCookie > httpCookies = new ArrayList <>();
234
+
235
+ String maxAgeAndExpiresValue = String .format ("; Max-Age=%d; Expires=%s" , maxAge , expires );
236
+ httpCookies .add (new HttpCookie (MAX_AGE_AND_EXPIRES , maxAgeAndExpiresValue ));
237
+
238
+ String maxAgeOnlyValue = String .format ("; Max-Age=%d" , maxAge );
239
+ httpCookies .add (new HttpCookie (MAX_AGE_ONLY , maxAgeOnlyValue ));
240
+
241
+ String expiresValue = String .format ("; Expires=%s" , expires );
242
+ httpCookies .add (new HttpCookie (EXPIRES_ONLY , expiresValue ));
243
+
244
+ return httpCookies ;
245
+ }
246
+
175
247
private Buffer randomBody (int size ) {
176
248
Buffer responseBody = new Buffer ();
177
249
Random rnd = new Random ();
@@ -211,7 +283,9 @@ static List<Named<ClientHttpConnector>> connectors() {
211
283
return Arrays .asList (
212
284
named ("Reactor Netty" , new ReactorClientHttpConnector ()),
213
285
named ("Jetty" , new JettyClientHttpConnector ()),
214
- named ("HttpComponents" , new HttpComponentsClientHttpConnector ())
286
+ named ("HttpComponents" , new HttpComponentsClientHttpConnector ()),
287
+ named ("Jdk" , new JdkClientHttpConnector ()),
288
+ named ("Reactor Netty 2" , new ReactorNetty2ClientHttpConnector ())
215
289
);
216
290
}
217
291
0 commit comments