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