Skip to content

Commit f528c39

Browse files
committed
Add UnknownHttpStatusCodeException
This is more specific exception raised instead of RestClientException when the raw HTTP status code received from the server is not one of the HttpStatus enum values. Issue: SPR-9406
1 parent de38c03 commit f528c39

File tree

5 files changed

+141
-17
lines changed

5 files changed

+141
-17
lines changed

spring-web/src/main/java/org/springframework/http/HttpStatus.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,8 +445,8 @@ public int value() {
445445
return this.value;
446446
}
447447

448-
private static Series valueOf(HttpStatus status) {
449-
int seriesCode = status.value() / 100;
448+
public static Series valueOf(int status) {
449+
int seriesCode = status / 100;
450450
for (Series series : values()) {
451451
if (series.value == seriesCode) {
452452
return series;
@@ -455,6 +455,10 @@ private static Series valueOf(HttpStatus status) {
455455
throw new IllegalArgumentException("No matching constant for [" + status + "]");
456456
}
457457

458+
public static Series valueOf(HttpStatus status) {
459+
return valueOf(status.value);
460+
}
461+
458462
}
459463

460464
}

spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
* method.
3737
*
3838
* @author Arjen Poutsma
39+
* @author Rossen Stoyanchev
3940
* @since 3.0
4041
* @see RestTemplate#setErrorHandler
4142
*/
@@ -45,16 +46,17 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler {
4546
* Delegates to {@link #hasError(HttpStatus)} with the response status code.
4647
*/
4748
public boolean hasError(ClientHttpResponse response) throws IOException {
48-
return hasError(getStatusCode(response));
49+
return hasError(getHttpStatusCode(response));
4950
}
5051

51-
private HttpStatus getStatusCode(ClientHttpResponse response) throws IOException {
52+
private HttpStatus getHttpStatusCode(ClientHttpResponse response) throws IOException {
5253
HttpStatus statusCode;
5354
try {
5455
statusCode = response.getStatusCode();
5556
}
5657
catch (IllegalArgumentException ex) {
57-
throw new RestClientException("Unknown status code [" + response.getRawStatusCode() + "]");
58+
throw new UnknownHttpStatusCodeException(response.getRawStatusCode(),
59+
response.getStatusText(), response.getHeaders(), getResponseBody(response), getCharset(response));
5860
}
5961
return statusCode;
6062
}
@@ -80,16 +82,14 @@ protected boolean hasError(HttpStatus statusCode) {
8082
* and a {@link RestClientException} in other cases.
8183
*/
8284
public void handleError(ClientHttpResponse response) throws IOException {
83-
HttpStatus statusCode = getStatusCode(response);
84-
HttpHeaders headers = response.getHeaders();
85-
MediaType contentType = headers.getContentType();
86-
Charset charset = contentType != null ? contentType.getCharSet() : null;
87-
byte[] body = getResponseBody(response);
85+
HttpStatus statusCode = getHttpStatusCode(response);
8886
switch (statusCode.series()) {
8987
case CLIENT_ERROR:
90-
throw new HttpClientErrorException(statusCode, response.getStatusText(), headers, body, charset);
88+
throw new HttpClientErrorException(statusCode, response.getStatusText(),
89+
response.getHeaders(), getResponseBody(response), getCharset(response));
9190
case SERVER_ERROR:
92-
throw new HttpServerErrorException(statusCode, response.getStatusText(), headers, body, charset);
91+
throw new HttpServerErrorException(statusCode, response.getStatusText(),
92+
response.getHeaders(), getResponseBody(response), getCharset(response));
9393
default:
9494
throw new RestClientException("Unknown status code [" + statusCode + "]");
9595
}
@@ -108,4 +108,10 @@ private byte[] getResponseBody(ClientHttpResponse response) {
108108
return new byte[0];
109109
}
110110

111+
private Charset getCharset(ClientHttpResponse response) {
112+
HttpHeaders headers = response.getHeaders();
113+
MediaType contentType = headers.getContentType();
114+
return contentType != null ? contentType.getCharSet() : null;
115+
}
116+
111117
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright 2002-2012 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.web.client;
17+
18+
import java.io.UnsupportedEncodingException;
19+
import java.nio.charset.Charset;
20+
21+
import org.springframework.http.HttpHeaders;
22+
import org.springframework.http.HttpStatus;
23+
24+
/**
25+
* Exception thrown when an unknown (or custom) HTTP status code is received.
26+
*
27+
* @author Rossen Stoyanchev
28+
* @since 3.2
29+
*/
30+
public class UnknownHttpStatusCodeException extends RestClientException {
31+
32+
private static final long serialVersionUID = 4702443689088991600L;
33+
34+
private static final String DEFAULT_CHARSET = "ISO-8859-1";
35+
36+
private final int rawStatusCode;
37+
38+
private final String statusText;
39+
40+
private final byte[] responseBody;
41+
42+
private final HttpHeaders responseHeaders;
43+
44+
private final String responseCharset;
45+
46+
47+
/**
48+
* Construct a new instance of {@code HttpStatusCodeException} based on an
49+
* {@link HttpStatus}, status text, and response body content.
50+
* @param rawStatusCode the raw status code value
51+
* @param statusText the status text
52+
* @param responseHeaders the response headers, may be {@code null}
53+
* @param responseBody the response body content, may be {@code null}
54+
* @param responseCharset the response body charset, may be {@code null}
55+
*/
56+
public UnknownHttpStatusCodeException(int rawStatusCode, String statusText,
57+
HttpHeaders responseHeaders, byte[] responseBody, Charset responseCharset) {
58+
59+
super("Unknown status code [" + String.valueOf(rawStatusCode) + "]" + " " + statusText);
60+
this.rawStatusCode = rawStatusCode;
61+
this.statusText = statusText;
62+
this.responseHeaders = responseHeaders;
63+
this.responseBody = responseBody != null ? responseBody : new byte[0];
64+
this.responseCharset = responseCharset != null ? responseCharset.name() : DEFAULT_CHARSET;
65+
}
66+
67+
/**
68+
* Return the raw HTTP status code value.
69+
*/
70+
public int getRawStatusCode() {
71+
return this.rawStatusCode;
72+
}
73+
74+
/**
75+
* Return the HTTP status text.
76+
*/
77+
public String getStatusText() {
78+
return this.statusText;
79+
}
80+
81+
/**
82+
* Return the HTTP response headers.
83+
*/
84+
public HttpHeaders getResponseHeaders() {
85+
return this.responseHeaders;
86+
}
87+
88+
/**
89+
* Return the response body as a byte array.
90+
*/
91+
public byte[] getResponseBodyAsByteArray() {
92+
return responseBody;
93+
}
94+
95+
/**
96+
* Return the response body as a string.
97+
*/
98+
public String getResponseBodyAsString() {
99+
try {
100+
return new String(responseBody, responseCharset);
101+
}
102+
catch (UnsupportedEncodingException ex) {
103+
// should not occur
104+
throw new InternalError(ex.getMessage());
105+
}
106+
}
107+
108+
}

spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public void handleError() throws Exception {
7575

7676
expect(response.getStatusCode()).andReturn(HttpStatus.NOT_FOUND);
7777
expect(response.getStatusText()).andReturn("Not Found");
78-
expect(response.getHeaders()).andReturn(headers);
78+
expect(response.getHeaders()).andReturn(headers).atLeastOnce();
7979
expect(response.getBody()).andReturn(new ByteArrayInputStream("Hello World".getBytes("UTF-8")));
8080

8181
replay(response);
@@ -98,7 +98,7 @@ public void handleErrorIOException() throws Exception {
9898

9999
expect(response.getStatusCode()).andReturn(HttpStatus.NOT_FOUND);
100100
expect(response.getStatusText()).andReturn("Not Found");
101-
expect(response.getHeaders()).andReturn(headers);
101+
expect(response.getHeaders()).andReturn(headers).atLeastOnce();
102102
expect(response.getBody()).andThrow(new IOException());
103103

104104
replay(response);
@@ -115,7 +115,7 @@ public void handleErrorNullResponse() throws Exception {
115115

116116
expect(response.getStatusCode()).andReturn(HttpStatus.NOT_FOUND);
117117
expect(response.getStatusText()).andReturn("Not Found");
118-
expect(response.getHeaders()).andReturn(headers);
118+
expect(response.getHeaders()).andReturn(headers).atLeastOnce();
119119
expect(response.getBody()).andReturn(null);
120120

121121
replay(response);
@@ -127,10 +127,16 @@ public void handleErrorNullResponse() throws Exception {
127127

128128
// SPR-9406
129129

130-
@Test(expected=RestClientException.class)
130+
@Test(expected=UnknownHttpStatusCodeException.class)
131131
public void unknownStatusCode() throws Exception {
132+
HttpHeaders headers = new HttpHeaders();
133+
headers.setContentType(MediaType.TEXT_PLAIN);
134+
132135
expect(response.getStatusCode()).andThrow(new IllegalArgumentException("No matching constant for 999"));
133136
expect(response.getRawStatusCode()).andReturn(999);
137+
expect(response.getStatusText()).andReturn("Custom status code");
138+
expect(response.getHeaders()).andReturn(headers).atLeastOnce();
139+
expect(response.getBody()).andReturn(null);
134140

135141
replay(response);
136142

src/dist/changelog.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Changes in version 3.2 RC1 (2012-11-02)
5858
* support inferred base package for @ComponentScan (SPR-9586)
5959
* sort candidate @AspectJ methods deterministically (SPR-9729)
6060
* improve SimpleStreamingClientHttpRequest performance (SPR-9530)
61-
61+
* added UnknownHttpStatusCodeException (SPR-9406)
6262

6363
Changes in version 3.2 M2 (2012-09-11)
6464
--------------------------------------

0 commit comments

Comments
 (0)