Skip to content

Commit 1c1b942

Browse files
committed
DefaultResponseErrorHandler detects non-standard error code as well
Issue: SPR-17439
1 parent 85b5c5a commit 1c1b942

File tree

4 files changed

+123
-38
lines changed

4 files changed

+123
-38
lines changed

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

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -419,58 +419,62 @@ public String getReasonPhrase() {
419419
return this.reasonPhrase;
420420
}
421421

422+
/**
423+
* Return the HTTP status series of this status code.
424+
* @see HttpStatus.Series
425+
*/
426+
public Series series() {
427+
return Series.valueOf(this);
428+
}
429+
422430
/**
423431
* Whether this status code is in the HTTP series
424432
* {@link org.springframework.http.HttpStatus.Series#INFORMATIONAL}.
425433
* This is a shortcut for checking the value of {@link #series()}.
434+
* @see #series()
426435
*/
427436
public boolean is1xxInformational() {
428-
return Series.INFORMATIONAL.equals(series());
437+
return (series() == Series.INFORMATIONAL);
429438
}
430439

431440
/**
432441
* Whether this status code is in the HTTP series
433442
* {@link org.springframework.http.HttpStatus.Series#SUCCESSFUL}.
434443
* This is a shortcut for checking the value of {@link #series()}.
444+
* @see #series()
435445
*/
436446
public boolean is2xxSuccessful() {
437-
return Series.SUCCESSFUL.equals(series());
447+
return (series() == Series.SUCCESSFUL);
438448
}
439449

440450
/**
441451
* Whether this status code is in the HTTP series
442452
* {@link org.springframework.http.HttpStatus.Series#REDIRECTION}.
443453
* This is a shortcut for checking the value of {@link #series()}.
454+
* @see #series()
444455
*/
445456
public boolean is3xxRedirection() {
446-
return Series.REDIRECTION.equals(series());
457+
return (series() == Series.REDIRECTION);
447458
}
448459

449-
450460
/**
451461
* Whether this status code is in the HTTP series
452462
* {@link org.springframework.http.HttpStatus.Series#CLIENT_ERROR}.
453463
* This is a shortcut for checking the value of {@link #series()}.
464+
* @see #series()
454465
*/
455466
public boolean is4xxClientError() {
456-
return Series.CLIENT_ERROR.equals(series());
467+
return (series() == Series.CLIENT_ERROR);
457468
}
458469

459470
/**
460471
* Whether this status code is in the HTTP series
461472
* {@link org.springframework.http.HttpStatus.Series#SERVER_ERROR}.
462473
* This is a shortcut for checking the value of {@link #series()}.
474+
* @see #series()
463475
*/
464476
public boolean is5xxServerError() {
465-
return Series.SERVER_ERROR.equals(series());
466-
}
467-
468-
/**
469-
* Returns the HTTP status series of this status code.
470-
* @see HttpStatus.Series
471-
*/
472-
public Series series() {
473-
return Series.valueOf(this);
477+
return (series() == Series.SERVER_ERROR);
474478
}
475479

476480
/**
@@ -523,18 +527,30 @@ public int value() {
523527
return this.value;
524528
}
525529

526-
public static Series valueOf(int status) {
527-
int seriesCode = status / 100;
530+
/**
531+
* Return the enum constant of this type with the corresponding series.
532+
* @param status a standard HTTP status enum value
533+
* @return the enum constant of this type with the corresponding series
534+
* @throws IllegalArgumentException if this enum has no corresponding constant
535+
*/
536+
public static Series valueOf(HttpStatus status) {
537+
return valueOf(status.value);
538+
}
539+
540+
/**
541+
* Return the enum constant of this type with the corresponding series.
542+
* @param statusCode the HTTP status code (potentially non-standard)
543+
* @return the enum constant of this type with the corresponding series
544+
* @throws IllegalArgumentException if this enum has no corresponding constant
545+
*/
546+
public static Series valueOf(int statusCode) {
547+
int seriesCode = statusCode / 100;
528548
for (Series series : values()) {
529549
if (series.value == seriesCode) {
530550
return series;
531551
}
532552
}
533-
throw new IllegalArgumentException("No matching constant for [" + status + "]");
534-
}
535-
536-
public static Series valueOf(HttpStatus status) {
537-
return valueOf(status.value);
553+
throw new IllegalArgumentException("No matching constant for [" + statusCode + "]");
538554
}
539555
}
540556

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

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@
4343
public class DefaultResponseErrorHandler implements ResponseErrorHandler {
4444

4545
/**
46-
* Delegates to {@link #hasError(HttpStatus)} with the response status code.
46+
* Delegates to {@link #hasError(HttpStatus)} (for a standard status enum value) or
47+
* {@link #hasError(int)} (for an unknown status code) with the response status code.
48+
* @see ClientHttpResponse#getRawStatusCode()
49+
* @see #hasError(HttpStatus)
50+
* @see #hasError(int)
4751
*/
4852
@Override
4953
public boolean hasError(ClientHttpResponse response) throws IOException {
@@ -53,7 +57,7 @@ public boolean hasError(ClientHttpResponse response) throws IOException {
5357
return hasError(statusCode);
5458
}
5559
}
56-
return false;
60+
return hasError(rawStatusCode);
5761
}
5862

5963
/**
@@ -62,13 +66,31 @@ public boolean hasError(ClientHttpResponse response) throws IOException {
6266
* {@link HttpStatus.Series#CLIENT_ERROR CLIENT_ERROR} or
6367
* {@link HttpStatus.Series#SERVER_ERROR SERVER_ERROR}.
6468
* Can be overridden in subclasses.
65-
* @param statusCode the HTTP status code
66-
* @return {@code true} if the response has an error; {@code false} otherwise
67-
* @see #getHttpStatusCode(ClientHttpResponse)
69+
* @param statusCode the HTTP status code as enum value
70+
* @return {@code true} if the response indicates an error; {@code false} otherwise
71+
* @see HttpStatus#is4xxClientError()
72+
* @see HttpStatus#is5xxServerError()
6873
*/
6974
protected boolean hasError(HttpStatus statusCode) {
70-
return (statusCode.series() == HttpStatus.Series.CLIENT_ERROR ||
71-
statusCode.series() == HttpStatus.Series.SERVER_ERROR);
75+
return (statusCode.is4xxClientError() || statusCode.is5xxServerError());
76+
}
77+
78+
/**
79+
* Template method called from {@link #hasError(ClientHttpResponse)}.
80+
* <p>The default implementation checks if the given status code is
81+
* {@code HttpStatus.Series#CLIENT_ERROR CLIENT_ERROR} or
82+
* {@code HttpStatus.Series#SERVER_ERROR SERVER_ERROR}.
83+
* Can be overridden in subclasses.
84+
* @param unknownStatusCode the HTTP status code as raw value
85+
* @return {@code true} if the response indicates an error; {@code false} otherwise
86+
* @since 4.3.21
87+
* @see HttpStatus.Series#CLIENT_ERROR
88+
* @see HttpStatus.Series#SERVER_ERROR
89+
*/
90+
protected boolean hasError(int unknownStatusCode) {
91+
int seriesCode = unknownStatusCode / 100;
92+
return (seriesCode == HttpStatus.Series.CLIENT_ERROR.value() ||
93+
seriesCode == HttpStatus.Series.SERVER_ERROR.value());
7294
}
7395

7496
/**
@@ -93,7 +115,6 @@ public void handleError(ClientHttpResponse response) throws IOException {
93115
}
94116
}
95117

96-
97118
/**
98119
* Determine the HTTP status of the given response.
99120
* <p>Note: Only called from {@link #handleError}, not from {@link #hasError}.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ public interface ResponseErrorHandler {
3434
* <p>Implementations will typically inspect the
3535
* {@link ClientHttpResponse#getStatusCode() HttpStatus} of the response.
3636
* @param response the response to inspect
37-
* @return {@code true} if the response has an error; {@code false} otherwise
37+
* @return {@code true} if the response indicates an error; {@code false} otherwise
3838
* @throws IOException in case of I/O errors
3939
*/
4040
boolean hasError(ClientHttpResponse response) throws IOException;

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

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,20 @@ public void handleErrorNullResponse() throws Exception {
103103
handler.handleError(response);
104104
}
105105

106+
@Test // SPR-16108
107+
public void hasErrorForUnknownStatusCode() throws Exception {
108+
HttpHeaders headers = new HttpHeaders();
109+
headers.setContentType(MediaType.TEXT_PLAIN);
110+
111+
given(response.getRawStatusCode()).willReturn(999);
112+
given(response.getStatusText()).willReturn("Custom status code");
113+
given(response.getHeaders()).willReturn(headers);
114+
115+
assertFalse(handler.hasError(response));
116+
}
117+
106118
@Test(expected = UnknownHttpStatusCodeException.class) // SPR-9406
107-
public void unknownStatusCode() throws Exception {
119+
public void handleErrorUnknownStatusCode() throws Exception {
108120
HttpHeaders headers = new HttpHeaders();
109121
headers.setContentType(MediaType.TEXT_PLAIN);
110122

@@ -115,23 +127,59 @@ public void unknownStatusCode() throws Exception {
115127
handler.handleError(response);
116128
}
117129

118-
@Test // SPR-16108
119-
public void hasErrorForUnknownStatusCode() throws Exception {
130+
@Test // SPR-17461
131+
public void hasErrorForCustomClientError() throws Exception {
120132
HttpHeaders headers = new HttpHeaders();
121133
headers.setContentType(MediaType.TEXT_PLAIN);
122134

123-
given(response.getRawStatusCode()).willReturn(999);
135+
given(response.getRawStatusCode()).willReturn(499);
124136
given(response.getStatusText()).willReturn("Custom status code");
125137
given(response.getHeaders()).willReturn(headers);
126138

127-
assertFalse(handler.hasError(response));
139+
assertTrue(handler.hasError(response));
140+
}
141+
142+
@Test(expected = UnknownHttpStatusCodeException.class)
143+
public void handleErrorForCustomClientError() throws Exception {
144+
HttpHeaders headers = new HttpHeaders();
145+
headers.setContentType(MediaType.TEXT_PLAIN);
146+
147+
given(response.getStatusCode()).willThrow(new IllegalArgumentException("No matching constant for 499"));
148+
given(response.getStatusText()).willReturn("Custom status code");
149+
given(response.getHeaders()).willReturn(headers);
150+
151+
handler.handleError(response);
152+
}
153+
154+
@Test // SPR-17461
155+
public void hasErrorForCustomServerError() throws Exception {
156+
HttpHeaders headers = new HttpHeaders();
157+
headers.setContentType(MediaType.TEXT_PLAIN);
158+
159+
given(response.getRawStatusCode()).willReturn(599);
160+
given(response.getStatusText()).willReturn("Custom status code");
161+
given(response.getHeaders()).willReturn(headers);
162+
163+
assertTrue(handler.hasError(response));
164+
}
165+
166+
@Test(expected = UnknownHttpStatusCodeException.class)
167+
public void handleErrorForCustomServerError() throws Exception {
168+
HttpHeaders headers = new HttpHeaders();
169+
headers.setContentType(MediaType.TEXT_PLAIN);
170+
171+
given(response.getStatusCode()).willThrow(new IllegalArgumentException("No matching constant for 599"));
172+
given(response.getStatusText()).willReturn("Custom status code");
173+
given(response.getHeaders()).willReturn(headers);
174+
175+
handler.handleError(response);
128176
}
129177

130178
@Test // SPR-16604
131179
public void bodyAvailableAfterHasErrorForUnknownStatusCode() throws Exception {
132180
HttpHeaders headers = new HttpHeaders();
133181
headers.setContentType(MediaType.TEXT_PLAIN);
134-
TestByteArrayInputStream body = new TestByteArrayInputStream("Hello World".getBytes("UTF-8"));
182+
TestByteArrayInputStream body = new TestByteArrayInputStream("Hello World".getBytes(UTF8));
135183

136184
given(response.getRawStatusCode()).willReturn(999);
137185
given(response.getStatusText()).willReturn("Custom status code");

0 commit comments

Comments
 (0)