Skip to content

Commit ca4b6e8

Browse files
committed
Introduce HttpStatusCode interface
This commit introduces HttpStatusCode, an interface implemented by HttpStatus. Instances of HttpStatusCode are obtained via static valueOf(int) factory method, returning a HttpStatus enum entry if available, and a default implementation otherwise. The underlying reason behind this change is HTTP status codes are not enumerable, but instead range from 100-999. Closes gh-28214
1 parent c23edf7 commit ca4b6e8

File tree

3 files changed

+209
-48
lines changed

3 files changed

+209
-48
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright 2002-2022 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+
* https://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+
17+
package org.springframework.http;
18+
19+
import java.io.Serializable;
20+
21+
import org.jetbrains.annotations.NotNull;
22+
23+
/**
24+
* Default implementation of {@link HttpStatusCode}.
25+
*
26+
* @author Arjen Poutsma
27+
* @since 6.0
28+
*/
29+
final class DefaultHttpStatusCode
30+
implements HttpStatusCode, Comparable<HttpStatusCode>, Serializable {
31+
32+
private static final long serialVersionUID = 7017664779360718111L;
33+
34+
private final int value;
35+
36+
37+
public DefaultHttpStatusCode(int value) {
38+
this.value = value;
39+
}
40+
41+
@Override
42+
public int value() {
43+
return this.value;
44+
}
45+
46+
@Override
47+
public boolean is1xxInformational() {
48+
return hundreds() == 1;
49+
}
50+
51+
@Override
52+
public boolean is2xxSuccessful() {
53+
return hundreds() == 2;
54+
}
55+
56+
@Override
57+
public boolean is3xxRedirection() {
58+
return hundreds() == 3;
59+
}
60+
61+
@Override
62+
public boolean is4xxClientError() {
63+
return hundreds() == 4;
64+
}
65+
66+
@Override
67+
public boolean is5xxServerError() {
68+
return hundreds() == 5;
69+
}
70+
71+
@Override
72+
public boolean isError() {
73+
int hundreds = hundreds();
74+
return hundreds == 4 || hundreds == 5;
75+
}
76+
77+
private int hundreds() {
78+
return this.value / 100;
79+
}
80+
81+
@Override
82+
public int compareTo(@NotNull HttpStatusCode o) {
83+
return Integer.compare(this.value, o.value());
84+
}
85+
86+
@Override
87+
public int hashCode() {
88+
return this.value;
89+
}
90+
91+
@Override
92+
public boolean equals(Object obj) {
93+
if (obj instanceof HttpStatusCode other) {
94+
return this.value == other.value();
95+
}
96+
else {
97+
return false;
98+
}
99+
}
100+
101+
@Override
102+
public String toString() {
103+
return Integer.toString(this.value);
104+
}
105+
}

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

Lines changed: 8 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
* @see <a href="https://www.iana.org/assignments/http-status-codes">HTTP Status Code Registry</a>
3232
* @see <a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes">List of HTTP status codes - Wikipedia</a>
3333
*/
34-
public enum HttpStatus {
34+
public enum HttpStatus implements HttpStatusCode {
3535

3636
// 1xx Informational
3737

@@ -436,9 +436,7 @@ public enum HttpStatus {
436436
}
437437

438438

439-
/**
440-
* Return the integer value of this status code.
441-
*/
439+
@Override
442440
public int value() {
443441
return this.value;
444442
}
@@ -458,70 +456,32 @@ public String getReasonPhrase() {
458456
return this.reasonPhrase;
459457
}
460458

461-
/**
462-
* Whether this status code is in the HTTP series
463-
* {@link org.springframework.http.HttpStatus.Series#INFORMATIONAL}.
464-
* <p>This is a shortcut for checking the value of {@link #series()}.
465-
* @since 4.0
466-
* @see #series()
467-
*/
459+
@Override
468460
public boolean is1xxInformational() {
469461
return (series() == Series.INFORMATIONAL);
470462
}
471463

472-
/**
473-
* Whether this status code is in the HTTP series
474-
* {@link org.springframework.http.HttpStatus.Series#SUCCESSFUL}.
475-
* <p>This is a shortcut for checking the value of {@link #series()}.
476-
* @since 4.0
477-
* @see #series()
478-
*/
464+
@Override
479465
public boolean is2xxSuccessful() {
480466
return (series() == Series.SUCCESSFUL);
481467
}
482468

483-
/**
484-
* Whether this status code is in the HTTP series
485-
* {@link org.springframework.http.HttpStatus.Series#REDIRECTION}.
486-
* <p>This is a shortcut for checking the value of {@link #series()}.
487-
* @since 4.0
488-
* @see #series()
489-
*/
469+
@Override
490470
public boolean is3xxRedirection() {
491471
return (series() == Series.REDIRECTION);
492472
}
493473

494-
/**
495-
* Whether this status code is in the HTTP series
496-
* {@link org.springframework.http.HttpStatus.Series#CLIENT_ERROR}.
497-
* <p>This is a shortcut for checking the value of {@link #series()}.
498-
* @since 4.0
499-
* @see #series()
500-
*/
474+
@Override
501475
public boolean is4xxClientError() {
502476
return (series() == Series.CLIENT_ERROR);
503477
}
504478

505-
/**
506-
* Whether this status code is in the HTTP series
507-
* {@link org.springframework.http.HttpStatus.Series#SERVER_ERROR}.
508-
* <p>This is a shortcut for checking the value of {@link #series()}.
509-
* @since 4.0
510-
* @see #series()
511-
*/
479+
@Override
512480
public boolean is5xxServerError() {
513481
return (series() == Series.SERVER_ERROR);
514482
}
515483

516-
/**
517-
* Whether this status code is in the HTTP series
518-
* {@link org.springframework.http.HttpStatus.Series#CLIENT_ERROR} or
519-
* {@link org.springframework.http.HttpStatus.Series#SERVER_ERROR}.
520-
* <p>This is a shortcut for checking the value of {@link #series()}.
521-
* @since 5.0
522-
* @see #is4xxClientError()
523-
* @see #is5xxServerError()
524-
*/
484+
@Override
525485
public boolean isError() {
526486
return (is4xxClientError() || is5xxServerError());
527487
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright 2002-2021 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+
* https://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+
17+
package org.springframework.http;
18+
19+
import org.springframework.util.Assert;
20+
21+
/**
22+
* Represents an HTTP response status code. Implemented by {@link HttpStatus},
23+
* but defined as interface to allow for values not in that enumeration.
24+
*
25+
* @author Arjen Poutsma
26+
* @since 6.0
27+
* @see <a href="https://www.iana.org/assignments/http-status-codes">HTTP Status Code Registry</a>
28+
* @see <a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes">List of HTTP status codes - Wikipedia</a>
29+
*/
30+
public sealed interface HttpStatusCode permits DefaultHttpStatusCode, HttpStatus {
31+
32+
/**
33+
* Return the integer value of this status code.
34+
*/
35+
int value();
36+
37+
/**
38+
* Whether this status code is in the Informational class ({@code 1xx}).
39+
* @see <a href="https://datatracker.ietf.org/doc/html/rfc2616#section-10.1">RFC 2616</a>
40+
*/
41+
boolean is1xxInformational();
42+
43+
/**
44+
* Whether this status code is in the Successful class ({@code 2xx}).
45+
* @see <a href="https://datatracker.ietf.org/doc/html/rfc2616#section-10.2">RFC 2616</a>
46+
*/
47+
boolean is2xxSuccessful();
48+
49+
/**
50+
* Whether this status code is in the Redirection class ({@code 3xx}).
51+
* @see <a href="https://datatracker.ietf.org/doc/html/rfc2616#section-10.3">RFC 2616</a>
52+
*/
53+
boolean is3xxRedirection();
54+
55+
/**
56+
* Whether this status code is in the Client Error class ({@code 4xx}).
57+
* @see <a href="https://datatracker.ietf.org/doc/html/rfc2616#section-10.4">RFC 2616</a>
58+
*/
59+
boolean is4xxClientError();
60+
61+
/**
62+
* Whether this status code is in the Server Error class ({@code 5xx}).
63+
* @see <a href="https://datatracker.ietf.org/doc/html/rfc2616#section-10.5">RFC 2616</a>
64+
*/
65+
boolean is5xxServerError();
66+
67+
/**
68+
* Whether this status code is in the Client or Server Error class
69+
* @see <a href="https://datatracker.ietf.org/doc/html/rfc2616#section-10.4">RFC 2616</a>
70+
* @see <a href="https://datatracker.ietf.org/doc/html/rfc2616#section-10.3">RFC 2616</a>
71+
* ({@code 4xx} or {@code 5xx}).
72+
* @see #is4xxClientError()
73+
* @see #is5xxServerError()
74+
*/
75+
boolean isError();
76+
77+
78+
/**
79+
* Return an {@code HttpStatusCode} object for the given integer value.
80+
* @param code the status code as integer
81+
* @return the corresponding {@code HttpStatusCode}
82+
* @throws IllegalArgumentException if {@code code} is not a three-digit
83+
* positive number
84+
*/
85+
static HttpStatusCode valueOf(int code) {
86+
Assert.isTrue(code >= 100 && code <= 999, "Code '" + code + "' should be a thee-digit positive integer");
87+
HttpStatus status = HttpStatus.resolve(code);
88+
if (status != null) {
89+
return status;
90+
}
91+
else {
92+
return new DefaultHttpStatusCode(code);
93+
}
94+
}
95+
96+
}

0 commit comments

Comments
 (0)