faraday 0.8.0.rc2 → 0.8.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.
@@ -3,17 +3,29 @@ module Faraday
3
3
  class Patron < Faraday::Adapter
4
4
  dependency 'patron'
5
5
 
6
+ def initialize(app, &block)
7
+ super(app)
8
+ @block = block if block_given?
9
+ end
10
+
6
11
  def call(env)
7
12
  super
8
13
 
9
14
  # TODO: support streaming requests
10
15
  env[:body] = env[:body].read if env[:body].respond_to? :read
11
16
 
12
- session = ::Patron::Session.new
17
+ session = @session ||= create_session
13
18
 
14
19
  if req = env[:request]
15
20
  session.timeout = session.connect_timeout = req[:timeout] if req[:timeout]
16
21
  session.connect_timeout = req[:open_timeout] if req[:open_timeout]
22
+
23
+ if proxy = req[:proxy]
24
+ session.proxy = proxy[:uri].to_s
25
+ if proxy[:user] && proxy[:password]
26
+ prepend_proxy_auth_string(proxy, session)
27
+ end
28
+ end
17
29
  end
18
30
 
19
31
  response = begin
@@ -38,6 +50,16 @@ module Faraday
38
50
  actions << :options unless actions.include? :options
39
51
  end
40
52
  end
53
+
54
+ def create_session
55
+ session = ::Patron::Session.new
56
+ @block.call(session) if @block
57
+ session
58
+ end
59
+ end
60
+
61
+ def prepend_proxy_auth_string(proxy, session)
62
+ session.proxy.insert(7, "#{proxy[:user]}:#{proxy[:password]}@")
41
63
  end
42
64
  end
43
65
  end
@@ -0,0 +1,59 @@
1
+ require 'timeout'
2
+
3
+ module Faraday
4
+ class Adapter
5
+ # Sends requests to a Rack app.
6
+ #
7
+ # Examples
8
+ #
9
+ # class MyRackApp
10
+ # def call(env)
11
+ # [200, {'Content-Type' => 'text/html'}, ["hello world"]]
12
+ # end
13
+ # end
14
+ #
15
+ # Faraday.new do |conn|
16
+ # conn.adapter :rack, MyRackApp
17
+ # end
18
+ class Rack < Faraday::Adapter
19
+ dependency 'rack/test'
20
+
21
+ # not prefixed with "HTTP_"
22
+ SPECIAL_HEADERS = %w[ CONTENT_LENGTH CONTENT_TYPE ]
23
+
24
+ def initialize(faraday_app, rack_app)
25
+ super(faraday_app)
26
+ mock_session = ::Rack::MockSession.new(rack_app)
27
+ @session = ::Rack::Test::Session.new(mock_session)
28
+ end
29
+
30
+ def call(env)
31
+ super
32
+ rack_env = {
33
+ :method => env[:method],
34
+ :input => env[:body].respond_to?(:read) ? env[:body].read : env[:body]
35
+ }
36
+
37
+ env[:request_headers].each do |name, value|
38
+ name = name.upcase.tr('-', '_')
39
+ name = "HTTP_#{name}" unless SPECIAL_HEADERS.include? name
40
+ rack_env[name] = value
41
+ end if env[:request_headers]
42
+
43
+ timeout = env[:request][:timeout] || env[:request][:open_timeout]
44
+ response = if timeout
45
+ Timer.timeout(timeout, Faraday::Error::TimeoutError) { execute_request(env, rack_env) }
46
+ else
47
+ execute_request(env, rack_env)
48
+ end
49
+
50
+ save_response(env, response.status, response.body, response.headers)
51
+ @app.call env
52
+ end
53
+
54
+ def execute_request(env, rack_env)
55
+ @session.request(env[:url].to_s, rack_env)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -63,10 +63,11 @@ module Faraday
63
63
  def configure_ssl(req, env)
64
64
  ssl = env[:ssl]
65
65
 
66
- req.ssl_cert = ssl[:client_cert_file] if ssl[:client_cert_file]
67
- req.ssl_key = ssl[:client_key_file] if ssl[:client_key_file]
68
- req.ssl_cacert = ssl[:ca_file] if ssl[:ca_file]
69
- req.ssl_capath = ssl[:ca_path] if ssl[:ca_path]
66
+ req.ssl_version = ssl[:version] if ssl[:version]
67
+ req.ssl_cert = ssl[:client_cert_file] if ssl[:client_cert_file]
68
+ req.ssl_key = ssl[:client_key_file] if ssl[:client_key_file]
69
+ req.ssl_cacert = ssl[:ca_file] if ssl[:ca_file]
70
+ req.ssl_capath = ssl[:ca_path] if ssl[:ca_path]
70
71
  end
71
72
 
72
73
  def configure_proxy(req, env)
@@ -1,4 +1,3 @@
1
- require 'base64'
2
1
  require 'cgi'
3
2
  require 'set'
4
3
  require 'forwardable'
@@ -11,8 +10,8 @@ module Faraday
11
10
  METHODS = Set.new [:get, :post, :put, :delete, :head, :patch, :options]
12
11
  METHODS_WITH_BODIES = Set.new [:post, :put, :patch, :options]
13
12
 
14
- attr_reader :params, :headers, :url_prefix, :builder, :options, :ssl, :parallel_manager
15
- attr_writer :default_parallel_manager
13
+ attr_reader :params, :headers, :url_prefix, :builder, :options, :ssl, :parallel_manager
14
+ attr_writer :default_parallel_manager
16
15
 
17
16
  # :url
18
17
  # :params
@@ -103,11 +102,18 @@ module Faraday
103
102
  end
104
103
 
105
104
  def basic_auth(login, pass)
106
- @builder.insert(0, Faraday::Request::BasicAuthentication, login, pass)
105
+ headers[Faraday::Request::Authorization::KEY] =
106
+ Faraday::Request::BasicAuthentication.header(login, pass)
107
107
  end
108
108
 
109
- def token_auth(token, options = {})
110
- @builder.insert(0, Faraday::Request::TokenAuthentication, token, options)
109
+ def token_auth(token, options = nil)
110
+ headers[Faraday::Request::Authorization::KEY] =
111
+ Faraday::Request::TokenAuthentication.header(token, options)
112
+ end
113
+
114
+ def authorization(type, token)
115
+ headers[Faraday::Request::Authorization::KEY] =
116
+ Faraday::Request::Authorization.header(type, token)
111
117
  end
112
118
 
113
119
  # Internal: Traverse the middleware stack in search of a
@@ -118,9 +124,10 @@ module Faraday
118
124
  # Returns a parallel manager or nil if not found.
119
125
  def default_parallel_manager
120
126
  @default_parallel_manager ||= begin
121
- handler = @builder.handlers.find { |h|
127
+ handler = @builder.handlers.detect do |h|
122
128
  h.klass.respond_to?(:supports_parallel?) and h.klass.supports_parallel?
123
- }
129
+ end
130
+
124
131
  if handler then handler.klass.setup_parallel_manager
125
132
  elsif block_given? then yield
126
133
  end
@@ -155,10 +162,14 @@ module Faraday
155
162
  end
156
163
 
157
164
  # normalize URI() behavior across Ruby versions
158
- def self.URI url
159
- url.respond_to?(:host) ? url :
160
- url.respond_to?(:to_str) ? Kernel.URI(url) :
161
- raise(ArgumentError, "bad argument (expected URI object or URI string)")
165
+ def self.URI(url)
166
+ if url.respond_to?(:host)
167
+ url
168
+ elsif url.respond_to?(:to_str)
169
+ Kernel.URI(url)
170
+ else
171
+ raise ArgumentError, "bad argument (expected URI object or URI string)"
172
+ end
162
173
  end
163
174
 
164
175
  def_delegators :url_prefix, :scheme, :scheme=, :host, :host=, :port, :port=
@@ -15,18 +15,20 @@ module Faraday
15
15
 
16
16
  autoload_all 'faraday/request',
17
17
  :UrlEncoded => 'url_encoded',
18
- :Multipart => 'multipart',
19
- :Retry => 'retry',
20
- :Timeout => 'timeout',
18
+ :Multipart => 'multipart',
19
+ :Retry => 'retry',
20
+ :Timeout => 'timeout',
21
+ :Authorization => 'authorization',
21
22
  :BasicAuthentication => 'basic_authentication',
22
23
  :TokenAuthentication => 'token_authentication'
23
24
 
24
25
  register_middleware \
25
26
  :url_encoded => :UrlEncoded,
26
- :multipart => :Multipart,
27
- :retry => :Retry,
28
- :basic_auth => :BasicAuthentication,
29
- :token_auth => :TokenAuthentication
27
+ :multipart => :Multipart,
28
+ :retry => :Retry,
29
+ :authorization => :Authorization,
30
+ :basic_auth => :BasicAuthentication,
31
+ :token_auth => :TokenAuthentication
30
32
 
31
33
  def self.create(request_method)
32
34
  new(request_method).tap do |request|
@@ -0,0 +1,40 @@
1
+ module Faraday
2
+ class Request::Authorization < Faraday::Middleware
3
+ KEY = "Authorization".freeze
4
+
5
+ # Public
6
+ def self.header(type, token)
7
+ case token
8
+ when String, Symbol then "#{type} #{token}"
9
+ when Hash then build_hash(type.to_s, token)
10
+ else
11
+ raise ArgumentError, "Can't build an Authorization #{type} header from #{token.inspect}"
12
+ end
13
+ end
14
+
15
+ # Internal
16
+ def self.build_hash(type, hash)
17
+ offset = KEY.size + type.size + 3
18
+ comma = ",\n#{' ' * offset}"
19
+ values = []
20
+ hash.each do |key, value|
21
+ values << "#{key}=#{value.to_s.inspect}"
22
+ end
23
+ "#{type} #{values * comma}"
24
+ end
25
+
26
+ def initialize(app, type, token)
27
+ @header_value = self.class.header(type, token)
28
+ super(app)
29
+ end
30
+
31
+ # Public
32
+ def call(env)
33
+ unless env[:request_headers][KEY]
34
+ env[:request_headers][KEY] = @header_value
35
+ end
36
+ @app.call(env)
37
+ end
38
+ end
39
+ end
40
+
@@ -1,17 +1,13 @@
1
1
  require 'base64'
2
2
 
3
3
  module Faraday
4
- class Request::BasicAuthentication < Faraday::Middleware
5
- def initialize(app, login, pass)
6
- super(app)
7
- @header_value = "Basic #{Base64.encode64([login, pass].join(':')).gsub("\n", '')}"
8
- end
9
-
10
- def call(env)
11
- unless env[:request_headers]['Authorization']
12
- env[:request_headers]['Authorization'] = @header_value
13
- end
14
- @app.call(env)
4
+ class Request::BasicAuthentication < Request::Authorization
5
+ # Public
6
+ def self.header(login, pass)
7
+ value = Base64.encode64([login, pass].join(':'))
8
+ value.gsub!("\n", '')
9
+ super(:Basic, value)
15
10
  end
16
11
  end
17
12
  end
13
+
@@ -1,21 +1,15 @@
1
1
  module Faraday
2
- class Request::TokenAuthentication < Faraday::Middleware
3
- def initialize(app, token, options={})
4
- super(app)
5
-
6
- values = ["token=#{token.to_s.inspect}"]
7
- options.each do |key, value|
8
- values << "#{key}=#{value.to_s.inspect}"
9
- end
10
- comma = ",\n#{' ' * ('Authorization: Token '.size)}"
11
- @header_value = "Token #{values * comma}"
2
+ class Request::TokenAuthentication < Request::Authorization
3
+ # Public
4
+ def self.header(token, options = nil)
5
+ options ||= {}
6
+ options[:token] = token
7
+ super :Token, options
12
8
  end
13
9
 
14
- def call(env)
15
- unless env[:request_headers]['Authorization']
16
- env[:request_headers]['Authorization'] = @header_value
17
- end
18
- @app.call(env)
10
+ def initialize(app, token, options = nil)
11
+ super(app, token, options)
19
12
  end
20
13
  end
21
14
  end
15
+
@@ -0,0 +1,14 @@
1
+ require File.expand_path('../integration', __FILE__)
2
+
3
+ module Adapters
4
+ class DefaultTest < Faraday::TestCase
5
+
6
+ def adapter() :default end
7
+
8
+ Integration.apply(self, :NonParallel) do
9
+ # default stack is not configured with Multipart
10
+ undef :test_POST_sends_files
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require File.expand_path('../integration', __FILE__)
2
+
3
+ module Adapters
4
+ class EMHttpTest < Faraday::TestCase
5
+
6
+ def adapter() :em_http end
7
+
8
+ Integration.apply(self, :Parallel) do
9
+ # https://github.com/eventmachine/eventmachine/pull/289
10
+ undef :test_timeout
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require File.expand_path('../integration', __FILE__)
2
+
3
+ module Adapters
4
+ class EMSynchronyTest < Faraday::TestCase
5
+
6
+ def adapter() :em_synchrony end
7
+
8
+ Integration.apply(self, :Parallel) do
9
+ # https://github.com/eventmachine/eventmachine/pull/289
10
+ undef :test_timeout
11
+ end
12
+
13
+ end unless RUBY_VERSION < '1.9' or (defined? RUBY_ENGINE and 'jruby' == RUBY_ENGINE)
14
+ end
@@ -0,0 +1,23 @@
1
+ require File.expand_path('../integration', __FILE__)
2
+
3
+ module Adapters
4
+ class ExconTest < Faraday::TestCase
5
+
6
+ def adapter() :excon end
7
+
8
+ # https://github.com/geemus/excon/issues/98
9
+ if defined?(RUBY_ENGINE) and "rbx" == RUBY_ENGINE
10
+ warn "Warning: Skipping Excon tests on Rubinius"
11
+ else
12
+ Integration.apply(self, :NonParallel) do
13
+ # https://github.com/eventmachine/eventmachine/pull/289
14
+ undef :test_timeout
15
+
16
+ # FIXME: this test fails on Travis with
17
+ # "Faraday::Error::ClientError: the server responded with status 400"
18
+ undef :test_POST_sends_files if ENV['CI']
19
+ end
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,190 @@
1
+ require 'forwardable'
2
+ require File.expand_path("../../helper", __FILE__)
3
+
4
+ module Adapters
5
+ # Adapter integration tests. To use, implement two methods:
6
+ #
7
+ # `#adapter` required. returns a symbol for the adapter middleware name
8
+ # `#adapter_options` optional. extra arguments for building an adapter
9
+ module Integration
10
+ def self.apply(base, *extras)
11
+ if Faraday::TestCase::LIVE_SERVER
12
+ ([:Common] + extras).each {|name| base.send(:include, self.const_get(name)) }
13
+ yield if block_given?
14
+ elsif !defined? @warned
15
+ warn "Warning: Not running integration tests against a live server."
16
+ warn "Start the server `ruby test/live_server.rb` and set the LIVE=1 env variable."
17
+ @warned = true
18
+ end
19
+ end
20
+
21
+ module Parallel
22
+ def test_in_parallel
23
+ resp1, resp2 = nil, nil
24
+
25
+ connection = create_connection
26
+ connection.in_parallel do
27
+ resp1 = connection.get('echo?a=1')
28
+ resp2 = connection.get('echo?b=2')
29
+ assert connection.in_parallel?
30
+ assert_nil resp1.body
31
+ assert_nil resp2.body
32
+ end
33
+ assert !connection.in_parallel?
34
+ assert_equal 'get ?{"a"=>"1"}', resp1.body
35
+ assert_equal 'get ?{"b"=>"2"}', resp2.body
36
+ end
37
+ end
38
+
39
+ module NonParallel
40
+ def test_no_parallel_support
41
+ connection = create_connection
42
+ response = nil
43
+
44
+ err = capture_warnings do
45
+ connection.in_parallel do
46
+ response = connection.get('echo').body
47
+ end
48
+ end
49
+ assert response
50
+ assert_match "no parallel-capable adapter on Faraday stack", err
51
+ assert_match __FILE__, err
52
+ end
53
+ end
54
+
55
+ module Compression
56
+ def test_GET_handles_compression
57
+ res = get('echo_header', :name => 'accept-encoding')
58
+ assert_match /gzip;.+\bdeflate\b/, res.body
59
+ end
60
+ end
61
+
62
+ module Common
63
+ extend Forwardable
64
+ def_delegators :create_connection, :get, :head, :put, :post, :patch, :delete, :run_request
65
+
66
+ def test_GET_retrieves_the_response_body
67
+ assert_equal 'get', get('echo').body
68
+ end
69
+
70
+ def test_GET_send_url_encoded_params
71
+ assert_equal %(get ?{"name"=>"zack"}), get('echo', :name => 'zack').body
72
+ end
73
+
74
+ def test_GET_retrieves_the_response_headers
75
+ response = get('echo')
76
+ assert_match(/text\/plain/, response.headers['Content-Type'], 'original case fail')
77
+ assert_match(/text\/plain/, response.headers['content-type'], 'lowercase fail')
78
+ end
79
+
80
+ def test_GET_handles_headers_with_multiple_values
81
+ assert_equal 'one, two', get('multi').headers['set-cookie']
82
+ end
83
+
84
+ def test_GET_with_body
85
+ response = get('echo') do |req|
86
+ req.body = {'bodyrock' => true}
87
+ end
88
+ assert_equal %(get {"bodyrock"=>"true"}), response.body
89
+ end
90
+
91
+ def test_GET_sends_user_agent
92
+ response = get('echo_header', {:name => 'user-agent'}, :user_agent => 'Agent Faraday')
93
+ assert_equal 'Agent Faraday', response.body
94
+ end
95
+
96
+ def test_POST_send_url_encoded_params
97
+ assert_equal %(post {"name"=>"zack"}), post('echo', :name => 'zack').body
98
+ end
99
+
100
+ def test_POST_send_url_encoded_nested_params
101
+ resp = post('echo', 'name' => {'first' => 'zack'})
102
+ assert_equal %(post {"name"=>{"first"=>"zack"}}), resp.body
103
+ end
104
+
105
+ def test_POST_retrieves_the_response_headers
106
+ assert_match(/text\/plain/, post('echo').headers['content-type'])
107
+ end
108
+
109
+ def test_POST_sends_files
110
+ resp = post('file') do |req|
111
+ req.body = {'uploaded_file' => Faraday::UploadIO.new(__FILE__, 'text/x-ruby')}
112
+ end
113
+ assert_equal "file integration.rb text/x-ruby", resp.body
114
+ end
115
+
116
+ def test_PUT_send_url_encoded_params
117
+ assert_equal %(put {"name"=>"zack"}), put('echo', :name => 'zack').body
118
+ end
119
+
120
+ def test_PUT_send_url_encoded_nested_params
121
+ resp = put('echo', 'name' => {'first' => 'zack'})
122
+ assert_equal %(put {"name"=>{"first"=>"zack"}}), resp.body
123
+ end
124
+
125
+ def test_PUT_retrieves_the_response_headers
126
+ assert_match(/text\/plain/, put('echo').headers['content-type'])
127
+ end
128
+
129
+ def test_PATCH_send_url_encoded_params
130
+ assert_equal %(patch {"name"=>"zack"}), patch('echo', :name => 'zack').body
131
+ end
132
+
133
+ def test_OPTIONS
134
+ resp = run_request(:options, 'echo', nil, {})
135
+ assert_equal 'options', resp.body
136
+ end
137
+
138
+ def test_HEAD_retrieves_no_response_body
139
+ # FIXME: some adapters return empty string, some nil
140
+ assert_equal '', head('echo').body.to_s
141
+ end
142
+
143
+ def test_HEAD_retrieves_the_response_headers
144
+ assert_match(/text\/plain/, head('echo').headers['content-type'])
145
+ end
146
+
147
+ def test_DELETE_retrieves_the_response_headers
148
+ assert_match(/text\/plain/, delete('echo').headers['content-type'])
149
+ end
150
+
151
+ def test_DELETE_retrieves_the_body
152
+ assert_equal %(delete), delete('echo').body
153
+ end
154
+
155
+ def test_timeout
156
+ conn = create_connection(:request => {:timeout => 1, :open_timeout => 1})
157
+ assert_raise Faraday::Error::TimeoutError do
158
+ conn.get '/slow'
159
+ end
160
+ end
161
+
162
+ def adapter
163
+ raise NotImplementedError.new("Need to override #adapter")
164
+ end
165
+
166
+ # extra options to pass when building the adapter
167
+ def adapter_options
168
+ []
169
+ end
170
+
171
+ def create_connection(options = {})
172
+ if adapter == :default
173
+ builder_block = nil
174
+ else
175
+ builder_block = Proc.new do |b|
176
+ b.request :multipart
177
+ b.request :url_encoded
178
+ b.adapter adapter, *adapter_options
179
+ end
180
+ end
181
+
182
+ Faraday::Connection.new(Faraday::TestCase::LIVE_SERVER, options, &builder_block).tap do |conn|
183
+ conn.headers['X-Faraday-Adapter'] = adapter.to_s
184
+ adapter_handler = conn.builder.handlers.last
185
+ conn.builder.insert_before adapter_handler, Faraday::Response::RaiseError
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end