avdi-faraday 0.8.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.
Files changed (60) hide show
  1. data/Gemfile +27 -0
  2. data/LICENSE.md +20 -0
  3. data/README.md +250 -0
  4. data/Rakefile +87 -0
  5. data/config.ru +6 -0
  6. data/faraday.gemspec +86 -0
  7. data/lib/faraday.rb +276 -0
  8. data/lib/faraday/adapter.rb +71 -0
  9. data/lib/faraday/adapter/em_http.rb +217 -0
  10. data/lib/faraday/adapter/em_synchrony.rb +89 -0
  11. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +66 -0
  12. data/lib/faraday/adapter/excon.rb +59 -0
  13. data/lib/faraday/adapter/httpclient.rb +92 -0
  14. data/lib/faraday/adapter/net_http.rb +116 -0
  15. data/lib/faraday/adapter/net_http_persistent.rb +37 -0
  16. data/lib/faraday/adapter/patron.rb +65 -0
  17. data/lib/faraday/adapter/rack.rb +57 -0
  18. data/lib/faraday/adapter/test.rb +162 -0
  19. data/lib/faraday/adapter/typhoeus.rb +107 -0
  20. data/lib/faraday/builder.rb +184 -0
  21. data/lib/faraday/connection.rb +468 -0
  22. data/lib/faraday/error.rb +40 -0
  23. data/lib/faraday/middleware.rb +41 -0
  24. data/lib/faraday/request.rb +101 -0
  25. data/lib/faraday/request/authorization.rb +40 -0
  26. data/lib/faraday/request/basic_authentication.rb +13 -0
  27. data/lib/faraday/request/multipart.rb +62 -0
  28. data/lib/faraday/request/retry.rb +67 -0
  29. data/lib/faraday/request/token_authentication.rb +15 -0
  30. data/lib/faraday/request/url_encoded.rb +35 -0
  31. data/lib/faraday/response.rb +99 -0
  32. data/lib/faraday/response/logger.rb +34 -0
  33. data/lib/faraday/response/raise_error.rb +16 -0
  34. data/lib/faraday/upload_io.rb +23 -0
  35. data/lib/faraday/utils.rb +274 -0
  36. data/script/test +91 -0
  37. data/test/adapters/default_test.rb +14 -0
  38. data/test/adapters/em_http_test.rb +19 -0
  39. data/test/adapters/em_synchrony_test.rb +20 -0
  40. data/test/adapters/excon_test.rb +15 -0
  41. data/test/adapters/httpclient_test.rb +16 -0
  42. data/test/adapters/integration.rb +193 -0
  43. data/test/adapters/logger_test.rb +37 -0
  44. data/test/adapters/net_http_persistent_test.rb +11 -0
  45. data/test/adapters/net_http_test.rb +49 -0
  46. data/test/adapters/patron_test.rb +17 -0
  47. data/test/adapters/rack_test.rb +26 -0
  48. data/test/adapters/test_middleware_test.rb +70 -0
  49. data/test/adapters/typhoeus_test.rb +20 -0
  50. data/test/authentication_middleware_test.rb +65 -0
  51. data/test/connection_test.rb +375 -0
  52. data/test/env_test.rb +183 -0
  53. data/test/helper.rb +75 -0
  54. data/test/live_server.rb +57 -0
  55. data/test/middleware/retry_test.rb +62 -0
  56. data/test/middleware_stack_test.rb +203 -0
  57. data/test/middleware_test.rb +12 -0
  58. data/test/request_middleware_test.rb +108 -0
  59. data/test/response_middleware_test.rb +74 -0
  60. metadata +182 -0
@@ -0,0 +1,276 @@
1
+ # Public: This is the main namespace for Faraday. You can either use it to
2
+ # create Faraday::Connection objects, or access it directly.
3
+ #
4
+ # Examples
5
+ #
6
+ # Faraday.get "http://faraday.com"
7
+ #
8
+ # conn = Faraday.new "http://faraday.com"
9
+ # conn.get '/'
10
+ #
11
+ module Faraday
12
+ VERSION = "0.8.1"
13
+
14
+ class << self
15
+ # Public: Gets or sets the root path that Faraday is being loaded from.
16
+ # This is the root from where the libraries are auto-loaded from.
17
+ attr_accessor :root_path
18
+
19
+ # Public: Gets or sets the path that the Faraday libs are loaded from.
20
+ attr_accessor :lib_path
21
+
22
+ # Public: Gets or sets the Symbol key identifying a default Adapter to use
23
+ # for the default Faraday::Connection.
24
+ attr_accessor :default_adapter
25
+
26
+ # Public: Sets the default Faraday::Connection for simple scripts that
27
+ # access the Faraday constant directly.
28
+ #
29
+ # Faraday.get "https://faraday.com"
30
+ attr_writer :default_connection
31
+
32
+ # Public: Sets the default options used when calling Faraday#new.
33
+ attr_writer :default_connection_options
34
+
35
+ # Public: Initializes a new Faraday::Connection.
36
+ #
37
+ # url - The optional String base URL to use as a prefix for all
38
+ # requests. Can also be the options Hash.
39
+ # options - The optional Hash used to configure this Faraday::Connection.
40
+ # Any of these values will be set on every request made, unless
41
+ # overridden for a specific request.
42
+ # :url - String base URL.
43
+ # :params - Hash of URI query unencoded key/value pairs.
44
+ # :headers - Hash of unencoded HTTP header key/value pairs.
45
+ # :request - Hash of request options.
46
+ # :ssl - Hash of SSL options.
47
+ # :proxy - Hash of Proxy options.
48
+ #
49
+ # Examples
50
+ #
51
+ # Faraday.new 'http://faraday.com'
52
+ #
53
+ # # http://faraday.com?page=1
54
+ # Faraday.new 'http://faraday.com', :params => {:page => 1}
55
+ #
56
+ # # same
57
+ #
58
+ # Faraday.new :url => 'http://faraday.com',
59
+ # :params => {:page => 1}
60
+ #
61
+ # Returns a Faraday::Connection.
62
+ def new(url = nil, options = {})
63
+ block = block_given? ? Proc.new : nil
64
+ options = Faraday::Utils.deep_merge(default_connection_options, options)
65
+ Faraday::Connection.new(url, options, &block)
66
+ end
67
+
68
+ # Internal: Requires internal Faraday libraries.
69
+ #
70
+ # *libs - One or more relative String names to Faraday classes.
71
+ #
72
+ # Returns nothing.
73
+ def require_libs(*libs)
74
+ libs.each do |lib|
75
+ require "#{lib_path}/#{lib}"
76
+ end
77
+ end
78
+
79
+ alias require_lib require_libs
80
+
81
+ private
82
+ # Internal: Proxies method calls on the Faraday constant to
83
+ # #default_connection.
84
+ def method_missing(name, *args, &block)
85
+ default_connection.send(name, *args, &block)
86
+ end
87
+ end
88
+
89
+ self.root_path = File.expand_path "..", __FILE__
90
+ self.lib_path = File.expand_path "../faraday", __FILE__
91
+ self.default_adapter = :net_http
92
+
93
+ # Gets the default connection used for simple scripts.
94
+ #
95
+ # Returns a Faraday::Connection, configured with the #default_adapter.
96
+ def self.default_connection
97
+ @default_connection ||= Connection.new
98
+ end
99
+
100
+ # Gets the default connection options used when calling Faraday#new.
101
+ #
102
+ # Returns an options Hash.
103
+ def self.default_connection_options
104
+ @default_connection_options ||= {}
105
+ end
106
+
107
+ if (!defined?(RUBY_ENGINE) || "ruby" == RUBY_ENGINE) && RUBY_VERSION < '1.9'
108
+ begin
109
+ require 'system_timer'
110
+ Timer = SystemTimer
111
+ rescue LoadError
112
+ warn "Faraday: you may want to install system_timer for reliable timeouts"
113
+ end
114
+ end
115
+
116
+ unless const_defined? :Timer
117
+ require 'timeout'
118
+ Timer = Timeout
119
+ end
120
+
121
+ # Public: Adds the ability for other modules to register and lookup
122
+ # middleware classes.
123
+ module MiddlewareRegistry
124
+ # Public: Register middleware class(es) on the current module.
125
+ #
126
+ # mapping - A Hash mapping Symbol keys to classes. Classes can be expressed
127
+ # as fully qualified constant, or a Proc that will be lazily
128
+ # called to return the former.
129
+ #
130
+ # Examples
131
+ #
132
+ # module Faraday
133
+ # class Whatever
134
+ # # Middleware looked up by :foo returns Faraday::Whatever::Foo.
135
+ # register_middleware :foo => Foo
136
+ # end
137
+ # end
138
+ #
139
+ # Returns nothing.
140
+ def register_middleware(mapping)
141
+ (@registered_middleware ||= {}).update(mapping)
142
+ end
143
+
144
+ # Public: Lookup middleware class with a registered Symbol shortcut.
145
+ #
146
+ # key - The Symbol key for the registered middleware.
147
+ #
148
+ # Examples
149
+ #
150
+ # module Faraday
151
+ # class Whatever
152
+ # register_middleware :foo => Foo
153
+ # end
154
+ # end
155
+ #
156
+ # Faraday::Whatever.lookup_middleware(:foo)
157
+ # # => Faraday::Whatever::Foo
158
+ #
159
+ # Returns a middleware Class.
160
+ def lookup_middleware(key)
161
+ unless defined? @registered_middleware and found = @registered_middleware[key]
162
+ raise "#{key.inspect} is not registered on #{self}"
163
+ end
164
+ found = @registered_middleware[key] = found.call if found.is_a? Proc
165
+ found.is_a?(Module) ? found : const_get(found)
166
+ end
167
+ end
168
+
169
+ # Internal: Adds the ability for other modules to manage autoloadable
170
+ # constants.
171
+ module AutoloadHelper
172
+ # Internal: Registers the constants to be auto loaded.
173
+ #
174
+ # prefix - The String require prefix. If the path is inside Faraday, then
175
+ # it will be prefixed with the root path of this loaded Faraday
176
+ # version.
177
+ # options - Hash of Symbol => String library names.
178
+ #
179
+ # Examples.
180
+ #
181
+ # Faraday.autoload_all 'faraday/foo',
182
+ # :Bar => 'bar'
183
+ #
184
+ # # requires faraday/foo/bar to load Faraday::Bar.
185
+ # Faraday::Bar
186
+ #
187
+ #
188
+ # Returns nothing.
189
+ def autoload_all(prefix, options)
190
+ if prefix =~ /^faraday(\/|$)/i
191
+ prefix = File.join(Faraday.root_path, prefix)
192
+ end
193
+ options.each do |const_name, path|
194
+ autoload const_name, File.join(prefix, path)
195
+ end
196
+ end
197
+
198
+ # Internal: Loads each autoloaded constant. If thread safety is a concern,
199
+ # wrap this in a Mutex.
200
+ #
201
+ # Returns nothing.
202
+ def load_autoloaded_constants
203
+ constants.each do |const|
204
+ const_get(const) if autoload?(const)
205
+ end
206
+ end
207
+
208
+ # Internal: Filters the module's contents with those that have been already
209
+ # autoloaded.
210
+ #
211
+ # Returns an Array of Class/Module objects.
212
+ def all_loaded_constants
213
+ constants.map { |c| const_get(c) }.
214
+ select { |a| a.respond_to?(:loaded?) && a.loaded? }
215
+ end
216
+ end
217
+
218
+ extend AutoloadHelper
219
+
220
+ # Public: Register middleware classes under a short name.
221
+ #
222
+ # type - A Symbol specifying the kind of middleware (default: :middleware)
223
+ # mapping - A Hash mapping Symbol keys to classes. Classes can be expressed
224
+ # as fully qualified constant, or a Proc that will be lazily called
225
+ # to return the former.
226
+ #
227
+ # Examples
228
+ #
229
+ # Faraday.register_middleware :aloha => MyModule::Aloha
230
+ # Faraday.register_middleware :response, :boom => MyModule::Boom
231
+ #
232
+ # # shortcuts are now available in Builder:
233
+ # builder.use :aloha
234
+ # builder.response :boom
235
+ #
236
+ # Returns nothing.
237
+ def self.register_middleware(type, mapping = nil)
238
+ type, mapping = :middleware, type if mapping.nil?
239
+ component = self.const_get(type.to_s.capitalize)
240
+ component.register_middleware(mapping)
241
+ end
242
+
243
+ autoload_all "faraday",
244
+ :Middleware => 'middleware',
245
+ :Builder => 'builder',
246
+ :Request => 'request',
247
+ :Response => 'response',
248
+ :CompositeReadIO => 'upload_io',
249
+ :UploadIO => 'upload_io',
250
+ :Parts => 'upload_io'
251
+
252
+ require_libs "utils", "connection", "adapter", "error"
253
+ end
254
+
255
+
256
+ # not pulling in active-support JUST for this method. And I love this method.
257
+ class Object
258
+ # The primary purpose of this method is to "tap into" a method chain,
259
+ # in order to perform operations on intermediate results within the chain.
260
+ #
261
+ # Examples
262
+ #
263
+ # (1..10).tap { |x| puts "original: #{x.inspect}" }.to_a.
264
+ # tap { |x| puts "array: #{x.inspect}" }.
265
+ # select { |x| x%2 == 0 }.
266
+ # tap { |x| puts "evens: #{x.inspect}" }.
267
+ # map { |x| x*x }.
268
+ # tap { |x| puts "squares: #{x.inspect}" }
269
+ #
270
+ # Yields self.
271
+ # Returns self.
272
+ def tap
273
+ yield self
274
+ self
275
+ end unless Object.respond_to?(:tap)
276
+ end
@@ -0,0 +1,71 @@
1
+ module Faraday
2
+ # Public: This is a base class for all Faraday adapters. Adapters are
3
+ # responsible for fulfilling a Faraday request.
4
+ class Adapter < Middleware
5
+ CONTENT_LENGTH = 'Content-Length'.freeze
6
+
7
+ extend AutoloadHelper
8
+ extend MiddlewareRegistry
9
+
10
+ autoload_all 'faraday/adapter',
11
+ :NetHttp => 'net_http',
12
+ :NetHttpPersistent => 'net_http_persistent',
13
+ :Typhoeus => 'typhoeus',
14
+ :EMSynchrony => 'em_synchrony',
15
+ :EMHttp => 'em_http',
16
+ :Patron => 'patron',
17
+ :Excon => 'excon',
18
+ :Test => 'test',
19
+ :Rack => 'rack',
20
+ :HTTPClient => 'httpclient'
21
+
22
+ register_middleware \
23
+ :test => :Test,
24
+ :net_http => :NetHttp,
25
+ :net_http_persistent => :NetHttpPersistent,
26
+ :typhoeus => :Typhoeus,
27
+ :patron => :Patron,
28
+ :em_synchrony => :EMSynchrony,
29
+ :em_http => :EMHttp,
30
+ :excon => :Excon,
31
+ :rack => :Rack,
32
+ :httpclient => :HTTPClient
33
+
34
+ # Public: This module marks an Adapter as supporting parallel requests.
35
+ module Parallelism
36
+ attr_writer :supports_parallel
37
+ def supports_parallel?() @supports_parallel end
38
+
39
+ def inherited(subclass)
40
+ super
41
+ subclass.supports_parallel = self.supports_parallel?
42
+ end
43
+ end
44
+
45
+ extend Parallelism
46
+ self.supports_parallel = false
47
+
48
+ def self.adapter?
49
+ true
50
+ end
51
+
52
+ def call(env)
53
+ if !env[:body] and Connection::METHODS_WITH_BODIES.include? env[:method]
54
+ # play nice and indicate we're sending an empty body
55
+ env[:request_headers][CONTENT_LENGTH] = "0"
56
+ # Typhoeus hangs on PUT requests if body is nil
57
+ env[:body] = ''
58
+ end
59
+ end
60
+
61
+ def save_response(env, status, body, headers = nil)
62
+ env[:status] = status
63
+ env[:body] = body
64
+ env[:response_headers] = Utils::Headers.new.tap do |response_headers|
65
+ response_headers.update headers unless headers.nil?
66
+ yield response_headers if block_given?
67
+ end
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,217 @@
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
+ module Options
8
+ def connection_config(env)
9
+ options = {}
10
+ configure_ssl(options, env)
11
+ configure_proxy(options, env)
12
+ configure_timeout(options, env)
13
+ configure_socket(options, env)
14
+ options
15
+ end
16
+
17
+ def request_config(env)
18
+ options = {
19
+ :body => read_body(env),
20
+ :head => env[:request_headers],
21
+ # :keepalive => true,
22
+ # :file => 'path/to/file', # stream data off disk
23
+ }
24
+ configure_compression(options, env)
25
+ # configure_proxy_auth
26
+ # :proxy => {:authorization => [user, pass]}
27
+ # proxy[:username] && proxy[:password]
28
+ options
29
+ end
30
+
31
+ def read_body(env)
32
+ body = env[:body]
33
+ body.respond_to?(:read) ? body.read : body
34
+ end
35
+
36
+ def configure_ssl(options, env)
37
+ if ssl = env[:ssl]
38
+ # :ssl => {
39
+ # :private_key_file => '/tmp/server.key',
40
+ # :cert_chain_file => '/tmp/server.crt',
41
+ # :verify_peer => false
42
+ end
43
+ end
44
+
45
+ def configure_proxy(options, env)
46
+ if proxy = request_options(env)[:proxy]
47
+ options[:proxy] = {
48
+ :host => proxy[:uri].host,
49
+ :port => proxy[:uri].port
50
+ }
51
+ end
52
+ end
53
+
54
+ def configure_socket(options, env)
55
+ if bind = request_options(env)[:bind]
56
+ options[:bind] = {
57
+ :host => bind[:host],
58
+ :port => bind[:port]
59
+ }
60
+ end
61
+ end
62
+
63
+ def configure_timeout(options, env)
64
+ timeout, open_timeout = request_options(env).values_at(:timeout, :open_timeout)
65
+ options[:connect_timeout] = options[:inactivity_timeout] = timeout
66
+ options[:connect_timeout] = open_timeout if open_timeout
67
+ end
68
+
69
+ def configure_compression(options, env)
70
+ if env[:method] == :get and not options[:head].key? 'accept-encoding'
71
+ options[:head]['accept-encoding'] = 'gzip, compressed'
72
+ end
73
+ end
74
+
75
+ def request_options(env)
76
+ env[:request]
77
+ end
78
+ end
79
+
80
+ include Options
81
+
82
+ dependency 'em-http'
83
+
84
+ self.supports_parallel = true
85
+
86
+ def self.setup_parallel_manager(options = nil)
87
+ Manager.new
88
+ end
89
+
90
+ def call(env)
91
+ super
92
+ perform_request env
93
+ @app.call env
94
+ end
95
+
96
+ def perform_request(env)
97
+ if parallel?(env)
98
+ manager = env[:parallel_manager]
99
+ manager.add {
100
+ perform_single_request(env).
101
+ callback { env[:response].finish(env) }
102
+ }
103
+ else
104
+ unless EventMachine.reactor_running?
105
+ error = nil
106
+ # start EM, block until request is completed
107
+ EventMachine.run do
108
+ perform_single_request(env).
109
+ callback { EventMachine.stop }.
110
+ errback { |client|
111
+ error = error_message(client)
112
+ EventMachine.stop
113
+ }
114
+ end
115
+ raise_error(error) if error
116
+ else
117
+ # EM is running: instruct upstream that this is an async request
118
+ env[:parallel_manager] = true
119
+ perform_single_request(env).
120
+ callback { env[:response].finish(env) }.
121
+ errback {
122
+ # TODO: no way to communicate the error in async mode
123
+ raise NotImplementedError
124
+ }
125
+ end
126
+ end
127
+ end
128
+
129
+ # TODO: reuse the connection to support pipelining
130
+ def perform_single_request(env)
131
+ req = EventMachine::HttpRequest.new(env[:url], connection_config(env))
132
+ req.setup_request(env[:method], request_config(env)).callback { |client|
133
+ save_response(env, client.response_header.status, client.response) do |resp_headers|
134
+ client.response_header.each do |name, value|
135
+ resp_headers[name.to_sym] = value
136
+ end
137
+ end
138
+ }
139
+ end
140
+
141
+ def error_message(client)
142
+ client.error or "request failed"
143
+ end
144
+
145
+ def raise_error(msg)
146
+ errklass = Faraday::Error::ClientError
147
+ if msg == Errno::ETIMEDOUT
148
+ errklass = Faraday::Error::TimeoutError
149
+ msg = "request timed out"
150
+ elsif msg == Errno::ECONNREFUSED
151
+ errklass = Faraday::Error::ConnectionFailed
152
+ msg = "connection refused"
153
+ end
154
+ raise errklass, msg
155
+ end
156
+
157
+ def parallel?(env)
158
+ !!env[:parallel_manager]
159
+ end
160
+
161
+ # The parallel manager is designed to start an EventMachine loop
162
+ # and block until all registered requests have been completed.
163
+ class Manager
164
+ def initialize
165
+ reset
166
+ end
167
+
168
+ def reset
169
+ @registered_procs = []
170
+ @num_registered = 0
171
+ @num_succeeded = 0
172
+ @errors = []
173
+ @running = false
174
+ end
175
+
176
+ def running?() @running end
177
+
178
+ def add
179
+ if running?
180
+ perform_request { yield }
181
+ else
182
+ @registered_procs << Proc.new
183
+ end
184
+ @num_registered += 1
185
+ end
186
+
187
+ def run
188
+ if @num_registered > 0
189
+ @running = true
190
+ EventMachine.run do
191
+ @registered_procs.each do |proc|
192
+ perform_request(&proc)
193
+ end
194
+ end
195
+ if @errors.size > 0
196
+ raise Faraday::Error::ClientError, @errors.first || "connection failed"
197
+ end
198
+ end
199
+ ensure
200
+ reset
201
+ end
202
+
203
+ def perform_request
204
+ client = yield
205
+ client.callback { @num_succeeded += 1; check_finished }
206
+ client.errback { @errors << client.error; check_finished }
207
+ end
208
+
209
+ def check_finished
210
+ if @num_succeeded + @errors.size == @num_registered
211
+ EventMachine.stop
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
217
+ end