/*
* Copyright 2002-2017 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
*
* http://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.web.server.adapter;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Locale;
import java.util.TimeZone;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
import org.springframework.mock.http.server.reactive.test.MockServerWebExchange;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.mock.http.server.reactive.test.MockServerHttpRequest.get;
/**
* "checkNotModified" unit tests for {@link DefaultServerWebExchange}.
*
* @author Rossen Stoyanchev
*/
@RunWith(Parameterized.class)
public class DefaultServerWebExchangeCheckNotModifiedTests {
private static final String CURRENT_TIME = "Wed, 09 Apr 2014 09:57:42 GMT";
private SimpleDateFormat dateFormat;
private Instant currentDate;
@Parameter
public HttpMethod method;
@Parameters(name = "{0}")
static public Iterable<Object[]> safeMethods() {
return Arrays.asList(new Object[][] {
{HttpMethod.GET},
{HttpMethod.HEAD}
});
}
@Before
public void setup() throws URISyntaxException {
this.currentDate = Instant.now().truncatedTo(ChronoUnit.SECONDS);
this.dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
this.dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
}
@Test
public void checkNotModifiedNon2xxStatus() {
MockServerWebExchange exchange = get("/").ifModifiedSince(this.currentDate.toEpochMilli()).toExchange();
exchange.getResponse().setStatusCode(HttpStatus.NOT_MODIFIED);
assertFalse(exchange.checkNotModified(this.currentDate));
assertEquals(304, exchange.getResponse().getStatusCode().value());
assertEquals(-1, exchange.getResponse().getHeaders().getLastModified());
}
@Test // SPR-14559
public void checkNotModifiedInvalidIfNoneMatchHeader() {
String eTag = "\"etagvalue\"";
MockServerWebExchange exchange = get("/").ifNoneMatch("missingquotes").toExchange();
assertFalse(exchange.checkNotModified(eTag));
assertNull(exchange.getResponse().getStatusCode());
assertEquals(eTag, exchange.getResponse().getHeaders().getETag());
}
@Test
public void checkNotModifiedHeaderAlreadySet() {
MockServerWebExchange exchange = get("/").ifModifiedSince(currentDate.toEpochMilli()).toExchange();
exchange.getResponse().getHeaders().add("Last-Modified", CURRENT_TIME);
assertTrue(exchange.checkNotModified(currentDate));
assertEquals(304, exchange.getResponse().getStatusCode().value());
assertEquals(1, exchange.getResponse().getHeaders().get("Last-Modified").size());
assertEquals(CURRENT_TIME, exchange.getResponse().getHeaders().getFirst("Last-Modified"));
}
@Test
public void checkNotModifiedTimestamp() throws Exception {
MockServerWebExchange exchange = get("/").ifModifiedSince(currentDate.toEpochMilli()).toExchange();
assertTrue(exchange.checkNotModified(currentDate));
assertEquals(304, exchange.getResponse().getStatusCode().value());
assertEquals(currentDate.toEpochMilli(), exchange.getResponse().getHeaders().getLastModified());
}
@Test
public void checkModifiedTimestamp() {
Instant oneMinuteAgo = currentDate.minusSeconds(60);
MockServerWebExchange exchange = get("/").ifModifiedSince(oneMinuteAgo.toEpochMilli()).toExchange();
assertFalse(exchange.checkNotModified(currentDate));
assertNull(exchange.getResponse().getStatusCode());
assertEquals(currentDate.toEpochMilli(), exchange.getResponse().getHeaders().getLastModified());
}
@Test
public void checkNotModifiedETag() {
String eTag = "\"Foo\"";
MockServerWebExchange exchange = get("/").ifNoneMatch(eTag).toExchange();
assertTrue(exchange.checkNotModified(eTag));
assertEquals(304, exchange.getResponse().getStatusCode().value());
assertEquals(eTag, exchange.getResponse().getHeaders().getETag());
}
@Test
public void checkNotModifiedETagWithSeparatorChars() {
String eTag = "\"Foo, Bar\"";
MockServerWebExchange exchange = get("/").ifNoneMatch(eTag).toExchange();
assertTrue(exchange.checkNotModified(eTag));
assertEquals(304, exchange.getResponse().getStatusCode().value());
assertEquals(eTag, exchange.getResponse().getHeaders().getETag());
}
@Test
public void checkModifiedETag() {
String currentETag = "\"Foo\"";
String oldEtag = "Bar";
MockServerWebExchange exchange = get("/").ifNoneMatch(oldEtag).toExchange();
assertFalse(exchange.checkNotModified(currentETag));
assertNull(exchange.getResponse().getStatusCode());
assertEquals(currentETag, exchange.getResponse().getHeaders().getETag());
}
@Test
public void checkNotModifiedUnpaddedETag() {
String eTag = "Foo";
String paddedEtag = String.format("\"%s\"", eTag);
MockServerWebExchange exchange = get("/").ifNoneMatch(paddedEtag).toExchange();
assertTrue(exchange.checkNotModified(eTag));
assertEquals(304, exchange.getResponse().getStatusCode().value());
assertEquals(paddedEtag, exchange.getResponse().getHeaders().getETag());
}
@Test
public void checkModifiedUnpaddedETag() {
String currentETag = "Foo";
String oldEtag = "Bar";
MockServerWebExchange exchange = get("/").ifNoneMatch(oldEtag).toExchange();
assertFalse(exchange.checkNotModified(currentETag));
assertNull(exchange.getResponse().getStatusCode());
assertEquals(String.format("\"%s\"", currentETag), exchange.getResponse().getHeaders().getETag());
}
@Test
public void checkNotModifiedWildcardIsIgnored() {
String eTag = "\"Foo\"";
MockServerWebExchange exchange = get("/").ifNoneMatch("*").toExchange();
assertFalse(exchange.checkNotModified(eTag));
assertNull(exchange.getResponse().getStatusCode());
assertEquals(eTag, exchange.getResponse().getHeaders().getETag());
}
@Test
public void checkNotModifiedETagAndTimestamp() {
String eTag = "\"Foo\"";
long time = currentDate.toEpochMilli();
MockServerWebExchange exchange = get("/").ifNoneMatch(eTag).ifModifiedSince(time).toExchange();
assertTrue(exchange.checkNotModified(eTag, currentDate));
assertEquals(304, exchange.getResponse().getStatusCode().value());
assertEquals(eTag, exchange.getResponse().getHeaders().getETag());
assertEquals(time, exchange.getResponse().getHeaders().getLastModified());
}
// SPR-14224
@Test
public void checkNotModifiedETagAndModifiedTimestamp() {
String eTag = "\"Foo\"";
Instant oneMinuteAgo = currentDate.minusSeconds(60);
MockServerWebExchange exchange = get("/")
.ifNoneMatch(eTag)
.ifModifiedSince(oneMinuteAgo.toEpochMilli())
.toExchange();
assertTrue(exchange.checkNotModified(eTag, currentDate));
assertEquals(304, exchange.getResponse().getStatusCode().value());
assertEquals(eTag, exchange.getResponse().getHeaders().getETag());
assertEquals(currentDate.toEpochMilli(), exchange.getResponse().getHeaders().getLastModified());
}
@Test
public void checkModifiedETagAndNotModifiedTimestamp() throws Exception {
String currentETag = "\"Foo\"";
String oldEtag = "\"Bar\"";
long time = currentDate.toEpochMilli();
MockServerWebExchange exchange = get("/").ifNoneMatch(oldEtag).ifModifiedSince(time).toExchange();
assertFalse(exchange.checkNotModified(currentETag, currentDate));
assertNull(exchange.getResponse().getStatusCode());
assertEquals(currentETag, exchange.getResponse().getHeaders().getETag());
assertEquals(time, exchange.getResponse().getHeaders().getLastModified());
}
@Test
public void checkNotModifiedETagWeakStrong() {
String eTag = "\"Foo\"";
String weakEtag = String.format("W/%s", eTag);
MockServerWebExchange exchange = get("/").ifNoneMatch(eTag).toExchange();
assertTrue(exchange.checkNotModified(weakEtag));
assertEquals(304, exchange.getResponse().getStatusCode().value());
assertEquals(weakEtag, exchange.getResponse().getHeaders().getETag());
}
@Test
public void checkNotModifiedETagStrongWeak() {
String eTag = "\"Foo\"";
MockServerWebExchange exchange = get("/").ifNoneMatch(String.format("W/%s", eTag)).toExchange();
assertTrue(exchange.checkNotModified(eTag));
assertEquals(304, exchange.getResponse().getStatusCode().value());
assertEquals(eTag, exchange.getResponse().getHeaders().getETag());
}
@Test
public void checkNotModifiedMultipleETags() {
String eTag = "\"Bar\"";
String multipleETags = String.format("\"Foo\", %s", eTag);
MockServerWebExchange exchange = get("/").ifNoneMatch(multipleETags).toExchange();
assertTrue(exchange.checkNotModified(eTag));
assertEquals(304, exchange.getResponse().getStatusCode().value());
assertEquals(eTag, exchange.getResponse().getHeaders().getETag());
}
@Test
public void checkNotModifiedTimestampWithLengthPart() throws Exception {
long epochTime = dateFormat.parse(CURRENT_TIME).getTime();
String header = "Wed, 09 Apr 2014 09:57:42 GMT; length=13774";
MockServerWebExchange exchange = get("/").header("If-Modified-Since", header).toExchange();
assertTrue(exchange.checkNotModified(Instant.ofEpochMilli(epochTime)));
assertEquals(304, exchange.getResponse().getStatusCode().value());
assertEquals(epochTime, exchange.getResponse().getHeaders().getLastModified());
}
@Test
public void checkModifiedTimestampWithLengthPart() throws Exception {
long epochTime = dateFormat.parse(CURRENT_TIME).getTime();
String header = "Tue, 08 Apr 2014 09:57:42 GMT; length=13774";
MockServerWebExchange exchange = get("/").header("If-Modified-Since", header).toExchange();
assertFalse(exchange.checkNotModified(Instant.ofEpochMilli(epochTime)));
assertNull(exchange.getResponse().getStatusCode());
assertEquals(epochTime, exchange.getResponse().getHeaders().getLastModified());
}
@Test
public void checkNotModifiedTimestampConditionalPut() throws Exception {
Instant oneMinuteAgo = currentDate.minusSeconds(60);
long millis = currentDate.toEpochMilli();
MockServerWebExchange exchange = MockServerHttpRequest.put("/").ifUnmodifiedSince(millis).toExchange();
assertFalse(exchange.checkNotModified(oneMinuteAgo));
assertNull(exchange.getResponse().getStatusCode());
assertEquals(-1, exchange.getResponse().getHeaders().getLastModified());
}
@Test
public void checkNotModifiedTimestampConditionalPutConflict() throws Exception {
Instant oneMinuteAgo = currentDate.minusSeconds(60);
long millis = oneMinuteAgo.toEpochMilli();
MockServerWebExchange exchange = MockServerHttpRequest.put("/").ifUnmodifiedSince(millis).toExchange();
assertTrue(exchange.checkNotModified(currentDate));
assertEquals(412, exchange.getResponse().getStatusCode().value());
assertEquals(-1, exchange.getResponse().getHeaders().getLastModified());
}
}