http 5.3.1 → 6.0.0
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/CHANGELOG.md +241 -41
- data/LICENSE.txt +1 -1
- data/README.md +110 -13
- data/UPGRADING.md +491 -0
- data/http.gemspec +32 -29
- data/lib/http/base64.rb +11 -1
- data/lib/http/chainable/helpers.rb +62 -0
- data/lib/http/chainable/verbs.rb +136 -0
- data/lib/http/chainable.rb +232 -136
- data/lib/http/client.rb +158 -127
- data/lib/http/connection/internals.rb +141 -0
- data/lib/http/connection.rb +126 -97
- data/lib/http/content_type.rb +61 -6
- data/lib/http/errors.rb +25 -1
- data/lib/http/feature.rb +65 -5
- 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 +18 -3
- 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 +17 -36
- data/lib/http/headers.rb +172 -65
- 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 +76 -16
- data/lib/http/request.rb +214 -98
- 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 +38 -11
- data/lib/http/retriable/errors.rb +21 -0
- data/lib/http/retriable/performer.rb +82 -38
- 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 -2
- 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/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/{spec → test}/support/fakeio.rb +2 -2
- 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/{spec → test}/support/simplecov.rb +11 -2
- data/test/support/ssl_helper.rb +108 -0
- data/test/test_helper.rb +38 -0
- metadata +108 -168
- 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/rspec.yml +0 -9
- data/.rubocop/style.yml +0 -32
- data/.rubocop.yml +0 -11
- data/.rubocop_todo.yml +0 -219
- data/.yardopts +0 -2
- data/CHANGES_OLD.md +0 -1002
- data/Gemfile +0 -51
- data/Guardfile +0 -18
- data/Rakefile +0 -64
- data/lib/http/headers/mixin.rb +0 -34
- data/lib/http/retriable/client.rb +0 -37
- 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/features/raise_error_spec.rb +0 -62
- data/spec/lib/http/headers/mixin_spec.rb +0 -36
- data/spec/lib/http/headers/normalizer_spec.rb +0 -52
- 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 -530
- 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/retriable/delay_calculator_spec.rb +0 -69
- data/spec/lib/http/retriable/performer_spec.rb +0 -302
- 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 -535
- data/spec/regression_specs.rb +0 -24
- data/spec/spec_helper.rb +0 -89
- data/spec/support/black_hole.rb +0 -13
- data/spec/support/dummy_server/servlet.rb +0 -203
- data/spec/support/dummy_server.rb +0 -44
- 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/ssl_helper.rb +0 -104
- /data/{spec → test}/support/capture_warning.rb +0 -0
data/lib/http/headers.rb
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
require "forwardable"
|
|
4
4
|
|
|
5
5
|
require "http/errors"
|
|
6
|
-
require "http/headers/mixin"
|
|
7
6
|
require "http/headers/normalizer"
|
|
8
7
|
require "http/headers/known"
|
|
9
8
|
|
|
@@ -14,32 +13,56 @@ module HTTP
|
|
|
14
13
|
include Enumerable
|
|
15
14
|
|
|
16
15
|
class << self
|
|
17
|
-
# Coerces given
|
|
16
|
+
# Coerces given object into Headers
|
|
17
|
+
#
|
|
18
|
+
# @example
|
|
19
|
+
# headers = HTTP::Headers.coerce("Content-Type" => "text/plain")
|
|
18
20
|
#
|
|
19
21
|
# @raise [Error] if object can't be coerced
|
|
20
22
|
# @param [#to_hash, #to_h, #to_a] object
|
|
21
23
|
# @return [Headers]
|
|
24
|
+
# @api public
|
|
22
25
|
def coerce(object)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
end
|
|
30
|
-
end
|
|
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
|
|
31
|
+
|
|
31
32
|
headers = new
|
|
32
33
|
object.each { |k, v| headers.add k, v }
|
|
33
34
|
headers
|
|
34
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
|
|
35
45
|
alias [] coerce
|
|
36
46
|
|
|
47
|
+
# Returns the shared normalizer instance
|
|
48
|
+
#
|
|
49
|
+
# @example
|
|
50
|
+
# HTTP::Headers.normalizer
|
|
51
|
+
#
|
|
52
|
+
# @return [Headers::Normalizer]
|
|
53
|
+
# @api public
|
|
37
54
|
def normalizer
|
|
38
|
-
@normalizer ||= Headers::Normalizer
|
|
55
|
+
@normalizer ||= Normalizer.new #: Headers::Normalizer
|
|
39
56
|
end
|
|
40
57
|
end
|
|
41
58
|
|
|
42
|
-
#
|
|
59
|
+
# Creates a new empty headers container
|
|
60
|
+
#
|
|
61
|
+
# @example
|
|
62
|
+
# headers = HTTP::Headers.new
|
|
63
|
+
#
|
|
64
|
+
# @return [Headers]
|
|
65
|
+
# @api public
|
|
43
66
|
def initialize
|
|
44
67
|
# The @pile stores each header value using a three element array:
|
|
45
68
|
# 0 - the normalized header key, used for lookup
|
|
@@ -48,26 +71,46 @@ module HTTP
|
|
|
48
71
|
@pile = []
|
|
49
72
|
end
|
|
50
73
|
|
|
51
|
-
# Sets header
|
|
74
|
+
# Sets header, replacing any existing values
|
|
75
|
+
#
|
|
76
|
+
# @example
|
|
77
|
+
# headers.set("Content-Type", "text/plain")
|
|
52
78
|
#
|
|
53
79
|
# @param (see #add)
|
|
54
80
|
# @return [void]
|
|
81
|
+
# @api public
|
|
55
82
|
def set(name, value)
|
|
56
83
|
delete(name)
|
|
57
84
|
add(name, value)
|
|
58
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
|
|
59
95
|
alias []= set
|
|
60
96
|
|
|
61
|
-
# Removes header
|
|
97
|
+
# Removes header with the given name
|
|
98
|
+
#
|
|
99
|
+
# @example
|
|
100
|
+
# headers.delete("Content-Type")
|
|
62
101
|
#
|
|
63
102
|
# @param [#to_s] name header name
|
|
64
103
|
# @return [void]
|
|
104
|
+
# @api public
|
|
65
105
|
def delete(name)
|
|
66
|
-
name = normalize_header name
|
|
67
|
-
@pile.delete_if { |k, _| k
|
|
106
|
+
name = normalize_header name
|
|
107
|
+
@pile.delete_if { |k, _| k.eql?(name) }
|
|
68
108
|
end
|
|
69
109
|
|
|
70
|
-
# Appends header
|
|
110
|
+
# Appends header value(s) to the given name
|
|
111
|
+
#
|
|
112
|
+
# @example
|
|
113
|
+
# headers.add("Accept", "text/html")
|
|
71
114
|
#
|
|
72
115
|
# @param [String, Symbol] name header name. When specified as a string, the
|
|
73
116
|
# name is sent as-is. When specified as a symbol, the name is converted
|
|
@@ -77,16 +120,11 @@ module HTTP
|
|
|
77
120
|
# is sent as `"auth_key"`.
|
|
78
121
|
# @param [Array<#to_s>, #to_s] value header value(s) to be appended
|
|
79
122
|
# @return [void]
|
|
123
|
+
# @api public
|
|
80
124
|
def add(name, value)
|
|
81
|
-
lookup_name = normalize_header(name
|
|
82
|
-
wire_name =
|
|
83
|
-
|
|
84
|
-
name
|
|
85
|
-
when Symbol
|
|
86
|
-
lookup_name
|
|
87
|
-
else
|
|
88
|
-
raise HTTP::HeaderError, "HTTP header must be a String or Symbol: #{name.inspect}"
|
|
89
|
-
end
|
|
125
|
+
lookup_name = normalize_header(name)
|
|
126
|
+
wire_name = wire_name_for(name, lookup_name)
|
|
127
|
+
|
|
90
128
|
Array(value).each do |v|
|
|
91
129
|
@pile << [
|
|
92
130
|
lookup_name,
|
|
@@ -96,139 +134,208 @@ module HTTP
|
|
|
96
134
|
end
|
|
97
135
|
end
|
|
98
136
|
|
|
99
|
-
# Returns list of header values if any
|
|
137
|
+
# Returns list of header values if any
|
|
138
|
+
#
|
|
139
|
+
# @example
|
|
140
|
+
# headers.get("Content-Type")
|
|
100
141
|
#
|
|
101
142
|
# @return [Array<String>]
|
|
143
|
+
# @api public
|
|
102
144
|
def get(name)
|
|
103
|
-
name = normalize_header name
|
|
104
|
-
@pile.
|
|
145
|
+
name = normalize_header name
|
|
146
|
+
@pile.filter_map { |k, _, v| v if k.eql?(name) }
|
|
105
147
|
end
|
|
106
148
|
|
|
107
|
-
# Smart version of {#get}
|
|
149
|
+
# Smart version of {#get}
|
|
150
|
+
#
|
|
151
|
+
# @example
|
|
152
|
+
# headers["Content-Type"]
|
|
108
153
|
#
|
|
109
154
|
# @return [nil] if header was not set
|
|
110
155
|
# @return [String] if header has exactly one value
|
|
111
156
|
# @return [Array<String>] if header has more than one value
|
|
157
|
+
# @api public
|
|
112
158
|
def [](name)
|
|
113
159
|
values = get(name)
|
|
160
|
+
return if values.empty?
|
|
114
161
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
else values
|
|
119
|
-
end
|
|
162
|
+
return values unless values.one?
|
|
163
|
+
|
|
164
|
+
values.join
|
|
120
165
|
end
|
|
121
166
|
|
|
122
|
-
# Tells
|
|
167
|
+
# Tells whether header with given name is set
|
|
168
|
+
#
|
|
169
|
+
# @example
|
|
170
|
+
# headers.include?("Content-Type")
|
|
123
171
|
#
|
|
124
172
|
# @return [Boolean]
|
|
173
|
+
# @api public
|
|
125
174
|
def include?(name)
|
|
126
|
-
name = normalize_header name
|
|
127
|
-
@pile.any? { |k, _| k
|
|
175
|
+
name = normalize_header name
|
|
176
|
+
@pile.any? { |k, _| k.eql?(name) }
|
|
128
177
|
end
|
|
129
178
|
|
|
130
179
|
# Returns Rack-compatible headers Hash
|
|
131
180
|
#
|
|
181
|
+
# @example
|
|
182
|
+
# headers.to_h
|
|
183
|
+
#
|
|
132
184
|
# @return [Hash]
|
|
185
|
+
# @api public
|
|
133
186
|
def to_h
|
|
134
187
|
keys.to_h { |k| [k, self[k]] }
|
|
135
188
|
end
|
|
189
|
+
# @!method to_hash
|
|
190
|
+
# @see #to_h
|
|
191
|
+
# @return [Hash]
|
|
136
192
|
alias to_hash to_h
|
|
137
193
|
|
|
138
|
-
#
|
|
194
|
+
# Pattern matching interface
|
|
139
195
|
#
|
|
140
|
-
# @
|
|
141
|
-
|
|
142
|
-
|
|
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
|
|
143
205
|
end
|
|
144
206
|
|
|
145
|
-
# Returns human-readable representation of
|
|
207
|
+
# Returns human-readable representation of self instance
|
|
208
|
+
#
|
|
209
|
+
# @example
|
|
210
|
+
# headers.inspect
|
|
146
211
|
#
|
|
147
212
|
# @return [String]
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
end
|
|
213
|
+
# @api public
|
|
214
|
+
def inspect = "#<#{self.class}>"
|
|
151
215
|
|
|
152
|
-
# Returns list of header names
|
|
216
|
+
# Returns list of header names
|
|
217
|
+
#
|
|
218
|
+
# @example
|
|
219
|
+
# headers.keys
|
|
153
220
|
#
|
|
154
221
|
# @return [Array<String>]
|
|
222
|
+
# @api public
|
|
155
223
|
def keys
|
|
156
224
|
@pile.map { |_, k, _| k }.uniq
|
|
157
225
|
end
|
|
158
226
|
|
|
159
|
-
# 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
|
|
160
231
|
#
|
|
161
232
|
# @return [Boolean]
|
|
233
|
+
# @api public
|
|
162
234
|
def ==(other)
|
|
163
235
|
return false unless other.respond_to? :to_a
|
|
164
236
|
|
|
165
|
-
to_a
|
|
237
|
+
to_a.eql?(other.to_a)
|
|
166
238
|
end
|
|
167
239
|
|
|
168
|
-
# 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}" }
|
|
169
244
|
#
|
|
170
245
|
# @return [Enumerator] if no block given
|
|
171
246
|
# @return [Headers] self-reference
|
|
247
|
+
# @api public
|
|
172
248
|
def each
|
|
173
|
-
return to_enum
|
|
249
|
+
return to_enum unless block_given?
|
|
174
250
|
|
|
175
|
-
@pile.each { |item| yield(item
|
|
251
|
+
@pile.each { |item| yield(item.drop(1)) }
|
|
176
252
|
self
|
|
177
253
|
end
|
|
178
254
|
|
|
179
255
|
# @!method empty?
|
|
180
|
-
# Returns
|
|
256
|
+
# Returns true if self has no key/value pairs
|
|
257
|
+
#
|
|
258
|
+
# @example
|
|
259
|
+
# headers.empty?
|
|
181
260
|
#
|
|
182
261
|
# @return [Boolean]
|
|
262
|
+
# @api public
|
|
183
263
|
def_delegator :@pile, :empty?
|
|
184
264
|
|
|
185
265
|
# @!method hash
|
|
186
|
-
#
|
|
187
|
-
#
|
|
266
|
+
# Computes a hash-code for this headers container
|
|
267
|
+
#
|
|
268
|
+
# @example
|
|
269
|
+
# headers.hash
|
|
188
270
|
#
|
|
189
271
|
# @see http://www.ruby-doc.org/core/Object.html#method-i-hash
|
|
190
272
|
# @return [Fixnum]
|
|
273
|
+
# @api public
|
|
191
274
|
def_delegator :@pile, :hash
|
|
192
275
|
|
|
193
|
-
# Properly clones internal key/value storage
|
|
276
|
+
# Properly clones internal key/value storage
|
|
194
277
|
#
|
|
278
|
+
# @return [void]
|
|
195
279
|
# @api private
|
|
196
|
-
def initialize_copy(
|
|
197
|
-
super
|
|
280
|
+
def initialize_copy(_orig)
|
|
198
281
|
@pile = @pile.map(&:dup)
|
|
199
282
|
end
|
|
200
283
|
|
|
201
|
-
# Merges
|
|
284
|
+
# Merges other headers into self
|
|
285
|
+
#
|
|
286
|
+
# @example
|
|
287
|
+
# headers.merge!("Accept" => "text/html")
|
|
202
288
|
#
|
|
203
289
|
# @see #merge
|
|
204
290
|
# @return [void]
|
|
291
|
+
# @api public
|
|
205
292
|
def merge!(other)
|
|
206
|
-
self.class.coerce(other)
|
|
293
|
+
coerced = self.class.coerce(other)
|
|
294
|
+
names = coerced.keys
|
|
295
|
+
names.each { |name| set name, coerced.get(name) }
|
|
207
296
|
end
|
|
208
297
|
|
|
209
|
-
# Returns new instance with
|
|
298
|
+
# Returns new instance with other headers merged in
|
|
299
|
+
#
|
|
300
|
+
# @example
|
|
301
|
+
# new_headers = headers.merge("Accept" => "text/html")
|
|
210
302
|
#
|
|
211
303
|
# @see #merge!
|
|
212
304
|
# @return [Headers]
|
|
305
|
+
# @api public
|
|
213
306
|
def merge(other)
|
|
214
307
|
dup.tap { |dupped| dupped.merge! other }
|
|
215
308
|
end
|
|
216
309
|
|
|
217
310
|
private
|
|
218
311
|
|
|
219
|
-
#
|
|
220
|
-
|
|
221
|
-
|
|
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}"
|
|
321
|
+
end
|
|
222
322
|
end
|
|
223
323
|
|
|
324
|
+
# Transforms name to canonical HTTP header capitalization
|
|
325
|
+
#
|
|
326
|
+
# @return [String]
|
|
327
|
+
# @api private
|
|
328
|
+
def normalize_header(name) = self.class.normalizer.call(name)
|
|
329
|
+
|
|
224
330
|
# Ensures there is no new line character in the header value
|
|
225
331
|
#
|
|
226
332
|
# @param [String] value
|
|
227
333
|
# @raise [HeaderError] if value includes new line character
|
|
228
334
|
# @return [String] stringified header value
|
|
335
|
+
# @api private
|
|
229
336
|
def validate_value(value)
|
|
230
337
|
v = value.to_s
|
|
231
|
-
return v unless v.include?("\n")
|
|
338
|
+
return v unless v.include?("\n") || v.include?("\r")
|
|
232
339
|
|
|
233
340
|
raise HeaderError, "Invalid HTTP header field value: #{v.inspect}"
|
|
234
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
|