1#--
2# httpstatus.rb -- HTTPStatus Class
3#
4# Author: IPR -- Internet Programming with Ruby -- writers
5# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
6# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
7# reserved.
8#
9# $IPR: httpstatus.rb,v 1.11 2003/03/24 20:18:55 gotoyuzo Exp $
10
11module WEBrick
12
13  ##
14  # This module is used to manager HTTP status codes.
15  #
16  # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html for more
17  # information.
18  module HTTPStatus
19
20    ##
21    # Root of the HTTP status class hierarchy
22    class Status < StandardError
23      def initialize(*args) # :nodoc:
24        args[0] = AccessLog.escape(args[0]) unless args.empty?
25        super(*args)
26      end
27      class << self
28        attr_reader :code, :reason_phrase # :nodoc:
29      end
30
31      # Returns the HTTP status code
32      def code() self::class::code end
33
34      # Returns the HTTP status description
35      def reason_phrase() self::class::reason_phrase end
36
37      alias to_i code # :nodoc:
38    end
39
40    # Root of the HTTP info statuses
41    class Info        < Status; end
42    # Root of the HTTP sucess statuses
43    class Success     < Status; end
44    # Root of the HTTP redirect statuses
45    class Redirect    < Status; end
46    # Root of the HTTP error statuses
47    class Error       < Status; end
48    # Root of the HTTP client error statuses
49    class ClientError < Error; end
50    # Root of the HTTP server error statuses
51    class ServerError < Error; end
52
53    class EOFError < StandardError; end
54
55    # HTTP status codes and descriptions
56    StatusMessage = { # :nodoc:
57      100 => 'Continue',
58      101 => 'Switching Protocols',
59      200 => 'OK',
60      201 => 'Created',
61      202 => 'Accepted',
62      203 => 'Non-Authoritative Information',
63      204 => 'No Content',
64      205 => 'Reset Content',
65      206 => 'Partial Content',
66      207 => 'Multi-Status',
67      300 => 'Multiple Choices',
68      301 => 'Moved Permanently',
69      302 => 'Found',
70      303 => 'See Other',
71      304 => 'Not Modified',
72      305 => 'Use Proxy',
73      307 => 'Temporary Redirect',
74      400 => 'Bad Request',
75      401 => 'Unauthorized',
76      402 => 'Payment Required',
77      403 => 'Forbidden',
78      404 => 'Not Found',
79      405 => 'Method Not Allowed',
80      406 => 'Not Acceptable',
81      407 => 'Proxy Authentication Required',
82      408 => 'Request Timeout',
83      409 => 'Conflict',
84      410 => 'Gone',
85      411 => 'Length Required',
86      412 => 'Precondition Failed',
87      413 => 'Request Entity Too Large',
88      414 => 'Request-URI Too Large',
89      415 => 'Unsupported Media Type',
90      416 => 'Request Range Not Satisfiable',
91      417 => 'Expectation Failed',
92      422 => 'Unprocessable Entity',
93      423 => 'Locked',
94      424 => 'Failed Dependency',
95      426 => 'Upgrade Required',
96      428 => 'Precondition Required',
97      429 => 'Too Many Requests',
98      431 => 'Request Header Fields Too Large',
99      500 => 'Internal Server Error',
100      501 => 'Not Implemented',
101      502 => 'Bad Gateway',
102      503 => 'Service Unavailable',
103      504 => 'Gateway Timeout',
104      505 => 'HTTP Version Not Supported',
105      507 => 'Insufficient Storage',
106      511 => 'Network Authentication Required',
107    }
108
109    # Maps a status code to the corresponding Status class
110    CodeToError = {} # :nodoc:
111
112    # Creates a status or error class for each status code and
113    # populates the CodeToError map.
114    StatusMessage.each{|code, message|
115      message.freeze
116      var_name = message.gsub(/[ \-]/,'_').upcase
117      err_name = message.gsub(/[ \-]/,'')
118
119      case code
120      when 100...200; parent = Info
121      when 200...300; parent = Success
122      when 300...400; parent = Redirect
123      when 400...500; parent = ClientError
124      when 500...600; parent = ServerError
125      end
126
127      const_set("RC_#{var_name}", code)
128      err_class = Class.new(parent)
129      err_class.instance_variable_set(:@code, code)
130      err_class.instance_variable_set(:@reason_phrase, message)
131      const_set(err_name, err_class)
132      CodeToError[code] = err_class
133    }
134
135    ##
136    # Returns the description corresponding to the HTTP status +code+
137    #
138    #   WEBrick::HTTPStatus.reason_phrase 404
139    #   => "Not Found"
140    def reason_phrase(code)
141      StatusMessage[code.to_i]
142    end
143
144    ##
145    # Is +code+ an informational status?
146    def info?(code)
147      code.to_i >= 100 and code.to_i < 200
148    end
149
150    ##
151    # Is +code+ a successful status?
152    def success?(code)
153      code.to_i >= 200 and code.to_i < 300
154    end
155
156    ##
157    # Is +code+ a redirection status?
158    def redirect?(code)
159      code.to_i >= 300 and code.to_i < 400
160    end
161
162    ##
163    # Is +code+ an error status?
164    def error?(code)
165      code.to_i >= 400 and code.to_i < 600
166    end
167
168    ##
169    # Is +code+ a client error status?
170    def client_error?(code)
171      code.to_i >= 400 and code.to_i < 500
172    end
173
174    ##
175    # Is +code+ a server error status?
176    def server_error?(code)
177      code.to_i >= 500 and code.to_i < 600
178    end
179
180    ##
181    # Returns the status class corresponding to +code+
182    #
183    #   WEBrick::HTTPStatus[302]
184    #   => WEBrick::HTTPStatus::NotFound
185    #
186    def self.[](code)
187      CodeToError[code]
188    end
189
190    module_function :reason_phrase
191    module_function :info?, :success?, :redirect?, :error?
192    module_function :client_error?, :server_error?
193  end
194end
195