faraday 0.5.7 → 0.6.0

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