mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-21 13:31:46 +00:00
Merge commit 'v7.1.0-M1~1'
This commit is contained in:
@@ -181,6 +181,15 @@ class ContentDispositionTests {
|
||||
assertThat(cd.getFilename()).isEqualTo("foo\\bar \"baz\" qux \\\" quux.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseBackslashInName() {
|
||||
String s = "form-data; name=\"foo\\\"bar\"; filename=\"foo.txt\"";
|
||||
ContentDisposition cd = ContentDisposition.parse(s);
|
||||
assertThat(cd.getName()).isEqualTo("foo\"bar");
|
||||
assertThat(cd.getFilename()).isEqualTo("foo.txt");
|
||||
assertThat(cd.toString()).isEqualTo(s);
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseBackslashInLastPosition() {
|
||||
ContentDisposition cd = ContentDisposition.parse("form-data; name=\"foo\"; filename=\"bar\\\"");
|
||||
|
||||
+15
@@ -255,6 +255,21 @@ class PartEventHttpMessageReaderTests extends AbstractLeakCheckingTests {
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void partSizeTooLargeWithUnlimitedMemorySize() {
|
||||
MockServerHttpRequest request = createRequest("simple.multipart", "simple-boundary");
|
||||
|
||||
PartEventHttpMessageReader reader = new PartEventHttpMessageReader();
|
||||
reader.setMaxPartSize(10);
|
||||
reader.setMaxInMemorySize(-1);
|
||||
|
||||
Flux<PartEvent> result = reader.read(forClass(PartEvent.class), request, emptyMap());
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectError(DataBufferLimitException.class)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void formPartTooLarge() {
|
||||
MockServerHttpRequest request = createRequest(
|
||||
|
||||
+40
-6
@@ -35,7 +35,9 @@ import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.DecodingException;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferLimitException;
|
||||
import org.springframework.core.testfixture.io.buffer.AbstractLeakCheckingTests;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.MimeType;
|
||||
@@ -92,7 +94,7 @@ class Jaxb2XmlDecoderTests extends AbstractLeakCheckingTests {
|
||||
@Test
|
||||
void splitOneBranches() {
|
||||
Flux<XMLEvent> xmlEvents = this.xmlEventDecoder.decode(toDataBufferMono(POJO_ROOT), null, null, HINTS);
|
||||
Flux<List<XMLEvent>> result = Jaxb2Helper.split(xmlEvents, Set.of(new QName("pojo")));
|
||||
Flux<List<XMLEvent>> result = Jaxb2Helper.split(xmlEvents, Set.of(new QName("pojo")), null);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(events -> {
|
||||
@@ -113,8 +115,7 @@ class Jaxb2XmlDecoderTests extends AbstractLeakCheckingTests {
|
||||
@Test
|
||||
void splitMultipleBranches() {
|
||||
Flux<XMLEvent> xmlEvents = this.xmlEventDecoder.decode(toDataBufferMono(POJO_CHILD), null, null, HINTS);
|
||||
Flux<List<XMLEvent>> result = Jaxb2Helper.split(xmlEvents, Set.of(new QName("pojo")));
|
||||
|
||||
Flux<List<XMLEvent>> result = Jaxb2Helper.split(xmlEvents, Set.of(new QName("pojo")), null);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(events -> {
|
||||
@@ -143,6 +144,34 @@ class Jaxb2XmlDecoderTests extends AbstractLeakCheckingTests {
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void splitMultipleBranchesLimitExceeded() {
|
||||
|
||||
Flux<String> source = Flux.just(
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
|
||||
"<root><pojo><foo>foo</foo></pojo>",
|
||||
"<pojo><foo>foofoo</foo>",
|
||||
"<bar>barbar</bar></pojo>",
|
||||
"<root/>");
|
||||
|
||||
XmlEventDecoder.ReceivedByteTracker byteTracker = new XmlEventDecoder.ReceivedByteTracker(30);
|
||||
Map<String, Object> hints = Hints.from(XmlEventDecoder.BYTE_TRACKER_HINT, byteTracker);
|
||||
Flux<XMLEvent> xmlEvents = this.xmlEventDecoder.decode(source.map(this::toToDataBuffer), null, null, hints);
|
||||
Flux<List<XMLEvent>> result = Jaxb2Helper.split(xmlEvents, Set.of(new QName("pojo")), byteTracker);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(events -> {
|
||||
assertThat(events).hasSize(5);
|
||||
assertStartElement(events.get(0), "pojo");
|
||||
assertStartElement(events.get(1), "foo");
|
||||
assertCharacters(events.get(2), "foo");
|
||||
assertEndElement(events.get(3), "foo");
|
||||
assertEndElement(events.get(4), "pojo");
|
||||
})
|
||||
.expectError(DataBufferLimitException.class)
|
||||
.verify();
|
||||
}
|
||||
|
||||
private static void assertStartElement(XMLEvent event, String expectedLocalName) {
|
||||
assertThat(event.isStartElement()).isTrue();
|
||||
assertThat(event.asStartElement().getName().getLocalPart()).isEqualTo(expectedLocalName);
|
||||
@@ -263,13 +292,18 @@ class Jaxb2XmlDecoderTests extends AbstractLeakCheckingTests {
|
||||
|
||||
private Mono<DataBuffer> toDataBufferMono(String value) {
|
||||
return Mono.defer(() -> {
|
||||
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
DataBuffer buffer = this.bufferFactory.allocateBuffer(bytes.length);
|
||||
buffer.write(bytes);
|
||||
DataBuffer buffer = toToDataBuffer(value);
|
||||
return Mono.just(buffer);
|
||||
});
|
||||
}
|
||||
|
||||
private DataBuffer toToDataBuffer(String value) {
|
||||
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
DataBuffer buffer = this.bufferFactory.allocateBuffer(bytes.length);
|
||||
buffer.write(bytes);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@jakarta.xml.bind.annotation.XmlType
|
||||
@XmlSeeAlso(Child.class)
|
||||
public abstract static class Parent {
|
||||
|
||||
@@ -27,7 +27,6 @@ import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferLimitException;
|
||||
import org.springframework.core.testfixture.io.buffer.AbstractLeakCheckingTests;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -88,28 +87,6 @@ class XmlEventDecoderTests extends AbstractLeakCheckingTests {
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void toXMLEventsWithLimit() {
|
||||
|
||||
this.decoder.setMaxInMemorySize(6);
|
||||
|
||||
Flux<String> source = Flux.just(
|
||||
"<pojo>", "<foo>", "foofoo", "</foo>", "<bar>", "barbarbar", "</bar>", "</pojo>");
|
||||
|
||||
Flux<XMLEvent> events = this.decoder.decode(
|
||||
source.map(this::stringBuffer), null, null, Collections.emptyMap());
|
||||
|
||||
StepVerifier.create(events)
|
||||
.consumeNextWith(e -> assertThat(e.isStartDocument()).isTrue())
|
||||
.consumeNextWith(e -> assertStartElement(e, "pojo"))
|
||||
.consumeNextWith(e -> assertStartElement(e, "foo"))
|
||||
.consumeNextWith(e -> assertCharacters(e, "foofoo"))
|
||||
.consumeNextWith(e -> assertEndElement(e, "foo"))
|
||||
.consumeNextWith(e -> assertStartElement(e, "bar"))
|
||||
.expectError(DataBufferLimitException.class)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeErrorAalto() {
|
||||
Flux<DataBuffer> source = Flux.concat(
|
||||
|
||||
@@ -94,4 +94,25 @@ class EscapedErrorsTests {
|
||||
assertThat(ageError2.getCode()).as("Age error 2 code not escaped").isEqualTo("AGE_NOT_32 <tag>");
|
||||
}
|
||||
|
||||
@Test
|
||||
void noArgFieldErrorAccessorsEscapeRejectedValueAndMessage() {
|
||||
TestBean tb = new TestBean();
|
||||
tb.setName("<script>alert(1)</script>");
|
||||
|
||||
Errors errors = new EscapedErrors(new BindException(tb, "tb"));
|
||||
errors.rejectValue("name", "NAME_INVALID", null, "message: <tag>");
|
||||
|
||||
FieldError fieldError = errors.getFieldError();
|
||||
assertThat(fieldError.getDefaultMessage()).as("No-arg getFieldError() message escaped")
|
||||
.isEqualTo("message: <tag>");
|
||||
assertThat(fieldError.getRejectedValue()).as("No-arg getFieldError() rejected value escaped")
|
||||
.isEqualTo("<script>alert(1)</script>");
|
||||
|
||||
FieldError fieldErrorInList = errors.getFieldErrors().get(0);
|
||||
assertThat(fieldErrorInList.getDefaultMessage()).as("No-arg getFieldErrors() message escaped")
|
||||
.isEqualTo("message: <tag>");
|
||||
assertThat(fieldErrorInList.getRejectedValue()).as("No-arg getFieldErrors() rejected value escaped")
|
||||
.isEqualTo("<script>alert(1)</script>");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -74,11 +74,16 @@ class UrlHandlerFilterTests {
|
||||
|
||||
@Test
|
||||
void redirect() throws Exception {
|
||||
HttpStatus status = HttpStatus.PERMANENT_REDIRECT;
|
||||
UrlHandlerFilter filter = UrlHandlerFilter.trailingSlashHandler("/path/*").redirect(status).build();
|
||||
testRedirect("/**", "/path/123/", "/path/123");
|
||||
testRedirect("/**", "//path/123/", "/path/123");
|
||||
testRedirect("/**", "///path/123/", "///path/123");
|
||||
}
|
||||
|
||||
String path = "/path/123";
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", path + "/");
|
||||
private void testRedirect(String pattern, String path, String location) throws Exception {
|
||||
HttpStatus status = HttpStatus.PERMANENT_REDIRECT;
|
||||
UrlHandlerFilter filter = UrlHandlerFilter.trailingSlashHandler(pattern).redirect(status).build();
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", path);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
MockFilterChain chain = new MockFilterChain();
|
||||
|
||||
@@ -89,7 +94,7 @@ class UrlHandlerFilterTests {
|
||||
|
||||
assertThat(chain.getRequest()).isNull();
|
||||
assertThat(response.getStatus()).isEqualTo(status.value());
|
||||
assertThat(response.getHeader(HttpHeaders.LOCATION)).isEqualTo(path + "?" + queryString);
|
||||
assertThat(response.getHeader(HttpHeaders.LOCATION)).isEqualTo(location + "?" + queryString);
|
||||
assertThat(response.isCommitted()).isTrue();
|
||||
}
|
||||
|
||||
|
||||
+13
-8
@@ -17,12 +17,14 @@
|
||||
package org.springframework.web.filter.reactive;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@@ -59,20 +61,23 @@ class UrlHandlerFilterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void redirect() {
|
||||
HttpStatus status = HttpStatus.PERMANENT_REDIRECT;
|
||||
UrlHandlerFilter filter = UrlHandlerFilter.trailingSlashHandler("/path/*").redirect(status).build();
|
||||
void redirect() throws URISyntaxException {
|
||||
testRedirect("/**", new URI(null, null, "/path/123/", "foo=bar", null), "/path/123?foo=bar");
|
||||
// no way to create java.net.URI with leading slashes
|
||||
}
|
||||
|
||||
String path = "/path/123";
|
||||
String queryString = "foo=bar";
|
||||
MockServerHttpRequest original = MockServerHttpRequest.get(path + "/?" + queryString).build();
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(original);
|
||||
private static void testRedirect(String pattern, URI uri, String location) {
|
||||
HttpStatus status = HttpStatus.PERMANENT_REDIRECT;
|
||||
UrlHandlerFilter filter = UrlHandlerFilter.trailingSlashHandler(pattern).redirect(status).build();
|
||||
|
||||
MockServerHttpRequest request = MockServerHttpRequest.method(HttpMethod.GET, uri).build();
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
|
||||
assertThatThrownBy(() -> invokeFilter(filter, exchange))
|
||||
.hasMessageContaining("No argument value was captured");
|
||||
|
||||
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(status);
|
||||
assertThat(exchange.getResponse().getHeaders().getLocation()).isEqualTo(URI.create(path + "?" + queryString));
|
||||
assertThat(exchange.getResponse().getHeaders().getLocation()).isEqualTo(URI.create(location));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2002-present 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.web.util;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link SseUtils}.
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class SseUtilsTests {
|
||||
|
||||
@Test
|
||||
void appendFieldValueWithoutLineSeparatorAppendsAsIs() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("data:");
|
||||
SseUtils.appendFieldValue("data", "no newlines here", sb);
|
||||
assertThat(sb).hasToString("data:no newlines here");
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{1}")
|
||||
@MethodSource("newLineCharacters")
|
||||
void appendFieldValueReplacesLineSeparatorWithFieldPrefix(String newLine, String description) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("data:");
|
||||
SseUtils.appendFieldValue("data", "first" + newLine + "second", sb);
|
||||
assertThat(sb).hasToString("data:first\ndata:second");
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{1}")
|
||||
@MethodSource("newLineCharacters")
|
||||
void appendFieldValueUsesEmptyFieldForComments(String newLine, String description) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(":");
|
||||
SseUtils.appendFieldValue("", "first" + newLine + "second", sb);
|
||||
assertThat(sb).hasToString(":first\n:second");
|
||||
}
|
||||
|
||||
@Test
|
||||
void assertNoLineSeparatorAcceptsPlainContent() {
|
||||
SseUtils.assertNoLineSeparator("no newlines here");
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{1}")
|
||||
@MethodSource("newLineCharacters")
|
||||
void assertNoLineSeparatorRejectsLineSeparator(String newLine, String description) {
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
SseUtils.assertNoLineSeparator("first" + newLine + "second"));
|
||||
}
|
||||
|
||||
static Stream<Arguments> newLineCharacters() {
|
||||
return Stream.of(
|
||||
Arguments.of("\n", "LF"),
|
||||
Arguments.of("\r", "CR"),
|
||||
Arguments.of("\r\n", "CRLF")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user