http 0.7.4 → 0.8.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/.rubocop.yml +5 -2
- data/CHANGES.md +24 -7
- data/CONTRIBUTING.md +25 -0
- data/Gemfile +24 -22
- data/Guardfile +2 -2
- data/README.md +34 -4
- data/Rakefile +7 -7
- data/examples/parallel_requests_with_celluloid.rb +2 -2
- data/http.gemspec +12 -12
- data/lib/http.rb +11 -10
- data/lib/http/cache.rb +146 -0
- data/lib/http/cache/headers.rb +100 -0
- data/lib/http/cache/null_cache.rb +13 -0
- data/lib/http/chainable.rb +14 -3
- data/lib/http/client.rb +64 -80
- data/lib/http/connection.rb +139 -0
- data/lib/http/content_type.rb +2 -2
- data/lib/http/errors.rb +7 -1
- data/lib/http/headers.rb +21 -8
- data/lib/http/headers/mixin.rb +1 -1
- data/lib/http/mime_type.rb +2 -2
- data/lib/http/mime_type/adapter.rb +2 -2
- data/lib/http/mime_type/json.rb +4 -4
- data/lib/http/options.rb +65 -74
- data/lib/http/redirector.rb +3 -3
- data/lib/http/request.rb +20 -13
- data/lib/http/request/caching.rb +95 -0
- data/lib/http/request/writer.rb +5 -5
- data/lib/http/response.rb +15 -9
- data/lib/http/response/body.rb +21 -8
- data/lib/http/response/caching.rb +142 -0
- data/lib/http/response/io_body.rb +63 -0
- data/lib/http/response/parser.rb +1 -1
- data/lib/http/response/status.rb +4 -12
- data/lib/http/response/status/reasons.rb +53 -53
- data/lib/http/response/string_body.rb +53 -0
- data/lib/http/version.rb +1 -1
- data/spec/lib/http/cache/headers_spec.rb +77 -0
- data/spec/lib/http/cache_spec.rb +182 -0
- data/spec/lib/http/client_spec.rb +123 -95
- data/spec/lib/http/content_type_spec.rb +25 -25
- data/spec/lib/http/headers/mixin_spec.rb +8 -8
- data/spec/lib/http/headers_spec.rb +213 -173
- data/spec/lib/http/options/body_spec.rb +5 -5
- data/spec/lib/http/options/form_spec.rb +3 -3
- data/spec/lib/http/options/headers_spec.rb +7 -7
- data/spec/lib/http/options/json_spec.rb +3 -3
- data/spec/lib/http/options/merge_spec.rb +26 -22
- data/spec/lib/http/options/new_spec.rb +10 -10
- data/spec/lib/http/options/proxy_spec.rb +8 -8
- data/spec/lib/http/options_spec.rb +2 -2
- data/spec/lib/http/redirector_spec.rb +32 -32
- data/spec/lib/http/request/caching_spec.rb +133 -0
- data/spec/lib/http/request/writer_spec.rb +26 -26
- data/spec/lib/http/request_spec.rb +63 -58
- data/spec/lib/http/response/body_spec.rb +13 -13
- data/spec/lib/http/response/caching_spec.rb +201 -0
- data/spec/lib/http/response/io_body_spec.rb +35 -0
- data/spec/lib/http/response/status_spec.rb +25 -25
- data/spec/lib/http/response/string_body_spec.rb +35 -0
- data/spec/lib/http/response_spec.rb +64 -45
- data/spec/lib/http_spec.rb +103 -76
- data/spec/spec_helper.rb +10 -12
- data/spec/support/connection_reuse_shared.rb +100 -0
- data/spec/support/create_certs.rb +12 -12
- data/spec/support/dummy_server.rb +11 -11
- data/spec/support/dummy_server/servlet.rb +43 -31
- data/spec/support/proxy_server.rb +31 -25
- metadata +57 -8
- data/spec/support/example_server.rb +0 -30
- data/spec/support/example_server/servlet.rb +0 -102
data/lib/http/request/writer.rb
CHANGED
@@ -32,10 +32,10 @@ module HTTP
|
|
32
32
|
# Adds the headers to the header array for the given request body we are working
|
33
33
|
# with
|
34
34
|
def add_body_type_headers
|
35
|
-
if @body.is_a?(String) && !@headers[
|
35
|
+
if @body.is_a?(String) && !@headers["Content-Length"]
|
36
36
|
@request_header << "Content-Length: #{@body.bytesize}"
|
37
|
-
elsif @body.is_a?(Enumerable) &&
|
38
|
-
fail(RequestError,
|
37
|
+
elsif @body.is_a?(Enumerable) && "chunked" != @headers["Transfer-Encoding"]
|
38
|
+
fail(RequestError, "invalid transfer encoding")
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -64,11 +64,11 @@ module HTTP
|
|
64
64
|
@socket << chunk << CRLF
|
65
65
|
end
|
66
66
|
|
67
|
-
@socket <<
|
67
|
+
@socket << "0" << CRLF * 2
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
-
|
71
|
+
private
|
72
72
|
|
73
73
|
def validate_body_type!
|
74
74
|
return if VALID_BODY_TYPES.any? { |type| @body.is_a? type }
|
data/lib/http/response.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
require
|
1
|
+
require "forwardable"
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
3
|
+
require "http/headers"
|
4
|
+
require "http/content_type"
|
5
|
+
require "http/mime_type"
|
6
|
+
require "http/response/caching"
|
7
|
+
require "http/response/status"
|
8
|
+
require "time"
|
7
9
|
|
8
10
|
module HTTP
|
9
11
|
class Response
|
@@ -16,7 +18,7 @@ module HTTP
|
|
16
18
|
STATUS_CODES = Status::REASONS
|
17
19
|
|
18
20
|
# @deprecated Will be removed in 1.0.0
|
19
|
-
SYMBOL_TO_STATUS_CODE = Hash[STATUS_CODES.map { |k, v| [v.downcase.gsub(/\s|-/,
|
21
|
+
SYMBOL_TO_STATUS_CODE = Hash[STATUS_CODES.map { |k, v| [v.downcase.gsub(/\s|-/, "_").to_sym, k] }].freeze
|
20
22
|
|
21
23
|
# @return [Status]
|
22
24
|
attr_reader :status
|
@@ -29,7 +31,6 @@ module HTTP
|
|
29
31
|
|
30
32
|
def initialize(status, version, headers, body, uri = nil) # rubocop:disable ParameterLists
|
31
33
|
@version, @body, @uri = version, body, uri
|
32
|
-
|
33
34
|
@status = HTTP::Response::Status.new status
|
34
35
|
@headers = HTTP::Headers.coerce(headers || {})
|
35
36
|
end
|
@@ -73,7 +74,7 @@ module HTTP
|
|
73
74
|
#
|
74
75
|
# @return [HTTP::ContentType]
|
75
76
|
def content_type
|
76
|
-
@content_type ||= ContentType.parse headers[
|
77
|
+
@content_type ||= ContentType.parse headers["Content-Type"]
|
77
78
|
end
|
78
79
|
|
79
80
|
# MIME type of response (if any)
|
@@ -102,7 +103,12 @@ module HTTP
|
|
102
103
|
|
103
104
|
# Inspect a response
|
104
105
|
def inspect
|
105
|
-
"#<#{self.class}/#{@version} #{code} #{reason} #{headers.inspect}>"
|
106
|
+
"#<#{self.class}/#{@version} #{code} #{reason} #{headers.to_h.inspect}>"
|
107
|
+
end
|
108
|
+
|
109
|
+
# @return [HTTP::Response::Caching]
|
110
|
+
def caching
|
111
|
+
Caching.new self
|
106
112
|
end
|
107
113
|
end
|
108
114
|
end
|
data/lib/http/response/body.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "forwardable"
|
2
|
+
require "http/client"
|
3
3
|
|
4
4
|
module HTTP
|
5
5
|
class Response
|
@@ -10,13 +10,16 @@ module HTTP
|
|
10
10
|
def_delegator :to_s, :empty?
|
11
11
|
|
12
12
|
def initialize(client)
|
13
|
-
@client
|
14
|
-
@streaming
|
15
|
-
@contents
|
13
|
+
@client = client
|
14
|
+
@streaming = nil
|
15
|
+
@contents = nil
|
16
|
+
@active_seq = client.sequence_id
|
16
17
|
end
|
17
18
|
|
18
19
|
# (see HTTP::Client#readpartial)
|
19
20
|
def readpartial(*args)
|
21
|
+
check_sequence!
|
22
|
+
|
20
23
|
stream!
|
21
24
|
@client.readpartial(*args)
|
22
25
|
end
|
@@ -31,11 +34,13 @@ module HTTP
|
|
31
34
|
# @return [String] eagerly consume the entire body as a string
|
32
35
|
def to_s
|
33
36
|
return @contents if @contents
|
34
|
-
|
37
|
+
|
38
|
+
fail StateError, "body is being streamed" unless @streaming.nil?
|
39
|
+
check_sequence!
|
35
40
|
|
36
41
|
begin
|
37
42
|
@streaming = false
|
38
|
-
@contents =
|
43
|
+
@contents = ""
|
39
44
|
while (chunk = @client.readpartial)
|
40
45
|
@contents << chunk
|
41
46
|
end
|
@@ -48,9 +53,17 @@ module HTTP
|
|
48
53
|
end
|
49
54
|
alias_method :to_str, :to_s
|
50
55
|
|
56
|
+
def check_sequence!
|
57
|
+
return unless @active_seq != @client.sequence_id
|
58
|
+
|
59
|
+
fail StateError, "Sequence ID #{@active_seq} does not match #{@client.sequence_id}. You must read the entire request off."
|
60
|
+
end
|
61
|
+
|
62
|
+
private :check_sequence!
|
63
|
+
|
51
64
|
# Assert that the body is actively being streamed
|
52
65
|
def stream!
|
53
|
-
fail StateError,
|
66
|
+
fail StateError, "body has already been consumed" if @streaming == false
|
54
67
|
@streaming = true
|
55
68
|
end
|
56
69
|
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require "http/cache/headers"
|
2
|
+
require "http/response/string_body"
|
3
|
+
require "http/response/io_body"
|
4
|
+
|
5
|
+
module HTTP
|
6
|
+
class Response
|
7
|
+
# Decorator class for responses to provide convenience methods
|
8
|
+
# related to caching.
|
9
|
+
class Caching < DelegateClass(HTTP::Response)
|
10
|
+
CACHEABLE_RESPONSE_CODES = [200, 203, 300, 301, 410].freeze
|
11
|
+
|
12
|
+
def initialize(obj)
|
13
|
+
super
|
14
|
+
@requested_at = nil
|
15
|
+
@received_at = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [HTTP::Response::Caching]
|
19
|
+
def caching
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Boolean] true iff this response is stale
|
24
|
+
def stale?
|
25
|
+
expired? || cache_headers.must_revalidate?
|
26
|
+
end
|
27
|
+
|
28
|
+
# @returns [Boolean] true iff this response has expired
|
29
|
+
def expired?
|
30
|
+
current_age >= cache_headers.max_age
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Boolean] true iff this response is cacheable
|
34
|
+
#
|
35
|
+
# ---
|
36
|
+
# A Vary header field-value of "*" always fails to match and
|
37
|
+
# subsequent requests on that resource can only be properly
|
38
|
+
# interpreted by the
|
39
|
+
def cacheable?
|
40
|
+
@cacheable ||=
|
41
|
+
begin
|
42
|
+
CACHEABLE_RESPONSE_CODES.include?(code) \
|
43
|
+
&& !(cache_headers.vary_star? ||
|
44
|
+
cache_headers.no_store? ||
|
45
|
+
cache_headers.no_cache?)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Numeric] the current age (in seconds) of this response
|
50
|
+
#
|
51
|
+
# ---
|
52
|
+
# Algo from https://tools.ietf.org/html/rfc2616#section-13.2.3
|
53
|
+
def current_age
|
54
|
+
now = Time.now
|
55
|
+
age_value = headers.get("Age").map(&:to_i).max || 0
|
56
|
+
|
57
|
+
apparent_age = [0, received_at - server_response_time].max
|
58
|
+
corrected_received_age = [apparent_age, age_value].max
|
59
|
+
response_delay = [0, received_at - requested_at].max
|
60
|
+
corrected_initial_age = corrected_received_age + response_delay
|
61
|
+
resident_time = [0, now - received_at].max
|
62
|
+
|
63
|
+
corrected_initial_age + resident_time
|
64
|
+
end
|
65
|
+
|
66
|
+
# @return [Time] the time at which this response was requested
|
67
|
+
def requested_at
|
68
|
+
@requested_at ||= received_at
|
69
|
+
end
|
70
|
+
attr_writer :requested_at
|
71
|
+
|
72
|
+
# @return [Time] the time at which this response was received
|
73
|
+
def received_at
|
74
|
+
@received_at ||= Time.now
|
75
|
+
end
|
76
|
+
attr_writer :received_at
|
77
|
+
|
78
|
+
# Update self based on this response being revalidated by the
|
79
|
+
# server.
|
80
|
+
def validated!(validating_response)
|
81
|
+
headers.merge!(validating_response.headers)
|
82
|
+
self.requested_at = validating_response.requested_at
|
83
|
+
self.received_at = validating_response.received_at
|
84
|
+
end
|
85
|
+
|
86
|
+
# @return [HTTP::Cache::Headers] cache control headers helper object.
|
87
|
+
def cache_headers
|
88
|
+
@cache_headers ||= HTTP::Cache::Headers.new headers
|
89
|
+
end
|
90
|
+
|
91
|
+
def body
|
92
|
+
@body ||= if __getobj__.body.respond_to? :each
|
93
|
+
__getobj__.body
|
94
|
+
else
|
95
|
+
StringBody.new(__getobj__.body.to_s)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def body=(new_body)
|
100
|
+
@body = if new_body.respond_to?(:readpartial) && new_body.respond_to?(:read)
|
101
|
+
# IO-ish, probably a rack cache response body
|
102
|
+
IoBody.new(new_body)
|
103
|
+
|
104
|
+
elsif new_body.respond_to? :join
|
105
|
+
# probably an array of body parts (rack cache does this sometimes)
|
106
|
+
StringBody.new(new_body.join(""))
|
107
|
+
|
108
|
+
elsif new_body.respond_to? :readpartial
|
109
|
+
# normal body, just use it.
|
110
|
+
new_body
|
111
|
+
|
112
|
+
else
|
113
|
+
# backstop, just to_s it
|
114
|
+
StringBody.new(new_body.to_s)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def vary
|
119
|
+
headers.get("Vary").first
|
120
|
+
end
|
121
|
+
|
122
|
+
protected
|
123
|
+
|
124
|
+
# @return [Time] the time at which the server generated this response.
|
125
|
+
def server_response_time
|
126
|
+
headers.get("Date")
|
127
|
+
.map(&method(:to_time_or_epoch))
|
128
|
+
.max || begin
|
129
|
+
# set it if it is not already set
|
130
|
+
headers["Date"] = received_at.httpdate
|
131
|
+
received_at
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def to_time_or_epoch(t_str)
|
136
|
+
Time.httpdate(t_str)
|
137
|
+
rescue ArgumentError
|
138
|
+
Time.at(0)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module HTTP
|
2
|
+
class Response
|
3
|
+
# A Body class that wraps an IO, rather than a the client
|
4
|
+
# object.
|
5
|
+
class IoBody
|
6
|
+
include Enumerable
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
# @return [String,nil] the next `size` octets part of the
|
10
|
+
# body, or nil if whole body has already been read.
|
11
|
+
def readpartial(size = HTTP::Connection::BUFFER_SIZE)
|
12
|
+
stream!
|
13
|
+
return nil if stream.eof?
|
14
|
+
|
15
|
+
stream.readpartial(size)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Iterate over the body, allowing it to be enumerable
|
19
|
+
def each
|
20
|
+
while (part = readpartial)
|
21
|
+
yield part
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [String] eagerly consume the entire body as a string
|
26
|
+
def to_s
|
27
|
+
@contents ||= readall
|
28
|
+
end
|
29
|
+
alias_method :to_str, :to_s
|
30
|
+
|
31
|
+
def_delegator :to_s, :empty?
|
32
|
+
|
33
|
+
# Assert that the body is actively being streamed
|
34
|
+
def stream!
|
35
|
+
fail StateError, "body has already been consumed" if @streaming == false
|
36
|
+
@streaming = true
|
37
|
+
end
|
38
|
+
|
39
|
+
# Easier to interpret string inspect
|
40
|
+
def inspect
|
41
|
+
"#<#{self.class}:#{object_id.to_s(16)} @streaming=#{!!@streaming}>"
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def initialize(an_io)
|
47
|
+
@streaming = nil
|
48
|
+
@stream = an_io
|
49
|
+
end
|
50
|
+
|
51
|
+
attr_reader :contents, :stream
|
52
|
+
|
53
|
+
def readall
|
54
|
+
fail StateError, "body is being streamed" unless @streaming.nil?
|
55
|
+
|
56
|
+
@streaming = false
|
57
|
+
"".tap do |buf|
|
58
|
+
buf << stream.read until stream.eof?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/http/response/parser.rb
CHANGED
data/lib/http/response/status.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
require
|
1
|
+
require "delegate"
|
2
2
|
|
3
|
-
require
|
3
|
+
require "http/response/status/reasons"
|
4
4
|
|
5
5
|
module HTTP
|
6
6
|
class Response
|
@@ -22,7 +22,6 @@ module HTTP
|
|
22
22
|
when object.is_a?(String) then SYMBOL_CODES[symbolize object]
|
23
23
|
when object.is_a?(Symbol) then SYMBOL_CODES[object]
|
24
24
|
when object.is_a?(Numeric) then object.to_i
|
25
|
-
else nil
|
26
25
|
end
|
27
26
|
|
28
27
|
return new code if code
|
@@ -31,7 +30,7 @@ module HTTP
|
|
31
30
|
end
|
32
31
|
alias_method :[], :coerce
|
33
32
|
|
34
|
-
|
33
|
+
private
|
35
34
|
|
36
35
|
# Symbolizes given string
|
37
36
|
#
|
@@ -44,7 +43,7 @@ module HTTP
|
|
44
43
|
# @param [#to_s] str
|
45
44
|
# @return [Symbol]
|
46
45
|
def symbolize(str)
|
47
|
-
str.to_s.downcase.gsub(/-/,
|
46
|
+
str.to_s.downcase.gsub(/-/, " ").gsub(/[^a-z ]/, "").gsub(/\s+/, "_").to_sym
|
48
47
|
end
|
49
48
|
end
|
50
49
|
|
@@ -73,13 +72,6 @@ module HTTP
|
|
73
72
|
# @return [Fixnum] status code
|
74
73
|
attr_reader :code
|
75
74
|
|
76
|
-
if RUBY_VERSION < '1.9.0'
|
77
|
-
# @param [#to_i] code
|
78
|
-
def initialize(code)
|
79
|
-
super __setobj__ code
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
75
|
# @see REASONS
|
84
76
|
# @return [String, nil] status message
|
85
77
|
def reason
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "delegate"
|
2
2
|
|
3
3
|
module HTTP
|
4
4
|
class Response
|
@@ -13,59 +13,59 @@ module HTTP
|
|
13
13
|
#
|
14
14
|
# @return [Hash<Fixnum => String>]
|
15
15
|
REASONS = {
|
16
|
-
100 =>
|
17
|
-
101 =>
|
18
|
-
102 =>
|
19
|
-
200 =>
|
20
|
-
201 =>
|
21
|
-
202 =>
|
22
|
-
203 =>
|
23
|
-
204 =>
|
24
|
-
205 =>
|
25
|
-
206 =>
|
26
|
-
207 =>
|
27
|
-
226 =>
|
28
|
-
300 =>
|
29
|
-
301 =>
|
30
|
-
302 =>
|
31
|
-
303 =>
|
32
|
-
304 =>
|
33
|
-
305 =>
|
34
|
-
306 =>
|
35
|
-
307 =>
|
36
|
-
308 =>
|
37
|
-
400 =>
|
38
|
-
401 =>
|
39
|
-
402 =>
|
40
|
-
403 =>
|
41
|
-
404 =>
|
42
|
-
405 =>
|
43
|
-
406 =>
|
44
|
-
407 =>
|
45
|
-
408 =>
|
46
|
-
409 =>
|
47
|
-
410 =>
|
48
|
-
411 =>
|
49
|
-
412 =>
|
50
|
-
413 =>
|
51
|
-
414 =>
|
52
|
-
415 =>
|
53
|
-
416 =>
|
54
|
-
417 =>
|
16
|
+
100 => "Continue",
|
17
|
+
101 => "Switching Protocols",
|
18
|
+
102 => "Processing",
|
19
|
+
200 => "OK",
|
20
|
+
201 => "Created",
|
21
|
+
202 => "Accepted",
|
22
|
+
203 => "Non-Authoritative Information",
|
23
|
+
204 => "No Content",
|
24
|
+
205 => "Reset Content",
|
25
|
+
206 => "Partial Content",
|
26
|
+
207 => "Multi-Status",
|
27
|
+
226 => "IM Used",
|
28
|
+
300 => "Multiple Choices",
|
29
|
+
301 => "Moved Permanently",
|
30
|
+
302 => "Found",
|
31
|
+
303 => "See Other",
|
32
|
+
304 => "Not Modified",
|
33
|
+
305 => "Use Proxy",
|
34
|
+
306 => "Reserved",
|
35
|
+
307 => "Temporary Redirect",
|
36
|
+
308 => "Permanent Redirect",
|
37
|
+
400 => "Bad Request",
|
38
|
+
401 => "Unauthorized",
|
39
|
+
402 => "Payment Required",
|
40
|
+
403 => "Forbidden",
|
41
|
+
404 => "Not Found",
|
42
|
+
405 => "Method Not Allowed",
|
43
|
+
406 => "Not Acceptable",
|
44
|
+
407 => "Proxy Authentication Required",
|
45
|
+
408 => "Request Timeout",
|
46
|
+
409 => "Conflict",
|
47
|
+
410 => "Gone",
|
48
|
+
411 => "Length Required",
|
49
|
+
412 => "Precondition Failed",
|
50
|
+
413 => "Request Entity Too Large",
|
51
|
+
414 => "Request-URI Too Long",
|
52
|
+
415 => "Unsupported Media Type",
|
53
|
+
416 => "Requested Range Not Satisfiable",
|
54
|
+
417 => "Expectation Failed",
|
55
55
|
418 => "I'm a Teapot",
|
56
|
-
422 =>
|
57
|
-
423 =>
|
58
|
-
424 =>
|
59
|
-
426 =>
|
60
|
-
500 =>
|
61
|
-
501 =>
|
62
|
-
502 =>
|
63
|
-
503 =>
|
64
|
-
504 =>
|
65
|
-
505 =>
|
66
|
-
506 =>
|
67
|
-
507 =>
|
68
|
-
510 =>
|
56
|
+
422 => "Unprocessable Entity",
|
57
|
+
423 => "Locked",
|
58
|
+
424 => "Failed Dependency",
|
59
|
+
426 => "Upgrade Required",
|
60
|
+
500 => "Internal Server Error",
|
61
|
+
501 => "Not Implemented",
|
62
|
+
502 => "Bad Gateway",
|
63
|
+
503 => "Service Unavailable",
|
64
|
+
504 => "Gateway Timeout",
|
65
|
+
505 => "HTTP Version Not Supported",
|
66
|
+
506 => "Variant Also Negotiates",
|
67
|
+
507 => "Insufficient Storage",
|
68
|
+
510 => "Not Extended"
|
69
69
|
}.each { |_, v| v.freeze }.freeze
|
70
70
|
end
|
71
71
|
end
|