faraday 0.7.6 → 0.8.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- ## Leave these as is they will be modified for you by the rake gemspec task.
12
- ## If your rubyforge_project name is different, then edit it and comment out
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 = "HTTP/REST API client library."
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 = 'http://github.com/technoweenie/faraday'
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/json.rb
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
@@ -1,7 +1,8 @@
1
1
  module Faraday
2
- VERSION = "0.7.6"
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 AutoloadHelper
26
- def register_lookup_modules(mods)
27
- (@lookup_module_index ||= {}).update(mods)
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
- def lookup_module(key)
31
- return if !@lookup_module_index
32
- const_get @lookup_module_index[key] || key
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
- autoload_all 'faraday',
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
@@ -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
- register_lookup_modules \
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