avdi-faraday 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
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