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