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,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