faraday 0.5.7 → 0.6.0

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 (45) hide show
  1. data/Gemfile +9 -8
  2. data/README.md +99 -20
  3. data/faraday.gemspec +9 -11
  4. data/lib/faraday.rb +1 -1
  5. data/lib/faraday/adapter.rb +11 -86
  6. data/lib/faraday/adapter/action_dispatch.rb +3 -12
  7. data/lib/faraday/adapter/em_synchrony.rb +9 -24
  8. data/lib/faraday/adapter/excon.rb +7 -19
  9. data/lib/faraday/adapter/net_http.rb +37 -35
  10. data/lib/faraday/adapter/patron.rb +16 -23
  11. data/lib/faraday/adapter/test.rb +4 -10
  12. data/lib/faraday/adapter/typhoeus.rb +11 -39
  13. data/lib/faraday/builder.rb +82 -33
  14. data/lib/faraday/connection.rb +3 -10
  15. data/lib/faraday/error.rb +20 -15
  16. data/lib/faraday/middleware.rb +7 -2
  17. data/lib/faraday/request.rb +13 -10
  18. data/lib/faraday/request/json.rb +31 -0
  19. data/lib/faraday/request/multipart.rb +63 -0
  20. data/lib/faraday/request/url_encoded.rb +37 -0
  21. data/lib/faraday/response.rb +72 -30
  22. data/lib/faraday/response/logger.rb +34 -0
  23. data/lib/faraday/response/raise_error.rb +16 -0
  24. data/lib/faraday/upload_io.rb +14 -6
  25. data/lib/faraday/utils.rb +54 -17
  26. data/test/adapters/live_test.rb +36 -14
  27. data/test/adapters/logger_test.rb +1 -1
  28. data/test/adapters/net_http_test.rb +33 -0
  29. data/test/connection_test.rb +0 -39
  30. data/test/env_test.rb +84 -6
  31. data/test/helper.rb +17 -8
  32. data/test/live_server.rb +19 -17
  33. data/test/middleware_stack_test.rb +91 -0
  34. data/test/request_middleware_test.rb +75 -21
  35. data/test/response_middleware_test.rb +34 -31
  36. metadata +21 -17
  37. data/lib/faraday/adapter/logger.rb +0 -32
  38. data/lib/faraday/request/active_support_json.rb +0 -21
  39. data/lib/faraday/request/yajl.rb +0 -18
  40. data/lib/faraday/response/active_support_json.rb +0 -30
  41. data/lib/faraday/response/yajl.rb +0 -26
  42. data/test/adapters/typhoeus_test.rb +0 -31
  43. data/test/connection_app_test.rb +0 -60
  44. data/test/form_post_test.rb +0 -58
  45. data/test/multipart_test.rb +0 -48
@@ -1,7 +1,7 @@
1
1
  begin
2
2
  require 'net/https'
3
3
  rescue LoadError
4
- puts "no such file to load -- net/https. Make sure openssl is installed if you want ssl support"
4
+ warn "Warning: no such file to load -- net/https. Make sure openssl is installed if you want ssl support"
5
5
  require 'net/http'
6
6
  end
7
7
 
@@ -10,53 +10,55 @@ module Faraday
10
10
  class NetHttp < Faraday::Adapter
11
11
  def call(env)
12
12
  super
13
+ url = env[:url]
14
+ req = env[:request]
13
15
 
14
- is_ssl = env[:url].scheme == 'https'
16
+ http = net_http_class(env).new(url.host, url.inferred_port)
15
17
 
16
- http = net_http_class(env).new(env[:url].host, env[:url].port || (is_ssl ? 443 : 80))
17
- if http.use_ssl = is_ssl
18
+ if http.use_ssl = (url.scheme == 'https' && env[:ssl])
18
19
  ssl = env[:ssl]
19
- if ssl[:verify] == false
20
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
21
- else
22
- http.verify_mode = ssl[:verify]
23
- end
24
- http.cert = ssl[:client_cert] if ssl[:client_cert]
25
- http.key = ssl[:client_key] if ssl[:client_key]
26
- http.ca_file = ssl[:ca_file] if ssl[:ca_file]
20
+ http.verify_mode = ssl[:verify_mode] || ssl.fetch(:verify, true) ?
21
+ OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
22
+ http.cert = ssl[:client_cert] if ssl[:client_cert]
23
+ http.key = ssl[:client_key] if ssl[:client_key]
24
+ http.ca_file = ssl[:ca_file] if ssl[:ca_file]
25
+ http.cert_store = ssl[:cert_store] if ssl[:cert_store]
27
26
  end
28
- req = env[:request]
27
+
29
28
  http.read_timeout = http.open_timeout = req[:timeout] if req[:timeout]
30
- http.open_timeout = req[:open_timeout] if req[:open_timeout]
29
+ http.open_timeout = req[:open_timeout] if req[:open_timeout]
31
30
 
32
- full_path = full_path_for(env[:url].path, env[:url].query, env[:url].fragment)
33
- http_req = Net::HTTPGenericRequest.new(
34
- env[:method].to_s.upcase, # request method
35
- (env[:body] ? true : false), # is there data
36
- true, # does net/http love you, true or false?
37
- full_path, # request uri path
38
- env[:request_headers]) # request headers
31
+ if :get != env[:method]
32
+ http_request = Net::HTTPGenericRequest.new \
33
+ env[:method].to_s.upcase, # request method
34
+ !!env[:body], # is there data
35
+ true, # does net/http love you, true or false?
36
+ url.request_uri, # request uri path
37
+ env[:request_headers] # request headers
39
38
 
40
- if env[:body].respond_to?(:read)
41
- http_req.body_stream = env[:body]
42
- env[:body] = nil
39
+ if env[:body].respond_to?(:read)
40
+ http_request.body_stream = env[:body]
41
+ env[:body] = nil
42
+ end
43
43
  end
44
44
 
45
- http_resp = http.request http_req, env[:body]
46
-
47
- resp_headers = {}
48
- http_resp.each_header do |key, value|
49
- resp_headers[key] = value
45
+ begin
46
+ http_response = if :get == env[:method]
47
+ # prefer `get` to `request` because the former handles gzip (ruby 1.9)
48
+ http.get url.request_uri, env[:request_headers]
49
+ else
50
+ http.request http_request, env[:body]
51
+ end
52
+ rescue Errno::ECONNREFUSED
53
+ raise Error::ConnectionFailed, $!
50
54
  end
51
55
 
52
- env.update \
53
- :status => http_resp.code.to_i,
54
- :response_headers => resp_headers,
55
- :body => http_resp.body
56
+ http_response.each_header do |key, value|
57
+ response_headers(env)[key] = value
58
+ end
59
+ env.update :status => http_response.code.to_i, :body => http_response.body
56
60
 
57
61
  @app.call env
58
- rescue Errno::ECONNREFUSED => e
59
- raise Error::ConnectionFailed.new(e)
60
62
  end
61
63
 
62
64
  def net_http_class(env)
@@ -1,37 +1,30 @@
1
1
  module Faraday
2
2
  class Adapter
3
3
  class Patron < Faraday::Adapter
4
- begin
5
- require 'patron'
6
- rescue LoadError, NameError => e
7
- self.load_error = e
8
- end
4
+ dependency 'patron'
9
5
 
10
6
  def call(env)
11
7
  super
12
8
 
13
- sess = ::Patron::Session.new
14
- args = [env[:method], env[:url].to_s, env[:request_headers]]
15
- if Faraday::Connection::METHODS_WITH_BODIES.include?(env[:method])
16
- args.insert(2, env[:body].to_s)
9
+ # TODO: support streaming requests
10
+ env[:body] = env[:body].read if env[:body].respond_to? :read
11
+
12
+ session = ::Patron::Session.new
13
+
14
+ response = begin
15
+ if Connection::METHODS_WITH_BODIES.include? env[:method]
16
+ session.send(env[:method], env[:url].to_s, env[:body].to_s, env[:request_headers])
17
+ else
18
+ session.send(env[:method], env[:url].to_s, env[:request_headers])
19
+ end
20
+ rescue Errno::ECONNREFUSED
21
+ raise Error::ConnectionFailed, $!
17
22
  end
18
- resp = sess.send *args
19
23
 
20
- env.update \
21
- :status => resp.status,
22
- :response_headers => resp.headers.
23
- inject({}) { |memo, (k, v)| memo.update(k.downcase => v) },
24
- :body => resp.body
24
+ env.update :status => response.status, :body => response.body
25
+ response_headers(env).update response.headers
25
26
 
26
27
  @app.call env
27
- rescue Errno::ECONNREFUSED
28
- raise Error::ConnectionFailed.new(Errno::ECONNREFUSED)
29
- end
30
-
31
- # TODO: build in support for multipart streaming if patron supports it.
32
- def create_multipart(env, params, boundary = nil)
33
- stream = super
34
- stream.read
35
28
  end
36
29
  end
37
30
  end
@@ -11,7 +11,7 @@ module Faraday
11
11
  # resp = test.get '/nigiri/sake.json'
12
12
  # resp.body # => 'hi world'
13
13
  #
14
- class Test < Middleware
14
+ class Test < Faraday::Adapter
15
15
  attr_accessor :stubs
16
16
 
17
17
  def self.loaded?() false end
@@ -105,24 +105,18 @@ module Faraday
105
105
  end
106
106
 
107
107
  def call(env)
108
+ super
108
109
  normalized_path = Faraday::Utils.normalize_path(env[:url])
109
110
 
110
111
  if stub = stubs.match(env[:method], normalized_path, env[:body])
111
112
  status, headers, body = stub.block.call(env)
112
- env.update \
113
- :status => status,
114
- :response_headers => headers,
115
- :body => body
113
+ env.update :status => status, :body => body
114
+ response_headers(env).update headers
116
115
  else
117
116
  raise "no stubbed request for #{env[:method]} #{normalized_path} #{env[:body]}"
118
117
  end
119
118
  @app.call(env)
120
119
  end
121
-
122
- def create_multipart(env, params, boundary = nil)
123
- stream = super
124
- stream.read
125
- end
126
120
  end
127
121
  end
128
122
  end
@@ -7,20 +7,19 @@ module Faraday
7
7
  options.empty? ? ::Typhoeus::Hydra.hydra : ::Typhoeus::Hydra.new(options)
8
8
  end
9
9
 
10
- begin
11
- require 'typhoeus'
12
- rescue LoadError, NameError => e
13
- self.load_error = e
14
- end
10
+ dependency 'typhoeus'
15
11
 
16
12
  def call(env)
17
13
  super
18
14
 
19
- req = ::Typhoeus::Request.new env[:url].to_s,
15
+ # TODO: support streaming requests
16
+ env[:body] = env[:body].read if env[:body].respond_to? :read
17
+
18
+ req = ::Typhoeus::Request.new env[:url].to_s,
20
19
  :method => env[:method],
21
20
  :body => env[:body],
22
21
  :headers => env[:request_headers],
23
- :disable_ssl_peer_verification => (env[:ssl][:verify] == false)
22
+ :disable_ssl_peer_verification => (env[:ssl] && !env[:ssl].fetch(:verify, true))
24
23
 
25
24
  env_req = env[:request]
26
25
  req.timeout = req.connect_timeout = (env_req[:timeout] * 1000) if env_req[:timeout]
@@ -28,45 +27,18 @@ module Faraday
28
27
 
29
28
  is_parallel = !!env[:parallel_manager]
30
29
  req.on_complete do |resp|
31
- env.update \
32
- :status => resp.code,
33
- :response_headers => parse_response_headers(resp.headers),
34
- :body => resp.body
35
- env[:response].finish(env) if !is_parallel
30
+ env.update :status => resp.code, :body => resp.body
31
+ response_headers(env).parse resp.headers
32
+ env[:response].finish(env) if is_parallel
36
33
  end
37
34
 
38
35
  hydra = env[:parallel_manager] || self.class.setup_parallel_manager
39
36
  hydra.queue req
40
-
41
- if !is_parallel
42
- hydra.run
43
- end
37
+ hydra.run unless is_parallel
44
38
 
45
39
  @app.call env
46
40
  rescue Errno::ECONNREFUSED
47
- raise Error::ConnectionFailed.new(Errno::ECONNREFUSED)
48
- end
49
-
50
- def in_parallel(options = {})
51
- @hydra = ::Typhoeus::Hydra.new(options)
52
- yield
53
- @hydra.run
54
- @hydra = nil
55
- end
56
-
57
- def parse_response_headers(header_string)
58
- return {} unless header_string && !header_string.empty?
59
- Hash[*header_string.split(/\r\n/).
60
- tap { |a| a.shift }. # drop the HTTP status line
61
- map { |h| h.split(/:\s+/,2) }. # split key and value
62
- reject { |(k, v)| k.nil? }. # Ignore blank lines
63
- map { |(k, v)| [k.downcase, v] }.flatten]
64
- end
65
-
66
- # TODO: build in support for multipart streaming if typhoeus supports it.
67
- def create_multipart(env, params, boundary = nil)
68
- stream = super
69
- stream.read
41
+ raise Error::ConnectionFailed, $!
70
42
  end
71
43
  end
72
44
  end
@@ -1,60 +1,84 @@
1
1
  module Faraday
2
2
  # Possibly going to extend this a bit.
3
3
  #
4
- # Faraday::Connection.new(:url => 'http://sushi.com') do |b|
5
- # b.request :yajl # Faraday::Request::Yajl
6
- # b.adapter :logger # Faraday::Adapter::Logger
7
- # b.response :yajl # Faraday::Response::Yajl
4
+ # Faraday::Connection.new(:url => 'http://sushi.com') do |builder|
5
+ # builder.request :url_encoded # Faraday::Request::UrlEncoded
6
+ # builder.adapter :net_http # Faraday::Adapter::NetHttp
8
7
  # end
9
8
  class Builder
10
9
  attr_accessor :handlers
11
10
 
12
11
  def self.create
13
- block = block_given? ? Proc.new : nil
14
- Builder.new(&block)
12
+ new { |builder| yield builder }
15
13
  end
16
14
 
17
- def self.inner_app
18
- lambda do |env|
19
- env[:parallel_manager] ? env[:response] : env[:response].finish(env)
15
+ # borrowed from ActiveSupport::Dependencies::Reference &
16
+ # ActionDispatch::MiddlewareStack::Middleware
17
+ class Handler
18
+ @@constants = Hash.new { |h, k|
19
+ h[k] = k.respond_to?(:constantize) ? k.constantize : Object.const_get(k)
20
+ }
21
+
22
+ attr_reader :name
23
+
24
+ def initialize(klass, *args, &block)
25
+ @name = klass.to_s
26
+ @@constants[@name] = klass if klass.respond_to?(:name)
27
+ @args, @block = args, block
28
+ end
29
+
30
+ def klass() @@constants[@name] end
31
+ def inspect() @name end
32
+
33
+ def ==(other)
34
+ if other.respond_to? :name
35
+ klass == other
36
+ else
37
+ @name == other.to_s
38
+ end
39
+ end
40
+
41
+ def build(app)
42
+ klass.new(app, *@args, &@block)
20
43
  end
21
44
  end
22
45
 
23
46
  def initialize(handlers = [])
24
47
  @handlers = handlers
25
- build(Proc.new) if block_given?
48
+ if block_given?
49
+ build(&Proc.new)
50
+ elsif @handlers.empty?
51
+ # default stack, if nothing else is configured
52
+ self.request :url_encoded
53
+ self.adapter Faraday.default_adapter
54
+ end
26
55
  end
27
56
 
28
57
  def build(options = {})
29
- inner = @handlers.shift
30
- if !options[:keep]
31
- @handlers.clear
32
- end
58
+ @handlers.clear unless options[:keep]
33
59
  yield self if block_given?
34
- run(inner || self.class.inner_app)
35
60
  end
36
61
 
37
- def [](index)
38
- # @handlers are stored in reverse order
39
- @handlers[-(index+1)]
62
+ def [](idx)
63
+ @handlers[idx]
40
64
  end
41
65
 
42
- def run(app)
43
- @handlers.unshift app
66
+ def ==(other)
67
+ other.is_a?(self.class) && @handlers == other.handlers
44
68
  end
45
69
 
46
- def to_app
47
- if @handlers.empty?
48
- build { |b| b.adapter Faraday.default_adapter }
49
- end
70
+ def dup
71
+ self.class.new(@handlers.dup)
72
+ end
50
73
 
51
- inner_app = @handlers.first
52
- @handlers[1..-1].inject(inner_app) { |app, middleware| middleware.call(app) }
74
+ def to_app(inner_app)
75
+ # last added handler is the deepest and thus closest to the inner app
76
+ @handlers.reverse.inject(inner_app) { |app, handler| handler.build(app) }
53
77
  end
54
78
 
55
79
  def use(klass, *args)
56
80
  block = block_given? ? Proc.new : nil
57
- run(lambda { |app| klass.new(app, *args, &block) })
81
+ @handlers << self.class::Handler.new(klass, *args, &block)
58
82
  end
59
83
 
60
84
  def request(key, *args)
@@ -72,17 +96,42 @@ module Faraday
72
96
  use_symbol(Faraday::Adapter, key, *args, &block)
73
97
  end
74
98
 
99
+ ## methods to push onto the various positions in the stack:
100
+
101
+ def insert(index, *args, &block)
102
+ index = assert_index(index)
103
+ handler = self.class::Handler.new(*args, &block)
104
+ @handlers.insert(index, handler)
105
+ end
106
+
107
+ alias_method :insert_before, :insert
108
+
109
+ def insert_after(index, *args, &block)
110
+ index = assert_index(index)
111
+ insert(index + 1, *args, &block)
112
+ end
113
+
114
+ def swap(index, *args, &block)
115
+ index = assert_index(index)
116
+ @handlers.delete_at(index)
117
+ insert(index, *args, &block)
118
+ end
119
+
120
+ def delete(handler)
121
+ @handlers.delete(handler)
122
+ end
123
+
124
+ private
125
+
75
126
  def use_symbol(mod, key, *args)
76
127
  block = block_given? ? Proc.new : nil
77
128
  use(mod.lookup_module(key), *args, &block)
78
129
  end
79
130
 
80
- def ==(other)
81
- other.is_a?(self.class) && @handlers == other.handlers
82
- end
83
-
84
- def dup
85
- self.class.new(@handlers.dup)
131
+ def assert_index(index)
132
+ idx = index.is_a?(Integer) ? index : @handlers.index(index)
133
+ raise "No such handler: #{index.inspect}" unless idx
134
+ idx
86
135
  end
87
136
  end
88
137
  end
@@ -22,7 +22,7 @@ module Faraday
22
22
  options = url
23
23
  url = options[:url]
24
24
  end
25
- @headers = HeaderHash.new
25
+ @headers = Headers.new
26
26
  @params = {}
27
27
  @options = options[:request] || {}
28
28
  @ssl = options[:ssl] || {}
@@ -33,9 +33,7 @@ module Faraday
33
33
  merge_headers @headers, options[:headers] if options[:headers]
34
34
 
35
35
  if block_given?
36
- block = Proc.new
37
- @builder = Builder.new
38
- @builder.build { block.call(self) }
36
+ @builder = Builder.create { |b| yield b }
39
37
  else
40
38
  @builder = options[:builder] || Builder.new
41
39
  end
@@ -121,7 +119,7 @@ module Faraday
121
119
  case arg
122
120
  when String then {:uri => proxy_arg_to_uri(arg)}
123
121
  when URI then {:uri => arg}
124
- when Hash then arg
122
+ when Hash
125
123
  if arg[:uri] = proxy_arg_to_uri(arg[:uri])
126
124
  arg
127
125
  else
@@ -164,11 +162,6 @@ module Faraday
164
162
  @path_prefix = value
165
163
  end
166
164
 
167
- # return the assembled Rack application for this instance.
168
- def to_app
169
- @builder.to_app
170
- end
171
-
172
165
  def run_request(method, url, body, headers)
173
166
  if !METHODS.include?(method)
174
167
  raise ArgumentError, "unknown http method: #{method}"