http 6.0.0-java
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 +7 -0
- data/CHANGELOG.md +267 -0
- data/CONTRIBUTING.md +26 -0
- data/LICENSE.txt +20 -0
- data/README.md +263 -0
- data/SECURITY.md +17 -0
- data/UPGRADING.md +491 -0
- data/http.gemspec +48 -0
- data/lib/http/base64.rb +22 -0
- data/lib/http/chainable/helpers.rb +62 -0
- data/lib/http/chainable/verbs.rb +136 -0
- data/lib/http/chainable.rb +377 -0
- data/lib/http/client.rb +230 -0
- data/lib/http/connection/internals.rb +141 -0
- data/lib/http/connection.rb +265 -0
- data/lib/http/content_type.rb +89 -0
- data/lib/http/errors.rb +67 -0
- data/lib/http/feature.rb +86 -0
- data/lib/http/features/auto_deflate.rb +230 -0
- data/lib/http/features/auto_inflate.rb +64 -0
- data/lib/http/features/caching/entry.rb +178 -0
- data/lib/http/features/caching/in_memory_store.rb +63 -0
- data/lib/http/features/caching.rb +216 -0
- data/lib/http/features/digest_auth.rb +234 -0
- data/lib/http/features/instrumentation.rb +149 -0
- data/lib/http/features/logging.rb +231 -0
- data/lib/http/features/normalize_uri.rb +34 -0
- data/lib/http/features/raise_error.rb +37 -0
- data/lib/http/form_data/composite_io.rb +106 -0
- data/lib/http/form_data/file.rb +95 -0
- data/lib/http/form_data/multipart/param.rb +62 -0
- data/lib/http/form_data/multipart.rb +106 -0
- data/lib/http/form_data/part.rb +52 -0
- data/lib/http/form_data/readable.rb +58 -0
- data/lib/http/form_data/urlencoded.rb +175 -0
- data/lib/http/form_data/version.rb +8 -0
- data/lib/http/form_data.rb +102 -0
- data/lib/http/headers/known.rb +90 -0
- data/lib/http/headers/normalizer.rb +50 -0
- data/lib/http/headers.rb +343 -0
- data/lib/http/mime_type/adapter.rb +43 -0
- data/lib/http/mime_type/json.rb +41 -0
- data/lib/http/mime_type.rb +96 -0
- data/lib/http/options/definitions.rb +189 -0
- data/lib/http/options.rb +241 -0
- data/lib/http/redirector.rb +157 -0
- data/lib/http/request/body.rb +181 -0
- data/lib/http/request/builder.rb +184 -0
- data/lib/http/request/proxy.rb +83 -0
- data/lib/http/request/writer.rb +186 -0
- data/lib/http/request.rb +375 -0
- data/lib/http/response/body.rb +172 -0
- data/lib/http/response/inflater.rb +60 -0
- data/lib/http/response/parser.rb +223 -0
- data/lib/http/response/status/reasons.rb +79 -0
- data/lib/http/response/status.rb +263 -0
- data/lib/http/response.rb +350 -0
- data/lib/http/retriable/delay_calculator.rb +91 -0
- data/lib/http/retriable/errors.rb +35 -0
- data/lib/http/retriable/performer.rb +197 -0
- data/lib/http/session.rb +280 -0
- data/lib/http/timeout/global.rb +229 -0
- data/lib/http/timeout/null.rb +225 -0
- data/lib/http/timeout/per_operation.rb +197 -0
- data/lib/http/uri/normalizer.rb +82 -0
- data/lib/http/uri/parsing.rb +182 -0
- data/lib/http/uri.rb +376 -0
- data/lib/http/version.rb +6 -0
- data/lib/http.rb +36 -0
- data/sig/deps.rbs +122 -0
- data/sig/http.rbs +1619 -0
- data/test/http/base64_test.rb +28 -0
- data/test/http/client_test.rb +739 -0
- data/test/http/connection_test.rb +1533 -0
- data/test/http/content_type_test.rb +190 -0
- data/test/http/errors_test.rb +28 -0
- data/test/http/feature_test.rb +49 -0
- data/test/http/features/auto_deflate_test.rb +317 -0
- data/test/http/features/auto_inflate_test.rb +213 -0
- data/test/http/features/caching_test.rb +942 -0
- data/test/http/features/digest_auth_test.rb +996 -0
- data/test/http/features/instrumentation_test.rb +246 -0
- data/test/http/features/logging_test.rb +654 -0
- data/test/http/features/normalize_uri_test.rb +41 -0
- data/test/http/features/raise_error_test.rb +77 -0
- data/test/http/form_data/composite_io_test.rb +215 -0
- data/test/http/form_data/file_test.rb +255 -0
- data/test/http/form_data/fixtures/the-http-gem.info +1 -0
- data/test/http/form_data/multipart_test.rb +303 -0
- data/test/http/form_data/part_test.rb +90 -0
- data/test/http/form_data/urlencoded_test.rb +164 -0
- data/test/http/form_data_test.rb +232 -0
- data/test/http/headers/normalizer_test.rb +93 -0
- data/test/http/headers_test.rb +888 -0
- data/test/http/mime_type/json_test.rb +39 -0
- data/test/http/mime_type_test.rb +150 -0
- data/test/http/options/base_uri_test.rb +148 -0
- data/test/http/options/body_test.rb +21 -0
- data/test/http/options/features_test.rb +38 -0
- data/test/http/options/form_test.rb +21 -0
- data/test/http/options/headers_test.rb +32 -0
- data/test/http/options/json_test.rb +21 -0
- data/test/http/options/merge_test.rb +78 -0
- data/test/http/options/new_test.rb +37 -0
- data/test/http/options/proxy_test.rb +32 -0
- data/test/http/options_test.rb +575 -0
- data/test/http/redirector_test.rb +639 -0
- data/test/http/request/body_test.rb +318 -0
- data/test/http/request/builder_test.rb +623 -0
- data/test/http/request/writer_test.rb +391 -0
- data/test/http/request_test.rb +1733 -0
- data/test/http/response/body_test.rb +292 -0
- data/test/http/response/parser_test.rb +105 -0
- data/test/http/response/status_test.rb +322 -0
- data/test/http/response_test.rb +502 -0
- data/test/http/retriable/delay_calculator_test.rb +194 -0
- data/test/http/retriable/errors_test.rb +71 -0
- data/test/http/retriable/performer_test.rb +551 -0
- data/test/http/session_test.rb +424 -0
- data/test/http/timeout/global_test.rb +239 -0
- data/test/http/timeout/null_test.rb +218 -0
- data/test/http/timeout/per_operation_test.rb +220 -0
- data/test/http/uri/normalizer_test.rb +89 -0
- data/test/http/uri_test.rb +1140 -0
- data/test/http/version_test.rb +15 -0
- data/test/http_test.rb +818 -0
- data/test/regression_tests.rb +27 -0
- data/test/support/capture_warning.rb +10 -0
- data/test/support/dummy_server/encoding_routes.rb +47 -0
- data/test/support/dummy_server/routes.rb +201 -0
- data/test/support/dummy_server/servlet.rb +81 -0
- data/test/support/dummy_server.rb +200 -0
- data/test/support/fakeio.rb +21 -0
- data/test/support/http_handling_shared/connection_reuse_tests.rb +97 -0
- data/test/support/http_handling_shared/timeout_tests.rb +134 -0
- data/test/support/http_handling_shared.rb +11 -0
- data/test/support/proxy_server.rb +207 -0
- data/test/support/servers/runner.rb +67 -0
- data/test/support/simplecov.rb +28 -0
- data/test/support/ssl_helper.rb +108 -0
- data/test/test_helper.rb +38 -0
- metadata +218 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "llhttp"
|
|
4
|
+
|
|
5
|
+
module HTTP
|
|
6
|
+
class Response
|
|
7
|
+
# HTTP response parser backed by LLHttp
|
|
8
|
+
# @api private
|
|
9
|
+
class Parser
|
|
10
|
+
# The underlying LLHttp parser
|
|
11
|
+
# @return [LLHttp::Parser] the underlying parser
|
|
12
|
+
# @api private
|
|
13
|
+
attr_reader :parser
|
|
14
|
+
|
|
15
|
+
# The parsed response headers
|
|
16
|
+
# @return [HTTP::Headers] the parsed headers
|
|
17
|
+
# @api private
|
|
18
|
+
attr_reader :headers
|
|
19
|
+
|
|
20
|
+
# The parsed HTTP status code
|
|
21
|
+
# @return [Integer, nil] the parsed status code
|
|
22
|
+
# @api private
|
|
23
|
+
attr_reader :status_code
|
|
24
|
+
|
|
25
|
+
# The parsed HTTP version string
|
|
26
|
+
# @return [String, nil] the parsed HTTP version
|
|
27
|
+
# @api private
|
|
28
|
+
attr_reader :http_version
|
|
29
|
+
|
|
30
|
+
# Create a new response parser
|
|
31
|
+
# @return [Parser]
|
|
32
|
+
# @api private
|
|
33
|
+
def initialize
|
|
34
|
+
@handler = Handler.new(self)
|
|
35
|
+
@parser = LLHttp::Parser.new(@handler, type: :response)
|
|
36
|
+
reset
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Reset parser to initial state
|
|
40
|
+
# @return [void]
|
|
41
|
+
# @api private
|
|
42
|
+
def reset
|
|
43
|
+
@parser.reset
|
|
44
|
+
@handler.reset
|
|
45
|
+
@header_finished = false
|
|
46
|
+
@message_finished = false
|
|
47
|
+
@headers = Headers.new
|
|
48
|
+
@chunk = nil
|
|
49
|
+
@status_code = nil
|
|
50
|
+
@http_version = nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Feed data into the parser
|
|
54
|
+
# @return [Parser]
|
|
55
|
+
# @api private
|
|
56
|
+
def add(data)
|
|
57
|
+
parser << data
|
|
58
|
+
|
|
59
|
+
self
|
|
60
|
+
rescue LLHttp::Error => e
|
|
61
|
+
raise IOError, e.message
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @see #add
|
|
65
|
+
# @api private
|
|
66
|
+
alias << add
|
|
67
|
+
|
|
68
|
+
# Reset parser state for informational (1xx) responses
|
|
69
|
+
# @return [void]
|
|
70
|
+
# @api private
|
|
71
|
+
def reset_for_informational
|
|
72
|
+
@handler.reset
|
|
73
|
+
@header_finished = false
|
|
74
|
+
@message_finished = false
|
|
75
|
+
@headers = Headers.new
|
|
76
|
+
@chunk = nil
|
|
77
|
+
@status_code = nil
|
|
78
|
+
@http_version = nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Mark headers as finished
|
|
82
|
+
# @return [void]
|
|
83
|
+
# @api private
|
|
84
|
+
def mark_header_finished
|
|
85
|
+
@header_finished = true
|
|
86
|
+
@status_code = @parser.status_code
|
|
87
|
+
@http_version = "#{@parser.http_major}.#{@parser.http_minor}"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Check if headers have been parsed
|
|
91
|
+
# @return [Boolean]
|
|
92
|
+
# @api private
|
|
93
|
+
def headers?
|
|
94
|
+
@header_finished
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Add a parsed header field and value
|
|
98
|
+
# @return [void]
|
|
99
|
+
# @api private
|
|
100
|
+
def add_header(name, value)
|
|
101
|
+
@headers.add(name, value)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Mark the message as fully parsed
|
|
105
|
+
# @return [void]
|
|
106
|
+
# @api private
|
|
107
|
+
def mark_message_finished
|
|
108
|
+
@message_finished = true
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Check if the full message has been parsed
|
|
112
|
+
# @return [Boolean]
|
|
113
|
+
# @api private
|
|
114
|
+
def finished?
|
|
115
|
+
@message_finished
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Append a body chunk to the buffer
|
|
119
|
+
# @return [void]
|
|
120
|
+
# @api private
|
|
121
|
+
def add_body(chunk)
|
|
122
|
+
if @chunk
|
|
123
|
+
@chunk << chunk
|
|
124
|
+
else
|
|
125
|
+
@chunk = chunk
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Read up to size bytes from the body buffer
|
|
130
|
+
# @return [String, nil]
|
|
131
|
+
# @api private
|
|
132
|
+
def read(size)
|
|
133
|
+
return if @chunk.nil?
|
|
134
|
+
|
|
135
|
+
if @chunk.bytesize <= size
|
|
136
|
+
chunk = @chunk
|
|
137
|
+
@chunk = nil
|
|
138
|
+
else
|
|
139
|
+
chunk = @chunk.byteslice(0, size)
|
|
140
|
+
@chunk[0, size] = ""
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
chunk
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Delegate handler for LLHttp parser callbacks
|
|
147
|
+
# @api private
|
|
148
|
+
class Handler < LLHttp::Delegate
|
|
149
|
+
# Create a new parser handler
|
|
150
|
+
# @return [Handler]
|
|
151
|
+
# @api private
|
|
152
|
+
def initialize(target)
|
|
153
|
+
@target = target
|
|
154
|
+
super()
|
|
155
|
+
reset
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Reset handler state
|
|
159
|
+
# @return [void]
|
|
160
|
+
# @api private
|
|
161
|
+
def reset
|
|
162
|
+
@reading_header_value = false
|
|
163
|
+
@field_value = +""
|
|
164
|
+
@field = +""
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Handle a header field token
|
|
168
|
+
# @return [void]
|
|
169
|
+
# @api private
|
|
170
|
+
def on_header_field(field)
|
|
171
|
+
append_header if @reading_header_value
|
|
172
|
+
@field << field
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Handle a header value token
|
|
176
|
+
# @return [void]
|
|
177
|
+
# @api private
|
|
178
|
+
def on_header_value(value)
|
|
179
|
+
@reading_header_value = true
|
|
180
|
+
@field_value << value
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Handle headers complete callback
|
|
184
|
+
# @return [void]
|
|
185
|
+
# @api private
|
|
186
|
+
def on_headers_complete
|
|
187
|
+
append_header if @reading_header_value
|
|
188
|
+
@target.mark_header_finished
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Handle body data callback
|
|
192
|
+
# @return [void]
|
|
193
|
+
# @api private
|
|
194
|
+
def on_body(body)
|
|
195
|
+
@target.add_body(body)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Handle message complete callback
|
|
199
|
+
# @return [void]
|
|
200
|
+
# @api private
|
|
201
|
+
def on_message_complete
|
|
202
|
+
if Integer(@target.status_code) < 200
|
|
203
|
+
@target.reset_for_informational
|
|
204
|
+
else
|
|
205
|
+
@target.mark_message_finished
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
private
|
|
210
|
+
|
|
211
|
+
# Flush the current header to the parser
|
|
212
|
+
# @return [void]
|
|
213
|
+
# @api private
|
|
214
|
+
def append_header
|
|
215
|
+
@target.add_header(@field, @field_value)
|
|
216
|
+
@reading_header_value = false
|
|
217
|
+
@field_value = +""
|
|
218
|
+
@field = +""
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# AUTO-GENERATED FILE, DO NOT CHANGE IT MANUALLY
|
|
4
|
+
|
|
5
|
+
module HTTP
|
|
6
|
+
class Response
|
|
7
|
+
class Status
|
|
8
|
+
# Code to Reason map
|
|
9
|
+
#
|
|
10
|
+
# @example Usage
|
|
11
|
+
#
|
|
12
|
+
# REASONS[400] # => "Bad Request"
|
|
13
|
+
# REASONS[414] # => "Request-URI Too Long"
|
|
14
|
+
#
|
|
15
|
+
# @return [Hash<Fixnum => String>]
|
|
16
|
+
REASONS = {
|
|
17
|
+
100 => "Continue",
|
|
18
|
+
101 => "Switching Protocols",
|
|
19
|
+
102 => "Processing",
|
|
20
|
+
200 => "OK",
|
|
21
|
+
201 => "Created",
|
|
22
|
+
202 => "Accepted",
|
|
23
|
+
203 => "Non-Authoritative Information",
|
|
24
|
+
204 => "No Content",
|
|
25
|
+
205 => "Reset Content",
|
|
26
|
+
206 => "Partial Content",
|
|
27
|
+
207 => "Multi-Status",
|
|
28
|
+
208 => "Already Reported",
|
|
29
|
+
226 => "IM Used",
|
|
30
|
+
300 => "Multiple Choices",
|
|
31
|
+
301 => "Moved Permanently",
|
|
32
|
+
302 => "Found",
|
|
33
|
+
303 => "See Other",
|
|
34
|
+
304 => "Not Modified",
|
|
35
|
+
305 => "Use Proxy",
|
|
36
|
+
307 => "Temporary Redirect",
|
|
37
|
+
308 => "Permanent Redirect",
|
|
38
|
+
400 => "Bad Request",
|
|
39
|
+
401 => "Unauthorized",
|
|
40
|
+
402 => "Payment Required",
|
|
41
|
+
403 => "Forbidden",
|
|
42
|
+
404 => "Not Found",
|
|
43
|
+
405 => "Method Not Allowed",
|
|
44
|
+
406 => "Not Acceptable",
|
|
45
|
+
407 => "Proxy Authentication Required",
|
|
46
|
+
408 => "Request Timeout",
|
|
47
|
+
409 => "Conflict",
|
|
48
|
+
410 => "Gone",
|
|
49
|
+
411 => "Length Required",
|
|
50
|
+
412 => "Precondition Failed",
|
|
51
|
+
413 => "Payload Too Large",
|
|
52
|
+
414 => "URI Too Long",
|
|
53
|
+
415 => "Unsupported Media Type",
|
|
54
|
+
416 => "Range Not Satisfiable",
|
|
55
|
+
417 => "Expectation Failed",
|
|
56
|
+
421 => "Misdirected Request",
|
|
57
|
+
422 => "Unprocessable Entity",
|
|
58
|
+
423 => "Locked",
|
|
59
|
+
424 => "Failed Dependency",
|
|
60
|
+
426 => "Upgrade Required",
|
|
61
|
+
428 => "Precondition Required",
|
|
62
|
+
429 => "Too Many Requests",
|
|
63
|
+
431 => "Request Header Fields Too Large",
|
|
64
|
+
451 => "Unavailable For Legal Reasons",
|
|
65
|
+
500 => "Internal Server Error",
|
|
66
|
+
501 => "Not Implemented",
|
|
67
|
+
502 => "Bad Gateway",
|
|
68
|
+
503 => "Service Unavailable",
|
|
69
|
+
504 => "Gateway Timeout",
|
|
70
|
+
505 => "HTTP Version Not Supported",
|
|
71
|
+
506 => "Variant Also Negotiates",
|
|
72
|
+
507 => "Insufficient Storage",
|
|
73
|
+
508 => "Loop Detected",
|
|
74
|
+
510 => "Not Extended",
|
|
75
|
+
511 => "Network Authentication Required"
|
|
76
|
+
}.each_value(&:freeze).freeze
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "forwardable"
|
|
4
|
+
|
|
5
|
+
require "http/response/status/reasons"
|
|
6
|
+
|
|
7
|
+
module HTTP
|
|
8
|
+
class Response
|
|
9
|
+
# Represents an HTTP response status code with reason phrase
|
|
10
|
+
class Status
|
|
11
|
+
include Comparable
|
|
12
|
+
extend Forwardable
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
# Coerces given value to Status
|
|
16
|
+
#
|
|
17
|
+
# @example
|
|
18
|
+
# Status.coerce(:bad_request) # => Status.new(400)
|
|
19
|
+
#
|
|
20
|
+
# @raise [Error] if coercion is impossible
|
|
21
|
+
# @param [Symbol, #to_i] object
|
|
22
|
+
# @return [Status]
|
|
23
|
+
# @api public
|
|
24
|
+
def coerce(object)
|
|
25
|
+
code = case object
|
|
26
|
+
when String then SYMBOL_CODES.fetch(symbolize(object), nil)
|
|
27
|
+
when Symbol then SYMBOL_CODES.fetch(object, nil)
|
|
28
|
+
when Numeric then object
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
return new code if code
|
|
32
|
+
|
|
33
|
+
raise Error, "Can't coerce #{object.class}(#{object}) to #{self}"
|
|
34
|
+
end
|
|
35
|
+
alias [] coerce
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# Symbolizes given string
|
|
40
|
+
#
|
|
41
|
+
# @param [#to_s] str
|
|
42
|
+
# @return [Symbol]
|
|
43
|
+
# @api private
|
|
44
|
+
def symbolize(str)
|
|
45
|
+
str.downcase.tr("- ", "_").to_sym
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Code to Symbol map
|
|
50
|
+
#
|
|
51
|
+
# @example Usage
|
|
52
|
+
#
|
|
53
|
+
# SYMBOLS[400] # => :bad_request
|
|
54
|
+
# SYMBOLS[414] # => :request_uri_too_long
|
|
55
|
+
# SYMBOLS[418] # => :im_a_teapot
|
|
56
|
+
#
|
|
57
|
+
# @return [Hash<Fixnum => Symbol>]
|
|
58
|
+
SYMBOLS = REASONS.transform_values { |v| symbolize(v) }.freeze
|
|
59
|
+
|
|
60
|
+
# Reversed {SYMBOLS} map.
|
|
61
|
+
#
|
|
62
|
+
# @example Usage
|
|
63
|
+
#
|
|
64
|
+
# SYMBOL_CODES[:bad_request] # => 400
|
|
65
|
+
# SYMBOL_CODES[:request_uri_too_long] # => 414
|
|
66
|
+
# SYMBOL_CODES[:im_a_teapot] # => 418
|
|
67
|
+
#
|
|
68
|
+
# @return [Hash<Symbol => Fixnum>]
|
|
69
|
+
SYMBOL_CODES = SYMBOLS.to_h { |k, v| [v, k] }.freeze
|
|
70
|
+
|
|
71
|
+
# The numeric status code
|
|
72
|
+
#
|
|
73
|
+
# @example
|
|
74
|
+
# status.code # => 200
|
|
75
|
+
#
|
|
76
|
+
# @return [Fixnum] status code
|
|
77
|
+
# @api public
|
|
78
|
+
attr_reader :code
|
|
79
|
+
|
|
80
|
+
# @!method to_i
|
|
81
|
+
# Convert status to Integer
|
|
82
|
+
# @example
|
|
83
|
+
# status.to_i # => 200
|
|
84
|
+
# @return [Integer]
|
|
85
|
+
# @api public
|
|
86
|
+
|
|
87
|
+
# @!method to_int
|
|
88
|
+
# Implicit conversion to Integer
|
|
89
|
+
# @example
|
|
90
|
+
# status.to_int # => 200
|
|
91
|
+
# @return [Integer]
|
|
92
|
+
# @api public
|
|
93
|
+
def_delegators :@code, :to_i, :to_int
|
|
94
|
+
|
|
95
|
+
# Create a new Status from a value that responds to #to_i
|
|
96
|
+
#
|
|
97
|
+
# @example
|
|
98
|
+
# Status.new(200)
|
|
99
|
+
#
|
|
100
|
+
# @param [#to_i] obj
|
|
101
|
+
# @return [Status]
|
|
102
|
+
# @api public
|
|
103
|
+
def initialize(obj)
|
|
104
|
+
raise TypeError, "Expected #{obj.inspect} to respond to #to_i" unless obj.respond_to?(:to_i)
|
|
105
|
+
|
|
106
|
+
@code = obj.to_i
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Compare status codes for ordering
|
|
110
|
+
#
|
|
111
|
+
# @example
|
|
112
|
+
# Status.new(200) <=> Status.new(404) # => -1
|
|
113
|
+
#
|
|
114
|
+
# @param [#to_i] other
|
|
115
|
+
# @return [Integer, nil]
|
|
116
|
+
# @api public
|
|
117
|
+
def <=>(other)
|
|
118
|
+
return nil unless other.respond_to?(:to_i)
|
|
119
|
+
|
|
120
|
+
code <=> other.to_i
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Hash value based on status code
|
|
124
|
+
#
|
|
125
|
+
# @example
|
|
126
|
+
# Status.new(200).hash
|
|
127
|
+
#
|
|
128
|
+
# @return [Integer]
|
|
129
|
+
# @api public
|
|
130
|
+
def hash
|
|
131
|
+
code.hash
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Return the reason phrase for the status code
|
|
135
|
+
#
|
|
136
|
+
# @example
|
|
137
|
+
# status.reason # => "OK"
|
|
138
|
+
#
|
|
139
|
+
# @see REASONS
|
|
140
|
+
# @return [String, nil] status message
|
|
141
|
+
# @api public
|
|
142
|
+
def reason
|
|
143
|
+
REASONS[code]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Return string representation of HTTP status
|
|
147
|
+
#
|
|
148
|
+
# @example
|
|
149
|
+
# status.to_s # => "200 OK"
|
|
150
|
+
#
|
|
151
|
+
# @return [String]
|
|
152
|
+
# @api public
|
|
153
|
+
def to_s
|
|
154
|
+
reason ? "#{code} #{reason}" : code.to_s
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Check if status code is informational (1XX)
|
|
158
|
+
#
|
|
159
|
+
# @example
|
|
160
|
+
# status.informational? # => false
|
|
161
|
+
#
|
|
162
|
+
# @return [Boolean]
|
|
163
|
+
# @api public
|
|
164
|
+
def informational?
|
|
165
|
+
100 <= code && code < 200
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Check if status code is successful (2XX)
|
|
169
|
+
#
|
|
170
|
+
# @example
|
|
171
|
+
# status.success? # => true
|
|
172
|
+
#
|
|
173
|
+
# @return [Boolean]
|
|
174
|
+
# @api public
|
|
175
|
+
def success?
|
|
176
|
+
200 <= code && code < 300
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Check if status code is redirection (3XX)
|
|
180
|
+
#
|
|
181
|
+
# @example
|
|
182
|
+
# status.redirect? # => false
|
|
183
|
+
#
|
|
184
|
+
# @return [Boolean]
|
|
185
|
+
# @api public
|
|
186
|
+
def redirect?
|
|
187
|
+
300 <= code && code < 400
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Check if status code is client error (4XX)
|
|
191
|
+
#
|
|
192
|
+
# @example
|
|
193
|
+
# status.client_error? # => false
|
|
194
|
+
#
|
|
195
|
+
# @return [Boolean]
|
|
196
|
+
# @api public
|
|
197
|
+
def client_error?
|
|
198
|
+
400 <= code && code < 500
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Check if status code is server error (5XX)
|
|
202
|
+
#
|
|
203
|
+
# @example
|
|
204
|
+
# status.server_error? # => false
|
|
205
|
+
#
|
|
206
|
+
# @return [Boolean]
|
|
207
|
+
# @api public
|
|
208
|
+
def server_error?
|
|
209
|
+
500 <= code && code < 600
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Symbolized {#reason}
|
|
213
|
+
#
|
|
214
|
+
# @example
|
|
215
|
+
# status.to_sym # => :ok
|
|
216
|
+
#
|
|
217
|
+
# @return [nil] unless code is well-known (see REASONS)
|
|
218
|
+
# @return [Symbol]
|
|
219
|
+
# @api public
|
|
220
|
+
def to_sym
|
|
221
|
+
SYMBOLS[code]
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Printable version of HTTP Status
|
|
225
|
+
#
|
|
226
|
+
# @example
|
|
227
|
+
# status.inspect # => "#<HTTP::Response::Status 200 OK>"
|
|
228
|
+
#
|
|
229
|
+
# (see String#inspect)
|
|
230
|
+
# @return [String]
|
|
231
|
+
# @api public
|
|
232
|
+
def inspect
|
|
233
|
+
"#<#{self.class} #{self}>"
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Pattern matching interface for matching against status code and reason
|
|
237
|
+
#
|
|
238
|
+
# @example
|
|
239
|
+
# case response.status
|
|
240
|
+
# in { code: 200..299 }
|
|
241
|
+
# "success"
|
|
242
|
+
# in { code: 400.. }
|
|
243
|
+
# "error"
|
|
244
|
+
# end
|
|
245
|
+
#
|
|
246
|
+
# @param keys [Array<Symbol>, nil] keys to extract, or nil for all
|
|
247
|
+
# @return [Hash{Symbol => Object}]
|
|
248
|
+
# @api public
|
|
249
|
+
def deconstruct_keys(keys)
|
|
250
|
+
hash = { code: code, reason: reason }
|
|
251
|
+
keys ? hash.slice(*keys) : hash
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
SYMBOLS.each do |code, symbol|
|
|
255
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
256
|
+
def #{symbol}? # def bad_request?
|
|
257
|
+
#{code} == code # 400 == code
|
|
258
|
+
end # end
|
|
259
|
+
RUBY
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|