SPR-7909 - Sending large payloads with RestTemplate results in an OutOfMemoryError
This commit is contained in:
parent
33674933ea
commit
d0d6a07870
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
||||
/**
|
||||
* Abstract base for {@link ClientHttpRequest} that buffers output in a byte array before sending it over the wire.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.0.6
|
||||
*/
|
||||
abstract class AbstractBufferingClientHttpRequest extends AbstractClientHttpRequest {
|
||||
|
||||
private ByteArrayOutputStream bufferedOutput = new ByteArrayOutputStream();
|
||||
|
||||
@Override
|
||||
protected OutputStream getBodyInternal(HttpHeaders headers) throws IOException {
|
||||
return this.bufferedOutput;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
|
||||
byte[] bytes = this.bufferedOutput.toByteArray();
|
||||
if (headers.getContentLength() == -1) {
|
||||
headers.setContentLength(bytes.length);
|
||||
}
|
||||
ClientHttpResponse result = executeInternal(headers, bytes);
|
||||
this.bufferedOutput = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract template method that writes the given headers and content to the HTTP request.
|
||||
* @param headers the HTTP headers
|
||||
* @param bufferedOutput the body content
|
||||
* @return the response object for the executed request
|
||||
*/
|
||||
protected abstract ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput)
|
||||
throws IOException;
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package org.springframework.http.client;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
|
|
@ -29,27 +28,32 @@ import org.springframework.util.Assert;
|
|||
* @author Arjen Poutsma
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class AbstractClientHttpRequest implements ClientHttpRequest {
|
||||
abstract class AbstractClientHttpRequest implements ClientHttpRequest {
|
||||
|
||||
private boolean executed = false;
|
||||
|
||||
private final HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
private final ByteArrayOutputStream bufferedOutput = new ByteArrayOutputStream();
|
||||
|
||||
|
||||
public final HttpHeaders getHeaders() {
|
||||
return executed ? HttpHeaders.readOnlyHttpHeaders(headers) : this.headers;
|
||||
}
|
||||
|
||||
public final OutputStream getBody() throws IOException {
|
||||
checkExecuted();
|
||||
return this.bufferedOutput;
|
||||
return getBodyInternal(this.headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract template method that returns the body.
|
||||
*
|
||||
* @param headers the HTTP headers
|
||||
* @return the body output stream
|
||||
*/
|
||||
protected abstract OutputStream getBodyInternal(HttpHeaders headers) throws IOException;
|
||||
|
||||
public final ClientHttpResponse execute() throws IOException {
|
||||
checkExecuted();
|
||||
ClientHttpResponse result = executeInternal(this.headers, this.bufferedOutput.toByteArray());
|
||||
ClientHttpResponse result = executeInternal(this.headers);
|
||||
this.executed = true;
|
||||
return result;
|
||||
}
|
||||
|
|
@ -58,14 +62,13 @@ public abstract class AbstractClientHttpRequest implements ClientHttpRequest {
|
|||
Assert.state(!this.executed, "ClientHttpRequest already executed");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Abstract template method that writes the given headers and content to the HTTP request.
|
||||
*
|
||||
* @param headers the HTTP headers
|
||||
* @param bufferedOutput the body content
|
||||
* @return the response object for the executed request
|
||||
*/
|
||||
protected abstract ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput)
|
||||
throws IOException;
|
||||
protected abstract ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException;
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -28,19 +28,18 @@ import org.springframework.http.HttpMethod;
|
|||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
/**
|
||||
* {@link ClientHttpRequest} implementation that uses standard J2SE facilities to execute requests.
|
||||
* {@link ClientHttpRequest} implementation that uses standard J2SE facilities to execute buffered requests.
|
||||
* Created via the {@link SimpleClientHttpRequestFactory}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.0
|
||||
* @see SimpleClientHttpRequestFactory#createRequest(java.net.URI, HttpMethod)
|
||||
*/
|
||||
final class SimpleClientHttpRequest extends AbstractClientHttpRequest {
|
||||
final class BufferingSimpleClientHttpRequest extends AbstractBufferingClientHttpRequest {
|
||||
|
||||
private final HttpURLConnection connection;
|
||||
|
||||
|
||||
SimpleClientHttpRequest(HttpURLConnection connection) {
|
||||
BufferingSimpleClientHttpRequest(HttpURLConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
@ -41,7 +41,7 @@ import org.springframework.http.HttpMethod;
|
|||
* @since 3.0
|
||||
* @see CommonsClientHttpRequestFactory#createRequest(java.net.URI, HttpMethod)
|
||||
*/
|
||||
final class CommonsClientHttpRequest extends AbstractClientHttpRequest {
|
||||
final class CommonsClientHttpRequest extends AbstractBufferingClientHttpRequest {
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -36,8 +36,14 @@ import org.springframework.util.Assert;
|
|||
*/
|
||||
public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory {
|
||||
|
||||
private static final int DEFAULT_CHUNK_SIZE = 4096;
|
||||
|
||||
private Proxy proxy;
|
||||
|
||||
private boolean bufferRequestBody = true;
|
||||
|
||||
private int chunkSize = DEFAULT_CHUNK_SIZE;
|
||||
|
||||
/**
|
||||
* Sets the {@link Proxy} to use for this request factory.
|
||||
*/
|
||||
|
|
@ -45,16 +51,48 @@ public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory
|
|||
this.proxy = proxy;
|
||||
}
|
||||
|
||||
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
|
||||
HttpURLConnection connection = openConnection(uri.toURL(), proxy);
|
||||
prepareConnection(connection, httpMethod.name());
|
||||
return new SimpleClientHttpRequest(connection);
|
||||
/**
|
||||
* Indicates whether this request factory should buffer the {@linkplain ClientHttpRequest#getBody() request body}
|
||||
* internally.
|
||||
* <p>Default is {@code true}. When sending large amounts of data via POST or PUT, it is recommended to change this
|
||||
* property to {@code false}, so as not to run out of memory. This will result in a {@link ClientHttpRequest}
|
||||
* that either streams directly to the underlying {@link HttpURLConnection} (if the
|
||||
* {@link org.springframework.http.HttpHeaders#getContentLength() Content-Length} is known in advance), or that will
|
||||
* use "Chunked transfer encoding" (if the {@code Content-Length} is not known in advance).
|
||||
*
|
||||
* @see #setChunkSize(int)
|
||||
* @see HttpURLConnection#setFixedLengthStreamingMode(int)
|
||||
*/
|
||||
public void setBufferRequestBody(boolean bufferRequestBody) {
|
||||
this.bufferRequestBody = bufferRequestBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens and returns a connection to the given URL.
|
||||
* <p>The default implementation uses the given {@linkplain #setProxy(java.net.Proxy) proxy} - if any - to open a
|
||||
* connection.
|
||||
* Sets the number of bytes to write in each chunk when not buffering request bodies locally.
|
||||
* <p>Note that this parameter is only used when {@link #setBufferRequestBody(boolean) bufferRequestBody} is set
|
||||
* to {@code false}, and the {@link org.springframework.http.HttpHeaders#getContentLength() Content-Length}
|
||||
* is not known in advance.
|
||||
*
|
||||
* @see #setBufferRequestBody(boolean)
|
||||
*/
|
||||
public void setChunkSize(int chunkSize) {
|
||||
this.chunkSize = chunkSize;
|
||||
}
|
||||
|
||||
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
|
||||
HttpURLConnection connection = openConnection(uri.toURL(), proxy);
|
||||
prepareConnection(connection, httpMethod.name());
|
||||
if (bufferRequestBody) {
|
||||
return new BufferingSimpleClientHttpRequest(connection);
|
||||
}
|
||||
else {
|
||||
return new StreamingSimpleClientHttpRequest(connection, chunkSize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens and returns a connection to the given URL. <p>The default implementation uses the given {@linkplain
|
||||
* #setProxy(java.net.Proxy) proxy} - if any - to open a connection.
|
||||
*
|
||||
* @param url the URL to open a connection to
|
||||
* @param proxy the proxy to use, may be {@code null}
|
||||
|
|
@ -68,8 +106,8 @@ public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory
|
|||
}
|
||||
|
||||
/**
|
||||
* Template method for preparing the given {@link HttpURLConnection}.
|
||||
* <p>The default implementation prepares the connection for input and output, and sets the HTTP method.
|
||||
* Template method for preparing the given {@link HttpURLConnection}. <p>The default implementation prepares the
|
||||
* connection for input and output, and sets the HTTP method.
|
||||
*
|
||||
* @param connection the connection to prepare
|
||||
* @param httpMethod the HTTP request method ({@code GET}, {@code POST}, etc.)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -26,7 +26,8 @@ import org.springframework.util.StringUtils;
|
|||
|
||||
/**
|
||||
* {@link ClientHttpResponse} implementation that uses standard J2SE facilities.
|
||||
* Obtained via the {@link SimpleClientHttpRequest#execute()}.
|
||||
* Obtained via {@link BufferingSimpleClientHttpRequest#execute()} and
|
||||
* {@link StreamingSimpleClientHttpRequest#execute()}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.0
|
||||
|
|
|
|||
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* 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.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
/**
|
||||
* {@link ClientHttpRequest} implementation that uses standard J2SE facilities to execute streaming requests.
|
||||
* Created via the {@link SimpleClientHttpRequestFactory}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 3.0
|
||||
* @see SimpleClientHttpRequestFactory#createRequest(java.net.URI, HttpMethod)
|
||||
*/
|
||||
public class StreamingSimpleClientHttpRequest extends AbstractClientHttpRequest {
|
||||
|
||||
private final HttpURLConnection connection;
|
||||
|
||||
private final int chunkSize;
|
||||
|
||||
private OutputStream body;
|
||||
|
||||
StreamingSimpleClientHttpRequest(HttpURLConnection connection, int chunkSize) {
|
||||
this.connection = connection;
|
||||
this.chunkSize = chunkSize;
|
||||
}
|
||||
|
||||
public HttpMethod getMethod() {
|
||||
return HttpMethod.valueOf(this.connection.getRequestMethod());
|
||||
}
|
||||
|
||||
public URI getURI() {
|
||||
try {
|
||||
return this.connection.getURL().toURI();
|
||||
}
|
||||
catch (URISyntaxException ex) {
|
||||
throw new IllegalStateException("Could not get HttpURLConnection URI: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OutputStream getBodyInternal(HttpHeaders headers) throws IOException {
|
||||
if (body == null) {
|
||||
int contentLength = (int) headers.getContentLength();
|
||||
if (contentLength >= 0) {
|
||||
this.connection.setFixedLengthStreamingMode(contentLength);
|
||||
}
|
||||
else {
|
||||
this.connection.setChunkedStreamingMode(chunkSize);
|
||||
}
|
||||
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
|
||||
String headerName = entry.getKey();
|
||||
for (String headerValue : entry.getValue()) {
|
||||
this.connection.addRequestProperty(headerName, headerValue);
|
||||
}
|
||||
}
|
||||
this.connection.connect();
|
||||
this.body = this.connection.getOutputStream();
|
||||
}
|
||||
return new NonClosingOutputStream(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
|
||||
try {
|
||||
if (body != null) {
|
||||
body.close();
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
return new SimpleClientHttpResponse(this.connection);
|
||||
}
|
||||
|
||||
private static class NonClosingOutputStream extends FilterOutputStream {
|
||||
|
||||
private NonClosingOutputStream(OutputStream out) {
|
||||
super(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -19,7 +19,6 @@ package org.springframework.http.converter;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
|
|
@ -94,19 +93,25 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
|||
this.partConverters.add(new ResourceHttpMessageConverter());
|
||||
}
|
||||
|
||||
/** Add a message body converter. Such a converters is used to convert objects to MIME parts. */
|
||||
/**
|
||||
* Add a message body converter. Such a converters is used to convert objects to MIME parts.
|
||||
*/
|
||||
public final void addPartConverter(HttpMessageConverter<?> partConverter) {
|
||||
Assert.notNull(partConverter, "'partConverter' must not be NULL");
|
||||
this.partConverters.add(partConverter);
|
||||
}
|
||||
|
||||
/** Set the message body converters to use. These converters are used to convert objects to MIME parts. */
|
||||
/**
|
||||
* Set the message body converters to use. These converters are used to convert objects to MIME parts.
|
||||
*/
|
||||
public final void setPartConverters(List<HttpMessageConverter<?>> partConverters) {
|
||||
Assert.notEmpty(partConverters, "'partConverters' must not be empty");
|
||||
this.partConverters = partConverters;
|
||||
}
|
||||
|
||||
/** Sets the character set used for writing form data. */
|
||||
/**
|
||||
* Sets the character set used for writing form data.
|
||||
*/
|
||||
public void setCharset(Charset charset) {
|
||||
this.charset = charset;
|
||||
}
|
||||
|
|
@ -196,7 +201,8 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
|||
if (contentType != null) {
|
||||
outputMessage.getHeaders().setContentType(contentType);
|
||||
charset = contentType.getCharSet() != null ? contentType.getCharSet() : this.charset;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
outputMessage.getHeaders().setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
charset = this.charset;
|
||||
}
|
||||
|
|
@ -218,7 +224,9 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
|||
builder.append('&');
|
||||
}
|
||||
}
|
||||
FileCopyUtils.copy(builder.toString(), new OutputStreamWriter(outputMessage.getBody(), charset));
|
||||
byte[] bytes = builder.toString().getBytes(charset.name());
|
||||
outputMessage.getHeaders().setContentLength(bytes.length);
|
||||
FileCopyUtils.copy(bytes, outputMessage.getBody());
|
||||
}
|
||||
|
||||
private void writeMultipart(MultiValueMap<String, Object> parts, HttpOutputMessage outputMessage)
|
||||
|
|
@ -328,7 +336,9 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
|||
}
|
||||
}
|
||||
|
||||
/** Implementation of {@link org.springframework.http.HttpOutputMessage} used for writing multipart data. */
|
||||
/**
|
||||
* Implementation of {@link org.springframework.http.HttpOutputMessage} used for writing multipart data.
|
||||
*/
|
||||
private class MultipartHttpOutputMessage implements HttpOutputMessage {
|
||||
|
||||
private final HttpHeaders headers = new HttpHeaders();
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue