faraday 0.7.6 → 0.8.0.rc2
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.
- data/Gemfile +1 -4
- data/README.md +148 -131
- data/Rakefile +12 -60
- data/faraday.gemspec +11 -32
- data/lib/faraday.rb +59 -12
- data/lib/faraday/adapter.rb +17 -1
- data/lib/faraday/adapter/em_http.rb +204 -0
- data/lib/faraday/adapter/em_synchrony.rb +66 -16
- data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +66 -0
- data/lib/faraday/adapter/net_http.rb +1 -1
- data/lib/faraday/adapter/test.rb +13 -6
- data/lib/faraday/adapter/typhoeus.rb +64 -29
- data/lib/faraday/builder.rb +12 -17
- data/lib/faraday/connection.rb +131 -102
- data/lib/faraday/middleware.rb +17 -9
- data/lib/faraday/request.rb +40 -25
- data/lib/faraday/request/basic_authentication.rb +17 -0
- data/lib/faraday/request/retry.rb +21 -0
- data/lib/faraday/request/token_authentication.rb +21 -0
- data/lib/faraday/response.rb +2 -1
- data/lib/faraday/utils.rb +122 -18
- data/test/adapters/live_test.rb +35 -30
- data/test/adapters/logger_test.rb +2 -2
- data/test/adapters/typhoeus_test.rb +1 -1
- data/test/authentication_middleware_test.rb +48 -0
- data/test/connection_test.rb +171 -83
- data/test/env_test.rb +28 -3
- data/test/helper.rb +1 -1
- data/test/middleware/retry_test.rb +25 -0
- data/test/middleware_stack_test.rb +56 -2
- data/test/request_middleware_test.rb +2 -47
- data/test/response_middleware_test.rb +4 -4
- metadata +25 -39
- data/lib/faraday/request/json.rb +0 -35
data/faraday.gemspec
CHANGED
@@ -1,47 +1,22 @@
|
|
1
|
-
## This is the rakegem gemspec template. Make sure you read and understand
|
2
|
-
## all of the comments. Some sections require modification, and others can
|
3
|
-
## be deleted if you don't need them. Once you understand the contents of
|
4
|
-
## this file, feel free to delete any comments that begin with two hash marks.
|
5
|
-
## You can find comprehensive Gem::Specification documentation, at
|
6
|
-
## http://docs.rubygems.org/read/chapter/20
|
7
1
|
Gem::Specification.new do |s|
|
8
2
|
s.specification_version = 2 if s.respond_to? :specification_version=
|
9
3
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.3.5") if s.respond_to? :required_rubygems_version=
|
10
4
|
|
11
|
-
|
12
|
-
|
13
|
-
## the sub! line in the Rakefile
|
14
|
-
s.name = 'faraday'
|
15
|
-
s.version = '0.7.6'
|
16
|
-
s.date = '2012-01-21'
|
17
|
-
s.rubyforge_project = 'faraday'
|
5
|
+
s.name = 'faraday'
|
6
|
+
s.version = '0.8.0.rc2'
|
18
7
|
|
19
|
-
## Make sure your summary is short. The description may be as long
|
20
|
-
## as you like.
|
21
8
|
s.summary = "HTTP/REST API client library."
|
22
|
-
s.description
|
9
|
+
# TODO: s.description
|
23
10
|
|
24
|
-
## List the primary authors. If there are a bunch of authors, it's probably
|
25
|
-
## better to set the email to an email list or something. If you don't have
|
26
|
-
## a custom homepage, consider using your GitHub URL or the like.
|
27
11
|
s.authors = ["Rick Olson"]
|
28
12
|
s.email = 'technoweenie@gmail.com'
|
29
|
-
s.homepage = '
|
13
|
+
s.homepage = 'https://github.com/technoweenie/faraday'
|
30
14
|
|
31
|
-
## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as
|
32
|
-
## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'
|
33
|
-
s.require_paths = %w[lib]
|
34
|
-
|
35
|
-
s.add_dependency 'addressable', '~> 2.2'
|
36
15
|
s.add_dependency 'multipart-post', '~> 1.1'
|
37
|
-
s.add_dependency 'rack', '~> 1.1'
|
38
16
|
s.add_development_dependency 'rake'
|
39
17
|
s.add_development_dependency 'test-unit'
|
40
18
|
s.add_development_dependency 'webmock'
|
41
19
|
|
42
|
-
## Leave this section as-is. It will be automatically generated from the
|
43
|
-
## contents of your Git repository via the gemspec task. DO NOT REMOVE
|
44
|
-
## THE MANIFEST COMMENTS, they are used as delimiters by the task.
|
45
20
|
# = MANIFEST =
|
46
21
|
s.files = %w[
|
47
22
|
Gemfile
|
@@ -53,7 +28,9 @@ Gem::Specification.new do |s|
|
|
53
28
|
lib/faraday.rb
|
54
29
|
lib/faraday/adapter.rb
|
55
30
|
lib/faraday/adapter/action_dispatch.rb
|
31
|
+
lib/faraday/adapter/em_http.rb
|
56
32
|
lib/faraday/adapter/em_synchrony.rb
|
33
|
+
lib/faraday/adapter/em_synchrony/parallel_manager.rb
|
57
34
|
lib/faraday/adapter/excon.rb
|
58
35
|
lib/faraday/adapter/net_http.rb
|
59
36
|
lib/faraday/adapter/patron.rb
|
@@ -64,8 +41,10 @@ Gem::Specification.new do |s|
|
|
64
41
|
lib/faraday/error.rb
|
65
42
|
lib/faraday/middleware.rb
|
66
43
|
lib/faraday/request.rb
|
67
|
-
lib/faraday/request/
|
44
|
+
lib/faraday/request/basic_authentication.rb
|
68
45
|
lib/faraday/request/multipart.rb
|
46
|
+
lib/faraday/request/retry.rb
|
47
|
+
lib/faraday/request/token_authentication.rb
|
69
48
|
lib/faraday/request/url_encoded.rb
|
70
49
|
lib/faraday/response.rb
|
71
50
|
lib/faraday/response/logger.rb
|
@@ -77,17 +56,17 @@ Gem::Specification.new do |s|
|
|
77
56
|
test/adapters/net_http_test.rb
|
78
57
|
test/adapters/test_middleware_test.rb
|
79
58
|
test/adapters/typhoeus_test.rb
|
59
|
+
test/authentication_middleware_test.rb
|
80
60
|
test/connection_test.rb
|
81
61
|
test/env_test.rb
|
82
62
|
test/helper.rb
|
83
63
|
test/live_server.rb
|
64
|
+
test/middleware/retry_test.rb
|
84
65
|
test/middleware_stack_test.rb
|
85
66
|
test/request_middleware_test.rb
|
86
67
|
test/response_middleware_test.rb
|
87
68
|
]
|
88
69
|
# = MANIFEST =
|
89
70
|
|
90
|
-
## Test files will be grabbed from the file list. Make sure the path glob
|
91
|
-
## matches what you actually use.
|
92
71
|
s.test_files = s.files.select { |path| path =~ %r{^test/*/.+\.rb} }
|
93
72
|
end
|
data/lib/faraday.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
module Faraday
|
2
|
-
VERSION = "0.
|
2
|
+
VERSION = "0.8.0.rc2"
|
3
3
|
|
4
4
|
class << self
|
5
|
+
attr_accessor :root_path, :lib_path
|
5
6
|
attr_accessor :default_adapter
|
6
7
|
attr_writer :default_connection
|
7
8
|
|
@@ -10,29 +11,54 @@ module Faraday
|
|
10
11
|
Faraday::Connection.new(url, options, &block)
|
11
12
|
end
|
12
13
|
|
14
|
+
def require_libs(*libs)
|
15
|
+
libs.each do |lib|
|
16
|
+
require "#{lib_path}/#{lib}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
alias require_lib require_libs
|
21
|
+
|
13
22
|
private
|
14
23
|
def method_missing(name, *args, &block)
|
15
24
|
default_connection.send(name, *args, &block)
|
16
25
|
end
|
17
26
|
end
|
18
27
|
|
28
|
+
self.root_path = File.expand_path "..", __FILE__
|
29
|
+
self.lib_path = File.expand_path "../faraday", __FILE__
|
19
30
|
self.default_adapter = :net_http
|
20
31
|
|
21
32
|
def self.default_connection
|
22
33
|
@default_connection ||= Connection.new
|
23
34
|
end
|
24
35
|
|
25
|
-
module
|
26
|
-
|
27
|
-
|
36
|
+
module MiddlewareRegistry
|
37
|
+
# Internal: Register middleware class(es) on the current module.
|
38
|
+
#
|
39
|
+
# mapping - A Hash mapping Symbol keys to classes. See
|
40
|
+
# Faraday.register_middleware for more details.
|
41
|
+
def register_middleware(mapping)
|
42
|
+
(@registered_middleware ||= {}).update(mapping)
|
28
43
|
end
|
29
44
|
|
30
|
-
|
31
|
-
|
32
|
-
|
45
|
+
# Internal: Lookup middleware class with a registered Symbol shortcut.
|
46
|
+
#
|
47
|
+
# Returns a middleware Class.
|
48
|
+
def lookup_middleware(key)
|
49
|
+
unless defined? @registered_middleware and found = @registered_middleware[key]
|
50
|
+
raise "#{key.inspect} is not registered on #{self}"
|
51
|
+
end
|
52
|
+
found = @registered_middleware[key] = found.call if found.is_a? Proc
|
53
|
+
found.is_a?(Module) ? found : const_get(found)
|
33
54
|
end
|
55
|
+
end
|
34
56
|
|
57
|
+
module AutoloadHelper
|
35
58
|
def autoload_all(prefix, options)
|
59
|
+
if prefix =~ /^faraday(\/|$)/i
|
60
|
+
prefix = File.join(Faraday.root_path, prefix)
|
61
|
+
end
|
36
62
|
options.each do |const_name, path|
|
37
63
|
autoload const_name, File.join(prefix, path)
|
38
64
|
end
|
@@ -54,7 +80,30 @@ module Faraday
|
|
54
80
|
|
55
81
|
extend AutoloadHelper
|
56
82
|
|
57
|
-
|
83
|
+
# Public: register middleware classes under a short name.
|
84
|
+
#
|
85
|
+
# type - A Symbol specifying the kind of middleware (default: :middleware)
|
86
|
+
# mapping - A Hash mapping Symbol keys to classes. Classes can be expressed
|
87
|
+
# as fully qualified constant, or a Proc that will be lazily called
|
88
|
+
# to return the former.
|
89
|
+
#
|
90
|
+
# Examples
|
91
|
+
#
|
92
|
+
# Faraday.register_middleware :aloha => MyModule::Aloha
|
93
|
+
# Faraday.register_middleware :response, :boom => MyModule::Boom
|
94
|
+
#
|
95
|
+
# # shortcuts are now available in Builder:
|
96
|
+
# builder.use :aloha
|
97
|
+
# builder.response :boom
|
98
|
+
#
|
99
|
+
# Returns nothing.
|
100
|
+
def self.register_middleware type, mapping = nil
|
101
|
+
type, mapping = :middleware, type if mapping.nil?
|
102
|
+
component = self.const_get(type.to_s.capitalize)
|
103
|
+
component.register_middleware(mapping)
|
104
|
+
end
|
105
|
+
|
106
|
+
autoload_all "faraday",
|
58
107
|
:Middleware => 'middleware',
|
59
108
|
:Builder => 'builder',
|
60
109
|
:Request => 'request',
|
@@ -62,12 +111,10 @@ module Faraday
|
|
62
111
|
:CompositeReadIO => 'upload_io',
|
63
112
|
:UploadIO => 'upload_io',
|
64
113
|
:Parts => 'upload_io'
|
114
|
+
|
115
|
+
require_libs "utils", "connection", "adapter", "error"
|
65
116
|
end
|
66
117
|
|
67
|
-
require 'faraday/utils'
|
68
|
-
require 'faraday/connection'
|
69
|
-
require 'faraday/adapter'
|
70
|
-
require 'faraday/error'
|
71
118
|
|
72
119
|
# not pulling in active-support JUST for this method.
|
73
120
|
class Object
|
data/lib/faraday/adapter.rb
CHANGED
@@ -3,25 +3,41 @@ module Faraday
|
|
3
3
|
CONTENT_LENGTH = 'Content-Length'.freeze
|
4
4
|
|
5
5
|
extend AutoloadHelper
|
6
|
+
extend MiddlewareRegistry
|
6
7
|
|
7
8
|
autoload_all 'faraday/adapter',
|
8
9
|
:ActionDispatch => 'action_dispatch',
|
9
10
|
:NetHttp => 'net_http',
|
10
11
|
:Typhoeus => 'typhoeus',
|
11
12
|
:EMSynchrony => 'em_synchrony',
|
13
|
+
:EMHttp => 'em_http',
|
12
14
|
:Patron => 'patron',
|
13
15
|
:Excon => 'excon',
|
14
16
|
:Test => 'test'
|
15
17
|
|
16
|
-
|
18
|
+
register_middleware \
|
17
19
|
:action_dispatch => :ActionDispatch,
|
18
20
|
:test => :Test,
|
19
21
|
:net_http => :NetHttp,
|
20
22
|
:typhoeus => :Typhoeus,
|
21
23
|
:patron => :Patron,
|
22
24
|
:em_synchrony => :EMSynchrony,
|
25
|
+
:em_http => :EMHttp,
|
23
26
|
:excon => :Excon
|
24
27
|
|
28
|
+
module Parallelism
|
29
|
+
attr_writer :supports_parallel
|
30
|
+
def supports_parallel?() @supports_parallel end
|
31
|
+
|
32
|
+
def inherited(subclass)
|
33
|
+
super
|
34
|
+
subclass.supports_parallel = self.supports_parallel?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
extend Parallelism
|
39
|
+
self.supports_parallel = false
|
40
|
+
|
25
41
|
def call(env)
|
26
42
|
if !env[:body] and Connection::METHODS_WITH_BODIES.include? env[:method]
|
27
43
|
# play nice and indicate we're sending an empty body
|
@@ -0,0 +1,204 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Adapter
|
3
|
+
# EventMachine adapter is useful for either asynchronous requests
|
4
|
+
# when in EM reactor loop or for making parallel requests in
|
5
|
+
# synchronous code.
|
6
|
+
class EMHttp < Faraday::Adapter
|
7
|
+
|
8
|
+
dependency 'em-http'
|
9
|
+
|
10
|
+
self.supports_parallel = true
|
11
|
+
|
12
|
+
def self.setup_parallel_manager(options = nil)
|
13
|
+
Manager.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
super
|
18
|
+
perform_request env
|
19
|
+
@app.call env
|
20
|
+
end
|
21
|
+
|
22
|
+
def perform_request(env)
|
23
|
+
if parallel?(env)
|
24
|
+
manager = env[:parallel_manager]
|
25
|
+
manager.add {
|
26
|
+
perform_single_request(env).
|
27
|
+
callback { env[:response].finish(env) }
|
28
|
+
}
|
29
|
+
else
|
30
|
+
unless EventMachine.reactor_running?
|
31
|
+
error = nil
|
32
|
+
# start EM, block until request is completed
|
33
|
+
EventMachine.run do
|
34
|
+
perform_single_request(env).
|
35
|
+
callback { EventMachine.stop }.
|
36
|
+
errback { |client|
|
37
|
+
error = error_message(client)
|
38
|
+
EventMachine.stop
|
39
|
+
}
|
40
|
+
end
|
41
|
+
raise_error(error) if error
|
42
|
+
else
|
43
|
+
# EM is running: instruct upstream that this is an async request
|
44
|
+
env[:parallel_manager] = true
|
45
|
+
perform_single_request(env).
|
46
|
+
callback { env[:response].finish(env) }.
|
47
|
+
errback {
|
48
|
+
# TODO: no way to communicate the error in async mode
|
49
|
+
raise NotImplementedError
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# TODO: reuse the connection to support pipelining
|
56
|
+
def perform_single_request(env)
|
57
|
+
req = EventMachine::HttpRequest.new(env[:url], connection_config(env))
|
58
|
+
req.setup_request(env[:method], request_config(env)).callback { |client|
|
59
|
+
save_response(env, client.response_header.status, client.response) do |resp_headers|
|
60
|
+
client.response_header.each do |name, value|
|
61
|
+
resp_headers[name.to_sym] = value
|
62
|
+
end
|
63
|
+
end
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def error_message(client)
|
68
|
+
client.error or "request failed"
|
69
|
+
end
|
70
|
+
|
71
|
+
def raise_error(msg)
|
72
|
+
errklass = Faraday::Error::ClientError
|
73
|
+
if msg == Errno::ETIMEDOUT
|
74
|
+
errklass = Faraday::Error::TimeoutError
|
75
|
+
msg = "request timed out"
|
76
|
+
elsif msg == Errno::ECONNREFUSED
|
77
|
+
errklass = Faraday::Error::ConnectionFailed
|
78
|
+
msg = "connection refused"
|
79
|
+
end
|
80
|
+
raise errklass, msg
|
81
|
+
end
|
82
|
+
|
83
|
+
def connection_config(env)
|
84
|
+
options = {}
|
85
|
+
configure_ssl(options, env)
|
86
|
+
configure_proxy(options, env)
|
87
|
+
configure_timeout(options, env)
|
88
|
+
options
|
89
|
+
end
|
90
|
+
|
91
|
+
def request_config(env)
|
92
|
+
options = {
|
93
|
+
:body => read_body(env),
|
94
|
+
:head => env[:request_headers],
|
95
|
+
# :keepalive => true,
|
96
|
+
# :file => 'path/to/file', # stream data off disk
|
97
|
+
}
|
98
|
+
configure_compression(options, env)
|
99
|
+
# configure_proxy_auth
|
100
|
+
# :proxy => {:authorization => [user, pass]}
|
101
|
+
# proxy[:username] && proxy[:password]
|
102
|
+
options
|
103
|
+
end
|
104
|
+
|
105
|
+
def read_body(env)
|
106
|
+
body = env[:body]
|
107
|
+
body.respond_to?(:read) ? body.read : body
|
108
|
+
end
|
109
|
+
|
110
|
+
def configure_ssl(options, env)
|
111
|
+
if ssl = env[:ssl]
|
112
|
+
# :ssl => {
|
113
|
+
# :private_key_file => '/tmp/server.key',
|
114
|
+
# :cert_chain_file => '/tmp/server.crt',
|
115
|
+
# :verify_peer => false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def configure_proxy(options, env)
|
120
|
+
if proxy = request_options(env)[:proxy]
|
121
|
+
options[:proxy] = {
|
122
|
+
:host => proxy[:uri].host,
|
123
|
+
:port => proxy[:uri].port
|
124
|
+
}
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def configure_timeout(options, env)
|
129
|
+
timeout, open_timeout = request_options(env).values_at(:timeout, :open_timeout)
|
130
|
+
options[:connect_timeout] = options[:inactivity_timeout] = timeout
|
131
|
+
options[:connect_timeout] = open_timeout if open_timeout
|
132
|
+
end
|
133
|
+
|
134
|
+
def configure_compression(options, env)
|
135
|
+
if env[:method] == :get and not options[:head].key? 'accept-encoding'
|
136
|
+
options[:head]['accept-encoding'] = 'gzip, compressed'
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def request_options(env)
|
141
|
+
env[:request]
|
142
|
+
end
|
143
|
+
|
144
|
+
def parallel?(env)
|
145
|
+
!!env[:parallel_manager]
|
146
|
+
end
|
147
|
+
|
148
|
+
# The parallel manager is designed to start an EventMachine loop
|
149
|
+
# and block until all registered requests have been completed.
|
150
|
+
class Manager
|
151
|
+
def initialize
|
152
|
+
reset
|
153
|
+
end
|
154
|
+
|
155
|
+
def reset
|
156
|
+
@registered_procs = []
|
157
|
+
@num_registered = 0
|
158
|
+
@num_succeeded = 0
|
159
|
+
@errors = []
|
160
|
+
@running = false
|
161
|
+
end
|
162
|
+
|
163
|
+
def running?() @running end
|
164
|
+
|
165
|
+
def add
|
166
|
+
if running?
|
167
|
+
perform_request { yield }
|
168
|
+
else
|
169
|
+
@registered_procs << Proc.new
|
170
|
+
end
|
171
|
+
@num_registered += 1
|
172
|
+
end
|
173
|
+
|
174
|
+
def run
|
175
|
+
if @num_registered > 0
|
176
|
+
@running = true
|
177
|
+
EventMachine.run do
|
178
|
+
@registered_procs.each do |proc|
|
179
|
+
perform_request(&proc)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
if @errors.size > 0
|
183
|
+
raise Faraday::Error::ClientError, @errors.first || "connection failed"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
ensure
|
187
|
+
reset
|
188
|
+
end
|
189
|
+
|
190
|
+
def perform_request
|
191
|
+
client = yield
|
192
|
+
client.callback { @num_succeeded += 1; check_finished }
|
193
|
+
client.errback { @errors << client.error; check_finished }
|
194
|
+
end
|
195
|
+
|
196
|
+
def check_finished
|
197
|
+
if @num_succeeded + @errors.size == @num_registered
|
198
|
+
EventMachine.stop
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|