SPR-7909 - Sending large payloads with RestTemplate results in an OutOfMemoryError
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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,6 +17,7 @@
|
||||
package org.springframework.http.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
@@ -45,12 +46,12 @@ import static org.junit.Assert.*;
|
||||
|
||||
public abstract class AbstractHttpRequestFactoryTestCase {
|
||||
|
||||
private ClientHttpRequestFactory factory;
|
||||
protected ClientHttpRequestFactory factory;
|
||||
|
||||
protected static String baseUrl;
|
||||
|
||||
private static Server jettyServer;
|
||||
|
||||
private static String baseUrl;
|
||||
|
||||
@BeforeClass
|
||||
public static void startJettyServer() throws Exception {
|
||||
int port = FreePortScanner.getFreePort();
|
||||
@@ -64,7 +65,7 @@ public abstract class AbstractHttpRequestFactoryTestCase {
|
||||
jettyContext.addServlet(new ServletHolder(new MethodServlet("GET")), "/methods/get");
|
||||
jettyContext.addServlet(new ServletHolder(new MethodServlet("HEAD")), "/methods/head");
|
||||
jettyContext.addServlet(new ServletHolder(new MethodServlet("OPTIONS")), "/methods/options");
|
||||
jettyContext.addServlet(new ServletHolder(new MethodServlet("POST")), "/methods/post");
|
||||
jettyContext.addServlet(new ServletHolder(new PostServlet()), "/methods/post");
|
||||
jettyContext.addServlet(new ServletHolder(new MethodServlet("PUT")), "/methods/put");
|
||||
jettyContext.addServlet(new ServletHolder(new RedirectServlet("/status/ok")), "/redirect");
|
||||
jettyServer.start();
|
||||
@@ -87,8 +88,7 @@ public abstract class AbstractHttpRequestFactoryTestCase {
|
||||
@Test
|
||||
public void status() throws Exception {
|
||||
URI uri = new URI(baseUrl + "/status/notfound");
|
||||
ClientHttpRequest request =
|
||||
factory.createRequest(uri, HttpMethod.GET);
|
||||
ClientHttpRequest request = factory.createRequest(uri, HttpMethod.GET);
|
||||
assertEquals("Invalid HTTP method", HttpMethod.GET, request.getMethod());
|
||||
assertEquals("Invalid HTTP URI", uri, request.getURI());
|
||||
ClientHttpResponse response = request.execute();
|
||||
@@ -105,6 +105,7 @@ public abstract class AbstractHttpRequestFactoryTestCase {
|
||||
String headerValue2 = "value2";
|
||||
request.getHeaders().add(headerName, headerValue2);
|
||||
byte[] body = "Hello World".getBytes("UTF-8");
|
||||
request.getHeaders().setContentLength(body.length);
|
||||
FileCopyUtils.copy(body, request.getBody());
|
||||
ClientHttpResponse response = request.execute();
|
||||
assertEquals("Invalid status code", HttpStatus.OK, response.getStatusCode());
|
||||
@@ -159,6 +160,7 @@ public abstract class AbstractHttpRequestFactoryTestCase {
|
||||
try {
|
||||
ClientHttpRequest request = factory.createRequest(new URI(baseUrl + "/methods/" + path), method);
|
||||
response = request.execute();
|
||||
assertEquals("Invalid response status", HttpStatus.OK, response.getStatusCode());
|
||||
assertEquals("Invalid method", path.toUpperCase(Locale.ENGLISH), request.getMethod().name());
|
||||
}
|
||||
finally {
|
||||
@@ -167,16 +169,18 @@ public abstract class AbstractHttpRequestFactoryTestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void redirect() throws Exception {
|
||||
ClientHttpResponse response = null;
|
||||
try {
|
||||
ClientHttpRequest request = factory.createRequest(new URI(baseUrl + "/redirect"), HttpMethod.PUT);
|
||||
response = request.execute();
|
||||
assertEquals("Invalid Location value", new URI(baseUrl + "/status/ok"), response.getHeaders().getLocation());
|
||||
assertEquals("Invalid Location value", new URI(baseUrl + "/status/ok"),
|
||||
response.getHeaders().getLocation());
|
||||
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
if (response != null) {
|
||||
response.close();
|
||||
response = null;
|
||||
@@ -187,13 +191,14 @@ public abstract class AbstractHttpRequestFactoryTestCase {
|
||||
response = request.execute();
|
||||
assertNull("Invalid Location value", response.getHeaders().getLocation());
|
||||
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
if (response != null) {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Servlet that sets a given status code. */
|
||||
private static class StatusServlet extends GenericServlet {
|
||||
|
||||
@@ -221,6 +226,31 @@ public abstract class AbstractHttpRequestFactoryTestCase {
|
||||
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
|
||||
HttpServletRequest httpReq = (HttpServletRequest) req;
|
||||
assertEquals("Invalid HTTP method", method, httpReq.getMethod());
|
||||
res.setContentLength(0);
|
||||
((HttpServletResponse) res).setStatus(200);
|
||||
}
|
||||
}
|
||||
|
||||
private static class PostServlet extends MethodServlet {
|
||||
|
||||
private PostServlet() {
|
||||
super("POST");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
|
||||
super.service(req, res);
|
||||
long contentLength = req.getContentLength();
|
||||
if (contentLength != -1) {
|
||||
InputStream in = req.getInputStream();
|
||||
long byteCount = 0;
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = in.read(buffer)) != -1) {
|
||||
byteCount += bytesRead;
|
||||
}
|
||||
assertEquals("Invalid content-length", contentLength, byteCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2011 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,7 @@
|
||||
|
||||
package org.springframework.http.client;
|
||||
|
||||
public class SimpleHttpRequestFactoryTests extends AbstractHttpRequestFactoryTestCase {
|
||||
public class BufferedSimpleHttpRequestFactoryTests extends AbstractHttpRequestFactoryTestCase {
|
||||
|
||||
@Override
|
||||
protected ClientHttpRequestFactory createRequestFactory() {
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.http.client;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class StreamingSimpleHttpRequestFactoryTests extends AbstractHttpRequestFactoryTestCase {
|
||||
|
||||
@Override
|
||||
protected ClientHttpRequestFactory createRequestFactory() {
|
||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
||||
factory.setStreaming(true);
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void largeFileUpload() throws Exception {
|
||||
Random rnd = new Random();
|
||||
ClientHttpResponse response = null;
|
||||
try {
|
||||
ClientHttpRequest request = factory.createRequest(new URI(baseUrl + "/methods/post"), HttpMethod.POST);
|
||||
final int BUF_SIZE = 4096;
|
||||
final int ITERATIONS = Integer.MAX_VALUE / BUF_SIZE;
|
||||
final int contentLength = ITERATIONS * BUF_SIZE;
|
||||
// request.getHeaders().setContentLength(contentLength);
|
||||
OutputStream body = request.getBody();
|
||||
for (int i=0; i < ITERATIONS; i++) {
|
||||
byte[] buffer = new byte[BUF_SIZE];
|
||||
rnd.nextBytes(buffer);
|
||||
body.write(buffer);
|
||||
}
|
||||
response = request.execute();
|
||||
assertEquals("Invalid response status", HttpStatus.OK, response.getStatusCode());
|
||||
}
|
||||
finally {
|
||||
if (response != null) {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -30,7 +30,6 @@ import org.apache.commons.fileupload.FileItemFactory;
|
||||
import org.apache.commons.fileupload.FileUpload;
|
||||
import org.apache.commons.fileupload.RequestContext;
|
||||
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -45,9 +44,9 @@ import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/** @author Arjen Poutsma */
|
||||
public class FormHttpMessageConverterTests {
|
||||
|
||||
private FormHttpMessageConverter converter;
|
||||
@@ -60,13 +59,13 @@ public class FormHttpMessageConverterTests {
|
||||
@Test
|
||||
public void canRead() {
|
||||
assertTrue(converter.canRead(MultiValueMap.class, new MediaType("application", "x-www-form-urlencoded")));
|
||||
assertFalse(converter.canRead(MultiValueMap.class, new MediaType("multipart","form-data")));
|
||||
assertFalse(converter.canRead(MultiValueMap.class, new MediaType("multipart", "form-data")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canWrite() {
|
||||
assertTrue(converter.canWrite(MultiValueMap.class, new MediaType("application", "x-www-form-urlencoded")));
|
||||
assertTrue(converter.canWrite(MultiValueMap.class, new MediaType("multipart","form-data")));
|
||||
assertTrue(converter.canWrite(MultiValueMap.class, new MediaType("multipart", "form-data")));
|
||||
assertTrue(converter.canWrite(MultiValueMap.class, MediaType.ALL));
|
||||
}
|
||||
|
||||
@@ -77,7 +76,7 @@ public class FormHttpMessageConverterTests {
|
||||
Charset iso88591 = Charset.forName("ISO-8859-1");
|
||||
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(iso88591));
|
||||
inputMessage.getHeaders().setContentType(new MediaType("application", "x-www-form-urlencoded", iso88591));
|
||||
MultiValueMap<String, String> result = (MultiValueMap<String, String>) converter.read(null, inputMessage);
|
||||
MultiValueMap<String, String> result = converter.read(null, inputMessage);
|
||||
|
||||
assertEquals("Invalid result", 3, result.size());
|
||||
assertEquals("Invalid result", "value 1", result.getFirst("name 1"));
|
||||
@@ -97,19 +96,21 @@ public class FormHttpMessageConverterTests {
|
||||
body.add("name 3", null);
|
||||
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
|
||||
converter.write(body, MediaType.APPLICATION_FORM_URLENCODED, outputMessage);
|
||||
Charset iso88591 = Charset.forName("ISO-8859-1");
|
||||
assertEquals("Invalid result", "name+1=value+1&name+2=value+2%2B1&name+2=value+2%2B2&name+3",
|
||||
outputMessage.getBodyAsString(iso88591));
|
||||
outputMessage.getBodyAsString(Charset.forName("UTF-8")));
|
||||
assertEquals("Invalid content-type", new MediaType("application", "x-www-form-urlencoded"),
|
||||
outputMessage.getHeaders().getContentType());
|
||||
assertEquals("Invalid content-length", outputMessage.getBodyAsBytes().length,
|
||||
outputMessage.getHeaders().getContentLength());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void writeMultipart() throws Exception {
|
||||
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
|
||||
parts.add("name 1", "value 1");
|
||||
parts.add("name 2", "value 2+1");
|
||||
parts.add("name 2", "value 2+2");
|
||||
|
||||
Resource logo = new ClassPathResource("/org/springframework/http/converter/logo.jpg");
|
||||
parts.add("logo", logo);
|
||||
Source xml = new StreamSource(new StringReader("<root><child/></root>"));
|
||||
@@ -122,7 +123,7 @@ public class FormHttpMessageConverterTests {
|
||||
converter.write(parts, MediaType.MULTIPART_FORM_DATA, outputMessage);
|
||||
|
||||
final MediaType contentType = outputMessage.getHeaders().getContentType();
|
||||
assertNotNull(contentType.getParameter("boundary"));
|
||||
assertNotNull("No boundary found", contentType.getParameter("boundary"));
|
||||
|
||||
// see if Commons FileUpload can read what we wrote
|
||||
FileItemFactory fileItemFactory = new DiskFileItemFactory();
|
||||
@@ -157,6 +158,7 @@ public class FormHttpMessageConverterTests {
|
||||
}
|
||||
|
||||
private static class MockHttpOutputMessageRequestContext implements RequestContext {
|
||||
|
||||
private final MockHttpOutputMessage outputMessage;
|
||||
|
||||
private MockHttpOutputMessageRequestContext(MockHttpOutputMessage outputMessage) {
|
||||
|
||||
Reference in New Issue
Block a user