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,183 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+ class EnvTest < Faraday::TestCase
4
+ def setup
5
+ @conn = Faraday.new :url => 'http://sushi.com/api',
6
+ :headers => {'Mime-Version' => '1.0'},
7
+ :request => {:oauth => {:consumer_key => 'anonymous'}}
8
+
9
+ @conn.options[:timeout] = 3
10
+ @conn.options[:open_timeout] = 5
11
+ @conn.ssl[:verify] = false
12
+ @conn.proxy 'http://proxy.com'
13
+ end
14
+
15
+ def test_request_create_stores_method
16
+ env = make_env(:get)
17
+ assert_equal :get, env[:method]
18
+ end
19
+
20
+ def test_request_create_stores_uri
21
+ env = make_env do |req|
22
+ req.url 'foo.json', 'a' => 1
23
+ end
24
+ assert_equal 'http://sushi.com/api/foo.json?a=1', env[:url].to_s
25
+ end
26
+
27
+ def test_request_create_stores_headers
28
+ env = make_env do |req|
29
+ req['Server'] = 'Faraday'
30
+ end
31
+ headers = env[:request_headers]
32
+ assert_equal '1.0', headers['mime-version']
33
+ assert_equal 'Faraday', headers['server']
34
+ end
35
+
36
+ def test_request_create_stores_body
37
+ env = make_env do |req|
38
+ req.body = 'hi'
39
+ end
40
+ assert_equal 'hi', env[:body]
41
+ end
42
+
43
+ def test_global_request_options
44
+ env = make_env
45
+ assert_equal 3, env[:request][:timeout]
46
+ assert_equal 5, env[:request][:open_timeout]
47
+ end
48
+
49
+ def test_per_request_options
50
+ env = make_env do |req|
51
+ req.options[:timeout] = 10
52
+ req.options[:custom] = true
53
+ req.options[:oauth][:consumer_secret] = 'xyz'
54
+ end
55
+ assert_equal 10, env[:request][:timeout]
56
+ assert_equal 5, env[:request][:open_timeout]
57
+ assert_equal true, env[:request][:custom]
58
+
59
+ oauth_expected = {:consumer_secret => 'xyz', :consumer_key => 'anonymous'}
60
+ assert_equal oauth_expected, env[:request][:oauth]
61
+ end
62
+
63
+ def test_request_create_stores_ssl_options
64
+ env = make_env
65
+ assert_equal false, env[:ssl][:verify]
66
+ end
67
+
68
+ def test_request_create_stores_proxy_options
69
+ env = make_env
70
+ assert_equal 'proxy.com', env[:request][:proxy][:uri].host
71
+ end
72
+
73
+ private
74
+
75
+ def make_env(method = :get, connection = @conn, &block)
76
+ request = connection.build_request(method, &block)
77
+ request.to_env(connection)
78
+ end
79
+ end
80
+
81
+ class HeadersTest < Faraday::TestCase
82
+ def setup
83
+ @headers = Faraday::Utils::Headers.new
84
+ end
85
+
86
+ def test_normalizes_different_capitalizations
87
+ @headers['Content-Type'] = 'application/json'
88
+ assert_equal ['Content-Type'], @headers.keys
89
+ assert_equal 'application/json', @headers['Content-Type']
90
+ assert_equal 'application/json', @headers['CONTENT-TYPE']
91
+ assert_equal 'application/json', @headers['content-type']
92
+ assert @headers.include?('content-type')
93
+
94
+ @headers['content-type'] = 'application/xml'
95
+ assert_equal ['Content-Type'], @headers.keys
96
+ assert_equal 'application/xml', @headers['Content-Type']
97
+ assert_equal 'application/xml', @headers['CONTENT-TYPE']
98
+ assert_equal 'application/xml', @headers['content-type']
99
+ end
100
+
101
+ def test_delete_key
102
+ @headers['Content-Type'] = 'application/json'
103
+ assert_equal 1, @headers.size
104
+ assert @headers.include?('content-type')
105
+ assert_equal 'application/json', @headers.delete('content-type')
106
+ assert_equal 0, @headers.size
107
+ assert !@headers.include?('content-type')
108
+ assert_equal nil, @headers.delete('content-type')
109
+ end
110
+
111
+ def test_parse_response_headers_leaves_http_status_line_out
112
+ @headers.parse("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n")
113
+ assert_equal %w(Content-Type), @headers.keys
114
+ end
115
+
116
+ def test_parse_response_headers_parses_lower_cased_header_name_and_value
117
+ @headers.parse("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n")
118
+ assert_equal 'text/html', @headers['content-type']
119
+ end
120
+
121
+ def test_parse_response_headers_parses_lower_cased_header_name_and_value_with_colon
122
+ @headers.parse("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nLocation: http://sushi.com/\r\n\r\n")
123
+ assert_equal 'http://sushi.com/', @headers['location']
124
+ end
125
+
126
+ def test_parse_response_headers_parses_blank_lines
127
+ @headers.parse("HTTP/1.1 200 OK\r\n\r\nContent-Type: text/html\r\n\r\n")
128
+ assert_equal 'text/html', @headers['content-type']
129
+ end
130
+ end
131
+
132
+ class ResponseTest < Faraday::TestCase
133
+ def setup
134
+ @env = {
135
+ :status => 404, :body => 'yikes',
136
+ :response_headers => Faraday::Utils::Headers.new('Content-Type' => 'text/plain')
137
+ }
138
+ @response = Faraday::Response.new @env
139
+ end
140
+
141
+ def test_finished
142
+ assert @response.finished?
143
+ end
144
+
145
+ def test_error_on_finish
146
+ assert_raises RuntimeError do
147
+ @response.finish({})
148
+ end
149
+ end
150
+
151
+ def test_not_success
152
+ assert !@response.success?
153
+ end
154
+
155
+ def test_status
156
+ assert_equal 404, @response.status
157
+ end
158
+
159
+ def test_body
160
+ assert_equal 'yikes', @response.body
161
+ end
162
+
163
+ def test_headers
164
+ assert_equal 'text/plain', @response.headers['Content-Type']
165
+ assert_equal 'text/plain', @response['content-type']
166
+ end
167
+
168
+ def test_apply_request
169
+ @response.apply_request :body => 'a=b', :method => :post
170
+ assert_equal 'yikes', @response.body
171
+ assert_equal :post, @response.env[:method]
172
+ end
173
+
174
+ def test_marshal
175
+ @response = Faraday::Response.new
176
+ @response.on_complete { }
177
+ @response.finish @env.merge(:custom => 'moo')
178
+
179
+ loaded = Marshal.load Marshal.dump(@response)
180
+ assert_nil loaded.env[:custom]
181
+ assert_equal %w[body response_headers status], loaded.env.keys.map { |k| k.to_s }.sort
182
+ end
183
+ end
@@ -0,0 +1,75 @@
1
+ unless ENV['CI']
2
+ begin
3
+ require 'simplecov'
4
+ SimpleCov.start do
5
+ add_filter 'test'
6
+ end
7
+ rescue LoadError
8
+ end
9
+ end
10
+
11
+ require 'test/unit'
12
+ require 'webmock/test_unit'
13
+ WebMock.disable_net_connect! :allow_localhost => true
14
+
15
+ if ENV['LEFTRIGHT']
16
+ begin
17
+ require 'leftright'
18
+ rescue LoadError
19
+ puts "Run `gem install leftright` to install leftright."
20
+ end
21
+ end
22
+
23
+ require File.expand_path('../../lib/faraday', __FILE__)
24
+
25
+ begin
26
+ require 'ruby-debug'
27
+ rescue LoadError
28
+ # ignore
29
+ else
30
+ Debugger.start
31
+ end
32
+
33
+ require 'stringio'
34
+ require 'uri'
35
+
36
+ module Faraday
37
+ module LiveServerConfig
38
+ def live_server=(value)
39
+ @@live_server = case value
40
+ when /^http/
41
+ URI(value)
42
+ when /./
43
+ URI('http://127.0.0.1:4567')
44
+ end
45
+ end
46
+
47
+ def live_server?
48
+ defined? @@live_server
49
+ end
50
+
51
+ # Returns an object that responds to `host` and `port`.
52
+ def live_server
53
+ live_server? and @@live_server
54
+ end
55
+ end
56
+
57
+ class TestCase < Test::Unit::TestCase
58
+ extend LiveServerConfig
59
+ self.live_server = ENV['LIVE']
60
+
61
+ def test_default
62
+ assert true
63
+ end unless defined? ::MiniTest
64
+
65
+ def capture_warnings
66
+ old, $stderr = $stderr, StringIO.new
67
+ begin
68
+ yield
69
+ $stderr.string
70
+ ensure
71
+ $stderr = old
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,57 @@
1
+ require 'sinatra/base'
2
+
3
+ module Faraday
4
+ class LiveServer < Sinatra::Base
5
+ set :environment, :test
6
+ disable :logging
7
+ disable :protection
8
+
9
+ [:get, :post, :put, :patch, :delete, :options].each do |method|
10
+ send(method, '/echo') do
11
+ kind = request.request_method.downcase
12
+ out = kind.dup
13
+ out << ' ?' << request.GET.inspect if request.GET.any?
14
+ out << ' ' << request.POST.inspect if request.POST.any?
15
+
16
+ content_type 'text/plain'
17
+ return out
18
+ end
19
+ end
20
+
21
+ get '/echo_header' do
22
+ header = "HTTP_#{params[:name].tr('-', '_').upcase}"
23
+ request.env.fetch(header) { 'NONE' }
24
+ end
25
+
26
+ post '/file' do
27
+ if params[:uploaded_file].respond_to? :each_key
28
+ "file %s %s" % [
29
+ params[:uploaded_file][:filename],
30
+ params[:uploaded_file][:type]]
31
+ else
32
+ status 400
33
+ end
34
+ end
35
+
36
+ get '/multi' do
37
+ [200, { 'Set-Cookie' => 'one, two' }, '']
38
+ end
39
+
40
+ get '/who-am-i' do
41
+ request.env['REMOTE_ADDR']
42
+ end
43
+
44
+ get '/slow' do
45
+ sleep 10
46
+ [200, {}, 'ok']
47
+ end
48
+
49
+ error do |e|
50
+ "#{e.class}\n#{e.to_s}\n#{e.backtrace.join("\n")}"
51
+ end
52
+ end
53
+ end
54
+
55
+ if $0 == __FILE__
56
+ Faraday::LiveServer.run!
57
+ end
@@ -0,0 +1,62 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "helper"))
2
+
3
+ module Middleware
4
+ class RetryTest < Faraday::TestCase
5
+ def setup
6
+ @times_called = 0
7
+ end
8
+
9
+ def conn(*retry_args)
10
+ Faraday.new do |b|
11
+ b.request :retry, *retry_args
12
+ b.adapter :test do |stub|
13
+ stub.post('/unstable') {
14
+ @times_called += 1
15
+ @explode.call @times_called
16
+ }
17
+ end
18
+ end
19
+ end
20
+
21
+ def test_unhandled_error
22
+ @explode = lambda {|n| raise "boom!" }
23
+ assert_raise(RuntimeError) { conn.post("/unstable") }
24
+ assert_equal 1, @times_called
25
+ end
26
+
27
+ def test_handled_error
28
+ @explode = lambda {|n| raise Errno::ETIMEDOUT }
29
+ assert_raise(Errno::ETIMEDOUT) { conn.post("/unstable") }
30
+ assert_equal 3, @times_called
31
+ end
32
+
33
+ def test_legacy_max_retries
34
+ @explode = lambda {|n| raise Errno::ETIMEDOUT }
35
+ assert_raise(Errno::ETIMEDOUT) { conn(1).post("/unstable") }
36
+ assert_equal 2, @times_called
37
+ end
38
+
39
+ def test_new_max_retries
40
+ @explode = lambda {|n| raise Errno::ETIMEDOUT }
41
+ assert_raise(Errno::ETIMEDOUT) { conn(:max => 3).post("/unstable") }
42
+ assert_equal 4, @times_called
43
+ end
44
+
45
+ def test_interval
46
+ @explode = lambda {|n| raise Errno::ETIMEDOUT }
47
+ started = Time.now
48
+ assert_raise(Errno::ETIMEDOUT) {
49
+ conn(:max => 2, :interval => 0.1).post("/unstable")
50
+ }
51
+ assert_in_delta 0.2, Time.now - started, 0.02
52
+ end
53
+
54
+ def test_custom_exceptions
55
+ @explode = lambda {|n| raise "boom!" }
56
+ assert_raise(RuntimeError) {
57
+ conn(:exceptions => StandardError).post("/unstable")
58
+ }
59
+ assert_equal 3, @times_called
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,203 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+ class MiddlewareStackTest < Faraday::TestCase
4
+ # mock handler classes
5
+ class Handler < Struct.new(:app)
6
+ def call(env)
7
+ (env[:request_headers]['X-Middleware'] ||= '') << ":#{self.class.name.split('::').last}"
8
+ app.call(env)
9
+ end
10
+ end
11
+ class Apple < Handler; end
12
+ class Orange < Handler; end
13
+ class Banana < Handler; end
14
+ class TestAdapter < Handler
15
+ def self.adapter?; true; end
16
+ end
17
+
18
+ class Broken < Faraday::Middleware
19
+ dependency 'zomg/i_dont/exist'
20
+ end
21
+
22
+ def setup
23
+ @conn = Faraday::Connection.new
24
+ @builder = @conn.builder
25
+ end
26
+
27
+ def test_sets_default_adapter_if_none_set
28
+ default_middleware = Faraday::Request.lookup_middleware :url_encoded
29
+ default_adapter_klass = Faraday::Adapter.lookup_middleware Faraday.default_adapter
30
+ assert @builder[0] == default_middleware
31
+ assert @builder[1] == default_adapter_klass
32
+ end
33
+
34
+ def test_allows_rebuilding
35
+ build_stack Apple
36
+ build_stack Orange
37
+ assert_handlers %w[Orange]
38
+ end
39
+
40
+ def test_allows_extending
41
+ build_stack Apple
42
+ @conn.use Orange
43
+ assert_handlers %w[Apple Orange]
44
+ end
45
+
46
+ def test_builder_is_passed_to_new_faraday_connection
47
+ new_conn = Faraday::Connection.new :builder => @builder
48
+ assert_equal @builder, new_conn.builder
49
+ end
50
+
51
+ def test_insert_before
52
+ build_stack Apple, Orange
53
+ @builder.insert_before Apple, Banana
54
+ assert_handlers %w[Banana Apple Orange]
55
+ end
56
+
57
+ def test_insert_after
58
+ build_stack Apple, Orange
59
+ @builder.insert_after Apple, Banana
60
+ assert_handlers %w[Apple Banana Orange]
61
+ end
62
+
63
+ def test_swap_handlers
64
+ build_stack Apple, Orange
65
+ @builder.swap Apple, Banana
66
+ assert_handlers %w[Banana Orange]
67
+ end
68
+
69
+ def test_delete_handler
70
+ build_stack Apple, Orange
71
+ @builder.delete Apple
72
+ assert_handlers %w[Orange]
73
+ end
74
+
75
+ def test_stack_is_locked_after_making_requests
76
+ build_stack Apple
77
+ assert !@builder.locked?
78
+ @conn.get('/')
79
+ assert @builder.locked?
80
+
81
+ assert_raises Faraday::Builder::StackLocked do
82
+ @conn.use Orange
83
+ end
84
+ end
85
+
86
+ def test_duped_stack_is_unlocked
87
+ build_stack Apple
88
+ assert !@builder.locked?
89
+ @builder.lock!
90
+ assert @builder.locked?
91
+
92
+ duped_connection = @conn.dup
93
+ assert_equal @builder, duped_connection.builder
94
+ assert !duped_connection.builder.locked?
95
+ end
96
+
97
+ def test_handler_comparison
98
+ build_stack Apple
99
+ assert_equal @builder.handlers.first, Apple
100
+ assert_equal @builder.handlers[0,1], [Apple]
101
+ assert_equal @builder.handlers.first, Faraday::Builder::Handler.new(Apple)
102
+ end
103
+
104
+ def test_unregistered_symbol
105
+ err = assert_raise(RuntimeError) { build_stack :apple }
106
+ assert_equal ":apple is not registered on Faraday::Middleware", err.message
107
+ end
108
+
109
+ def test_registered_symbol
110
+ Faraday.register_middleware :apple => Apple
111
+ begin
112
+ build_stack :apple
113
+ assert_handlers %w[Apple]
114
+ ensure
115
+ unregister_middleware Faraday::Middleware, :apple
116
+ end
117
+ end
118
+
119
+ def test_registered_symbol_with_proc
120
+ Faraday.register_middleware :apple => lambda { Apple }
121
+ begin
122
+ build_stack :apple
123
+ assert_handlers %w[Apple]
124
+ ensure
125
+ unregister_middleware Faraday::Middleware, :apple
126
+ end
127
+ end
128
+
129
+ def test_registered_symbol_with_type
130
+ Faraday.register_middleware :request, :orange => Orange
131
+ begin
132
+ build_stack {|b| b.request :orange }
133
+ assert_handlers %w[Orange]
134
+ ensure
135
+ unregister_middleware Faraday::Request, :orange
136
+ end
137
+ end
138
+
139
+ def test_missing_dependencies
140
+ build_stack Broken
141
+ err = assert_raises RuntimeError do
142
+ @conn.get('/')
143
+ end
144
+ assert_match "missing dependency for MiddlewareStackTest::Broken: ", err.message
145
+ assert_match "zomg/i_dont/exist", err.message
146
+ end
147
+
148
+ def test_knows_when_contains_an_adapter
149
+ @builder.clear
150
+ assert !@builder.has_adapter?
151
+ assert !@conn.has_adapter?
152
+ @conn.use Apple
153
+ assert !@builder.has_adapter?
154
+ assert !@conn.has_adapter?
155
+ @conn.use TestAdapter
156
+ assert @builder.has_adapter?
157
+ assert @conn.has_adapter?
158
+ end
159
+
160
+ def test_replace_adapter
161
+ @builder.clear
162
+ @conn.use Apple
163
+ @conn.use TestAdapter
164
+ @conn.adapter = :test, :foo, :bar
165
+ assert_equal 'Faraday::Adapter::Test', @conn.adapter.name
166
+ assert_equal [:foo, :bar], @conn.adapter.args
167
+ end
168
+
169
+ def test_set_adapter_as_connection_option
170
+ conn = Faraday::Connection.new('http://example.org',
171
+ :adapter => [:test, :foo, :bar])
172
+ assert_equal 'Faraday::Adapter::Test', conn.adapter.name
173
+ assert_equal [:foo, :bar], conn.adapter.args
174
+ end
175
+
176
+ private
177
+
178
+ # make a stack with test adapter that reflects the order of middleware
179
+ def build_stack(*handlers)
180
+ @builder.build do |b|
181
+ handlers.each { |handler| b.use(*handler) }
182
+ yield b if block_given?
183
+
184
+ b.adapter :test do |stub|
185
+ stub.get '/' do |env|
186
+ # echo the "X-Middleware" request header in the body
187
+ [200, {}, env[:request_headers]['X-Middleware'].to_s]
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+ def assert_handlers(list)
194
+ echoed_list = @conn.get('/').body.to_s.split(':')
195
+ echoed_list.shift if echoed_list.first == ''
196
+ assert_equal list, echoed_list
197
+ end
198
+
199
+ def unregister_middleware(component, key)
200
+ # TODO: unregister API?
201
+ component.instance_variable_get('@registered_middleware').delete(key)
202
+ end
203
+ end