http 5.2.0 → 6.0.2
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/LICENSE.txt +1 -1
- data/README.md +110 -13
- data/http.gemspec +38 -35
- 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 +249 -129
- data/lib/http/client.rb +158 -127
- data/lib/http/connection/internals.rb +141 -0
- data/lib/http/connection.rb +128 -97
- data/lib/http/content_type.rb +61 -6
- data/lib/http/errors.rb +41 -1
- data/lib/http/feature.rb +67 -6
- data/lib/http/features/auto_deflate.rb +124 -17
- data/lib/http/features/auto_inflate.rb +38 -15
- 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 +97 -17
- data/lib/http/features/logging.rb +183 -5
- data/lib/http/features/normalize_uri.rb +17 -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 +3 -0
- data/lib/http/headers/normalizer.rb +50 -0
- data/lib/http/headers.rb +185 -92
- data/lib/http/mime_type/adapter.rb +24 -9
- data/lib/http/mime_type/json.rb +19 -4
- data/lib/http/mime_type.rb +21 -3
- data/lib/http/options/definitions.rb +189 -0
- data/lib/http/options.rb +172 -125
- data/lib/http/redirector.rb +80 -75
- data/lib/http/request/body.rb +87 -6
- data/lib/http/request/builder.rb +184 -0
- data/lib/http/request/proxy.rb +83 -0
- data/lib/http/request/writer.rb +78 -17
- data/lib/http/request.rb +216 -99
- data/lib/http/response/body.rb +103 -18
- data/lib/http/response/inflater.rb +35 -7
- data/lib/http/response/parser.rb +98 -4
- data/lib/http/response/status/reasons.rb +2 -4
- data/lib/http/response/status.rb +141 -31
- data/lib/http/response.rb +219 -61
- 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 +147 -34
- data/lib/http/timeout/null.rb +155 -9
- data/lib/http/timeout/per_operation.rb +139 -18
- data/lib/http/uri/normalizer.rb +82 -0
- data/lib/http/uri/parsing.rb +182 -0
- data/lib/http/uri.rb +289 -124
- data/lib/http/version.rb +2 -1
- data/lib/http.rb +11 -1
- data/sig/http.rbs +1619 -0
- metadata +42 -175
- data/.github/workflows/ci.yml +0 -67
- data/.gitignore +0 -15
- data/.rspec +0 -1
- data/.rubocop/layout.yml +0 -8
- data/.rubocop/metrics.yml +0 -4
- data/.rubocop/style.yml +0 -32
- data/.rubocop.yml +0 -11
- data/.rubocop_todo.yml +0 -206
- data/.yardopts +0 -2
- data/CHANGELOG.md +0 -41
- data/CHANGES_OLD.md +0 -1002
- data/CONTRIBUTING.md +0 -26
- data/Gemfile +0 -50
- data/Guardfile +0 -18
- data/Rakefile +0 -64
- data/SECURITY.md +0 -17
- data/lib/http/headers/mixin.rb +0 -34
- data/logo.png +0 -0
- data/spec/lib/http/client_spec.rb +0 -556
- data/spec/lib/http/connection_spec.rb +0 -88
- data/spec/lib/http/content_type_spec.rb +0 -47
- data/spec/lib/http/features/auto_deflate_spec.rb +0 -77
- data/spec/lib/http/features/auto_inflate_spec.rb +0 -86
- data/spec/lib/http/features/instrumentation_spec.rb +0 -81
- data/spec/lib/http/features/logging_spec.rb +0 -65
- data/spec/lib/http/headers/mixin_spec.rb +0 -36
- data/spec/lib/http/headers_spec.rb +0 -527
- data/spec/lib/http/options/body_spec.rb +0 -15
- data/spec/lib/http/options/features_spec.rb +0 -33
- data/spec/lib/http/options/form_spec.rb +0 -15
- data/spec/lib/http/options/headers_spec.rb +0 -24
- data/spec/lib/http/options/json_spec.rb +0 -15
- data/spec/lib/http/options/merge_spec.rb +0 -68
- data/spec/lib/http/options/new_spec.rb +0 -30
- data/spec/lib/http/options/proxy_spec.rb +0 -20
- data/spec/lib/http/options_spec.rb +0 -13
- data/spec/lib/http/redirector_spec.rb +0 -529
- data/spec/lib/http/request/body_spec.rb +0 -211
- data/spec/lib/http/request/writer_spec.rb +0 -121
- data/spec/lib/http/request_spec.rb +0 -234
- data/spec/lib/http/response/body_spec.rb +0 -85
- data/spec/lib/http/response/parser_spec.rb +0 -74
- data/spec/lib/http/response/status_spec.rb +0 -253
- data/spec/lib/http/response_spec.rb +0 -262
- data/spec/lib/http/uri/normalizer_spec.rb +0 -95
- data/spec/lib/http/uri_spec.rb +0 -71
- data/spec/lib/http_spec.rb +0 -506
- data/spec/regression_specs.rb +0 -24
- data/spec/spec_helper.rb +0 -88
- data/spec/support/black_hole.rb +0 -13
- data/spec/support/capture_warning.rb +0 -10
- data/spec/support/dummy_server/servlet.rb +0 -190
- data/spec/support/dummy_server.rb +0 -43
- data/spec/support/fakeio.rb +0 -21
- data/spec/support/fuubar.rb +0 -21
- data/spec/support/http_handling_shared.rb +0 -190
- data/spec/support/proxy_server.rb +0 -39
- data/spec/support/servers/config.rb +0 -11
- data/spec/support/servers/runner.rb +0 -19
- data/spec/support/simplecov.rb +0 -19
- data/spec/support/ssl_helper.rb +0 -104
data/lib/http/headers.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require "forwardable"
|
|
4
4
|
|
|
5
5
|
require "http/errors"
|
|
6
|
-
require "http/headers/
|
|
6
|
+
require "http/headers/normalizer"
|
|
7
7
|
require "http/headers/known"
|
|
8
8
|
|
|
9
9
|
module HTTP
|
|
@@ -12,14 +12,57 @@ module HTTP
|
|
|
12
12
|
extend Forwardable
|
|
13
13
|
include Enumerable
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
class << self
|
|
16
|
+
# Coerces given object into Headers
|
|
17
|
+
#
|
|
18
|
+
# @example
|
|
19
|
+
# headers = HTTP::Headers.coerce("Content-Type" => "text/plain")
|
|
20
|
+
#
|
|
21
|
+
# @raise [Error] if object can't be coerced
|
|
22
|
+
# @param [#to_hash, #to_h, #to_a] object
|
|
23
|
+
# @return [Headers]
|
|
24
|
+
# @api public
|
|
25
|
+
def coerce(object)
|
|
26
|
+
object = if object.respond_to?(:to_hash) then object.to_hash
|
|
27
|
+
elsif object.respond_to?(:to_h) then object.to_h
|
|
28
|
+
elsif object.respond_to?(:to_a) then object.to_a
|
|
29
|
+
else raise Error, "Can't coerce #{object.inspect} to Headers"
|
|
30
|
+
end
|
|
17
31
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
32
|
+
headers = new
|
|
33
|
+
object.each { |k, v| headers.add k, v }
|
|
34
|
+
headers
|
|
35
|
+
end
|
|
36
|
+
# @!method [](object)
|
|
37
|
+
# Coerces given object into Headers
|
|
38
|
+
#
|
|
39
|
+
# @example
|
|
40
|
+
# headers = HTTP::Headers["Content-Type" => "text/plain"]
|
|
41
|
+
#
|
|
42
|
+
# @see .coerce
|
|
43
|
+
# @return [Headers]
|
|
44
|
+
# @api public
|
|
45
|
+
alias [] coerce
|
|
46
|
+
|
|
47
|
+
# Returns the shared normalizer instance
|
|
48
|
+
#
|
|
49
|
+
# @example
|
|
50
|
+
# HTTP::Headers.normalizer
|
|
51
|
+
#
|
|
52
|
+
# @return [Headers::Normalizer]
|
|
53
|
+
# @api public
|
|
54
|
+
def normalizer
|
|
55
|
+
@normalizer ||= Normalizer.new #: Headers::Normalizer
|
|
56
|
+
end
|
|
57
|
+
end
|
|
21
58
|
|
|
22
|
-
#
|
|
59
|
+
# Creates a new empty headers container
|
|
60
|
+
#
|
|
61
|
+
# @example
|
|
62
|
+
# headers = HTTP::Headers.new
|
|
63
|
+
#
|
|
64
|
+
# @return [Headers]
|
|
65
|
+
# @api public
|
|
23
66
|
def initialize
|
|
24
67
|
# The @pile stores each header value using a three element array:
|
|
25
68
|
# 0 - the normalized header key, used for lookup
|
|
@@ -28,26 +71,46 @@ module HTTP
|
|
|
28
71
|
@pile = []
|
|
29
72
|
end
|
|
30
73
|
|
|
31
|
-
# Sets header
|
|
74
|
+
# Sets header, replacing any existing values
|
|
75
|
+
#
|
|
76
|
+
# @example
|
|
77
|
+
# headers.set("Content-Type", "text/plain")
|
|
32
78
|
#
|
|
33
79
|
# @param (see #add)
|
|
34
80
|
# @return [void]
|
|
81
|
+
# @api public
|
|
35
82
|
def set(name, value)
|
|
36
83
|
delete(name)
|
|
37
84
|
add(name, value)
|
|
38
85
|
end
|
|
86
|
+
# @!method []=(name, value)
|
|
87
|
+
# Sets header, replacing any existing values
|
|
88
|
+
#
|
|
89
|
+
# @example
|
|
90
|
+
# headers["Content-Type"] = "text/plain"
|
|
91
|
+
#
|
|
92
|
+
# @see #set
|
|
93
|
+
# @return [void]
|
|
94
|
+
# @api public
|
|
39
95
|
alias []= set
|
|
40
96
|
|
|
41
|
-
# Removes header
|
|
97
|
+
# Removes header with the given name
|
|
98
|
+
#
|
|
99
|
+
# @example
|
|
100
|
+
# headers.delete("Content-Type")
|
|
42
101
|
#
|
|
43
102
|
# @param [#to_s] name header name
|
|
44
103
|
# @return [void]
|
|
104
|
+
# @api public
|
|
45
105
|
def delete(name)
|
|
46
|
-
name = normalize_header name
|
|
47
|
-
@pile.delete_if { |k, _| k
|
|
106
|
+
name = normalize_header name
|
|
107
|
+
@pile.delete_if { |k, _| k.eql?(name) }
|
|
48
108
|
end
|
|
49
109
|
|
|
50
|
-
# Appends header
|
|
110
|
+
# Appends header value(s) to the given name
|
|
111
|
+
#
|
|
112
|
+
# @example
|
|
113
|
+
# headers.add("Accept", "text/html")
|
|
51
114
|
#
|
|
52
115
|
# @param [String, Symbol] name header name. When specified as a string, the
|
|
53
116
|
# name is sent as-is. When specified as a symbol, the name is converted
|
|
@@ -57,16 +120,11 @@ module HTTP
|
|
|
57
120
|
# is sent as `"auth_key"`.
|
|
58
121
|
# @param [Array<#to_s>, #to_s] value header value(s) to be appended
|
|
59
122
|
# @return [void]
|
|
123
|
+
# @api public
|
|
60
124
|
def add(name, value)
|
|
61
|
-
lookup_name = normalize_header(name
|
|
62
|
-
wire_name =
|
|
63
|
-
|
|
64
|
-
name
|
|
65
|
-
when Symbol
|
|
66
|
-
lookup_name
|
|
67
|
-
else
|
|
68
|
-
raise HTTP::HeaderError, "HTTP header must be a String or Symbol: #{name.inspect}"
|
|
69
|
-
end
|
|
125
|
+
lookup_name = normalize_header(name)
|
|
126
|
+
wire_name = wire_name_for(name, lookup_name)
|
|
127
|
+
|
|
70
128
|
Array(value).each do |v|
|
|
71
129
|
@pile << [
|
|
72
130
|
lookup_name,
|
|
@@ -76,173 +134,208 @@ module HTTP
|
|
|
76
134
|
end
|
|
77
135
|
end
|
|
78
136
|
|
|
79
|
-
# Returns list of header values if any
|
|
137
|
+
# Returns list of header values if any
|
|
138
|
+
#
|
|
139
|
+
# @example
|
|
140
|
+
# headers.get("Content-Type")
|
|
80
141
|
#
|
|
81
142
|
# @return [Array<String>]
|
|
143
|
+
# @api public
|
|
82
144
|
def get(name)
|
|
83
|
-
name = normalize_header name
|
|
84
|
-
@pile.
|
|
145
|
+
name = normalize_header name
|
|
146
|
+
@pile.filter_map { |k, _, v| v if k.eql?(name) }
|
|
85
147
|
end
|
|
86
148
|
|
|
87
|
-
# Smart version of {#get}
|
|
149
|
+
# Smart version of {#get}
|
|
150
|
+
#
|
|
151
|
+
# @example
|
|
152
|
+
# headers["Content-Type"]
|
|
88
153
|
#
|
|
89
154
|
# @return [nil] if header was not set
|
|
90
155
|
# @return [String] if header has exactly one value
|
|
91
156
|
# @return [Array<String>] if header has more than one value
|
|
157
|
+
# @api public
|
|
92
158
|
def [](name)
|
|
93
159
|
values = get(name)
|
|
160
|
+
return if values.empty?
|
|
94
161
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
else values
|
|
99
|
-
end
|
|
162
|
+
return values unless values.one?
|
|
163
|
+
|
|
164
|
+
values.join
|
|
100
165
|
end
|
|
101
166
|
|
|
102
|
-
# Tells
|
|
167
|
+
# Tells whether header with given name is set
|
|
168
|
+
#
|
|
169
|
+
# @example
|
|
170
|
+
# headers.include?("Content-Type")
|
|
103
171
|
#
|
|
104
172
|
# @return [Boolean]
|
|
173
|
+
# @api public
|
|
105
174
|
def include?(name)
|
|
106
|
-
name = normalize_header name
|
|
107
|
-
@pile.any? { |k, _| k
|
|
175
|
+
name = normalize_header name
|
|
176
|
+
@pile.any? { |k, _| k.eql?(name) }
|
|
108
177
|
end
|
|
109
178
|
|
|
110
179
|
# Returns Rack-compatible headers Hash
|
|
111
180
|
#
|
|
181
|
+
# @example
|
|
182
|
+
# headers.to_h
|
|
183
|
+
#
|
|
112
184
|
# @return [Hash]
|
|
185
|
+
# @api public
|
|
113
186
|
def to_h
|
|
114
187
|
keys.to_h { |k| [k, self[k]] }
|
|
115
188
|
end
|
|
189
|
+
# @!method to_hash
|
|
190
|
+
# @see #to_h
|
|
191
|
+
# @return [Hash]
|
|
116
192
|
alias to_hash to_h
|
|
117
193
|
|
|
118
|
-
#
|
|
194
|
+
# Pattern matching interface
|
|
119
195
|
#
|
|
120
|
-
# @
|
|
121
|
-
|
|
122
|
-
|
|
196
|
+
# @example
|
|
197
|
+
# headers.deconstruct_keys(%i[content_type])
|
|
198
|
+
#
|
|
199
|
+
# @param keys [Array<Symbol>, nil] keys to extract, or nil for all
|
|
200
|
+
# @return [Hash{Symbol => Object}]
|
|
201
|
+
# @api public
|
|
202
|
+
def deconstruct_keys(keys)
|
|
203
|
+
hash = @pile.map { |_, k, _| k }.to_h { |k| [k.tr("A-Z-", "a-z_").to_sym, self[k]] }
|
|
204
|
+
keys ? hash.slice(*keys) : hash
|
|
123
205
|
end
|
|
124
206
|
|
|
125
|
-
# Returns human-readable representation of
|
|
207
|
+
# Returns human-readable representation of self instance
|
|
208
|
+
#
|
|
209
|
+
# @example
|
|
210
|
+
# headers.inspect
|
|
126
211
|
#
|
|
127
212
|
# @return [String]
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
end
|
|
213
|
+
# @api public
|
|
214
|
+
def inspect = "#<#{self.class}>"
|
|
131
215
|
|
|
132
|
-
# Returns list of header names
|
|
216
|
+
# Returns list of header names
|
|
217
|
+
#
|
|
218
|
+
# @example
|
|
219
|
+
# headers.keys
|
|
133
220
|
#
|
|
134
221
|
# @return [Array<String>]
|
|
222
|
+
# @api public
|
|
135
223
|
def keys
|
|
136
224
|
@pile.map { |_, k, _| k }.uniq
|
|
137
225
|
end
|
|
138
226
|
|
|
139
|
-
# Compares headers to another Headers or Array of
|
|
227
|
+
# Compares headers to another Headers or Array of pairs
|
|
228
|
+
#
|
|
229
|
+
# @example
|
|
230
|
+
# headers == other_headers
|
|
140
231
|
#
|
|
141
232
|
# @return [Boolean]
|
|
233
|
+
# @api public
|
|
142
234
|
def ==(other)
|
|
143
235
|
return false unless other.respond_to? :to_a
|
|
144
236
|
|
|
145
|
-
to_a
|
|
237
|
+
to_a.eql?(other.to_a)
|
|
146
238
|
end
|
|
147
239
|
|
|
148
|
-
# Calls the given block once for each key/value pair
|
|
240
|
+
# Calls the given block once for each key/value pair
|
|
241
|
+
#
|
|
242
|
+
# @example
|
|
243
|
+
# headers.each { |name, value| puts "#{name}: #{value}" }
|
|
149
244
|
#
|
|
150
245
|
# @return [Enumerator] if no block given
|
|
151
246
|
# @return [Headers] self-reference
|
|
247
|
+
# @api public
|
|
152
248
|
def each
|
|
153
|
-
return to_enum
|
|
249
|
+
return to_enum unless block_given?
|
|
154
250
|
|
|
155
|
-
@pile.each { |item| yield(item
|
|
251
|
+
@pile.each { |item| yield(item.drop(1)) }
|
|
156
252
|
self
|
|
157
253
|
end
|
|
158
254
|
|
|
159
255
|
# @!method empty?
|
|
160
|
-
# Returns
|
|
256
|
+
# Returns true if self has no key/value pairs
|
|
257
|
+
#
|
|
258
|
+
# @example
|
|
259
|
+
# headers.empty?
|
|
161
260
|
#
|
|
162
261
|
# @return [Boolean]
|
|
262
|
+
# @api public
|
|
163
263
|
def_delegator :@pile, :empty?
|
|
164
264
|
|
|
165
265
|
# @!method hash
|
|
166
|
-
#
|
|
167
|
-
#
|
|
266
|
+
# Computes a hash-code for this headers container
|
|
267
|
+
#
|
|
268
|
+
# @example
|
|
269
|
+
# headers.hash
|
|
168
270
|
#
|
|
169
271
|
# @see http://www.ruby-doc.org/core/Object.html#method-i-hash
|
|
170
272
|
# @return [Fixnum]
|
|
273
|
+
# @api public
|
|
171
274
|
def_delegator :@pile, :hash
|
|
172
275
|
|
|
173
|
-
# Properly clones internal key/value storage
|
|
276
|
+
# Properly clones internal key/value storage
|
|
174
277
|
#
|
|
278
|
+
# @return [void]
|
|
175
279
|
# @api private
|
|
176
|
-
def initialize_copy(
|
|
177
|
-
super
|
|
280
|
+
def initialize_copy(_orig)
|
|
178
281
|
@pile = @pile.map(&:dup)
|
|
179
282
|
end
|
|
180
283
|
|
|
181
|
-
# Merges
|
|
284
|
+
# Merges other headers into self
|
|
285
|
+
#
|
|
286
|
+
# @example
|
|
287
|
+
# headers.merge!("Accept" => "text/html")
|
|
182
288
|
#
|
|
183
289
|
# @see #merge
|
|
184
290
|
# @return [void]
|
|
291
|
+
# @api public
|
|
185
292
|
def merge!(other)
|
|
186
|
-
self.class.coerce(other)
|
|
293
|
+
coerced = self.class.coerce(other)
|
|
294
|
+
names = coerced.keys
|
|
295
|
+
names.each { |name| set name, coerced.get(name) }
|
|
187
296
|
end
|
|
188
297
|
|
|
189
|
-
# Returns new instance with
|
|
298
|
+
# Returns new instance with other headers merged in
|
|
299
|
+
#
|
|
300
|
+
# @example
|
|
301
|
+
# new_headers = headers.merge("Accept" => "text/html")
|
|
190
302
|
#
|
|
191
303
|
# @see #merge!
|
|
192
304
|
# @return [Headers]
|
|
305
|
+
# @api public
|
|
193
306
|
def merge(other)
|
|
194
307
|
dup.tap { |dupped| dupped.merge! other }
|
|
195
308
|
end
|
|
196
309
|
|
|
197
|
-
|
|
198
|
-
# Coerces given `object` into Headers.
|
|
199
|
-
#
|
|
200
|
-
# @raise [Error] if object can't be coerced
|
|
201
|
-
# @param [#to_hash, #to_h, #to_a] object
|
|
202
|
-
# @return [Headers]
|
|
203
|
-
def coerce(object)
|
|
204
|
-
unless object.is_a? self
|
|
205
|
-
object = case
|
|
206
|
-
when object.respond_to?(:to_hash) then object.to_hash
|
|
207
|
-
when object.respond_to?(:to_h) then object.to_h
|
|
208
|
-
when object.respond_to?(:to_a) then object.to_a
|
|
209
|
-
else raise Error, "Can't coerce #{object.inspect} to Headers"
|
|
210
|
-
end
|
|
211
|
-
end
|
|
310
|
+
private
|
|
212
311
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
312
|
+
# Returns the wire name for a header
|
|
313
|
+
#
|
|
314
|
+
# @return [String]
|
|
315
|
+
# @api private
|
|
316
|
+
def wire_name_for(name, lookup_name)
|
|
317
|
+
case name
|
|
318
|
+
when String then name
|
|
319
|
+
when Symbol then lookup_name
|
|
320
|
+
else raise HeaderError, "HTTP header must be a String or Symbol: #{name.inspect}"
|
|
216
321
|
end
|
|
217
|
-
alias [] coerce
|
|
218
322
|
end
|
|
219
323
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
# Transforms `name` to canonical HTTP header capitalization
|
|
324
|
+
# Transforms name to canonical HTTP header capitalization
|
|
223
325
|
#
|
|
224
|
-
# @
|
|
225
|
-
# @
|
|
226
|
-
|
|
227
|
-
# @return [String] canonical HTTP header name
|
|
228
|
-
def normalize_header(name)
|
|
229
|
-
return name if name =~ CANONICAL_NAME_RE
|
|
230
|
-
|
|
231
|
-
normalized = name.split(/[\-_]/).each(&:capitalize!).join("-")
|
|
232
|
-
|
|
233
|
-
return normalized if normalized =~ COMPLIANT_NAME_RE
|
|
234
|
-
|
|
235
|
-
raise HeaderError, "Invalid HTTP header field name: #{name.inspect}"
|
|
236
|
-
end
|
|
326
|
+
# @return [String]
|
|
327
|
+
# @api private
|
|
328
|
+
def normalize_header(name) = self.class.normalizer.call(name)
|
|
237
329
|
|
|
238
330
|
# Ensures there is no new line character in the header value
|
|
239
331
|
#
|
|
240
332
|
# @param [String] value
|
|
241
333
|
# @raise [HeaderError] if value includes new line character
|
|
242
334
|
# @return [String] stringified header value
|
|
335
|
+
# @api private
|
|
243
336
|
def validate_value(value)
|
|
244
337
|
v = value.to_s
|
|
245
|
-
return v unless v.include?("\n")
|
|
338
|
+
return v unless v.include?("\n") || v.include?("\r")
|
|
246
339
|
|
|
247
340
|
raise HeaderError, "Invalid HTTP header field value: #{v.inspect}"
|
|
248
341
|
end
|
|
@@ -11,18 +11,33 @@ module HTTP
|
|
|
11
11
|
|
|
12
12
|
class << self
|
|
13
13
|
extend Forwardable
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
def_delegators :instance, :encode, :decode # steep:ignore
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Encodes data into the MIME type format
|
|
19
|
+
#
|
|
20
|
+
# @example
|
|
21
|
+
# adapter.encode("foo" => "bar")
|
|
22
|
+
#
|
|
23
|
+
# @return [String] encoded representation
|
|
24
|
+
# @raise [Error] if not implemented by subclass
|
|
25
|
+
# @api public
|
|
26
|
+
def encode(*)
|
|
27
|
+
raise Error, "#{self.class} does not supports #encode"
|
|
15
28
|
end
|
|
16
29
|
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
30
|
+
# Decodes data from the MIME type format
|
|
31
|
+
#
|
|
32
|
+
# @example
|
|
33
|
+
# adapter.decode("{\"foo\":\"bar\"}")
|
|
34
|
+
#
|
|
35
|
+
# @return [Object] decoded data
|
|
36
|
+
# @raise [Error] if not implemented by subclass
|
|
37
|
+
# @api public
|
|
38
|
+
def decode(*)
|
|
39
|
+
raise Error, "#{self.class} does not supports #decode"
|
|
24
40
|
end
|
|
25
|
-
# rubocop:enable Style/DocumentDynamicEvalDefinition
|
|
26
41
|
end
|
|
27
42
|
end
|
|
28
43
|
end
|
data/lib/http/mime_type/json.rb
CHANGED
|
@@ -4,17 +4,32 @@ require "json"
|
|
|
4
4
|
require "http/mime_type/adapter"
|
|
5
5
|
|
|
6
6
|
module HTTP
|
|
7
|
+
# MIME type registry and adapter interface
|
|
7
8
|
module MimeType
|
|
8
9
|
# JSON encode/decode MIME type adapter
|
|
9
10
|
class JSON < Adapter
|
|
10
11
|
# Encodes object to JSON
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# adapter = HTTP::MimeType::JSON.new
|
|
15
|
+
# adapter.encode(foo: "bar")
|
|
16
|
+
#
|
|
17
|
+
# @param [Object] obj object to encode
|
|
18
|
+
# @api public
|
|
19
|
+
# @return [String]
|
|
11
20
|
def encode(obj)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
::JSON.dump obj
|
|
21
|
+
obj.to_json
|
|
15
22
|
end
|
|
16
23
|
|
|
17
|
-
# Decodes JSON
|
|
24
|
+
# Decodes JSON string into Ruby object
|
|
25
|
+
#
|
|
26
|
+
# @example
|
|
27
|
+
# adapter = HTTP::MimeType::JSON.new
|
|
28
|
+
# adapter.decode('{"foo":"bar"}')
|
|
29
|
+
#
|
|
30
|
+
# @param [String] str JSON string to decode
|
|
31
|
+
# @api public
|
|
32
|
+
# @return [Object]
|
|
18
33
|
def decode(str)
|
|
19
34
|
::JSON.parse str
|
|
20
35
|
end
|
data/lib/http/mime_type.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "http/errors"
|
|
4
|
+
|
|
3
5
|
module HTTP
|
|
4
6
|
# MIME type encode/decode adapters
|
|
5
7
|
module MimeType
|
|
@@ -24,6 +26,7 @@ module HTTP
|
|
|
24
26
|
#
|
|
25
27
|
# @param [#to_s] type
|
|
26
28
|
# @param [#encode, #decode] adapter
|
|
29
|
+
# @api public
|
|
27
30
|
# @return [void]
|
|
28
31
|
def register_adapter(type, adapter)
|
|
29
32
|
adapters[type.to_s] = adapter
|
|
@@ -31,11 +34,15 @@ module HTTP
|
|
|
31
34
|
|
|
32
35
|
# Returns adapter associated with MIME type
|
|
33
36
|
#
|
|
37
|
+
# @example
|
|
38
|
+
# HTTP::MimeType["application/json"]
|
|
39
|
+
#
|
|
34
40
|
# @param [#to_s] type
|
|
35
41
|
# @raise [Error] if no adapter found
|
|
42
|
+
# @api public
|
|
36
43
|
# @return [Class]
|
|
37
44
|
def [](type)
|
|
38
|
-
adapters[normalize type] || raise(
|
|
45
|
+
adapters[normalize type] || raise(UnsupportedMimeTypeError, "Unknown MIME type: #{type}")
|
|
39
46
|
end
|
|
40
47
|
|
|
41
48
|
# Register a shortcut for MIME type
|
|
@@ -46,6 +53,7 @@ module HTTP
|
|
|
46
53
|
#
|
|
47
54
|
# @param [#to_s] type
|
|
48
55
|
# @param [#to_sym] shortcut
|
|
56
|
+
# @api public
|
|
49
57
|
# @return [void]
|
|
50
58
|
def register_alias(type, shortcut)
|
|
51
59
|
aliases[shortcut.to_sym] = type.to_s
|
|
@@ -53,7 +61,11 @@ module HTTP
|
|
|
53
61
|
|
|
54
62
|
# Resolves type by shortcut if possible
|
|
55
63
|
#
|
|
64
|
+
# @example
|
|
65
|
+
# HTTP::MimeType.normalize(:json)
|
|
66
|
+
#
|
|
56
67
|
# @param [#to_s] type
|
|
68
|
+
# @api public
|
|
57
69
|
# @return [String]
|
|
58
70
|
def normalize(type)
|
|
59
71
|
aliases.fetch type, type.to_s
|
|
@@ -61,12 +73,18 @@ module HTTP
|
|
|
61
73
|
|
|
62
74
|
private
|
|
63
75
|
|
|
64
|
-
#
|
|
76
|
+
# Returns the adapters registry hash
|
|
77
|
+
#
|
|
78
|
+
# @api private
|
|
79
|
+
# @return [Hash]
|
|
65
80
|
def adapters
|
|
66
81
|
@adapters ||= {}
|
|
67
82
|
end
|
|
68
83
|
|
|
69
|
-
#
|
|
84
|
+
# Returns the aliases registry hash
|
|
85
|
+
#
|
|
86
|
+
# @api private
|
|
87
|
+
# @return [Hash]
|
|
70
88
|
def aliases
|
|
71
89
|
@aliases ||= {}
|
|
72
90
|
end
|