faraday 1.10.1 → 2.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +198 -4
- data/LICENSE.md +1 -1
- data/README.md +34 -20
- data/Rakefile +6 -1
- data/examples/client_spec.rb +41 -19
- data/examples/client_test.rb +48 -22
- data/lib/faraday/adapter/test.rb +62 -13
- data/lib/faraday/adapter.rb +6 -10
- data/lib/faraday/connection.rb +72 -150
- data/lib/faraday/encoders/nested_params_encoder.rb +14 -7
- data/lib/faraday/error.rb +24 -5
- data/lib/faraday/logging/formatter.rb +28 -15
- data/lib/faraday/middleware.rb +43 -2
- data/lib/faraday/middleware_registry.rb +17 -63
- data/lib/faraday/options/connection_options.rb +7 -6
- data/lib/faraday/options/env.rb +85 -62
- data/lib/faraday/options/proxy_options.rb +7 -3
- data/lib/faraday/options/request_options.rb +7 -6
- data/lib/faraday/options/ssl_options.rb +59 -45
- data/lib/faraday/options.rb +7 -6
- data/lib/faraday/rack_builder.rb +23 -21
- data/lib/faraday/request/authorization.rb +33 -41
- data/lib/faraday/request/instrumentation.rb +5 -1
- data/lib/faraday/request/json.rb +18 -3
- data/lib/faraday/request/url_encoded.rb +5 -1
- data/lib/faraday/request.rb +15 -31
- data/lib/faraday/response/json.rb +25 -5
- data/lib/faraday/response/logger.rb +6 -0
- data/lib/faraday/response/raise_error.rb +45 -18
- data/lib/faraday/response.rb +9 -21
- data/lib/faraday/utils/headers.rb +15 -4
- data/lib/faraday/utils.rb +11 -7
- data/lib/faraday/version.rb +1 -1
- data/lib/faraday.rb +8 -44
- data/spec/faraday/adapter/test_spec.rb +65 -0
- data/spec/faraday/connection_spec.rb +165 -93
- data/spec/faraday/error_spec.rb +31 -6
- data/spec/faraday/middleware_registry_spec.rb +31 -0
- data/spec/faraday/middleware_spec.rb +161 -0
- data/spec/faraday/options/env_spec.rb +8 -2
- data/spec/faraday/options/options_spec.rb +1 -1
- data/spec/faraday/options/proxy_options_spec.rb +8 -0
- data/spec/faraday/params_encoders/nested_spec.rb +10 -1
- data/spec/faraday/rack_builder_spec.rb +26 -54
- data/spec/faraday/request/authorization_spec.rb +50 -28
- data/spec/faraday/request/instrumentation_spec.rb +5 -7
- data/spec/faraday/request/json_spec.rb +88 -0
- data/spec/faraday/request/url_encoded_spec.rb +12 -2
- data/spec/faraday/request_spec.rb +5 -15
- data/spec/faraday/response/json_spec.rb +93 -6
- data/spec/faraday/response/logger_spec.rb +38 -0
- data/spec/faraday/response/raise_error_spec.rb +111 -5
- data/spec/faraday/response_spec.rb +3 -1
- data/spec/faraday/utils/headers_spec.rb +31 -4
- data/spec/faraday/utils_spec.rb +64 -1
- data/spec/faraday_spec.rb +10 -4
- data/spec/spec_helper.rb +5 -6
- data/spec/support/fake_safe_buffer.rb +1 -1
- data/spec/support/faraday_middleware_subclasses.rb +18 -0
- data/spec/support/helper_methods.rb +0 -37
- data/spec/support/shared_examples/adapter.rb +2 -2
- data/spec/support/shared_examples/request_method.rb +22 -21
- metadata +24 -145
- data/lib/faraday/adapter/typhoeus.rb +0 -15
- data/lib/faraday/autoload.rb +0 -87
- data/lib/faraday/dependency_loader.rb +0 -37
- data/lib/faraday/deprecate.rb +0 -109
- data/lib/faraday/request/basic_authentication.rb +0 -20
- data/lib/faraday/request/token_authentication.rb +0 -20
- data/spec/faraday/adapter/em_http_spec.rb +0 -49
- data/spec/faraday/adapter/em_synchrony_spec.rb +0 -18
- data/spec/faraday/adapter/excon_spec.rb +0 -49
- data/spec/faraday/adapter/httpclient_spec.rb +0 -73
- data/spec/faraday/adapter/net_http_spec.rb +0 -64
- data/spec/faraday/adapter/patron_spec.rb +0 -18
- data/spec/faraday/adapter/rack_spec.rb +0 -8
- data/spec/faraday/adapter/typhoeus_spec.rb +0 -7
- data/spec/faraday/composite_read_io_spec.rb +0 -80
- data/spec/faraday/deprecate_spec.rb +0 -147
- data/spec/faraday/response/middleware_spec.rb +0 -68
- data/spec/support/webmock_rack_app.rb +0 -68
@@ -6,28 +6,32 @@ module Faraday
|
|
6
6
|
# client or server error responses.
|
7
7
|
class RaiseError < Middleware
|
8
8
|
# rubocop:disable Naming/ConstantName
|
9
|
-
ClientErrorStatuses = (400...500)
|
10
|
-
ServerErrorStatuses = (500...600)
|
9
|
+
ClientErrorStatuses = (400...500)
|
10
|
+
ServerErrorStatuses = (500...600)
|
11
|
+
ClientErrorStatusesWithCustomExceptions = {
|
12
|
+
400 => Faraday::BadRequestError,
|
13
|
+
401 => Faraday::UnauthorizedError,
|
14
|
+
403 => Faraday::ForbiddenError,
|
15
|
+
404 => Faraday::ResourceNotFound,
|
16
|
+
408 => Faraday::RequestTimeoutError,
|
17
|
+
409 => Faraday::ConflictError,
|
18
|
+
422 => Faraday::UnprocessableEntityError,
|
19
|
+
429 => Faraday::TooManyRequestsError
|
20
|
+
}.freeze
|
11
21
|
# rubocop:enable Naming/ConstantName
|
12
22
|
|
23
|
+
DEFAULT_OPTIONS = { include_request: true, allowed_statuses: [] }.freeze
|
24
|
+
|
13
25
|
def on_complete(env)
|
26
|
+
return if Array(options[:allowed_statuses]).include?(env[:status])
|
27
|
+
|
14
28
|
case env[:status]
|
15
|
-
when
|
16
|
-
raise
|
17
|
-
when 401
|
18
|
-
raise Faraday::UnauthorizedError, response_values(env)
|
19
|
-
when 403
|
20
|
-
raise Faraday::ForbiddenError, response_values(env)
|
21
|
-
when 404
|
22
|
-
raise Faraday::ResourceNotFound, response_values(env)
|
29
|
+
when *ClientErrorStatusesWithCustomExceptions.keys
|
30
|
+
raise ClientErrorStatusesWithCustomExceptions[env[:status]], response_values(env)
|
23
31
|
when 407
|
24
32
|
# mimic the behavior that we get with proxy requests with HTTPS
|
25
33
|
msg = %(407 "Proxy Authentication Required")
|
26
34
|
raise Faraday::ProxyAuthError.new(msg, response_values(env))
|
27
|
-
when 409
|
28
|
-
raise Faraday::ConflictError, response_values(env)
|
29
|
-
when 422
|
30
|
-
raise Faraday::UnprocessableEntityError, response_values(env)
|
31
35
|
when ClientErrorStatuses
|
32
36
|
raise Faraday::ClientError, response_values(env)
|
33
37
|
when ServerErrorStatuses
|
@@ -37,20 +41,43 @@ module Faraday
|
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
44
|
+
# Returns a hash of response data with the following keys:
|
45
|
+
# - status
|
46
|
+
# - headers
|
47
|
+
# - body
|
48
|
+
# - request
|
49
|
+
#
|
50
|
+
# The `request` key is omitted when the middleware is explicitly
|
51
|
+
# configured with the option `include_request: false`.
|
40
52
|
def response_values(env)
|
41
|
-
{
|
53
|
+
response = {
|
42
54
|
status: env.status,
|
43
55
|
headers: env.response_headers,
|
44
|
-
body: env.body
|
56
|
+
body: env.body
|
57
|
+
}
|
58
|
+
|
59
|
+
# Include the request data by default. If the middleware was explicitly
|
60
|
+
# configured to _not_ include request data, then omit it.
|
61
|
+
return response unless options[:include_request]
|
62
|
+
|
63
|
+
response.merge(
|
45
64
|
request: {
|
46
65
|
method: env.method,
|
66
|
+
url: env.url,
|
47
67
|
url_path: env.url.path,
|
48
|
-
params: env
|
68
|
+
params: query_params(env),
|
49
69
|
headers: env.request_headers,
|
50
70
|
body: env.request_body
|
51
71
|
}
|
52
|
-
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
def query_params(env)
|
76
|
+
env.request.params_encoder ||= Faraday::Utils.default_params_encoder
|
77
|
+
env.params_encoder.decode(env.url.query)
|
53
78
|
end
|
54
79
|
end
|
55
80
|
end
|
56
81
|
end
|
82
|
+
|
83
|
+
Faraday::Response.register_middleware(raise_error: Faraday::Response::RaiseError)
|
data/lib/faraday/response.rb
CHANGED
@@ -5,26 +5,9 @@ require 'forwardable'
|
|
5
5
|
module Faraday
|
6
6
|
# Response represents an HTTP response from making an HTTP request.
|
7
7
|
class Response
|
8
|
-
# Used for simple response middleware.
|
9
|
-
class Middleware < Faraday::Middleware
|
10
|
-
# Override this to modify the environment after the response has finished.
|
11
|
-
# Calls the `parse` method if defined
|
12
|
-
# `parse` method can be defined as private, public and protected
|
13
|
-
def on_complete(env)
|
14
|
-
return unless respond_to?(:parse, true) && env.parse_body?
|
15
|
-
|
16
|
-
env.body = parse(env.body)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
8
|
extend Forwardable
|
21
9
|
extend MiddlewareRegistry
|
22
10
|
|
23
|
-
register_middleware File.expand_path('response', __dir__),
|
24
|
-
raise_error: [:RaiseError, 'raise_error'],
|
25
|
-
logger: [:Logger, 'logger'],
|
26
|
-
json: [:Json, 'json']
|
27
|
-
|
28
11
|
def initialize(env = nil)
|
29
12
|
@env = Env.from(env) if env
|
30
13
|
@on_complete_callbacks = []
|
@@ -55,10 +38,10 @@ module Faraday
|
|
55
38
|
end
|
56
39
|
|
57
40
|
def on_complete(&block)
|
58
|
-
if
|
59
|
-
@on_complete_callbacks << block
|
60
|
-
else
|
41
|
+
if finished?
|
61
42
|
yield(env)
|
43
|
+
else
|
44
|
+
@on_complete_callbacks << block
|
62
45
|
end
|
63
46
|
self
|
64
47
|
end
|
@@ -78,7 +61,8 @@ module Faraday
|
|
78
61
|
def to_hash
|
79
62
|
{
|
80
63
|
status: env.status, body: env.body,
|
81
|
-
response_headers: env.response_headers
|
64
|
+
response_headers: env.response_headers,
|
65
|
+
url: env.url
|
82
66
|
}
|
83
67
|
end
|
84
68
|
|
@@ -101,3 +85,7 @@ module Faraday
|
|
101
85
|
end
|
102
86
|
end
|
103
87
|
end
|
88
|
+
|
89
|
+
require 'faraday/response/json'
|
90
|
+
require 'faraday/response/logger'
|
91
|
+
require 'faraday/response/raise_error'
|
@@ -62,10 +62,10 @@ module Faraday
|
|
62
62
|
super(key, val)
|
63
63
|
end
|
64
64
|
|
65
|
-
def fetch(key,
|
65
|
+
def fetch(key, ...)
|
66
66
|
key = KeyMap[key]
|
67
67
|
key = @names.fetch(key.downcase, key)
|
68
|
-
super(key,
|
68
|
+
super(key, ...)
|
69
69
|
end
|
70
70
|
|
71
71
|
def delete(key)
|
@@ -77,6 +77,12 @@ module Faraday
|
|
77
77
|
super(key)
|
78
78
|
end
|
79
79
|
|
80
|
+
def dig(key, *rest)
|
81
|
+
key = KeyMap[key]
|
82
|
+
key = @names.fetch(key.downcase, key)
|
83
|
+
super(key, *rest)
|
84
|
+
end
|
85
|
+
|
80
86
|
def include?(key)
|
81
87
|
@names.include? key.downcase
|
82
88
|
end
|
@@ -111,7 +117,7 @@ module Faraday
|
|
111
117
|
def parse(header_string)
|
112
118
|
return unless header_string && !header_string.empty?
|
113
119
|
|
114
|
-
headers = header_string.split(
|
120
|
+
headers = header_string.split("\r\n")
|
115
121
|
|
116
122
|
# Find the last set of response headers.
|
117
123
|
start_index = headers.rindex { |x| x.start_with?('HTTP/') } || 0
|
@@ -132,7 +138,12 @@ module Faraday
|
|
132
138
|
|
133
139
|
# Join multiple values with a comma.
|
134
140
|
def add_parsed(key, value)
|
135
|
-
|
141
|
+
if key?(key)
|
142
|
+
self[key] = self[key].to_s
|
143
|
+
self[key] << ', ' << value
|
144
|
+
else
|
145
|
+
self[key] = value
|
146
|
+
end
|
136
147
|
end
|
137
148
|
end
|
138
149
|
end
|
data/lib/faraday/utils.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'uri'
|
3
4
|
require 'faraday/utils/headers'
|
4
5
|
require 'faraday/utils/params_hash'
|
5
6
|
|
@@ -24,7 +25,7 @@ module Faraday
|
|
24
25
|
attr_writer :default_space_encoding
|
25
26
|
end
|
26
27
|
|
27
|
-
ESCAPE_RE = /[^a-zA-Z0-9 .~_-]
|
28
|
+
ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
|
28
29
|
|
29
30
|
def escape(str)
|
30
31
|
str.to_s.gsub(ESCAPE_RE) do |match|
|
@@ -36,7 +37,7 @@ module Faraday
|
|
36
37
|
CGI.unescape str.to_s
|
37
38
|
end
|
38
39
|
|
39
|
-
DEFAULT_SEP = /[&;] */n
|
40
|
+
DEFAULT_SEP = /[&;] */n
|
40
41
|
|
41
42
|
# Adapted from Rack
|
42
43
|
def parse_query(query)
|
@@ -51,6 +52,12 @@ module Faraday
|
|
51
52
|
@default_params_encoder ||= NestedParamsEncoder
|
52
53
|
end
|
53
54
|
|
55
|
+
def basic_header_from(login, pass)
|
56
|
+
value = ["#{login}:#{pass}"].pack('m') # Base64 encoding
|
57
|
+
value.delete!("\n")
|
58
|
+
"Basic #{value}"
|
59
|
+
end
|
60
|
+
|
54
61
|
class << self
|
55
62
|
attr_writer :default_params_encoder
|
56
63
|
end
|
@@ -71,10 +78,7 @@ module Faraday
|
|
71
78
|
end
|
72
79
|
|
73
80
|
def default_uri_parser
|
74
|
-
@default_uri_parser ||=
|
75
|
-
require 'uri'
|
76
|
-
Kernel.method(:URI)
|
77
|
-
end
|
81
|
+
@default_uri_parser ||= Kernel.method(:URI)
|
78
82
|
end
|
79
83
|
|
80
84
|
def default_uri_parser=(parser)
|
@@ -96,7 +100,7 @@ module Faraday
|
|
96
100
|
# Recursive hash update
|
97
101
|
def deep_merge!(target, hash)
|
98
102
|
hash.each do |key, value|
|
99
|
-
target[key] = if value.is_a?(Hash) && target[key].is_a?(Hash)
|
103
|
+
target[key] = if value.is_a?(Hash) && (target[key].is_a?(Hash) || target[key].is_a?(Options))
|
100
104
|
deep_merge(target[key], value)
|
101
105
|
else
|
102
106
|
value
|
data/lib/faraday/version.rb
CHANGED
data/lib/faraday.rb
CHANGED
@@ -4,16 +4,10 @@ require 'cgi'
|
|
4
4
|
require 'date'
|
5
5
|
require 'set'
|
6
6
|
require 'forwardable'
|
7
|
-
require 'faraday/middleware_registry'
|
8
|
-
require 'faraday/dependency_loader'
|
9
|
-
|
10
|
-
unless defined?(::Faraday::Timer)
|
11
|
-
require 'timeout'
|
12
|
-
::Faraday::Timer = Timeout
|
13
|
-
end
|
14
|
-
|
15
7
|
require 'faraday/version'
|
16
8
|
require 'faraday/methods'
|
9
|
+
require 'faraday/error'
|
10
|
+
require 'faraday/middleware_registry'
|
17
11
|
require 'faraday/utils'
|
18
12
|
require 'faraday/options'
|
19
13
|
require 'faraday/connection'
|
@@ -23,27 +17,7 @@ require 'faraday/middleware'
|
|
23
17
|
require 'faraday/adapter'
|
24
18
|
require 'faraday/request'
|
25
19
|
require 'faraday/response'
|
26
|
-
require 'faraday/error'
|
27
|
-
require 'faraday/request/url_encoded' # needed by multipart
|
28
|
-
|
29
|
-
# External Middleware gems and their aliases
|
30
|
-
require 'faraday/multipart'
|
31
|
-
require 'faraday/retry'
|
32
|
-
Faraday::Request::Multipart = Faraday::Multipart::Middleware
|
33
|
-
Faraday::Request::Retry = Faraday::Retry::Middleware
|
34
|
-
|
35
|
-
# External Adapters gems
|
36
|
-
unless defined?(JRUBY_VERSION)
|
37
|
-
require 'faraday/em_http'
|
38
|
-
require 'faraday/em_synchrony'
|
39
|
-
end
|
40
|
-
require 'faraday/excon'
|
41
|
-
require 'faraday/httpclient'
|
42
20
|
require 'faraday/net_http'
|
43
|
-
require 'faraday/net_http_persistent'
|
44
|
-
require 'faraday/patron'
|
45
|
-
require 'faraday/rack'
|
46
|
-
|
47
21
|
# This is the main namespace for Faraday.
|
48
22
|
#
|
49
23
|
# It provides methods to create {Connection} objects, and HTTP-related
|
@@ -80,6 +54,10 @@ module Faraday
|
|
80
54
|
# @return [Symbol] the new default_adapter.
|
81
55
|
attr_reader :default_adapter
|
82
56
|
|
57
|
+
# Option for the default_adapter
|
58
|
+
# @return [Hash] default_adapter options
|
59
|
+
attr_accessor :default_adapter_options
|
60
|
+
|
83
61
|
# Documented below, see default_connection
|
84
62
|
attr_writer :default_connection
|
85
63
|
|
@@ -116,23 +94,10 @@ module Faraday
|
|
116
94
|
# params: { page: 1 }
|
117
95
|
# # => Faraday::Connection to http://faraday.com?page=1
|
118
96
|
def new(url = nil, options = {}, &block)
|
119
|
-
options =
|
97
|
+
options = Utils.deep_merge(default_connection_options, options)
|
120
98
|
Faraday::Connection.new(url, options, &block)
|
121
99
|
end
|
122
100
|
|
123
|
-
# @private
|
124
|
-
# Internal: Requires internal Faraday libraries.
|
125
|
-
#
|
126
|
-
# @param libs [Array] one or more relative String names to Faraday classes.
|
127
|
-
# @return [void]
|
128
|
-
def require_libs(*libs)
|
129
|
-
libs.each do |lib|
|
130
|
-
require "#{lib_path}/#{lib}"
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
alias require_lib require_libs
|
135
|
-
|
136
101
|
# Documented elsewhere, see default_adapter reader
|
137
102
|
def default_adapter=(adapter)
|
138
103
|
@default_connection = nil
|
@@ -188,6 +153,5 @@ module Faraday
|
|
188
153
|
self.root_path = File.expand_path __dir__
|
189
154
|
self.lib_path = File.expand_path 'faraday', __dir__
|
190
155
|
self.default_adapter = :net_http
|
191
|
-
|
192
|
-
require_lib 'autoload' unless ENV['FARADAY_NO_AUTOLOAD']
|
156
|
+
self.default_adapter_options = {}
|
193
157
|
end
|
@@ -373,5 +373,70 @@ RSpec.describe Faraday::Adapter::Test do
|
|
373
373
|
it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent' }
|
374
374
|
it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent', x_special: 'special' }
|
375
375
|
end
|
376
|
+
|
377
|
+
describe 'body_match?' do
|
378
|
+
let(:stubs) do
|
379
|
+
described_class::Stubs.new do |stubs|
|
380
|
+
stubs.post('/no_check') { [200, {}, 'ok'] }
|
381
|
+
stubs.post('/with_string', 'abc') { [200, {}, 'ok'] }
|
382
|
+
stubs.post(
|
383
|
+
'/with_proc',
|
384
|
+
->(request_body) { JSON.parse(request_body, symbolize_names: true) == { x: '!', a: [{ m: [{ a: true }], n: 123 }] } },
|
385
|
+
{ content_type: 'application/json' }
|
386
|
+
) do
|
387
|
+
[200, {}, 'ok']
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
context 'when trying without any args for body' do
|
393
|
+
subject(:without_body) { connection.post('/no_check') }
|
394
|
+
|
395
|
+
it { expect(without_body.status).to eq 200 }
|
396
|
+
end
|
397
|
+
|
398
|
+
context 'when trying with string body stubs' do
|
399
|
+
subject(:with_string) { connection.post('/with_string', 'abc') }
|
400
|
+
|
401
|
+
it { expect(with_string.status).to eq 200 }
|
402
|
+
end
|
403
|
+
|
404
|
+
context 'when trying with proc body stubs' do
|
405
|
+
subject(:with_proc) do
|
406
|
+
connection.post('/with_proc', JSON.dump(a: [{ n: 123, m: [{ a: true }] }], x: '!'), { 'Content-Type' => 'application/json' })
|
407
|
+
end
|
408
|
+
|
409
|
+
it { expect(with_proc.status).to eq 200 }
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
describe 'request timeout' do
|
415
|
+
subject(:request) do
|
416
|
+
connection.get('/sleep') do |req|
|
417
|
+
req.options.timeout = timeout
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
before do
|
422
|
+
stubs.get('/sleep') do
|
423
|
+
sleep(0.01)
|
424
|
+
[200, {}, '']
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
context 'when request is within timeout' do
|
429
|
+
let(:timeout) { 1 }
|
430
|
+
|
431
|
+
it { expect(request.status).to eq 200 }
|
432
|
+
end
|
433
|
+
|
434
|
+
context 'when request is too slow' do
|
435
|
+
let(:timeout) { 0.001 }
|
436
|
+
|
437
|
+
it 'raises an exception' do
|
438
|
+
expect { request }.to raise_error(Faraday::TimeoutError)
|
439
|
+
end
|
440
|
+
end
|
376
441
|
end
|
377
442
|
end
|