faraday 0.7.4 → 1.0.1
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 +276 -0
- data/LICENSE.md +1 -1
- data/README.md +40 -153
- data/Rakefile +4 -139
- data/examples/client_spec.rb +65 -0
- data/examples/client_test.rb +79 -0
- data/lib/faraday/adapter/em_http.rb +286 -0
- data/lib/faraday/adapter/em_http_ssl_patch.rb +62 -0
- data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +69 -0
- data/lib/faraday/adapter/em_synchrony.rb +120 -36
- data/lib/faraday/adapter/excon.rb +108 -12
- data/lib/faraday/adapter/httpclient.rb +152 -0
- data/lib/faraday/adapter/net_http.rb +187 -43
- data/lib/faraday/adapter/net_http_persistent.rb +91 -0
- data/lib/faraday/adapter/patron.rb +106 -10
- data/lib/faraday/adapter/rack.rb +75 -0
- data/lib/faraday/adapter/test.rb +160 -61
- data/lib/faraday/adapter/typhoeus.rb +7 -46
- data/lib/faraday/adapter.rb +105 -33
- data/lib/faraday/adapter_registry.rb +30 -0
- data/lib/faraday/autoload.rb +95 -0
- data/lib/faraday/connection.rb +525 -157
- data/lib/faraday/dependency_loader.rb +37 -0
- data/lib/faraday/encoders/flat_params_encoder.rb +98 -0
- data/lib/faraday/encoders/nested_params_encoder.rb +171 -0
- data/lib/faraday/error.rb +122 -30
- data/lib/faraday/file_part.rb +128 -0
- data/lib/faraday/logging/formatter.rb +105 -0
- data/lib/faraday/middleware.rb +14 -22
- data/lib/faraday/middleware_registry.rb +129 -0
- data/lib/faraday/options/connection_options.rb +22 -0
- data/lib/faraday/options/env.rb +181 -0
- data/lib/faraday/options/proxy_options.rb +28 -0
- data/lib/faraday/options/request_options.rb +22 -0
- data/lib/faraday/options/ssl_options.rb +59 -0
- data/lib/faraday/options.rb +222 -0
- data/lib/faraday/param_part.rb +53 -0
- data/lib/faraday/parameters.rb +5 -0
- data/lib/faraday/rack_builder.rb +248 -0
- data/lib/faraday/request/authorization.rb +55 -0
- data/lib/faraday/request/basic_authentication.rb +20 -0
- data/lib/faraday/request/instrumentation.rb +54 -0
- data/lib/faraday/request/multipart.rb +84 -48
- data/lib/faraday/request/retry.rb +239 -0
- data/lib/faraday/request/token_authentication.rb +20 -0
- data/lib/faraday/request/url_encoded.rb +46 -27
- data/lib/faraday/request.rb +112 -50
- data/lib/faraday/response/logger.rb +24 -25
- data/lib/faraday/response/raise_error.rb +40 -11
- data/lib/faraday/response.rb +44 -35
- data/lib/faraday/utils/headers.rb +139 -0
- data/lib/faraday/utils/params_hash.rb +61 -0
- data/lib/faraday/utils.rb +72 -117
- data/lib/faraday.rb +142 -64
- data/spec/external_adapters/faraday_specs_setup.rb +14 -0
- data/spec/faraday/adapter/em_http_spec.rb +47 -0
- data/spec/faraday/adapter/em_synchrony_spec.rb +16 -0
- data/spec/faraday/adapter/excon_spec.rb +49 -0
- data/spec/faraday/adapter/httpclient_spec.rb +73 -0
- data/spec/faraday/adapter/net_http_persistent_spec.rb +57 -0
- data/spec/faraday/adapter/net_http_spec.rb +64 -0
- data/spec/faraday/adapter/patron_spec.rb +18 -0
- data/spec/faraday/adapter/rack_spec.rb +8 -0
- data/spec/faraday/adapter/typhoeus_spec.rb +7 -0
- data/spec/faraday/adapter_registry_spec.rb +28 -0
- data/spec/faraday/adapter_spec.rb +55 -0
- data/spec/faraday/composite_read_io_spec.rb +80 -0
- data/spec/faraday/connection_spec.rb +691 -0
- data/spec/faraday/error_spec.rb +45 -0
- data/spec/faraday/middleware_spec.rb +26 -0
- data/spec/faraday/options/env_spec.rb +70 -0
- data/spec/faraday/options/options_spec.rb +297 -0
- data/spec/faraday/options/proxy_options_spec.rb +37 -0
- data/spec/faraday/options/request_options_spec.rb +19 -0
- data/spec/faraday/params_encoders/flat_spec.rb +34 -0
- data/spec/faraday/params_encoders/nested_spec.rb +134 -0
- data/spec/faraday/rack_builder_spec.rb +196 -0
- data/spec/faraday/request/authorization_spec.rb +88 -0
- data/spec/faraday/request/instrumentation_spec.rb +76 -0
- data/spec/faraday/request/multipart_spec.rb +274 -0
- data/spec/faraday/request/retry_spec.rb +242 -0
- data/spec/faraday/request/url_encoded_spec.rb +83 -0
- data/spec/faraday/request_spec.rb +109 -0
- data/spec/faraday/response/logger_spec.rb +220 -0
- data/spec/faraday/response/middleware_spec.rb +68 -0
- data/spec/faraday/response/raise_error_spec.rb +106 -0
- data/spec/faraday/response_spec.rb +75 -0
- data/spec/faraday/utils/headers_spec.rb +82 -0
- data/spec/faraday/utils_spec.rb +56 -0
- data/spec/faraday_spec.rb +37 -0
- data/spec/spec_helper.rb +132 -0
- data/spec/support/disabling_stub.rb +14 -0
- data/spec/support/fake_safe_buffer.rb +15 -0
- data/spec/support/helper_methods.rb +133 -0
- data/spec/support/shared_examples/adapter.rb +104 -0
- data/spec/support/shared_examples/params_encoder.rb +18 -0
- data/spec/support/shared_examples/request_method.rb +234 -0
- data/spec/support/streaming_response_checker.rb +35 -0
- data/spec/support/webmock_rack_app.rb +68 -0
- metadata +126 -126
- data/Gemfile +0 -29
- data/config.ru +0 -6
- data/faraday.gemspec +0 -92
- data/lib/faraday/adapter/action_dispatch.rb +0 -29
- data/lib/faraday/builder.rb +0 -160
- data/lib/faraday/request/json.rb +0 -35
- data/lib/faraday/upload_io.rb +0 -23
- data/test/adapters/live_test.rb +0 -205
- data/test/adapters/logger_test.rb +0 -37
- data/test/adapters/net_http_test.rb +0 -33
- data/test/adapters/test_middleware_test.rb +0 -70
- data/test/connection_test.rb +0 -254
- data/test/env_test.rb +0 -158
- data/test/helper.rb +0 -41
- data/test/live_server.rb +0 -45
- data/test/middleware_stack_test.rb +0 -118
- data/test/request_middleware_test.rb +0 -116
- data/test/response_middleware_test.rb +0 -74
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Faraday
|
|
4
|
+
# Multipart value used to POST data with a content type.
|
|
5
|
+
class ParamPart
|
|
6
|
+
# @param value [String] Uploaded content as a String.
|
|
7
|
+
# @param content_type [String] String content type of the value.
|
|
8
|
+
# @param content_id [String] Optional String of this value's Content-ID.
|
|
9
|
+
#
|
|
10
|
+
# @return [Faraday::ParamPart]
|
|
11
|
+
def initialize(value, content_type, content_id = nil)
|
|
12
|
+
@value = value
|
|
13
|
+
@content_type = content_type
|
|
14
|
+
@content_id = content_id
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Converts this value to a form part.
|
|
18
|
+
#
|
|
19
|
+
# @param boundary [String] String multipart boundary that must not exist in
|
|
20
|
+
# the content exactly.
|
|
21
|
+
# @param key [String] String key name for this value.
|
|
22
|
+
#
|
|
23
|
+
# @return [Faraday::Parts::Part]
|
|
24
|
+
def to_part(boundary, key)
|
|
25
|
+
Faraday::Parts::Part.new(boundary, key, value, headers)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Returns a Hash of String key/value pairs.
|
|
29
|
+
#
|
|
30
|
+
# @return [Hash]
|
|
31
|
+
def headers
|
|
32
|
+
{
|
|
33
|
+
'Content-Type' => content_type,
|
|
34
|
+
'Content-ID' => content_id
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# The content to upload.
|
|
39
|
+
#
|
|
40
|
+
# @return [String]
|
|
41
|
+
attr_reader :value
|
|
42
|
+
|
|
43
|
+
# The value's content type.
|
|
44
|
+
#
|
|
45
|
+
# @return [String]
|
|
46
|
+
attr_reader :content_type
|
|
47
|
+
|
|
48
|
+
# The value's content ID, if given.
|
|
49
|
+
#
|
|
50
|
+
# @return [String, nil]
|
|
51
|
+
attr_reader :content_id
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'faraday/adapter_registry'
|
|
4
|
+
|
|
5
|
+
module Faraday
|
|
6
|
+
# A Builder that processes requests into responses by passing through an inner
|
|
7
|
+
# middleware stack (heavily inspired by Rack).
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# Faraday::Connection.new(url: 'http://sushi.com') do |builder|
|
|
11
|
+
# builder.request :url_encoded # Faraday::Request::UrlEncoded
|
|
12
|
+
# builder.adapter :net_http # Faraday::Adapter::NetHttp
|
|
13
|
+
# end
|
|
14
|
+
class RackBuilder
|
|
15
|
+
# Used to detect missing arguments
|
|
16
|
+
NO_ARGUMENT = Object.new
|
|
17
|
+
|
|
18
|
+
attr_accessor :handlers
|
|
19
|
+
|
|
20
|
+
# Error raised when trying to modify the stack after calling `lock!`
|
|
21
|
+
class StackLocked < RuntimeError; end
|
|
22
|
+
|
|
23
|
+
# borrowed from ActiveSupport::Dependencies::Reference &
|
|
24
|
+
# ActionDispatch::MiddlewareStack::Middleware
|
|
25
|
+
class Handler
|
|
26
|
+
REGISTRY = Faraday::AdapterRegistry.new
|
|
27
|
+
|
|
28
|
+
attr_reader :name
|
|
29
|
+
|
|
30
|
+
def initialize(klass, *args, &block)
|
|
31
|
+
@name = klass.to_s
|
|
32
|
+
REGISTRY.set(klass) if klass.respond_to?(:name)
|
|
33
|
+
@args = args
|
|
34
|
+
@block = block
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def klass
|
|
38
|
+
REGISTRY.get(@name)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def inspect
|
|
42
|
+
@name
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def ==(other)
|
|
46
|
+
if other.is_a? Handler
|
|
47
|
+
name == other.name
|
|
48
|
+
elsif other.respond_to? :name
|
|
49
|
+
klass == other
|
|
50
|
+
else
|
|
51
|
+
@name == other.to_s
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def build(app = nil)
|
|
56
|
+
klass.new(app, *@args, &@block)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def initialize(handlers = [], adapter = nil, &block)
|
|
61
|
+
@adapter = adapter
|
|
62
|
+
@handlers = handlers
|
|
63
|
+
if block_given?
|
|
64
|
+
build(&block)
|
|
65
|
+
elsif @handlers.empty?
|
|
66
|
+
# default stack, if nothing else is configured
|
|
67
|
+
request :url_encoded
|
|
68
|
+
self.adapter Faraday.default_adapter
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def build(options = {})
|
|
73
|
+
raise_if_locked
|
|
74
|
+
@handlers.clear unless options[:keep]
|
|
75
|
+
yield(self) if block_given?
|
|
76
|
+
adapter(Faraday.default_adapter) unless @adapter
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def [](idx)
|
|
80
|
+
@handlers[idx]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Locks the middleware stack to ensure no further modifications are made.
|
|
84
|
+
def lock!
|
|
85
|
+
@handlers.freeze
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def locked?
|
|
89
|
+
@handlers.frozen?
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def use(klass, *args, &block)
|
|
93
|
+
if klass.is_a? Symbol
|
|
94
|
+
use_symbol(Faraday::Middleware, klass, *args, &block)
|
|
95
|
+
else
|
|
96
|
+
raise_if_locked
|
|
97
|
+
raise_if_adapter(klass)
|
|
98
|
+
@handlers << self.class::Handler.new(klass, *args, &block)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def request(key, *args, &block)
|
|
103
|
+
use_symbol(Faraday::Request, key, *args, &block)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def response(key, *args, &block)
|
|
107
|
+
use_symbol(Faraday::Response, key, *args, &block)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def adapter(klass = NO_ARGUMENT, *args, &block)
|
|
111
|
+
return @adapter if klass == NO_ARGUMENT
|
|
112
|
+
|
|
113
|
+
klass = Faraday::Adapter.lookup_middleware(klass) if klass.is_a?(Symbol)
|
|
114
|
+
@adapter = self.class::Handler.new(klass, *args, &block)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
## methods to push onto the various positions in the stack:
|
|
118
|
+
|
|
119
|
+
def insert(index, *args, &block)
|
|
120
|
+
raise_if_locked
|
|
121
|
+
index = assert_index(index)
|
|
122
|
+
handler = self.class::Handler.new(*args, &block)
|
|
123
|
+
@handlers.insert(index, handler)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
alias insert_before insert
|
|
127
|
+
|
|
128
|
+
def insert_after(index, *args, &block)
|
|
129
|
+
index = assert_index(index)
|
|
130
|
+
insert(index + 1, *args, &block)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def swap(index, *args, &block)
|
|
134
|
+
raise_if_locked
|
|
135
|
+
index = assert_index(index)
|
|
136
|
+
@handlers.delete_at(index)
|
|
137
|
+
insert(index, *args, &block)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def delete(handler)
|
|
141
|
+
raise_if_locked
|
|
142
|
+
@handlers.delete(handler)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Processes a Request into a Response by passing it through this Builder's
|
|
146
|
+
# middleware stack.
|
|
147
|
+
#
|
|
148
|
+
# @param connection [Faraday::Connection]
|
|
149
|
+
# @param request [Faraday::Request]
|
|
150
|
+
#
|
|
151
|
+
# @return [Faraday::Response]
|
|
152
|
+
def build_response(connection, request)
|
|
153
|
+
app.call(build_env(connection, request))
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# The "rack app" wrapped in middleware. All requests are sent here.
|
|
157
|
+
#
|
|
158
|
+
# The builder is responsible for creating the app object. After this,
|
|
159
|
+
# the builder gets locked to ensure no further modifications are made
|
|
160
|
+
# to the middleware stack.
|
|
161
|
+
#
|
|
162
|
+
# Returns an object that responds to `call` and returns a Response.
|
|
163
|
+
def app
|
|
164
|
+
@app ||= begin
|
|
165
|
+
lock!
|
|
166
|
+
to_app
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def to_app
|
|
171
|
+
# last added handler is the deepest and thus closest to the inner app
|
|
172
|
+
# adapter is always the last one
|
|
173
|
+
@handlers.reverse.inject(@adapter.build) do |app, handler|
|
|
174
|
+
handler.build(app)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def ==(other)
|
|
179
|
+
other.is_a?(self.class) &&
|
|
180
|
+
@handlers == other.handlers &&
|
|
181
|
+
@adapter == other.adapter
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def dup
|
|
185
|
+
self.class.new(@handlers.dup, @adapter.dup)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# ENV Keys
|
|
189
|
+
# :method - a symbolized request method (:get, :post)
|
|
190
|
+
# :body - the request body that will eventually be converted to a string.
|
|
191
|
+
# :url - URI instance for the current request.
|
|
192
|
+
# :status - HTTP response status code
|
|
193
|
+
# :request_headers - hash of HTTP Headers to be sent to the server
|
|
194
|
+
# :response_headers - Hash of HTTP headers from the server
|
|
195
|
+
# :parallel_manager - sent if the connection is in parallel mode
|
|
196
|
+
# :request - Hash of options for configuring the request.
|
|
197
|
+
# :timeout - open/read timeout Integer in seconds
|
|
198
|
+
# :open_timeout - read timeout Integer in seconds
|
|
199
|
+
# :proxy - Hash of proxy options
|
|
200
|
+
# :uri - Proxy Server URI
|
|
201
|
+
# :user - Proxy server username
|
|
202
|
+
# :password - Proxy server password
|
|
203
|
+
# :ssl - Hash of options for configuring SSL requests.
|
|
204
|
+
def build_env(connection, request)
|
|
205
|
+
exclusive_url = connection.build_exclusive_url(
|
|
206
|
+
request.path, request.params,
|
|
207
|
+
request.options.params_encoder
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
Env.new(request.method, request.body, exclusive_url,
|
|
211
|
+
request.options, request.headers, connection.ssl,
|
|
212
|
+
connection.parallel_manager)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
private
|
|
216
|
+
|
|
217
|
+
LOCK_ERR = "can't modify middleware stack after making a request"
|
|
218
|
+
|
|
219
|
+
def raise_if_locked
|
|
220
|
+
raise StackLocked, LOCK_ERR if locked?
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def raise_if_adapter(klass)
|
|
224
|
+
return unless is_adapter?(klass)
|
|
225
|
+
|
|
226
|
+
raise 'Adapter should be set using the `adapter` method, not `use`'
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def adapter_set?
|
|
230
|
+
!@adapter.nil?
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def is_adapter?(klass) # rubocop:disable Naming/PredicateName
|
|
234
|
+
klass.ancestors.include?(Faraday::Adapter)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def use_symbol(mod, key, *args, &block)
|
|
238
|
+
use(mod.lookup_middleware(key), *args, &block)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def assert_index(index)
|
|
242
|
+
idx = index.is_a?(Integer) ? index : @handlers.index(index)
|
|
243
|
+
raise "No such handler: #{index.inspect}" unless idx
|
|
244
|
+
|
|
245
|
+
idx
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Faraday
|
|
4
|
+
class Request
|
|
5
|
+
# Request middleware for the Authorization HTTP header
|
|
6
|
+
class Authorization < Faraday::Middleware
|
|
7
|
+
unless defined?(::Faraday::Request::Authorization::KEY)
|
|
8
|
+
KEY = 'Authorization'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @param type [String, Symbol]
|
|
12
|
+
# @param token [String, Symbol, Hash]
|
|
13
|
+
# @return [String] a header value
|
|
14
|
+
def self.header(type, token)
|
|
15
|
+
case token
|
|
16
|
+
when String, Symbol
|
|
17
|
+
"#{type} #{token}"
|
|
18
|
+
when Hash
|
|
19
|
+
build_hash(type.to_s, token)
|
|
20
|
+
else
|
|
21
|
+
raise ArgumentError,
|
|
22
|
+
"Can't build an Authorization #{type}" \
|
|
23
|
+
"header from #{token.inspect}"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @param type [String]
|
|
28
|
+
# @param hash [Hash]
|
|
29
|
+
# @return [String] type followed by comma-separated key=value pairs
|
|
30
|
+
# @api private
|
|
31
|
+
def self.build_hash(type, hash)
|
|
32
|
+
comma = ', '
|
|
33
|
+
values = []
|
|
34
|
+
hash.each do |key, value|
|
|
35
|
+
values << "#{key}=#{value.to_s.inspect}"
|
|
36
|
+
end
|
|
37
|
+
"#{type} #{values * comma}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @param app [#call]
|
|
41
|
+
# @param type [String, Symbol] Type of Authorization
|
|
42
|
+
# @param token [String, Symbol, Hash] Token value for the Authorization
|
|
43
|
+
def initialize(app, type, token)
|
|
44
|
+
@header_value = self.class.header(type, token)
|
|
45
|
+
super(app)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @param env [Faraday::Env]
|
|
49
|
+
def call(env)
|
|
50
|
+
env.request_headers[KEY] = @header_value unless env.request_headers[KEY]
|
|
51
|
+
@app.call(env)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'base64'
|
|
4
|
+
|
|
5
|
+
module Faraday
|
|
6
|
+
class Request
|
|
7
|
+
# Authorization middleware for Basic Authentication.
|
|
8
|
+
class BasicAuthentication < load_middleware(:authorization)
|
|
9
|
+
# @param login [String]
|
|
10
|
+
# @param pass [String]
|
|
11
|
+
#
|
|
12
|
+
# @return [String] a Basic Authentication header line
|
|
13
|
+
def self.header(login, pass)
|
|
14
|
+
value = Base64.encode64([login, pass].join(':'))
|
|
15
|
+
value.delete!("\n")
|
|
16
|
+
super(:Basic, value)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Faraday
|
|
4
|
+
class Request
|
|
5
|
+
# Middleware for instrumenting Requests.
|
|
6
|
+
class Instrumentation < Faraday::Middleware
|
|
7
|
+
# Options class used in Request::Instrumentation class.
|
|
8
|
+
class Options < Faraday::Options.new(:name, :instrumenter)
|
|
9
|
+
# @return [String]
|
|
10
|
+
def name
|
|
11
|
+
self[:name] ||= 'request.faraday'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @return [Class]
|
|
15
|
+
def instrumenter
|
|
16
|
+
self[:instrumenter] ||= ActiveSupport::Notifications
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Instruments requests using Active Support.
|
|
21
|
+
#
|
|
22
|
+
# Measures time spent only for synchronous requests.
|
|
23
|
+
#
|
|
24
|
+
# @example Using ActiveSupport::Notifications to measure time spent
|
|
25
|
+
# for Faraday requests.
|
|
26
|
+
# ActiveSupport::Notifications
|
|
27
|
+
# .subscribe('request.faraday') do |name, starts, ends, _, env|
|
|
28
|
+
# url = env[:url]
|
|
29
|
+
# http_method = env[:method].to_s.upcase
|
|
30
|
+
# duration = ends - starts
|
|
31
|
+
# $stderr.puts '[%s] %s %s (%.3f s)' %
|
|
32
|
+
# [url.host, http_method, url.request_uri, duration]
|
|
33
|
+
# end
|
|
34
|
+
# @param app [#call]
|
|
35
|
+
# @param options [nil, Hash] Options hash
|
|
36
|
+
# @option options [String] :name ('request.faraday')
|
|
37
|
+
# Name of the instrumenter
|
|
38
|
+
# @option options [Class] :instrumenter (ActiveSupport::Notifications)
|
|
39
|
+
# Active Support instrumenter class.
|
|
40
|
+
def initialize(app, options = nil)
|
|
41
|
+
super(app)
|
|
42
|
+
@name, @instrumenter = Options.from(options)
|
|
43
|
+
.values_at(:name, :instrumenter)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @param env [Faraday::Env]
|
|
47
|
+
def call(env)
|
|
48
|
+
@instrumenter.instrument(@name, env) do
|
|
49
|
+
@app.call(env)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -1,61 +1,97 @@
|
|
|
1
|
-
|
|
2
|
-
class Request::Multipart < Request::UrlEncoded
|
|
3
|
-
self.mime_type = 'multipart/form-data'.freeze
|
|
4
|
-
DEFAULT_BOUNDARY = "-----------RubyMultipartPost".freeze
|
|
5
|
-
|
|
6
|
-
def call(env)
|
|
7
|
-
match_content_type(env) do |params|
|
|
8
|
-
env[:request] ||= {}
|
|
9
|
-
env[:request][:boundary] ||= DEFAULT_BOUNDARY
|
|
10
|
-
env[:request_headers][CONTENT_TYPE] += ";boundary=#{env[:request][:boundary]}"
|
|
11
|
-
env[:body] = create_multipart(env, params)
|
|
12
|
-
end
|
|
13
|
-
@app.call env
|
|
14
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
15
2
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
3
|
+
require File.expand_path('url_encoded', __dir__)
|
|
4
|
+
require 'securerandom'
|
|
5
|
+
|
|
6
|
+
module Faraday
|
|
7
|
+
class Request
|
|
8
|
+
# Middleware for supporting multi-part requests.
|
|
9
|
+
class Multipart < UrlEncoded
|
|
10
|
+
self.mime_type = 'multipart/form-data'
|
|
11
|
+
unless defined?(::Faraday::Request::Multipart::DEFAULT_BOUNDARY_PREFIX)
|
|
12
|
+
DEFAULT_BOUNDARY_PREFIX = '-----------RubyMultipartPost'
|
|
13
|
+
end
|
|
23
14
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
15
|
+
# Checks for files in the payload, otherwise leaves everything untouched.
|
|
16
|
+
#
|
|
17
|
+
# @param env [Faraday::Env]
|
|
18
|
+
def call(env)
|
|
19
|
+
match_content_type(env) do |params|
|
|
20
|
+
env.request.boundary ||= unique_boundary
|
|
21
|
+
env.request_headers[CONTENT_TYPE] +=
|
|
22
|
+
"; boundary=#{env.request.boundary}"
|
|
23
|
+
env.body = create_multipart(env, params)
|
|
30
24
|
end
|
|
25
|
+
@app.call env
|
|
31
26
|
end
|
|
32
|
-
false
|
|
33
|
-
end
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
28
|
+
# @param env [Faraday::Env]
|
|
29
|
+
def process_request?(env)
|
|
30
|
+
type = request_type(env)
|
|
31
|
+
env.body.respond_to?(:each_key) && !env.body.empty? && (
|
|
32
|
+
(type.empty? && has_multipart?(env.body)) ||
|
|
33
|
+
(type == self.class.mime_type)
|
|
34
|
+
)
|
|
39
35
|
end
|
|
40
|
-
parts << Faraday::Parts::EpiloguePart.new(boundary)
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
# Returns true if obj is an enumerable with values that are multipart.
|
|
38
|
+
#
|
|
39
|
+
# @param obj [Object]
|
|
40
|
+
# @return [Boolean]
|
|
41
|
+
def has_multipart?(obj) # rubocop:disable Naming/PredicateName
|
|
42
|
+
if obj.respond_to?(:each)
|
|
43
|
+
(obj.respond_to?(:values) ? obj.values : obj).each do |val|
|
|
44
|
+
return true if val.respond_to?(:content_type) || has_multipart?(val)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
false
|
|
48
|
+
end
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
params
|
|
49
|
-
|
|
50
|
+
# @param env [Faraday::Env]
|
|
51
|
+
# @param params [Hash]
|
|
52
|
+
def create_multipart(env, params)
|
|
53
|
+
boundary = env.request.boundary
|
|
54
|
+
parts = process_params(params) do |key, value|
|
|
55
|
+
part(boundary, key, value)
|
|
56
|
+
end
|
|
57
|
+
parts << Faraday::Parts::EpiloguePart.new(boundary)
|
|
58
|
+
|
|
59
|
+
body = Faraday::CompositeReadIO.new(parts)
|
|
60
|
+
env.request_headers[Faraday::Env::ContentLength] = body.length.to_s
|
|
61
|
+
body
|
|
62
|
+
end
|
|
50
63
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
process_params(values, key, all, &block)
|
|
55
|
-
when Hash
|
|
56
|
-
process_params(value, key, all, &block)
|
|
64
|
+
def part(boundary, key, value)
|
|
65
|
+
if value.respond_to?(:to_part)
|
|
66
|
+
value.to_part(boundary, key)
|
|
57
67
|
else
|
|
58
|
-
|
|
68
|
+
Faraday::Parts::Part.new(boundary, key, value)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# @return [String]
|
|
73
|
+
def unique_boundary
|
|
74
|
+
"#{DEFAULT_BOUNDARY_PREFIX}-#{SecureRandom.hex}"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @param params [Hash]
|
|
78
|
+
# @param prefix [String]
|
|
79
|
+
# @param pieces [Array]
|
|
80
|
+
def process_params(params, prefix = nil, pieces = nil, &block)
|
|
81
|
+
params.inject(pieces || []) do |all, (key, value)|
|
|
82
|
+
key = "#{prefix}[#{key}]" if prefix
|
|
83
|
+
|
|
84
|
+
case value
|
|
85
|
+
when Array
|
|
86
|
+
values = value.inject([]) { |a, v| a << [nil, v] }
|
|
87
|
+
process_params(values, key, all, &block)
|
|
88
|
+
when Hash
|
|
89
|
+
process_params(value, key, all, &block)
|
|
90
|
+
else
|
|
91
|
+
# rubocop:disable Performance/RedundantBlockCall
|
|
92
|
+
all << block.call(key, value)
|
|
93
|
+
# rubocop:enable Performance/RedundantBlockCall
|
|
94
|
+
end
|
|
59
95
|
end
|
|
60
96
|
end
|
|
61
97
|
end
|