forked from spring-projects/spring-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Revisit empty body response support in HTTP client
Prior to this commit, HTTP responses without body (response status 204 or 304, Content-Length: 0) were handled properly by RestTemplates. But some other cases were not properly managed, throwing exceptions for valid HTTP responses. This commit better handles HTTP responses, using a response wrapper that can tell if a response: * has no message body (HTTP status 1XX, 204, 304 or Content-Length:0) * has an empty message body This covers rfc7230 Section 3.3.3. Issue: SPR-8016
- Loading branch information
Showing
5 changed files
with
184 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
122 changes: 122 additions & 0 deletions
122
...eb/src/main/java/org/springframework/web/client/MessageBodyClientHttpResponseWrapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package org.springframework.web.client; | ||
|
||
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.client.ClientHttpResponse; | ||
|
||
/** | ||
* Implementation of {@link ClientHttpResponse} that can not only check if the response | ||
* has a message body, but also if its length is 0 (i.e. empty) by actually reading the input stream. | ||
* | ||
* @author Brian Clozel | ||
* @since 4.1 | ||
* @see <a href="http://tools.ietf.org/html/rfc7230#section-3.3.3">rfc7230 Section 3.3.3</a> | ||
*/ | ||
class MessageBodyClientHttpResponseWrapper implements ClientHttpResponse { | ||
|
||
private PushbackInputStream pushbackInputStream; | ||
|
||
private final ClientHttpResponse response; | ||
|
||
public MessageBodyClientHttpResponseWrapper(ClientHttpResponse response) throws IOException { | ||
this.response = response; | ||
} | ||
|
||
/** | ||
* Indicates whether the response has a message body. | ||
* | ||
* <p>Implementation returns {@code false} for: | ||
* <ul> | ||
* <li>a response status of {@code 1XX}, {@code 204} or {@code 304}</li> | ||
* <li>a {@code Content-Length} header of {@code 0}</li> | ||
* </ul> | ||
* | ||
* @return {@code true} if the response has a message body, {@code false} otherwise | ||
* @throws IOException in case of I/O errors | ||
*/ | ||
public boolean hasMessageBody() throws IOException { | ||
HttpStatus responseStatus = this.getStatusCode(); | ||
if (responseStatus.is1xxInformational() || responseStatus == HttpStatus.NO_CONTENT || | ||
responseStatus == HttpStatus.NOT_MODIFIED) { | ||
return false; | ||
} | ||
else if(this.getHeaders().getContentLength() == 0) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
/** | ||
* Indicates whether the response has an empty message body. | ||
* | ||
* <p>Implementation tries to read the first bytes of the response stream: | ||
* <ul> | ||
* <li>if no bytes are available, the message body is empty</li> | ||
* <li>otherwise it is not empty and the stream is reset to its start for further reading</li> | ||
* </ul> | ||
* | ||
* @return {@code true} if the response has a zero-length message body, {@code false} otherwise | ||
* @throws IOException in case of I/O errors | ||
*/ | ||
public boolean hasEmptyMessageBody() throws IOException { | ||
InputStream body = this.response.getBody(); | ||
if (body == null) { | ||
return true; | ||
} | ||
else if (body.markSupported()) { | ||
body.mark(1); | ||
if (body.read() == -1) { | ||
return true; | ||
} | ||
else { | ||
body.reset(); | ||
return false; | ||
} | ||
} | ||
else { | ||
this.pushbackInputStream = new PushbackInputStream(body); | ||
int b = pushbackInputStream.read(); | ||
if (b == -1) { | ||
return true; | ||
} | ||
else { | ||
pushbackInputStream.unread(b); | ||
return false; | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public HttpStatus getStatusCode() throws IOException { | ||
return response.getStatusCode(); | ||
} | ||
|
||
@Override | ||
public int getRawStatusCode() throws IOException { | ||
return response.getRawStatusCode(); | ||
} | ||
|
||
@Override | ||
public String getStatusText() throws IOException { | ||
return response.getStatusText(); | ||
} | ||
|
||
@Override | ||
public void close() { | ||
response.close(); | ||
} | ||
|
||
@Override | ||
public InputStream getBody() throws IOException { | ||
return this.pushbackInputStream != null ? this.pushbackInputStream : response.getBody(); | ||
} | ||
|
||
@Override | ||
public HttpHeaders getHeaders() { | ||
return response.getHeaders(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.