Ensure HTTP classes don't close streams
Prior to this commit several HTTP classes made use of FileCopyUtils when reading from or writing to streams. This has the unfortunate side effect of closing streams that should really be left open. The problem is particularly noticeable when dealing with a FormHttpMessageConverter that is writing a multi-part response. Relevant HTTP classes have now been refactored to make use of a new StreamUtils class that works in a similar way FileCopyUtils but does not close streams. The NonClosingOutputStream class from SimpleStreamingClientHttpRequest has also been refactored to a StreamUtils method. Issue: SPR-10095
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -23,7 +23,7 @@ import java.net.URI;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link ClientHttpRequest} that wraps another request.
|
||||
@@ -53,8 +53,7 @@ final class BufferingClientHttpRequestWrapper extends AbstractBufferingClientHtt
|
||||
@Override
|
||||
protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
|
||||
this.request.getHeaders().putAll(headers);
|
||||
OutputStream body = this.request.getBody();
|
||||
FileCopyUtils.copy(bufferedOutput, body);
|
||||
StreamUtils.copy(bufferedOutput, this.request.getBody());
|
||||
ClientHttpResponse response = this.request.execute();
|
||||
return new BufferingClientHttpResponseWrapper(response);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -23,6 +23,7 @@ import java.io.InputStream;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link ClientHttpResponse} that reads the request's body into memory,
|
||||
@@ -61,7 +62,7 @@ final class BufferingClientHttpResponseWrapper implements ClientHttpResponse {
|
||||
|
||||
public InputStream getBody() throws IOException {
|
||||
if (this.body == null) {
|
||||
this.body = FileCopyUtils.copyToByteArray(this.response.getBody());
|
||||
this.body = StreamUtils.copyToByteArray(this.response.getBody());
|
||||
}
|
||||
return new ByteArrayInputStream(this.body);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -24,7 +24,7 @@ import java.util.List;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* Wrapper for a {@link ClientHttpRequest} that has support for {@link ClientHttpRequestInterceptor}s.
|
||||
@@ -86,7 +86,7 @@ class InterceptingClientHttpRequest extends AbstractBufferingClientHttpRequest {
|
||||
delegate.getHeaders().putAll(request.getHeaders());
|
||||
|
||||
if (body.length > 0) {
|
||||
FileCopyUtils.copy(body, delegate.getBody());
|
||||
StreamUtils.copy(body, delegate.getBody());
|
||||
}
|
||||
return delegate.execute();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.http.client;
|
||||
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
@@ -27,6 +26,7 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* {@link ClientHttpRequest} implementation that uses standard J2SE facilities to execute streaming requests.
|
||||
@@ -77,7 +77,7 @@ final class SimpleStreamingClientHttpRequest extends AbstractClientHttpRequest {
|
||||
this.connection.connect();
|
||||
this.body = this.connection.getOutputStream();
|
||||
}
|
||||
return new NonClosingOutputStream(this.body);
|
||||
return StreamUtils.nonClosing(this.body);
|
||||
}
|
||||
|
||||
private void writeHeaders(HttpHeaders headers) {
|
||||
@@ -106,26 +106,4 @@ final class SimpleStreamingClientHttpRequest extends AbstractClientHttpRequest {
|
||||
return new SimpleClientHttpResponse(this.connection);
|
||||
}
|
||||
|
||||
|
||||
private static class NonClosingOutputStream extends FilterOutputStream {
|
||||
|
||||
private NonClosingOutputStream(OutputStream out) {
|
||||
super(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException {
|
||||
super.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int let) throws IOException {
|
||||
out.write(b, off, let);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -22,7 +22,7 @@ import java.io.IOException;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HttpMessageConverter} that can read and write byte arrays.
|
||||
@@ -49,14 +49,9 @@ public class ByteArrayHttpMessageConverter extends AbstractHttpMessageConverter<
|
||||
@Override
|
||||
public byte[] readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException {
|
||||
long contentLength = inputMessage.getHeaders().getContentLength();
|
||||
if (contentLength >= 0) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream((int) contentLength);
|
||||
FileCopyUtils.copy(inputMessage.getBody(), bos);
|
||||
return bos.toByteArray();
|
||||
}
|
||||
else {
|
||||
return FileCopyUtils.copyToByteArray(inputMessage.getBody());
|
||||
}
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(contentLength >= 0 ? (int) contentLength : StreamUtils.BUFFER_SIZE);
|
||||
StreamUtils.copy(inputMessage.getBody(), bos);
|
||||
return bos.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,7 +61,7 @@ public class ByteArrayHttpMessageConverter extends AbstractHttpMessageConverter<
|
||||
|
||||
@Override
|
||||
protected void writeInternal(byte[] bytes, HttpOutputMessage outputMessage) throws IOException {
|
||||
FileCopyUtils.copy(bytes, outputMessage.getBody());
|
||||
StreamUtils.copy(bytes, outputMessage.getBody());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.http.converter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
@@ -37,9 +36,9 @@ import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -170,7 +169,7 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
||||
|
||||
MediaType contentType = inputMessage.getHeaders().getContentType();
|
||||
Charset charset = contentType.getCharSet() != null ? contentType.getCharSet() : this.charset;
|
||||
String body = FileCopyUtils.copyToString(new InputStreamReader(inputMessage.getBody(), charset));
|
||||
String body = StreamUtils.copyToString(inputMessage.getBody(), charset);
|
||||
|
||||
String[] pairs = StringUtils.tokenizeToStringArray(body, "&");
|
||||
|
||||
@@ -246,7 +245,7 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
||||
}
|
||||
byte[] bytes = builder.toString().getBytes(charset.name());
|
||||
outputMessage.getHeaders().setContentLength(bytes.length);
|
||||
FileCopyUtils.copy(bytes, outputMessage.getBody());
|
||||
StreamUtils.copy(bytes, outputMessage.getBody());
|
||||
}
|
||||
|
||||
private void writeMultipart(MultiValueMap<String, Object> parts, HttpOutputMessage outputMessage)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.http.converter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.activation.MimetypesFileTypeMap;
|
||||
|
||||
@@ -28,7 +29,7 @@ import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -61,7 +62,7 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
|
||||
protected Resource readInternal(Class<? extends Resource> clazz, HttpInputMessage inputMessage)
|
||||
throws IOException, HttpMessageNotReadableException {
|
||||
|
||||
byte[] body = FileCopyUtils.copyToByteArray(inputMessage.getBody());
|
||||
byte[] body = StreamUtils.copyToByteArray(inputMessage.getBody());
|
||||
return new ByteArrayResource(body);
|
||||
}
|
||||
|
||||
@@ -84,7 +85,7 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
|
||||
protected void writeInternal(Resource resource, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException {
|
||||
|
||||
FileCopyUtils.copy(resource.getInputStream(), outputMessage.getBody());
|
||||
StreamUtils.copy(resource.getInputStream(), outputMessage.getBody());
|
||||
outputMessage.getBody().flush();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -17,8 +17,6 @@
|
||||
package org.springframework.http.converter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
@@ -27,7 +25,7 @@ import java.util.List;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HttpMessageConverter} that can read and write strings.
|
||||
@@ -84,7 +82,7 @@ public class StringHttpMessageConverter extends AbstractHttpMessageConverter<Str
|
||||
@Override
|
||||
protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException {
|
||||
Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType());
|
||||
return FileCopyUtils.copyToString(new InputStreamReader(inputMessage.getBody(), charset));
|
||||
return StreamUtils.copyToString(inputMessage.getBody(), charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,7 +103,7 @@ public class StringHttpMessageConverter extends AbstractHttpMessageConverter<Str
|
||||
outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets());
|
||||
}
|
||||
Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType());
|
||||
FileCopyUtils.copy(s, new OutputStreamWriter(outputMessage.getBody(), charset));
|
||||
StreamUtils.copy(s, charset, outputMessage.getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user