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 import io.netty.util.internal.ObjectUtil;
23
24 import static io.netty.handler.codec.http.HttpConstants.SP;
25 import static io.netty.util.ByteProcessor.FIND_ASCII_SPACE;
26 import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
27 import static java.lang.Integer.parseInt;
28
29 /**
30  * The response code and its description of HTTP or its derived protocols, such as
31  * <a href="http://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol">RTSP</a> and
32  * <a href="http://en.wikipedia.org/wiki/Internet_Content_Adaptation_Protocol">ICAP</a>.
33  */

34 public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
35
36     /**
37      * 100 Continue
38      */

39     public static final HttpResponseStatus CONTINUE = newStatus(100, "Continue");
40
41     /**
42      * 101 Switching Protocols
43      */

44     public static final HttpResponseStatus SWITCHING_PROTOCOLS = newStatus(101, "Switching Protocols");
45
46     /**
47      * 102 Processing (WebDAV, RFC2518)
48      */

49     public static final HttpResponseStatus PROCESSING = newStatus(102, "Processing");
50
51     /**
52      * 200 OK
53      */

54     public static final HttpResponseStatus OK = newStatus(200, "OK");
55
56     /**
57      * 201 Created
58      */

59     public static final HttpResponseStatus CREATED = newStatus(201, "Created");
60
61     /**
62      * 202 Accepted
63      */

64     public static final HttpResponseStatus ACCEPTED = newStatus(202, "Accepted");
65
66     /**
67      * 203 Non-Authoritative Information (since HTTP/1.1)
68      */

69     public static final HttpResponseStatus NON_AUTHORITATIVE_INFORMATION =
70             newStatus(203, "Non-Authoritative Information");
71
72     /**
73      * 204 No Content
74      */

75     public static final HttpResponseStatus NO_CONTENT = newStatus(204, "No Content");
76
77     /**
78      * 205 Reset Content
79      */

80     public static final HttpResponseStatus RESET_CONTENT = newStatus(205, "Reset Content");
81
82     /**
83      * 206 Partial Content
84      */

85     public static final HttpResponseStatus PARTIAL_CONTENT = newStatus(206, "Partial Content");
86
87     /**
88      * 207 Multi-Status (WebDAV, RFC2518)
89      */

90     public static final HttpResponseStatus MULTI_STATUS = newStatus(207, "Multi-Status");
91
92     /**
93      * 300 Multiple Choices
94      */

95     public static final HttpResponseStatus MULTIPLE_CHOICES = newStatus(300, "Multiple Choices");
96
97     /**
98      * 301 Moved Permanently
99      */

100     public static final HttpResponseStatus MOVED_PERMANENTLY = newStatus(301, "Moved Permanently");
101
102     /**
103      * 302 Found
104      */

105     public static final HttpResponseStatus FOUND = newStatus(302, "Found");
106
107     /**
108      * 303 See Other (since HTTP/1.1)
109      */

110     public static final HttpResponseStatus SEE_OTHER = newStatus(303, "See Other");
111
112     /**
113      * 304 Not Modified
114      */

115     public static final HttpResponseStatus NOT_MODIFIED = newStatus(304, "Not Modified");
116
117     /**
118      * 305 Use Proxy (since HTTP/1.1)
119      */

120     public static final HttpResponseStatus USE_PROXY = newStatus(305, "Use Proxy");
121
122     /**
123      * 307 Temporary Redirect (since HTTP/1.1)
124      */

125     public static final HttpResponseStatus TEMPORARY_REDIRECT = newStatus(307, "Temporary Redirect");
126
127     /**
128      * 308 Permanent Redirect (RFC7538)
129      */

130     public static final HttpResponseStatus PERMANENT_REDIRECT = newStatus(308, "Permanent Redirect");
131
132     /**
133      * 400 Bad Request
134      */

135     public static final HttpResponseStatus BAD_REQUEST = newStatus(400, "Bad Request");
136
137     /**
138      * 401 Unauthorized
139      */

140     public static final HttpResponseStatus UNAUTHORIZED = newStatus(401, "Unauthorized");
141
142     /**
143      * 402 Payment Required
144      */

145     public static final HttpResponseStatus PAYMENT_REQUIRED = newStatus(402, "Payment Required");
146
147     /**
148      * 403 Forbidden
149      */

150     public static final HttpResponseStatus FORBIDDEN = newStatus(403, "Forbidden");
151
152     /**
153      * 404 Not Found
154      */

155     public static final HttpResponseStatus NOT_FOUND = newStatus(404, "Not Found");
156
157     /**
158      * 405 Method Not Allowed
159      */

160     public static final HttpResponseStatus METHOD_NOT_ALLOWED = newStatus(405, "Method Not Allowed");
161
162     /**
163      * 406 Not Acceptable
164      */

165     public static final HttpResponseStatus NOT_ACCEPTABLE = newStatus(406, "Not Acceptable");
166
167     /**
168      * 407 Proxy Authentication Required
169      */

170     public static final HttpResponseStatus PROXY_AUTHENTICATION_REQUIRED =
171             newStatus(407, "Proxy Authentication Required");
172
173     /**
174      * 408 Request Timeout
175      */

176     public static final HttpResponseStatus REQUEST_TIMEOUT = newStatus(408, "Request Timeout");
177
178     /**
179      * 409 Conflict
180      */

181     public static final HttpResponseStatus CONFLICT = newStatus(409, "Conflict");
182
183     /**
184      * 410 Gone
185      */

186     public static final HttpResponseStatus GONE = newStatus(410, "Gone");
187
188     /**
189      * 411 Length Required
190      */

191     public static final HttpResponseStatus LENGTH_REQUIRED = newStatus(411, "Length Required");
192
193     /**
194      * 412 Precondition Failed
195      */

196     public static final HttpResponseStatus PRECONDITION_FAILED = newStatus(412, "Precondition Failed");
197
198     /**
199      * 413 Request Entity Too Large
200      */

201     public static final HttpResponseStatus REQUEST_ENTITY_TOO_LARGE =
202             newStatus(413, "Request Entity Too Large");
203
204     /**
205      * 414 Request-URI Too Long
206      */

207     public static final HttpResponseStatus REQUEST_URI_TOO_LONG = newStatus(414, "Request-URI Too Long");
208
209     /**
210      * 415 Unsupported Media Type
211      */

212     public static final HttpResponseStatus UNSUPPORTED_MEDIA_TYPE = newStatus(415, "Unsupported Media Type");
213
214     /**
215      * 416 Requested Range Not Satisfiable
216      */

217     public static final HttpResponseStatus REQUESTED_RANGE_NOT_SATISFIABLE =
218             newStatus(416, "Requested Range Not Satisfiable");
219
220     /**
221      * 417 Expectation Failed
222      */

223     public static final HttpResponseStatus EXPECTATION_FAILED = newStatus(417, "Expectation Failed");
224
225     /**
226      * 421 Misdirected Request
227      *
228      * @see <a href="https://tools.ietf.org/html/rfc7540#section-9.1.2">421 (Misdirected Request) Status Code</a>
229      */

230     public static final HttpResponseStatus MISDIRECTED_REQUEST = newStatus(421, "Misdirected Request");
231
232     /**
233      * 422 Unprocessable Entity (WebDAV, RFC4918)
234      */

235     public static final HttpResponseStatus UNPROCESSABLE_ENTITY = newStatus(422, "Unprocessable Entity");
236
237     /**
238      * 423 Locked (WebDAV, RFC4918)
239      */

240     public static final HttpResponseStatus LOCKED = newStatus(423, "Locked");
241
242     /**
243      * 424 Failed Dependency (WebDAV, RFC4918)
244      */

245     public static final HttpResponseStatus FAILED_DEPENDENCY = newStatus(424, "Failed Dependency");
246
247     /**
248      * 425 Unordered Collection (WebDAV, RFC3648)
249      */

250     public static final HttpResponseStatus UNORDERED_COLLECTION = newStatus(425, "Unordered Collection");
251
252     /**
253      * 426 Upgrade Required (RFC2817)
254      */

255     public static final HttpResponseStatus UPGRADE_REQUIRED = newStatus(426, "Upgrade Required");
256
257     /**
258      * 428 Precondition Required (RFC6585)
259      */

260     public static final HttpResponseStatus PRECONDITION_REQUIRED = newStatus(428, "Precondition Required");
261
262     /**
263      * 429 Too Many Requests (RFC6585)
264      */

265     public static final HttpResponseStatus TOO_MANY_REQUESTS = newStatus(429, "Too Many Requests");
266
267     /**
268      * 431 Request Header Fields Too Large (RFC6585)
269      */

270     public static final HttpResponseStatus REQUEST_HEADER_FIELDS_TOO_LARGE =
271             newStatus(431, "Request Header Fields Too Large");
272
273     /**
274      * 500 Internal Server Error
275      */

276     public static final HttpResponseStatus INTERNAL_SERVER_ERROR = newStatus(500, "Internal Server Error");
277
278     /**
279      * 501 Not Implemented
280      */

281     public static final HttpResponseStatus NOT_IMPLEMENTED = newStatus(501, "Not Implemented");
282
283     /**
284      * 502 Bad Gateway
285      */

286     public static final HttpResponseStatus BAD_GATEWAY = newStatus(502, "Bad Gateway");
287
288     /**
289      * 503 Service Unavailable
290      */

291     public static final HttpResponseStatus SERVICE_UNAVAILABLE = newStatus(503, "Service Unavailable");
292
293     /**
294      * 504 Gateway Timeout
295      */

296     public static final HttpResponseStatus GATEWAY_TIMEOUT = newStatus(504, "Gateway Timeout");
297
298     /**
299      * 505 HTTP Version Not Supported
300      */

301     public static final HttpResponseStatus HTTP_VERSION_NOT_SUPPORTED =
302             newStatus(505, "HTTP Version Not Supported");
303
304     /**
305      * 506 Variant Also Negotiates (RFC2295)
306      */

307     public static final HttpResponseStatus VARIANT_ALSO_NEGOTIATES = newStatus(506, "Variant Also Negotiates");
308
309     /**
310      * 507 Insufficient Storage (WebDAV, RFC4918)
311      */

312     public static final HttpResponseStatus INSUFFICIENT_STORAGE = newStatus(507, "Insufficient Storage");
313
314     /**
315      * 510 Not Extended (RFC2774)
316      */

317     public static final HttpResponseStatus NOT_EXTENDED = newStatus(510, "Not Extended");
318
319     /**
320      * 511 Network Authentication Required (RFC6585)
321      */

322     public static final HttpResponseStatus NETWORK_AUTHENTICATION_REQUIRED =
323             newStatus(511, "Network Authentication Required");
324
325     private static HttpResponseStatus newStatus(int statusCode, String reasonPhrase) {
326         return new HttpResponseStatus(statusCode, reasonPhrase, true);
327     }
328
329     /**
330      * Returns the {@link HttpResponseStatus} represented by the specified code.
331      * If the specified code is a standard HTTP status code, a cached instance
332      * will be returned.  Otherwise, a new instance will be returned.
333      */

334     public static HttpResponseStatus valueOf(int code) {
335         HttpResponseStatus status = valueOf0(code);
336         return status != null ? status : new HttpResponseStatus(code);
337     }
338
339     private static HttpResponseStatus valueOf0(int code) {
340         switch (code) {
341         case 100:
342             return CONTINUE;
343         case 101:
344             return SWITCHING_PROTOCOLS;
345         case 102:
346             return PROCESSING;
347         case 200:
348             return OK;
349         case 201:
350             return CREATED;
351         case 202:
352             return ACCEPTED;
353         case 203:
354             return NON_AUTHORITATIVE_INFORMATION;
355         case 204:
356             return NO_CONTENT;
357         case 205:
358             return RESET_CONTENT;
359         case 206:
360             return PARTIAL_CONTENT;
361         case 207:
362             return MULTI_STATUS;
363         case 300:
364             return MULTIPLE_CHOICES;
365         case 301:
366             return MOVED_PERMANENTLY;
367         case 302:
368             return FOUND;
369         case 303:
370             return SEE_OTHER;
371         case 304:
372             return NOT_MODIFIED;
373         case 305:
374             return USE_PROXY;
375         case 307:
376             return TEMPORARY_REDIRECT;
377         case 308:
378             return PERMANENT_REDIRECT;
379         case 400:
380             return BAD_REQUEST;
381         case 401:
382             return UNAUTHORIZED;
383         case 402:
384             return PAYMENT_REQUIRED;
385         case 403:
386             return FORBIDDEN;
387         case 404:
388             return NOT_FOUND;
389         case 405:
390             return METHOD_NOT_ALLOWED;
391         case 406:
392             return NOT_ACCEPTABLE;
393         case 407:
394             return PROXY_AUTHENTICATION_REQUIRED;
395         case 408:
396             return REQUEST_TIMEOUT;
397         case 409:
398             return CONFLICT;
399         case 410:
400             return GONE;
401         case 411:
402             return LENGTH_REQUIRED;
403         case 412:
404             return PRECONDITION_FAILED;
405         case 413:
406             return REQUEST_ENTITY_TOO_LARGE;
407         case 414:
408             return REQUEST_URI_TOO_LONG;
409         case 415:
410             return UNSUPPORTED_MEDIA_TYPE;
411         case 416:
412             return REQUESTED_RANGE_NOT_SATISFIABLE;
413         case 417:
414             return EXPECTATION_FAILED;
415         case 421:
416             return MISDIRECTED_REQUEST;
417         case 422:
418             return UNPROCESSABLE_ENTITY;
419         case 423:
420             return LOCKED;
421         case 424:
422             return FAILED_DEPENDENCY;
423         case 425:
424             return UNORDERED_COLLECTION;
425         case 426:
426             return UPGRADE_REQUIRED;
427         case 428:
428             return PRECONDITION_REQUIRED;
429         case 429:
430             return TOO_MANY_REQUESTS;
431         case 431:
432             return REQUEST_HEADER_FIELDS_TOO_LARGE;
433         case 500:
434             return INTERNAL_SERVER_ERROR;
435         case 501:
436             return NOT_IMPLEMENTED;
437         case 502:
438             return BAD_GATEWAY;
439         case 503:
440             return SERVICE_UNAVAILABLE;
441         case 504:
442             return GATEWAY_TIMEOUT;
443         case 505:
444             return HTTP_VERSION_NOT_SUPPORTED;
445         case 506:
446             return VARIANT_ALSO_NEGOTIATES;
447         case 507:
448             return INSUFFICIENT_STORAGE;
449         case 510:
450             return NOT_EXTENDED;
451         case 511:
452             return NETWORK_AUTHENTICATION_REQUIRED;
453         }
454         return null;
455     }
456
457     /**
458      * Returns the {@link HttpResponseStatus} represented by the specified {@code code} and {@code reasonPhrase}.
459      * If the specified code is a standard HTTP status {@code code} and {@code reasonPhrase}, a cached instance
460      * will be returned. Otherwise, a new instance will be returned.
461      * @param code The response code value.
462      * @param reasonPhrase The response code reason phrase.
463      * @return the {@link HttpResponseStatus} represented by the specified {@code code} and {@code reasonPhrase}.
464      */

465     public static HttpResponseStatus valueOf(int code, String reasonPhrase) {
466         HttpResponseStatus responseStatus = valueOf0(code);
467         return responseStatus != null && responseStatus.reasonPhrase().contentEquals(reasonPhrase) ? responseStatus :
468                 new HttpResponseStatus(code, reasonPhrase);
469     }
470
471     /**
472      * Parses the specified HTTP status line into a {@link HttpResponseStatus}. The expected formats of the line are:
473      * <ul>
474      * <li>{@code statusCode} (e.g. 200)</li>
475      * <li>{@code statusCode} {@code reasonPhrase} (e.g. 404 Not Found)</li>
476      * </ul>
477      *
478      * @throws IllegalArgumentException if the specified status line is malformed
479      */

480     public static HttpResponseStatus parseLine(CharSequence line) {
481         return (line instanceof AsciiString) ? parseLine((AsciiString) line) : parseLine(line.toString());
482     }
483
484     /**
485      * Parses the specified HTTP status line into a {@link HttpResponseStatus}. The expected formats of the line are:
486      * <ul>
487      * <li>{@code statusCode} (e.g. 200)</li>
488      * <li>{@code statusCode} {@code reasonPhrase} (e.g. 404 Not Found)</li>
489      * </ul>
490      *
491      * @throws IllegalArgumentException if the specified status line is malformed
492      */

493     public static HttpResponseStatus parseLine(String line) {
494         try {
495             int space = line.indexOf(' ');
496             return space == -1 ? valueOf(parseInt(line)) :
497                     valueOf(parseInt(line.substring(0, space)), line.substring(space + 1));
498         } catch (Exception e) {
499             throw new IllegalArgumentException("malformed status line: " + line, e);
500         }
501     }
502
503     /**
504      * Parses the specified HTTP status line into a {@link HttpResponseStatus}. The expected formats of the line are:
505      * <ul>
506      * <li>{@code statusCode} (e.g. 200)</li>
507      * <li>{@code statusCode} {@code reasonPhrase} (e.g. 404 Not Found)</li>
508      * </ul>
509      *
510      * @throws IllegalArgumentException if the specified status line is malformed
511      */

512     public static HttpResponseStatus parseLine(AsciiString line) {
513         try {
514             int space = line.forEachByte(FIND_ASCII_SPACE);
515             return space == -1 ? valueOf(line.parseInt()) : valueOf(line.parseInt(0, space), line.toString(space + 1));
516         } catch (Exception e) {
517             throw new IllegalArgumentException("malformed status line: " + line, e);
518         }
519     }
520
521     private final int code;
522     private final AsciiString codeAsText;
523     private HttpStatusClass codeClass;
524
525     private final String reasonPhrase;
526     private final byte[] bytes;
527
528     /**
529      * Creates a new instance with the specified {@code code} and the auto-generated default reason phrase.
530      */

531     private HttpResponseStatus(int code) {
532         this(code, HttpStatusClass.valueOf(code).defaultReasonPhrase() + " (" + code + ')', false);
533     }
534
535     /**
536      * Creates a new instance with the specified {@code code} and its {@code reasonPhrase}.
537      */

538     public HttpResponseStatus(int code, String reasonPhrase) {
539         this(code, reasonPhrase, false);
540     }
541
542     private HttpResponseStatus(int code, String reasonPhrase, boolean bytes) {
543         checkPositiveOrZero(code, "code");
544         ObjectUtil.checkNotNull(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(ByteBuf buf) {
637         if (bytes == null) {
638             ByteBufUtil.copy(codeAsText, buf);
639             buf.writeByte(SP);
640             buf.writeCharSequence(reasonPhrase, CharsetUtil.US_ASCII);
641         } else {
642             buf.writeBytes(bytes);
643         }
644     }
645 }
646