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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 @Override
@SuppressWarnings({"unchecked", "rawtypes", "resource"}) @SuppressWarnings({"unchecked", "rawtypes", "resource"})
public T extractData(ClientHttpResponse response) throws IOException { public T extractData(ClientHttpResponse response) throws IOException {
MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response); IntrospectingClientHttpResponse responseWrapper = new IntrospectingClientHttpResponse(response);
if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) { if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
return null; 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.InputStream;
import java.io.PushbackInputStream; import java.io.PushbackInputStream;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode; import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.ClientHttpResponse;
@ -32,19 +31,18 @@ import org.springframework.lang.Nullable;
* by actually reading the input stream. * by actually reading the input stream.
* *
* @author Brian Clozel * @author Brian Clozel
* @author Rossen Stoyanchev
* @since 4.1.5 * @since 4.1.5
* @see <a href="https://tools.ietf.org/html/rfc7230#section-3.3.3">RFC 7230 Section 3.3.3</a> * @see <a href="https://tools.ietf.org/html/rfc7230#section-3.3.3">RFC 7230 Section 3.3.3</a>
*/ */
class MessageBodyClientHttpResponseWrapper implements ClientHttpResponse { class IntrospectingClientHttpResponse extends ClientHttpResponseDecorator {
private final ClientHttpResponse response;
@Nullable @Nullable
private PushbackInputStream pushbackInputStream; private PushbackInputStream pushbackInputStream;
public MessageBodyClientHttpResponseWrapper(ClientHttpResponse response) { public IntrospectingClientHttpResponse(ClientHttpResponse response) {
this.response = response; super(response);
} }
@ -82,7 +80,7 @@ class MessageBodyClientHttpResponseWrapper implements ClientHttpResponse {
*/ */
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
public boolean hasEmptyMessageBody() throws IOException { public boolean hasEmptyMessageBody() throws IOException {
InputStream body = this.response.getBody(); InputStream body = getDelegate().getBody();
// Per contract body shouldn't be null, but check anyway.. // Per contract body shouldn't be null, but check anyway..
if (body == null) { if (body == null) {
return true; return true;
@ -111,35 +109,9 @@ class MessageBodyClientHttpResponseWrapper implements ClientHttpResponse {
} }
@Override
public HttpHeaders getHeaders() {
return this.response.getHeaders();
}
@Override @Override
public InputStream getBody() throws IOException { public InputStream getBody() throws IOException {
return (this.pushbackInputStream != null ? this.pushbackInputStream : this.response.getBody()); return (this.pushbackInputStream != null ? this.pushbackInputStream : getDelegate().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();
} }
} }

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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; import static org.mockito.Mockito.mock;
/** /**
* Unit tests for {@link MessageBodyClientHttpResponseWrapper}. * Unit tests for {@link IntrospectingClientHttpResponse}.
* *
* @since 5.3.10 * @since 5.3.10
* @author Yin-Jui Liao * @author Yin-Jui Liao
*/ */
class MessageBodyClientHttpResponseWrapperTests { class IntrospectingClientHttpResponseTests {
private final ClientHttpResponse response = mock(ClientHttpResponse.class); private final ClientHttpResponse response = mock(ClientHttpResponse.class);
private final MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response); private final IntrospectingClientHttpResponse wrappedResponse = new IntrospectingClientHttpResponse(response);
@Test @Test
void messageBodyDoesNotExist() throws Exception { void messageBodyDoesNotExist() throws Exception {
given(response.getBody()).willReturn(null); given(response.getBody()).willReturn(null);
assertThat(responseWrapper.hasEmptyMessageBody()).isTrue(); assertThat(wrappedResponse.hasEmptyMessageBody()).isTrue();
} }
@Test @Test
void messageBodyExists() throws Exception { void messageBodyExists() throws Exception {
InputStream stream = new ByteArrayInputStream("content".getBytes()); InputStream stream = new ByteArrayInputStream("content".getBytes());
given(response.getBody()).willReturn(stream); given(response.getBody()).willReturn(stream);
assertThat(responseWrapper.hasEmptyMessageBody()).isFalse(); assertThat(wrappedResponse.hasEmptyMessageBody()).isFalse();
} }
} }