mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-27 01:19:02 +00:00
Merge branch '6.2.x'
This commit is contained in:
+7
-9
@@ -898,16 +898,14 @@ public abstract class AbstractMockHttpServletRequestBuilder<B extends AbstractMo
|
||||
request.setContextPath(this.contextPath);
|
||||
request.setServletPath(this.servletPath);
|
||||
|
||||
if ("".equals(this.pathInfo)) {
|
||||
if (!requestUri.startsWith(this.contextPath + this.servletPath)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid servlet path [" + this.servletPath + "] for request URI [" + requestUri + "]");
|
||||
}
|
||||
String extraPath = requestUri.substring(this.contextPath.length() + this.servletPath.length());
|
||||
this.pathInfo = (StringUtils.hasText(extraPath) ?
|
||||
UrlPathHelper.defaultInstance.decodeRequestString(request, extraPath) : null);
|
||||
String path = this.pathInfo;
|
||||
if ("".equals(path)) {
|
||||
Assert.isTrue(requestUri.startsWith(this.contextPath + this.servletPath),
|
||||
() -> "Invalid servlet path [" + this.servletPath + "] for request URI [" + requestUri + "]");
|
||||
String other = requestUri.substring(this.contextPath.length() + this.servletPath.length());
|
||||
path = (StringUtils.hasText(other) ? UrlPathHelper.defaultInstance.decodeRequestString(request, other) : null);
|
||||
}
|
||||
request.setPathInfo(this.pathInfo);
|
||||
request.setPathInfo(path);
|
||||
}
|
||||
|
||||
private void addRequestParams(MockHttpServletRequest request, MultiValueMap<String, String> map) {
|
||||
|
||||
+9
@@ -32,6 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* Tests for {@link AbstractMockHttpServletRequestBuilder}
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Réda Housni Alaoui
|
||||
*/
|
||||
class AbstractMockHttpServletRequestBuilderTests {
|
||||
|
||||
@@ -128,6 +129,14 @@ class AbstractMockHttpServletRequestBuilderTests {
|
||||
assertThat(buildRequest(builder).getHeader("API-Version")).isEqualTo("1.1");
|
||||
}
|
||||
|
||||
@Test // gh-35493
|
||||
void pathInfoIsNotMutatedByBuildMethod() {
|
||||
TestRequestBuilder builder = new TestRequestBuilder(HttpMethod.GET).uri("/b");
|
||||
assertThat(buildRequest(builder).getPathInfo()).isEqualTo("/b");
|
||||
builder.uri("/a");
|
||||
assertThat(buildRequest(builder).getPathInfo()).isEqualTo("/a");
|
||||
}
|
||||
|
||||
private MockHttpServletRequest buildRequest(AbstractMockHttpServletRequestBuilder<?> builder) {
|
||||
return builder.buildRequest(this.servletContext);
|
||||
}
|
||||
|
||||
+42
-25
@@ -52,12 +52,14 @@ import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
/**
|
||||
* {@code HttpMessageWriter} that can write a {@link Resource}.
|
||||
* {@code HttpMessageWriter} that can write a {@link Resource} from both`` client
|
||||
* and server perspectives.
|
||||
*
|
||||
* <p>Also an implementation of {@code HttpMessageWriter} with support for writing one
|
||||
* or more {@link ResourceRegion}'s based on the HTTP ranges specified in the request.
|
||||
* <p>From a server perspective, the server-side only write method supports
|
||||
* writing one or more {@link ResourceRegion}'s based on HTTP ranges specified
|
||||
* in the request.
|
||||
*
|
||||
* <p>For reading to a Resource, use {@link ResourceDecoder} wrapped with
|
||||
* <p>To read a Resource, use {@link ResourceDecoder} wrapped with
|
||||
* {@link DecoderHttpMessageReader}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
@@ -123,16 +125,19 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter<Resource> {
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
Mono<Resource> input = Mono.just(resource);
|
||||
DataBufferFactory factory = message.bufferFactory();
|
||||
Flux<DataBuffer> body = this.encoder.encode(input, factory, type, message.getHeaders().getContentType(), hints)
|
||||
.subscribeOn(Schedulers.boundedElastic());
|
||||
if (logger.isDebugEnabled()) {
|
||||
body = body.doOnNext(buffer -> Hints.touchDataBuffer(buffer, hints, logger));
|
||||
}
|
||||
return message.writeWith(body);
|
||||
|
||||
Mono<Resource> input = Mono.just(resource);
|
||||
DataBufferFactory factory = message.bufferFactory();
|
||||
MediaType contentType = message.getHeaders().getContentType();
|
||||
|
||||
Flux<DataBuffer> body = this.encoder.encode(input, factory, type, contentType, hints)
|
||||
.subscribeOn(Schedulers.boundedElastic());
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
body = body.doOnNext(buffer -> Hints.touchDataBuffer(buffer, hints, logger));
|
||||
}
|
||||
|
||||
return message.writeWith(body);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -140,7 +145,10 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter<Resource> {
|
||||
* Adds the default headers for the given resource to the given message.
|
||||
* @since 6.1
|
||||
*/
|
||||
public Mono<Void> addDefaultHeaders(ReactiveHttpOutputMessage message, Resource resource, @Nullable MediaType contentType, Map<String, Object> hints) {
|
||||
public Mono<Void> addDefaultHeaders(
|
||||
ReactiveHttpOutputMessage message, Resource resource, @Nullable MediaType contentType,
|
||||
Map<String, Object> hints) {
|
||||
|
||||
return Mono.defer(() -> {
|
||||
HttpHeaders headers = message.getHeaders();
|
||||
MediaType resourceMediaType = getResourceMediaType(contentType, resource, hints);
|
||||
@@ -150,16 +158,15 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter<Resource> {
|
||||
headers.set(HttpHeaders.ACCEPT_RANGES, "bytes");
|
||||
}
|
||||
|
||||
if (headers.getContentLength() < 0) {
|
||||
return lengthOf(resource)
|
||||
.flatMap(contentLength -> {
|
||||
headers.setContentLength(contentLength);
|
||||
return Mono.empty();
|
||||
});
|
||||
}
|
||||
else {
|
||||
if (headers.getContentLength() >= 0) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
return lengthOf(resource)
|
||||
.flatMap(contentLength -> {
|
||||
headers.setContentLength(contentLength);
|
||||
return Mono.empty();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -226,8 +233,7 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter<Resource> {
|
||||
ranges = request.getHeaders().getRange();
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
response.setStatusCode(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE);
|
||||
return response.setComplete();
|
||||
return handleInvalidRange(response);
|
||||
}
|
||||
|
||||
return Mono.from(inputStream).flatMap(resource -> {
|
||||
@@ -235,7 +241,13 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter<Resource> {
|
||||
return writeResource(resource, elementType, mediaType, response, hints);
|
||||
}
|
||||
response.setStatusCode(HttpStatus.PARTIAL_CONTENT);
|
||||
List<ResourceRegion> regions = HttpRange.toResourceRegions(ranges, resource);
|
||||
List<ResourceRegion> regions;
|
||||
try {
|
||||
regions = HttpRange.toResourceRegions(ranges, resource);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
return handleInvalidRange(response);
|
||||
}
|
||||
MediaType resourceMediaType = getResourceMediaType(mediaType, resource, hints);
|
||||
if (regions.size() == 1){
|
||||
ResourceRegion region = regions.get(0);
|
||||
@@ -261,6 +273,11 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter<Resource> {
|
||||
});
|
||||
}
|
||||
|
||||
private static Mono<Void> handleInvalidRange(ServerHttpResponse response) {
|
||||
response.setStatusCode(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE);
|
||||
return response.setComplete();
|
||||
}
|
||||
|
||||
private Mono<Void> writeSingleRegion(ResourceRegion region, ReactiveHttpOutputMessage message,
|
||||
Map<String, Object> hints) {
|
||||
|
||||
|
||||
+9
@@ -156,6 +156,15 @@ class ResourceHttpMessageWriterTests {
|
||||
assertThat(this.response.getStatusCode()).isEqualTo(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE);
|
||||
}
|
||||
|
||||
@Test // gh-35536
|
||||
void invalidRangePosition() {
|
||||
|
||||
testWrite(get("/").header(HttpHeaders.RANGE, "bytes=2000-5000").build());
|
||||
|
||||
assertThat(this.response.getHeaders().getFirst(HttpHeaders.ACCEPT_RANGES)).isEqualTo("bytes");
|
||||
assertThat(this.response.getStatusCode()).isEqualTo(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE);
|
||||
}
|
||||
|
||||
|
||||
private void testWrite(MockServerHttpRequest request) {
|
||||
Mono<Void> mono = this.writer.write(this.input, null, null, TEXT_PLAIN, request, this.response, HINTS);
|
||||
|
||||
Reference in New Issue
Block a user