View Javadoc
1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. 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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty5.handler.codec.http;
17  
18  import io.netty5.buffer.api.Buffer;
19  import io.netty5.util.AsciiString;
20  import io.netty5.util.CharsetUtil;
21  
22  import static io.netty5.handler.codec.http.HttpConstants.SP;
23  import static io.netty5.util.ByteProcessor.FIND_ASCII_SPACE;
24  import static io.netty5.util.internal.ObjectUtil.checkPositiveOrZero;
25  import static java.lang.Integer.parseInt;
26  import static java.util.Objects.requireNonNull;
27  
28  /**
29   * The response code and its description of HTTP or its derived protocols, such as
30   * <a href="https://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol">RTSP</a> and
31   * <a href="https://en.wikipedia.org/wiki/Internet_Content_Adaptation_Protocol">ICAP</a>.
32   */
33  public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
34  
35      /**
36       * 100 Continue
37       */
38      public static final HttpResponseStatus CONTINUE = newStatus(100, "Continue");
39  
40      /**
41       * 101 Switching Protocols
42       */
43      public static final HttpResponseStatus SWITCHING_PROTOCOLS = newStatus(101, "Switching Protocols");
44  
45      /**
46       * 102 Processing (WebDAV, RFC2518)
47       */
48      public static final HttpResponseStatus PROCESSING = newStatus(102, "Processing");
49  
50      /**
51       * 200 OK
52       */
53      public static final HttpResponseStatus OK = newStatus(200, "OK");
54  
55      /**
56       * 201 Created
57       */
58      public static final HttpResponseStatus CREATED = newStatus(201, "Created");
59  
60      /**
61       * 202 Accepted
62       */
63      public static final HttpResponseStatus ACCEPTED = newStatus(202, "Accepted");
64  
65      /**
66       * 203 Non-Authoritative Information (since HTTP/1.1)
67       */
68      public static final HttpResponseStatus NON_AUTHORITATIVE_INFORMATION =
69              newStatus(203, "Non-Authoritative Information");
70  
71      /**
72       * 204 No Content
73       */
74      public static final HttpResponseStatus NO_CONTENT = newStatus(204, "No Content");
75  
76      /**
77       * 205 Reset Content
78       */
79      public static final HttpResponseStatus RESET_CONTENT = newStatus(205, "Reset Content");
80  
81      /**
82       * 206 Partial Content
83       */
84      public static final HttpResponseStatus PARTIAL_CONTENT = newStatus(206, "Partial Content");
85  
86      /**
87       * 207 Multi-Status (WebDAV, RFC2518)
88       */
89      public static final HttpResponseStatus MULTI_STATUS = newStatus(207, "Multi-Status");
90  
91      /**
92       * 300 Multiple Choices
93       */
94      public static final HttpResponseStatus MULTIPLE_CHOICES = newStatus(300, "Multiple Choices");
95  
96      /**
97       * 301 Moved Permanently
98       */
99      public static final HttpResponseStatus MOVED_PERMANENTLY = newStatus(301, "Moved Permanently");
100 
101     /**
102      * 302 Found
103      */
104     public static final HttpResponseStatus FOUND = newStatus(302, "Found");
105 
106     /**
107      * 303 See Other (since HTTP/1.1)
108      */
109     public static final HttpResponseStatus SEE_OTHER = newStatus(303, "See Other");
110 
111     /**
112      * 304 Not Modified
113      */
114     public static final HttpResponseStatus NOT_MODIFIED = newStatus(304, "Not Modified");
115 
116     /**
117      * 305 Use Proxy (since HTTP/1.1)
118      */
119     public static final HttpResponseStatus USE_PROXY = newStatus(305, "Use Proxy");
120 
121     /**
122      * 307 Temporary Redirect (since HTTP/1.1)
123      */
124     public static final HttpResponseStatus TEMPORARY_REDIRECT = newStatus(307, "Temporary Redirect");
125 
126     /**
127      * 308 Permanent Redirect (RFC7538)
128      */
129     public static final HttpResponseStatus PERMANENT_REDIRECT = newStatus(308, "Permanent Redirect");
130 
131     /**
132      * 400 Bad Request
133      */
134     public static final HttpResponseStatus BAD_REQUEST = newStatus(400, "Bad Request");
135 
136     /**
137      * 401 Unauthorized
138      */
139     public static final HttpResponseStatus UNAUTHORIZED = newStatus(401, "Unauthorized");
140 
141     /**
142      * 402 Payment Required
143      */
144     public static final HttpResponseStatus PAYMENT_REQUIRED = newStatus(402, "Payment Required");
145 
146     /**
147      * 403 Forbidden
148      */
149     public static final HttpResponseStatus FORBIDDEN = newStatus(403, "Forbidden");
150 
151     /**
152      * 404 Not Found
153      */
154     public static final HttpResponseStatus NOT_FOUND = newStatus(404, "Not Found");
155 
156     /**
157      * 405 Method Not Allowed
158      */
159     public static final HttpResponseStatus METHOD_NOT_ALLOWED = newStatus(405, "Method Not Allowed");
160 
161     /**
162      * 406 Not Acceptable
163      */
164     public static final HttpResponseStatus NOT_ACCEPTABLE = newStatus(406, "Not Acceptable");
165 
166     /**
167      * 407 Proxy Authentication Required
168      */
169     public static final HttpResponseStatus PROXY_AUTHENTICATION_REQUIRED =
170             newStatus(407, "Proxy Authentication Required");
171 
172     /**
173      * 408 Request Timeout
174      */
175     public static final HttpResponseStatus REQUEST_TIMEOUT = newStatus(408, "Request Timeout");
176 
177     /**
178      * 409 Conflict
179      */
180     public static final HttpResponseStatus CONFLICT = newStatus(409, "Conflict");
181 
182     /**
183      * 410 Gone
184      */
185     public static final HttpResponseStatus GONE = newStatus(410, "Gone");
186 
187     /**
188      * 411 Length Required
189      */
190     public static final HttpResponseStatus LENGTH_REQUIRED = newStatus(411, "Length Required");
191 
192     /**
193      * 412 Precondition Failed
194      */
195     public static final HttpResponseStatus PRECONDITION_FAILED = newStatus(412, "Precondition Failed");
196 
197     /**
198      * 413 Request Entity Too Large
199      */
200     public static final HttpResponseStatus REQUEST_ENTITY_TOO_LARGE =
201             newStatus(413, "Request Entity Too Large");
202 
203     /**
204      * 414 Request-URI Too Long
205      */
206     public static final HttpResponseStatus REQUEST_URI_TOO_LONG = newStatus(414, "Request-URI Too Long");
207 
208     /**
209      * 415 Unsupported Media Type
210      */
211     public static final HttpResponseStatus UNSUPPORTED_MEDIA_TYPE = newStatus(415, "Unsupported Media Type");
212 
213     /**
214      * 416 Requested Range Not Satisfiable
215      */
216     public static final HttpResponseStatus REQUESTED_RANGE_NOT_SATISFIABLE =
217             newStatus(416, "Requested Range Not Satisfiable");
218 
219     /**
220      * 417 Expectation Failed
221      */
222     public static final HttpResponseStatus EXPECTATION_FAILED = newStatus(417, "Expectation Failed");
223 
224     /**
225      * 421 Misdirected Request
226      *
227      * @see <a href="https://tools.ietf.org/html/rfc7540#section-9.1.2">421 (Misdirected Request) Status Code</a>
228      */
229     public static final HttpResponseStatus MISDIRECTED_REQUEST = newStatus(421, "Misdirected Request");
230 
231     /**
232      * 422 Unprocessable Entity (WebDAV, RFC4918)
233      */
234     public static final HttpResponseStatus UNPROCESSABLE_ENTITY = newStatus(422, "Unprocessable Entity");
235 
236     /**
237      * 423 Locked (WebDAV, RFC4918)
238      */
239     public static final HttpResponseStatus LOCKED = newStatus(423, "Locked");
240 
241     /**
242      * 424 Failed Dependency (WebDAV, RFC4918)
243      */
244     public static final HttpResponseStatus FAILED_DEPENDENCY = newStatus(424, "Failed Dependency");
245 
246     /**
247      * 425 Unordered Collection (WebDAV, RFC3648)
248      */
249     public static final HttpResponseStatus UNORDERED_COLLECTION = newStatus(425, "Unordered Collection");
250 
251     /**
252      * 426 Upgrade Required (RFC2817)
253      */
254     public static final HttpResponseStatus UPGRADE_REQUIRED = newStatus(426, "Upgrade Required");
255 
256     /**
257      * 428 Precondition Required (RFC6585)
258      */
259     public static final HttpResponseStatus PRECONDITION_REQUIRED = newStatus(428, "Precondition Required");
260 
261     /**
262      * 429 Too Many Requests (RFC6585)
263      */
264     public static final HttpResponseStatus TOO_MANY_REQUESTS = newStatus(429, "Too Many Requests");
265 
266     /**
267      * 431 Request Header Fields Too Large (RFC6585)
268      */
269     public static final HttpResponseStatus REQUEST_HEADER_FIELDS_TOO_LARGE =
270             newStatus(431, "Request Header Fields Too Large");
271 
272     /**
273      * 500 Internal Server Error
274      */
275     public static final HttpResponseStatus INTERNAL_SERVER_ERROR = newStatus(500, "Internal Server Error");
276 
277     /**
278      * 501 Not Implemented
279      */
280     public static final HttpResponseStatus NOT_IMPLEMENTED = newStatus(501, "Not Implemented");
281 
282     /**
283      * 502 Bad Gateway
284      */
285     public static final HttpResponseStatus BAD_GATEWAY = newStatus(502, "Bad Gateway");
286 
287     /**
288      * 503 Service Unavailable
289      */
290     public static final HttpResponseStatus SERVICE_UNAVAILABLE = newStatus(503, "Service Unavailable");
291 
292     /**
293      * 504 Gateway Timeout
294      */
295     public static final HttpResponseStatus GATEWAY_TIMEOUT = newStatus(504, "Gateway Timeout");
296 
297     /**
298      * 505 HTTP Version Not Supported
299      */
300     public static final HttpResponseStatus HTTP_VERSION_NOT_SUPPORTED =
301             newStatus(505, "HTTP Version Not Supported");
302 
303     /**
304      * 506 Variant Also Negotiates (RFC2295)
305      */
306     public static final HttpResponseStatus VARIANT_ALSO_NEGOTIATES = newStatus(506, "Variant Also Negotiates");
307 
308     /**
309      * 507 Insufficient Storage (WebDAV, RFC4918)
310      */
311     public static final HttpResponseStatus INSUFFICIENT_STORAGE = newStatus(507, "Insufficient Storage");
312 
313     /**
314      * 510 Not Extended (RFC2774)
315      */
316     public static final HttpResponseStatus NOT_EXTENDED = newStatus(510, "Not Extended");
317 
318     /**
319      * 511 Network Authentication Required (RFC6585)
320      */
321     public static final HttpResponseStatus NETWORK_AUTHENTICATION_REQUIRED =
322             newStatus(511, "Network Authentication Required");
323 
324     private static HttpResponseStatus newStatus(int statusCode, String reasonPhrase) {
325         return new HttpResponseStatus(statusCode, reasonPhrase, true);
326     }
327 
328     /**
329      * Returns the {@link HttpResponseStatus} represented by the specified code.
330      * If the specified code is a standard HTTP status code, a cached instance
331      * will be returned.  Otherwise, a new instance will be returned.
332      */
333     public static HttpResponseStatus valueOf(int code) {
334         HttpResponseStatus status = valueOf0(code);
335         return status != null ? status : new HttpResponseStatus(code);
336     }
337 
338     private static HttpResponseStatus valueOf0(int code) {
339         switch (code) {
340         case 100:
341             return CONTINUE;
342         case 101:
343             return SWITCHING_PROTOCOLS;
344         case 102:
345             return PROCESSING;
346         case 200:
347             return OK;
348         case 201:
349             return CREATED;
350         case 202:
351             return ACCEPTED;
352         case 203:
353             return NON_AUTHORITATIVE_INFORMATION;
354         case 204:
355             return NO_CONTENT;
356         case 205:
357             return RESET_CONTENT;
358         case 206:
359             return PARTIAL_CONTENT;
360         case 207:
361             return MULTI_STATUS;
362         case 300:
363             return MULTIPLE_CHOICES;
364         case 301:
365             return MOVED_PERMANENTLY;
366         case 302:
367             return FOUND;
368         case 303:
369             return SEE_OTHER;
370         case 304:
371             return NOT_MODIFIED;
372         case 305:
373             return USE_PROXY;
374         case 307:
375             return TEMPORARY_REDIRECT;
376         case 308:
377             return PERMANENT_REDIRECT;
378         case 400:
379             return BAD_REQUEST;
380         case 401:
381             return UNAUTHORIZED;
382         case 402:
383             return PAYMENT_REQUIRED;
384         case 403:
385             return FORBIDDEN;
386         case 404:
387             return NOT_FOUND;
388         case 405:
389             return METHOD_NOT_ALLOWED;
390         case 406:
391             return NOT_ACCEPTABLE;
392         case 407:
393             return PROXY_AUTHENTICATION_REQUIRED;
394         case 408:
395             return REQUEST_TIMEOUT;
396         case 409:
397             return CONFLICT;
398         case 410:
399             return GONE;
400         case 411:
401             return LENGTH_REQUIRED;
402         case 412:
403             return PRECONDITION_FAILED;
404         case 413:
405             return REQUEST_ENTITY_TOO_LARGE;
406         case 414:
407             return REQUEST_URI_TOO_LONG;
408         case 415:
409             return UNSUPPORTED_MEDIA_TYPE;
410         case 416:
411             return REQUESTED_RANGE_NOT_SATISFIABLE;
412         case 417:
413             return EXPECTATION_FAILED;
414         case 421:
415             return MISDIRECTED_REQUEST;
416         case 422:
417             return UNPROCESSABLE_ENTITY;
418         case 423:
419             return LOCKED;
420         case 424:
421             return FAILED_DEPENDENCY;
422         case 425:
423             return UNORDERED_COLLECTION;
424         case 426:
425             return UPGRADE_REQUIRED;
426         case 428:
427             return PRECONDITION_REQUIRED;
428         case 429:
429             return TOO_MANY_REQUESTS;
430         case 431:
431             return REQUEST_HEADER_FIELDS_TOO_LARGE;
432         case 500:
433             return INTERNAL_SERVER_ERROR;
434         case 501:
435             return NOT_IMPLEMENTED;
436         case 502:
437             return BAD_GATEWAY;
438         case 503:
439             return SERVICE_UNAVAILABLE;
440         case 504:
441             return GATEWAY_TIMEOUT;
442         case 505:
443             return HTTP_VERSION_NOT_SUPPORTED;
444         case 506:
445             return VARIANT_ALSO_NEGOTIATES;
446         case 507:
447             return INSUFFICIENT_STORAGE;
448         case 510:
449             return NOT_EXTENDED;
450         case 511:
451             return NETWORK_AUTHENTICATION_REQUIRED;
452         }
453         return null;
454     }
455 
456     /**
457      * Returns the {@link HttpResponseStatus} represented by the specified {@code code} and {@code reasonPhrase}.
458      * If the specified code is a standard HTTP status {@code code} and {@code reasonPhrase}, a cached instance
459      * will be returned. Otherwise, a new instance will be returned.
460      * @param code The response code value.
461      * @param reasonPhrase The response code reason phrase.
462      * @return the {@link HttpResponseStatus} represented by the specified {@code code} and {@code reasonPhrase}.
463      */
464     public static HttpResponseStatus valueOf(int code, String reasonPhrase) {
465         HttpResponseStatus responseStatus = valueOf0(code);
466         return responseStatus != null && responseStatus.reasonPhrase().contentEquals(reasonPhrase) ? responseStatus :
467                 new HttpResponseStatus(code, reasonPhrase);
468     }
469 
470     /**
471      * Parses the specified HTTP status line into a {@link HttpResponseStatus}. The expected formats of the line are:
472      * <ul>
473      * <li>{@code statusCode} (e.g. 200)</li>
474      * <li>{@code statusCode} {@code reasonPhrase} (e.g. 404 Not Found)</li>
475      * </ul>
476      *
477      * @throws IllegalArgumentException if the specified status line is malformed
478      */
479     public static HttpResponseStatus parseLine(CharSequence line) {
480         return (line instanceof AsciiString) ? parseLine((AsciiString) line) : parseLine(line.toString());
481     }
482 
483     /**
484      * Parses the specified HTTP status line into a {@link HttpResponseStatus}. The expected formats of the line are:
485      * <ul>
486      * <li>{@code statusCode} (e.g. 200)</li>
487      * <li>{@code statusCode} {@code reasonPhrase} (e.g. 404 Not Found)</li>
488      * </ul>
489      *
490      * @throws IllegalArgumentException if the specified status line is malformed
491      */
492     public static HttpResponseStatus parseLine(String line) {
493         try {
494             int space = line.indexOf(' ');
495             return space == -1 ? valueOf(parseInt(line)) :
496                     valueOf(parseInt(line.substring(0, space)), line.substring(space + 1));
497         } catch (Exception e) {
498             throw new IllegalArgumentException("malformed status line: " + line, e);
499         }
500     }
501 
502     /**
503      * Parses the specified HTTP status line into a {@link HttpResponseStatus}. The expected formats of the line are:
504      * <ul>
505      * <li>{@code statusCode} (e.g. 200)</li>
506      * <li>{@code statusCode} {@code reasonPhrase} (e.g. 404 Not Found)</li>
507      * </ul>
508      *
509      * @throws IllegalArgumentException if the specified status line is malformed
510      */
511     public static HttpResponseStatus parseLine(AsciiString line) {
512         try {
513             int space = line.forEachByte(FIND_ASCII_SPACE);
514             return space == -1 ? valueOf(line.parseInt()) : valueOf(line.parseInt(0, space), line.toString(space + 1));
515         } catch (Exception e) {
516             throw new IllegalArgumentException("malformed status line: " + line, e);
517         }
518     }
519 
520     private final int code;
521     private final AsciiString codeAsText;
522     private HttpStatusClass codeClass;
523 
524     private final String reasonPhrase;
525     private final byte[] bytes;
526 
527     /**
528      * Creates a new instance with the specified {@code code} and the auto-generated default reason phrase.
529      */
530     private HttpResponseStatus(int code) {
531         this(code, HttpStatusClass.valueOf(code).defaultReasonPhrase() + " (" + code + ')', false);
532     }
533 
534     /**
535      * Creates a new instance with the specified {@code code} and its {@code reasonPhrase}.
536      */
537     public HttpResponseStatus(int code, String reasonPhrase) {
538         this(code, reasonPhrase, false);
539     }
540 
541     private HttpResponseStatus(int code, String reasonPhrase, boolean bytes) {
542         checkPositiveOrZero(code, "code");
543 
544         requireNonNull(reasonPhrase, "reasonPhrase");
545 
546         for (int i = 0; i < reasonPhrase.length(); i ++) {
547             char c = reasonPhrase.charAt(i);
548             // Check prohibited characters.
549             switch (c) {
550                 case '\n': case '\r':
551                     throw new IllegalArgumentException(
552                             "reasonPhrase contains one of the following prohibited characters: " +
553                                     "\\r\\n: " + reasonPhrase);
554             }
555         }
556 
557         this.code = code;
558         String codeString = Integer.toString(code);
559         codeAsText = new AsciiString(codeString);
560         this.reasonPhrase = reasonPhrase;
561         if (bytes) {
562             this.bytes = (codeString + ' ' + reasonPhrase).getBytes(CharsetUtil.US_ASCII);
563         } else {
564             this.bytes = null;
565         }
566     }
567 
568     /**
569      * Returns the code of this {@link HttpResponseStatus}.
570      */
571     public int code() {
572         return code;
573     }
574 
575     /**
576      * Returns the status code as {@link AsciiString}.
577      */
578     public AsciiString codeAsText() {
579         return codeAsText;
580     }
581 
582     /**
583      * Returns the reason phrase of this {@link HttpResponseStatus}.
584      */
585     public String reasonPhrase() {
586         return reasonPhrase;
587     }
588 
589     /**
590      * Returns the class of this {@link HttpResponseStatus}
591      */
592     public HttpStatusClass codeClass() {
593         HttpStatusClass type = this.codeClass;
594         if (type == null) {
595             this.codeClass = type = HttpStatusClass.valueOf(code);
596         }
597         return type;
598     }
599 
600     @Override
601     public int hashCode() {
602         return code();
603     }
604 
605     /**
606      * Equality of {@link HttpResponseStatus} only depends on {@link #code()}. The
607      * reason phrase is not considered for equality.
608      */
609     @Override
610     public boolean equals(Object o) {
611         if (!(o instanceof HttpResponseStatus)) {
612             return false;
613         }
614 
615         return code() == ((HttpResponseStatus) o).code();
616     }
617 
618     /**
619      * Equality of {@link HttpResponseStatus} only depends on {@link #code()}. The
620      * reason phrase is not considered for equality.
621      */
622     @Override
623     public int compareTo(HttpResponseStatus o) {
624         return code() - o.code();
625     }
626 
627     @Override
628     public String toString() {
629         return new StringBuilder(reasonPhrase.length() + 4)
630             .append(codeAsText)
631             .append(' ')
632             .append(reasonPhrase)
633             .toString();
634     }
635 
636     void encode(Buffer buf) {
637         if (bytes == null) {
638             buf.writeCharSequence(codeAsText, CharsetUtil.US_ASCII);
639             buf.writeByte(SP);
640             buf.writeCharSequence(reasonPhrase, CharsetUtil.US_ASCII);
641         } else {
642             buf.writeBytes(bytes);
643         }
644     }
645 }