Browse Source
This commit ensures that ClientHttpRequest implementations implement StreamingHttpOutputMessage, so that they do not expose an OutputStream, but store a handle capable of writing to a stream instead. Closes gh-30557pull/30570/head
21 changed files with 421 additions and 711 deletions
@ -0,0 +1,83 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2023 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 |
||||||
|
* |
||||||
|
* https://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.IOException; |
||||||
|
import java.io.OutputStream; |
||||||
|
|
||||||
|
import org.springframework.http.HttpHeaders; |
||||||
|
import org.springframework.http.StreamingHttpOutputMessage; |
||||||
|
import org.springframework.lang.Nullable; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
import org.springframework.util.FastByteArrayOutputStream; |
||||||
|
|
||||||
|
/** |
||||||
|
* Abstract base for {@link ClientHttpRequest} that also implement |
||||||
|
* {@link StreamingHttpOutputMessage}. Ensures that headers and |
||||||
|
* body are not written multiple times. |
||||||
|
* |
||||||
|
* @author Arjen Poutsma |
||||||
|
* @since 6.1 |
||||||
|
*/ |
||||||
|
abstract class AbstractStreamingClientHttpRequest extends AbstractClientHttpRequest |
||||||
|
implements StreamingHttpOutputMessage { |
||||||
|
|
||||||
|
@Nullable |
||||||
|
private Body body; |
||||||
|
|
||||||
|
@Nullable |
||||||
|
private FastByteArrayOutputStream bodyStream; |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
protected final OutputStream getBodyInternal(HttpHeaders headers) { |
||||||
|
Assert.state(this.body == null, "Invoke either getBody or setBody; not both"); |
||||||
|
|
||||||
|
if (this.bodyStream == null) { |
||||||
|
this.bodyStream = new FastByteArrayOutputStream(1024); |
||||||
|
} |
||||||
|
return this.bodyStream; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public final void setBody(Body body) { |
||||||
|
Assert.notNull(body, "Body must not be null"); |
||||||
|
assertNotExecuted(); |
||||||
|
Assert.state(this.bodyStream == null, "Invoke either getBody or setBody; not both"); |
||||||
|
|
||||||
|
this.body = body; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected final ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException { |
||||||
|
if (this.body == null && this.bodyStream != null) { |
||||||
|
this.body = outputStream -> this.bodyStream.writeTo(outputStream); |
||||||
|
} |
||||||
|
return executeInternal(headers, this.body); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Abstract template method that writes the given headers and content to the HTTP request. |
||||||
|
* @param headers the HTTP headers |
||||||
|
* @param body the HTTP body, may be {@code null} if no body was {@linkplain #setBody(Body) set} |
||||||
|
* @return the response object for the executed request |
||||||
|
* @since 6.1 |
||||||
|
*/ |
||||||
|
protected abstract ClientHttpResponse executeInternal(HttpHeaders headers, @Nullable Body body) throws IOException; |
||||||
|
|
||||||
|
} |
||||||
@ -1,183 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2002-2022 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 |
|
||||||
* |
|
||||||
* https://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.IOException; |
|
||||||
import java.io.InputStream; |
|
||||||
import java.io.OutputStream; |
|
||||||
import java.net.URI; |
|
||||||
import java.net.URISyntaxException; |
|
||||||
import java.util.List; |
|
||||||
import java.util.Set; |
|
||||||
|
|
||||||
import org.apache.hc.client5.http.classic.HttpClient; |
|
||||||
import org.apache.hc.core5.function.Supplier; |
|
||||||
import org.apache.hc.core5.http.ClassicHttpRequest; |
|
||||||
import org.apache.hc.core5.http.ClassicHttpResponse; |
|
||||||
import org.apache.hc.core5.http.Header; |
|
||||||
import org.apache.hc.core5.http.HttpEntity; |
|
||||||
import org.apache.hc.core5.http.HttpResponse; |
|
||||||
import org.apache.hc.core5.http.protocol.HttpContext; |
|
||||||
|
|
||||||
import org.springframework.http.HttpHeaders; |
|
||||||
import org.springframework.http.HttpMethod; |
|
||||||
import org.springframework.http.StreamingHttpOutputMessage; |
|
||||||
import org.springframework.lang.Nullable; |
|
||||||
import org.springframework.util.Assert; |
|
||||||
|
|
||||||
/** |
|
||||||
* {@link ClientHttpRequest} implementation based on |
|
||||||
* Apache HttpComponents HttpClient in streaming mode. |
|
||||||
* |
|
||||||
* <p>Created via the {@link HttpComponentsClientHttpRequestFactory}. |
|
||||||
* |
|
||||||
* @author Arjen Poutsma |
|
||||||
* @since 4.0 |
|
||||||
* @see HttpComponentsClientHttpRequestFactory#createRequest(java.net.URI, org.springframework.http.HttpMethod) |
|
||||||
*/ |
|
||||||
final class HttpComponentsStreamingClientHttpRequest extends AbstractClientHttpRequest |
|
||||||
implements StreamingHttpOutputMessage { |
|
||||||
|
|
||||||
private final HttpClient httpClient; |
|
||||||
|
|
||||||
private final ClassicHttpRequest httpRequest; |
|
||||||
|
|
||||||
private final HttpContext httpContext; |
|
||||||
|
|
||||||
@Nullable |
|
||||||
private Body body; |
|
||||||
|
|
||||||
|
|
||||||
HttpComponentsStreamingClientHttpRequest(HttpClient client, ClassicHttpRequest request, HttpContext context) { |
|
||||||
this.httpClient = client; |
|
||||||
this.httpRequest = request; |
|
||||||
this.httpContext = context; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public HttpMethod getMethod() { |
|
||||||
return HttpMethod.valueOf(this.httpRequest.getMethod()); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public URI getURI() { |
|
||||||
try { |
|
||||||
return this.httpRequest.getUri(); |
|
||||||
} |
|
||||||
catch (URISyntaxException ex) { |
|
||||||
throw new IllegalStateException(ex.getMessage(), ex); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setBody(Body body) { |
|
||||||
assertNotExecuted(); |
|
||||||
this.body = body; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected OutputStream getBodyInternal(HttpHeaders headers) { |
|
||||||
throw new UnsupportedOperationException("getBody not supported"); |
|
||||||
} |
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // execute(ClassicHttpRequest, HttpContext)
|
|
||||||
@Override |
|
||||||
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException { |
|
||||||
HttpComponentsClientHttpRequest.addHeaders(this.httpRequest, headers); |
|
||||||
|
|
||||||
if (this.body != null) { |
|
||||||
HttpEntity requestEntity = new StreamingHttpEntity(getHeaders(), this.body); |
|
||||||
this.httpRequest.setEntity(requestEntity); |
|
||||||
} |
|
||||||
HttpResponse httpResponse = this.httpClient.execute(this.httpRequest, this.httpContext); |
|
||||||
Assert.isInstanceOf(ClassicHttpResponse.class, httpResponse, |
|
||||||
"HttpResponse not an instance of ClassicHttpResponse"); |
|
||||||
return new HttpComponentsClientHttpResponse((ClassicHttpResponse) httpResponse); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
private static class StreamingHttpEntity implements HttpEntity { |
|
||||||
|
|
||||||
private final HttpHeaders headers; |
|
||||||
|
|
||||||
private final StreamingHttpOutputMessage.Body body; |
|
||||||
|
|
||||||
public StreamingHttpEntity(HttpHeaders headers, StreamingHttpOutputMessage.Body body) { |
|
||||||
this.headers = headers; |
|
||||||
this.body = body; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean isRepeatable() { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean isChunked() { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public long getContentLength() { |
|
||||||
return this.headers.getContentLength(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
@Nullable |
|
||||||
public String getContentType() { |
|
||||||
return this.headers.getFirst(HttpHeaders.CONTENT_TYPE); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
@Nullable |
|
||||||
public String getContentEncoding() { |
|
||||||
return this.headers.getFirst(HttpHeaders.CONTENT_ENCODING); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public InputStream getContent() throws IOException, IllegalStateException { |
|
||||||
throw new IllegalStateException("No content available"); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void writeTo(OutputStream outputStream) throws IOException { |
|
||||||
this.body.writeTo(outputStream); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean isStreaming() { |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
@Nullable |
|
||||||
public Supplier<List<? extends Header>> getTrailers() { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
@Nullable |
|
||||||
public Set<String> getTrailerNames() { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void close() throws IOException { |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,111 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2002-2022 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 |
|
||||||
* |
|
||||||
* https://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.IOException; |
|
||||||
import java.io.OutputStream; |
|
||||||
import java.net.HttpURLConnection; |
|
||||||
import java.net.URI; |
|
||||||
import java.net.URISyntaxException; |
|
||||||
|
|
||||||
import org.springframework.http.HttpHeaders; |
|
||||||
import org.springframework.http.HttpMethod; |
|
||||||
import org.springframework.lang.Nullable; |
|
||||||
import org.springframework.util.StreamUtils; |
|
||||||
|
|
||||||
/** |
|
||||||
* {@link ClientHttpRequest} implementation that uses standard JDK facilities to |
|
||||||
* execute streaming requests. Created via the {@link SimpleClientHttpRequestFactory}. |
|
||||||
* |
|
||||||
* @author Arjen Poutsma |
|
||||||
* @since 3.0 |
|
||||||
* @see SimpleClientHttpRequestFactory#createRequest(java.net.URI, HttpMethod) |
|
||||||
* @see org.springframework.http.client.support.HttpAccessor |
|
||||||
* @see org.springframework.web.client.RestTemplate |
|
||||||
*/ |
|
||||||
final class SimpleStreamingClientHttpRequest extends AbstractClientHttpRequest { |
|
||||||
|
|
||||||
private final HttpURLConnection connection; |
|
||||||
|
|
||||||
private final int chunkSize; |
|
||||||
|
|
||||||
@Nullable |
|
||||||
private OutputStream body; |
|
||||||
|
|
||||||
private final boolean outputStreaming; |
|
||||||
|
|
||||||
|
|
||||||
SimpleStreamingClientHttpRequest(HttpURLConnection connection, int chunkSize, boolean outputStreaming) { |
|
||||||
this.connection = connection; |
|
||||||
this.chunkSize = chunkSize; |
|
||||||
this.outputStreaming = outputStreaming; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public HttpMethod getMethod() { |
|
||||||
return HttpMethod.valueOf(this.connection.getRequestMethod()); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
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 (this.body == null) { |
|
||||||
if (this.outputStreaming) { |
|
||||||
long contentLength = headers.getContentLength(); |
|
||||||
if (contentLength >= 0) { |
|
||||||
this.connection.setFixedLengthStreamingMode(contentLength); |
|
||||||
} |
|
||||||
else { |
|
||||||
this.connection.setChunkedStreamingMode(this.chunkSize); |
|
||||||
} |
|
||||||
} |
|
||||||
SimpleBufferingClientHttpRequest.addHeaders(this.connection, headers); |
|
||||||
this.connection.connect(); |
|
||||||
this.body = this.connection.getOutputStream(); |
|
||||||
} |
|
||||||
return StreamUtils.nonClosing(this.body); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException { |
|
||||||
try { |
|
||||||
if (this.body != null) { |
|
||||||
this.body.close(); |
|
||||||
} |
|
||||||
else { |
|
||||||
SimpleBufferingClientHttpRequest.addHeaders(this.connection, headers); |
|
||||||
this.connection.connect(); |
|
||||||
// Immediately trigger the request in a no-output scenario as well
|
|
||||||
this.connection.getResponseCode(); |
|
||||||
} |
|
||||||
} |
|
||||||
catch (IOException ex) { |
|
||||||
// ignore
|
|
||||||
} |
|
||||||
return new SimpleClientHttpResponse(this.connection); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,102 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2002-2019 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 |
|
||||||
* |
|
||||||
* https://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.ByteArrayInputStream; |
|
||||||
import java.io.IOException; |
|
||||||
import java.io.InputStream; |
|
||||||
import java.net.HttpURLConnection; |
|
||||||
import java.net.ProtocolException; |
|
||||||
import java.net.URL; |
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test; |
|
||||||
|
|
||||||
import org.springframework.http.HttpMethod; |
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat; |
|
||||||
|
|
||||||
public class BufferedSimpleHttpRequestFactoryTests extends AbstractHttpRequestFactoryTests { |
|
||||||
|
|
||||||
@Override |
|
||||||
protected ClientHttpRequestFactory createRequestFactory() { |
|
||||||
return new SimpleClientHttpRequestFactory(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
@Test |
|
||||||
public void httpMethods() throws Exception { |
|
||||||
try { |
|
||||||
assertHttpMethod("patch", HttpMethod.PATCH); |
|
||||||
} |
|
||||||
catch (ProtocolException ex) { |
|
||||||
// Currently HttpURLConnection does not support HTTP PATCH
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void prepareConnectionWithRequestBody() throws Exception { |
|
||||||
URL uri = new URL("https://example.com"); |
|
||||||
testRequestBodyAllowed(uri, "GET", false); |
|
||||||
testRequestBodyAllowed(uri, "HEAD", false); |
|
||||||
testRequestBodyAllowed(uri, "OPTIONS", false); |
|
||||||
testRequestBodyAllowed(uri, "TRACE", false); |
|
||||||
testRequestBodyAllowed(uri, "PUT", true); |
|
||||||
testRequestBodyAllowed(uri, "POST", true); |
|
||||||
testRequestBodyAllowed(uri, "DELETE", true); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void deleteWithoutBodyDoesNotRaiseException() throws Exception { |
|
||||||
HttpURLConnection connection = new TestHttpURLConnection(new URL("https://example.com")); |
|
||||||
((SimpleClientHttpRequestFactory) this.factory).prepareConnection(connection, "DELETE"); |
|
||||||
SimpleBufferingClientHttpRequest request = new SimpleBufferingClientHttpRequest(connection, false); |
|
||||||
request.execute(); |
|
||||||
} |
|
||||||
|
|
||||||
private void testRequestBodyAllowed(URL uri, String httpMethod, boolean allowed) throws IOException { |
|
||||||
HttpURLConnection connection = new TestHttpURLConnection(uri); |
|
||||||
((SimpleClientHttpRequestFactory) this.factory).prepareConnection(connection, httpMethod); |
|
||||||
assertThat(connection.getDoOutput()).isEqualTo(allowed); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
private static class TestHttpURLConnection extends HttpURLConnection { |
|
||||||
|
|
||||||
public TestHttpURLConnection(URL uri) { |
|
||||||
super(uri); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void connect() throws IOException { |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void disconnect() { |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean usingProxy() { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public InputStream getInputStream() throws IOException { |
|
||||||
return new ByteArrayInputStream(new byte[0]); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,29 +0,0 @@ |
|||||||
/* |
|
||||||
* 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. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* https://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; |
|
||||||
|
|
||||||
|
|
||||||
public class NoOutputStreamingBufferedSimpleHttpRequestFactoryTests extends AbstractHttpRequestFactoryTests { |
|
||||||
|
|
||||||
@Override |
|
||||||
protected ClientHttpRequestFactory createRequestFactory() { |
|
||||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); |
|
||||||
factory.setOutputStreaming(false); |
|
||||||
return factory; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,29 +0,0 @@ |
|||||||
/* |
|
||||||
* 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. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* https://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; |
|
||||||
|
|
||||||
|
|
||||||
public class NoOutputStreamingStreamingSimpleHttpRequestFactoryTests extends AbstractHttpRequestFactoryTests { |
|
||||||
|
|
||||||
@Override |
|
||||||
protected ClientHttpRequestFactory createRequestFactory() { |
|
||||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); |
|
||||||
factory.setBufferRequestBody(false); |
|
||||||
factory.setOutputStreaming(false); |
|
||||||
return factory; |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,41 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2002-2018 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 |
|
||||||
* |
|
||||||
* https://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 org.junit.jupiter.api.Test; |
|
||||||
|
|
||||||
import org.springframework.http.HttpMethod; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author Arjen Poutsma |
|
||||||
*/ |
|
||||||
public class StreamingHttpComponentsClientHttpRequestFactoryTests extends AbstractHttpRequestFactoryTests { |
|
||||||
|
|
||||||
@Override |
|
||||||
protected ClientHttpRequestFactory createRequestFactory() { |
|
||||||
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); |
|
||||||
requestFactory.setBufferRequestBody(false); |
|
||||||
return requestFactory; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
@Test |
|
||||||
public void httpMethods() throws Exception { |
|
||||||
assertHttpMethod("patch", HttpMethod.PATCH); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,98 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2002-2022 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 |
|
||||||
* |
|
||||||
* https://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.Collections; |
|
||||||
import java.util.Random; |
|
||||||
|
|
||||||
import org.junit.jupiter.api.Disabled; |
|
||||||
import org.junit.jupiter.api.Test; |
|
||||||
|
|
||||||
import org.springframework.http.HttpHeaders; |
|
||||||
import org.springframework.http.HttpMethod; |
|
||||||
import org.springframework.http.HttpStatus; |
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author Arjen Poutsma |
|
||||||
*/ |
|
||||||
public class StreamingSimpleClientHttpRequestFactoryTests extends AbstractHttpRequestFactoryTests { |
|
||||||
|
|
||||||
@Override |
|
||||||
protected ClientHttpRequestFactory createRequestFactory() { |
|
||||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); |
|
||||||
factory.setBufferRequestBody(false); |
|
||||||
return factory; |
|
||||||
} |
|
||||||
|
|
||||||
@Test // SPR-8809
|
|
||||||
public void interceptor() throws Exception { |
|
||||||
final String headerName = "MyHeader"; |
|
||||||
final String headerValue = "MyValue"; |
|
||||||
ClientHttpRequestInterceptor interceptor = (request, body, execution) -> { |
|
||||||
request.getHeaders().add(headerName, headerValue); |
|
||||||
return execution.execute(request, body); |
|
||||||
}; |
|
||||||
InterceptingClientHttpRequestFactory factory = new InterceptingClientHttpRequestFactory( |
|
||||||
createRequestFactory(), Collections.singletonList(interceptor)); |
|
||||||
|
|
||||||
ClientHttpResponse response = null; |
|
||||||
try { |
|
||||||
ClientHttpRequest request = factory.createRequest(URI.create(baseUrl + "/echo"), HttpMethod.GET); |
|
||||||
response = request.execute(); |
|
||||||
assertThat(response.getStatusCode()).as("Invalid response status").isEqualTo(HttpStatus.OK); |
|
||||||
HttpHeaders responseHeaders = response.getHeaders(); |
|
||||||
assertThat(responseHeaders.getFirst(headerName)).as("Custom header invalid").isEqualTo(headerValue); |
|
||||||
} |
|
||||||
finally { |
|
||||||
if (response != null) { |
|
||||||
response.close(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
@Disabled |
|
||||||
public void largeFileUpload() throws Exception { |
|
||||||
Random rnd = new Random(); |
|
||||||
ClientHttpResponse response = null; |
|
||||||
try { |
|
||||||
ClientHttpRequest request = factory.createRequest(URI.create(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(); |
|
||||||
assertThat(response.getStatusCode()).as("Invalid response status").isEqualTo(HttpStatus.OK); |
|
||||||
} |
|
||||||
finally { |
|
||||||
if (response != null) { |
|
||||||
response.close(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
Loading…
Reference in new issue