Add ClientHttpResponseDecorator

See gh-28190
This commit is contained in:
rstoyanchev 2022-05-10 11:13:54 +01:00
parent 6b1a8452fa
commit 64795664b2
4 changed files with 99 additions and 43 deletions

View File

@ -0,0 +1,84 @@
/*
* 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.web.client;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.Assert;
/**
* Wrap and delegate to an existing {@link ClientHttpResponse}.
*
* @author Rossen Stoyanchev
* @since 6.0
*/
class ClientHttpResponseDecorator implements ClientHttpResponse {
private final ClientHttpResponse delegate;
public ClientHttpResponseDecorator(ClientHttpResponse delegate) {
Assert.notNull(delegate, "ClientHttpResponse delegate is required");
this.delegate = delegate;
}
/**
* Return the wrapped response.
*/
public ClientHttpResponse getDelegate() {
return this.delegate;
}
@Override
public HttpStatusCode getStatusCode() throws IOException {
return this.delegate.getStatusCode();
}
@SuppressWarnings("deprecation")
@Override
public int getRawStatusCode() throws IOException {
return this.delegate.getRawStatusCode();
}
@Override
public String getStatusText() throws IOException {
return this.delegate.getStatusText();
}
@Override
public HttpHeaders getHeaders() {
return this.delegate.getHeaders();
}
@Override
public InputStream getBody() throws IOException {
return this.delegate.getBody();
}
@Override
public void close() {
this.delegate.close();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* 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.
@ -86,7 +86,7 @@ public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> {
@Override
@SuppressWarnings({"unchecked", "rawtypes", "resource"})
public T extractData(ClientHttpResponse response) throws IOException {
MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
IntrospectingClientHttpResponse responseWrapper = new IntrospectingClientHttpResponse(response);
if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
return null;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* 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.
@ -20,7 +20,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.ClientHttpResponse;
@ -32,19 +31,18 @@ import org.springframework.lang.Nullable;
* by actually reading the input stream.
*
* @author Brian Clozel
* @author Rossen Stoyanchev
* @since 4.1.5
* @see <a href="https://tools.ietf.org/html/rfc7230#section-3.3.3">RFC 7230 Section 3.3.3</a>
*/
class MessageBodyClientHttpResponseWrapper implements ClientHttpResponse {
private final ClientHttpResponse response;
class IntrospectingClientHttpResponse extends ClientHttpResponseDecorator {
@Nullable
private PushbackInputStream pushbackInputStream;
public MessageBodyClientHttpResponseWrapper(ClientHttpResponse response) {
this.response = response;
public IntrospectingClientHttpResponse(ClientHttpResponse response) {
super(response);
}
@ -82,7 +80,7 @@ class MessageBodyClientHttpResponseWrapper implements ClientHttpResponse {
*/
@SuppressWarnings("ConstantConditions")
public boolean hasEmptyMessageBody() throws IOException {
InputStream body = this.response.getBody();
InputStream body = getDelegate().getBody();
// Per contract body shouldn't be null, but check anyway..
if (body == null) {
return true;
@ -111,35 +109,9 @@ class MessageBodyClientHttpResponseWrapper implements ClientHttpResponse {
}
@Override
public HttpHeaders getHeaders() {
return this.response.getHeaders();
}
@Override
public InputStream getBody() throws IOException {
return (this.pushbackInputStream != null ? this.pushbackInputStream : this.response.getBody());
}
@Override
public HttpStatusCode getStatusCode() throws IOException {
return this.response.getStatusCode();
}
@Override
@Deprecated
public int getRawStatusCode() throws IOException {
return this.response.getRawStatusCode();
}
@Override
public String getStatusText() throws IOException {
return this.response.getStatusText();
}
@Override
public void close() {
this.response.close();
return (this.pushbackInputStream != null ? this.pushbackInputStream : getDelegate().getBody());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* 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.
@ -28,29 +28,29 @@ import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Unit tests for {@link MessageBodyClientHttpResponseWrapper}.
* Unit tests for {@link IntrospectingClientHttpResponse}.
*
* @since 5.3.10
* @author Yin-Jui Liao
*/
class MessageBodyClientHttpResponseWrapperTests {
class IntrospectingClientHttpResponseTests {
private final ClientHttpResponse response = mock(ClientHttpResponse.class);
private final MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
private final IntrospectingClientHttpResponse wrappedResponse = new IntrospectingClientHttpResponse(response);
@Test
void messageBodyDoesNotExist() throws Exception {
given(response.getBody()).willReturn(null);
assertThat(responseWrapper.hasEmptyMessageBody()).isTrue();
assertThat(wrappedResponse.hasEmptyMessageBody()).isTrue();
}
@Test
void messageBodyExists() throws Exception {
InputStream stream = new ByteArrayInputStream("content".getBytes());
given(response.getBody()).willReturn(stream);
assertThat(responseWrapper.hasEmptyMessageBody()).isFalse();
assertThat(wrappedResponse.hasEmptyMessageBody()).isFalse();
}
}