Skip to content

Commit adc7f73

Browse files
committed
Merge branch '6.1.x'
2 parents af4a986 + d4ddbd5 commit adc7f73

File tree

4 files changed

+95
-69
lines changed

4 files changed

+95
-69
lines changed

spring-core/src/main/java/org/springframework/util/StreamUtils.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -178,18 +178,13 @@ public static long copyRange(InputStream in, OutputStream out, long start, long
178178
long bytesToCopy = end - start + 1;
179179
byte[] buffer = new byte[(int) Math.min(StreamUtils.BUFFER_SIZE, bytesToCopy)];
180180
while (bytesToCopy > 0) {
181-
int bytesRead = in.read(buffer);
181+
int bytesRead = (bytesToCopy < buffer.length ? in.read(buffer, 0, (int) bytesToCopy) :
182+
in.read(buffer));
182183
if (bytesRead == -1) {
183184
break;
184185
}
185-
else if (bytesRead <= bytesToCopy) {
186-
out.write(buffer, 0, bytesRead);
187-
bytesToCopy -= bytesRead;
188-
}
189-
else {
190-
out.write(buffer, 0, (int) bytesToCopy);
191-
bytesToCopy = 0;
192-
}
186+
out.write(buffer, 0, bytesRead);
187+
bytesToCopy -= bytesRead;
193188
}
194189
return (end - start + 1 - bytesToCopy);
195190
}
@@ -204,7 +199,9 @@ else if (bytesRead <= bytesToCopy) {
204199
*/
205200
@Contract("null -> fail")
206201
public static int drain(@Nullable InputStream in) throws IOException {
207-
Assert.notNull(in, "No InputStream specified");
202+
if (in == null) {
203+
return 0;
204+
}
208205
return (int) in.transferTo(OutputStream.nullOutputStream());
209206
}
210207

spring-core/src/test/java/org/springframework/util/StreamUtilsTests.java

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -38,13 +38,15 @@
3838
* Tests for {@link StreamUtils}.
3939
*
4040
* @author Phillip Webb
41+
* @author Juergen Hoeller
4142
*/
4243
class StreamUtilsTests {
4344

4445
private byte[] bytes = new byte[StreamUtils.BUFFER_SIZE + 10];
4546

4647
private String string = "";
4748

49+
4850
@BeforeEach
4951
void setup() {
5052
new Random().nextBytes(bytes);
@@ -53,6 +55,7 @@ void setup() {
5355
}
5456
}
5557

58+
5659
@Test
5760
void copyToByteArray() throws Exception {
5861
InputStream inputStream = new ByteArrayInputStream(bytes);
@@ -91,11 +94,30 @@ void copyStream() throws Exception {
9194
}
9295

9396
@Test
94-
void copyRange() throws Exception {
97+
void copyRangeWithinBuffer() throws Exception {
98+
ByteArrayOutputStream out = new ByteArrayOutputStream();
99+
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
100+
StreamUtils.copyRange(in, out, 0, 100);
101+
assertThat(in.available()).isEqualTo(bytes.length - 101);
102+
assertThat(out.toByteArray()).isEqualTo(Arrays.copyOfRange(bytes, 0, 101));
103+
}
104+
105+
@Test
106+
void copyRangeBeyondBuffer() throws Exception {
95107
ByteArrayOutputStream out = new ByteArrayOutputStream();
96-
StreamUtils.copyRange(new ByteArrayInputStream(bytes), out, 0, 100);
97-
byte[] range = Arrays.copyOfRange(bytes, 0, 101);
98-
assertThat(out.toByteArray()).isEqualTo(range);
108+
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
109+
StreamUtils.copyRange(in, out, 0, 8200);
110+
assertThat(in.available()).isEqualTo(1);
111+
assertThat(out.toByteArray()).isEqualTo(Arrays.copyOfRange(bytes, 0, 8201));
112+
}
113+
114+
@Test
115+
void copyRangeBeyondAvailable() throws Exception {
116+
ByteArrayOutputStream out = new ByteArrayOutputStream();
117+
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
118+
StreamUtils.copyRange(in, out, 0, 8300);
119+
assertThat(in.available()).isEqualTo(0);
120+
assertThat(out.toByteArray()).isEqualTo(Arrays.copyOfRange(bytes, 0, 8202));
99121
}
100122

101123
@Test
@@ -127,4 +149,5 @@ void nonClosingOutputStream() throws Exception {
127149
ordered.verify(source).write(bytes, 1, 2);
128150
ordered.verify(source, never()).close();
129151
}
152+
130153
}

spring-web/src/main/java/org/springframework/http/converter/ResourceHttpMessageConverter.java

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,23 @@ public String getFilename() {
108108
}
109109
}
110110

111+
@Override
112+
protected void writeInternal(Resource resource, HttpOutputMessage outputMessage)
113+
throws IOException, HttpMessageNotWritableException {
114+
115+
writeContent(resource, outputMessage);
116+
}
117+
118+
/**
119+
* Add the default headers for the given resource to the given message.
120+
* @since 6.0
121+
*/
122+
public void addDefaultHeaders(HttpOutputMessage message, Resource resource, @Nullable MediaType contentType)
123+
throws IOException {
124+
125+
addDefaultHeaders(message.getHeaders(), resource, contentType);
126+
}
127+
111128
@Override
112129
protected MediaType getDefaultContentType(Resource resource) {
113130
return MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM);
@@ -125,23 +142,15 @@ protected Long getContentLength(Resource resource, @Nullable MediaType contentTy
125142
return (contentLength < 0 ? null : contentLength);
126143
}
127144

128-
/**
129-
* Adds the default headers for the given resource to the given message.
130-
* @since 6.0
131-
*/
132-
public void addDefaultHeaders(HttpOutputMessage message, Resource resource, @Nullable MediaType contentType) throws IOException {
133-
addDefaultHeaders(message.getHeaders(), resource, contentType);
134-
}
135-
136145
@Override
137-
protected void writeInternal(Resource resource, HttpOutputMessage outputMessage)
138-
throws IOException, HttpMessageNotWritableException {
139-
140-
writeContent(resource, outputMessage);
146+
protected boolean supportsRepeatableWrites(Resource resource) {
147+
return !(resource instanceof InputStreamResource);
141148
}
142149

150+
143151
protected void writeContent(Resource resource, HttpOutputMessage outputMessage)
144152
throws IOException, HttpMessageNotWritableException {
153+
145154
// We cannot use try-with-resources here for the InputStream, since we have
146155
// custom handling of the close() method in a finally-block.
147156
try {
@@ -168,8 +177,4 @@ protected void writeContent(Resource resource, HttpOutputMessage outputMessage)
168177
}
169178
}
170179

171-
@Override
172-
protected boolean supportsRepeatableWrites(Resource resource) {
173-
return !(resource instanceof InputStreamResource);
174-
}
175180
}

spring-web/src/main/java/org/springframework/http/converter/ResourceRegionHttpMessageConverter.java

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -53,22 +53,6 @@ public ResourceRegionHttpMessageConverter() {
5353
}
5454

5555

56-
@Override
57-
@SuppressWarnings("unchecked")
58-
protected MediaType getDefaultContentType(Object object) {
59-
Resource resource = null;
60-
if (object instanceof ResourceRegion resourceRegion) {
61-
resource = resourceRegion.getResource();
62-
}
63-
else {
64-
Collection<ResourceRegion> regions = (Collection<ResourceRegion>) object;
65-
if (!regions.isEmpty()) {
66-
resource = regions.iterator().next().getResource();
67-
}
68-
}
69-
return MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM);
70-
}
71-
7256
@Override
7357
public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
7458
return false;
@@ -138,6 +122,43 @@ protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessa
138122
}
139123
}
140124

125+
@Override
126+
protected MediaType getDefaultContentType(Object object) {
127+
Resource resource = null;
128+
if (object instanceof ResourceRegion resourceRegion) {
129+
resource = resourceRegion.getResource();
130+
}
131+
else {
132+
@SuppressWarnings("unchecked")
133+
Collection<ResourceRegion> regions = (Collection<ResourceRegion>) object;
134+
if (!regions.isEmpty()) {
135+
resource = regions.iterator().next().getResource();
136+
}
137+
}
138+
return MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM);
139+
}
140+
141+
@Override
142+
protected boolean supportsRepeatableWrites(Object object) {
143+
if (object instanceof ResourceRegion resourceRegion) {
144+
return supportsRepeatableWrites(resourceRegion);
145+
}
146+
else {
147+
@SuppressWarnings("unchecked")
148+
Collection<ResourceRegion> regions = (Collection<ResourceRegion>) object;
149+
for (ResourceRegion region : regions) {
150+
if (!supportsRepeatableWrites(region)) {
151+
return false;
152+
}
153+
}
154+
return true;
155+
}
156+
}
157+
158+
private boolean supportsRepeatableWrites(ResourceRegion region) {
159+
return !(region.getResource() instanceof InputStreamResource);
160+
}
161+
141162

142163
protected void writeResourceRegion(ResourceRegion region, HttpOutputMessage outputMessage) throws IOException {
143164
Assert.notNull(region, "ResourceRegion must not be null");
@@ -240,24 +261,4 @@ private static void print(OutputStream os, String buf) throws IOException {
240261
os.write(buf.getBytes(StandardCharsets.US_ASCII));
241262
}
242263

243-
@Override
244-
@SuppressWarnings("unchecked")
245-
protected boolean supportsRepeatableWrites(Object object) {
246-
if (object instanceof ResourceRegion resourceRegion) {
247-
return supportsRepeatableWrites(resourceRegion);
248-
}
249-
else {
250-
Collection<ResourceRegion> regions = (Collection<ResourceRegion>) object;
251-
for (ResourceRegion region : regions) {
252-
if (!supportsRepeatableWrites(region)) {
253-
return false;
254-
}
255-
}
256-
return true;
257-
}
258-
}
259-
260-
private boolean supportsRepeatableWrites(ResourceRegion region) {
261-
return !(region.getResource() instanceof InputStreamResource);
262-
}
263264
}

0 commit comments

Comments
 (0)