faraday 0.8.0.rc2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -8,12 +8,14 @@ group :test do
8
8
  gem 'em-http-request', '~> 1.0', :require => 'em-http'
9
9
  gem 'em-synchrony', '~> 1.0', :require => ['em-synchrony', 'em-synchrony/em-http'], :platforms => :ruby_19
10
10
  gem 'excon', '~> 0.6'
11
+ gem 'net-http-persistent', '~> 2.5', :require => false
11
12
  gem 'leftright', '~> 0.9', :require => false
13
+ gem 'rack-test', '~> 0.6', :require => 'rack/test'
12
14
  end
13
15
 
14
16
  platforms :ruby do
15
- gem 'patron', '~> 0.4'
16
- gem 'typhoeus', '~> 0.3'
17
+ gem 'patron', '~> 0.4', '> 0.4.1'
18
+ gem 'typhoeus', '~> 0.3', '> 0.3.2'
17
19
  end
18
20
 
19
21
  platforms :jruby do
data/README.md CHANGED
@@ -1,9 +1,22 @@
1
- # faraday [![Build Status](https://secure.travis-ci.org/technoweenie/faraday.png?branch=master)][travis] [![Dependency Status](https://gemnasium.com/technoweenie/faraday.png?travis)][gemnasium]
2
- Modular HTTP client library that uses middleware. Heavily inspired by Rack.
3
-
1
+ # Faraday [![Build Status](https://secure.travis-ci.org/technoweenie/faraday.png?branch=master)][travis] [![Dependency Status](https://gemnasium.com/technoweenie/faraday.png?travis)][gemnasium]
4
2
  [travis]: http://travis-ci.org/technoweenie/faraday
5
3
  [gemnasium]: https://gemnasium.com/technoweenie/faraday
6
4
 
5
+ Faraday is an HTTP client lib that provides a common interface over many
6
+ adapters (such as Net::HTTP) and embraces the concept of Rack middleware when
7
+ processing the request/response cycle.
8
+
9
+ Faraday supports these adapters:
10
+
11
+ * Net/HTTP
12
+ * Excon
13
+ * Typhoeus
14
+ * Patron
15
+ * EventMachine
16
+
17
+ It also includes a Rack adapter for hitting loaded Rack applications through
18
+ Rack::Test, and a Test adapter for stubbing requests by hand.
19
+
7
20
  ## <a name="usage"></a>Usage
8
21
 
9
22
  ```ruby
@@ -23,7 +36,7 @@ end
23
36
  response = conn.get '/nigiri/sake.json' # GET http://sushi.com/nigiri/sake.json
24
37
  response.body
25
38
 
26
- conn.get '/nigiri', 'X-Awesome' => true # custom request header
39
+ conn.get '/nigiri', { :name => 'Maguro' } # GET /nigiri?name=Maguro
27
40
 
28
41
  conn.get do |req| # GET http://sushi.com/search?page=2&limit=100
29
42
  req.url '/search', :page => 2
@@ -65,24 +78,30 @@ response = Faraday.get 'http://sushi.com/nigiri/sake.json'
65
78
  ```
66
79
 
67
80
  ## Advanced middleware usage
68
- The order in which middleware is stacked is important. Like with Rack, the first middleware on the list wraps all others, while the last middleware is the innermost one, so that's usually the adapter.
81
+ The order in which middleware is stacked is important. Like with Rack, the
82
+ first middleware on the list wraps all others, while the last middleware is the
83
+ innermost one, so that's usually the adapter.
69
84
 
70
85
  ```ruby
71
86
  conn = Faraday.new(:url => 'http://sushi.com') do |builder|
72
87
  # POST/PUT params encoders:
73
- builder.request :multipart
74
- builder.request :url_encoded
88
+ builder.request :multipart
89
+ builder.request :url_encoded
75
90
 
76
- builder.adapter :net_http
91
+ builder.adapter :net_http
77
92
  end
78
93
  ```
79
94
 
80
95
  This request middleware setup affects POST/PUT requests in the following way:
81
96
 
82
- 1. `Request::Multipart` checks for files in the payload, otherwise leaves everything untouched;
83
- 2. `Request::UrlEncoded` encodes as "application/x-www-form-urlencoded" if not already encoded or of another type
97
+ 1. `Request::Multipart` checks for files in the payload, otherwise leaves
98
+ everything untouched;
99
+ 2. `Request::UrlEncoded` encodes as "application/x-www-form-urlencoded" if not
100
+ already encoded or of another type
84
101
 
85
- Swapping middleware means giving the other priority. Specifying the "Content-Type" for the request is explicitly stating which middleware should process it.
102
+ Swapping middleware means giving the other priority. Specifying the
103
+ "Content-Type" for the request is explicitly stating which middleware should
104
+ process it.
86
105
 
87
106
  Examples:
88
107
 
@@ -97,7 +116,8 @@ conn.put '/profile', payload
97
116
  ```
98
117
 
99
118
  ## Writing middleware
100
- Middleware are classes that respond to `call()`. They wrap the request/response cycle.
119
+ Middleware are classes that respond to `call()`. They wrap the request/response
120
+ cycle.
101
121
 
102
122
  ```ruby
103
123
  def call(env)
@@ -109,9 +129,12 @@ def call(env)
109
129
  end
110
130
  ```
111
131
 
112
- It's important to do all processing of the response only in the `on_complete` block. This enables middleware to work in parallel mode where requests are asynchronous.
132
+ It's important to do all processing of the response only in the `on_complete`
133
+ block. This enables middleware to work in parallel mode where requests are
134
+ asynchronous.
113
135
 
114
- The `env` is a hash with symbol keys that contains info about the request and, later, response. Some keys are:
136
+ The `env` is a hash with symbol keys that contains info about the request and,
137
+ later, response. Some keys are:
115
138
 
116
139
  ```
117
140
  # request phase
@@ -164,7 +187,6 @@ stubs.verify_stubbed_calls
164
187
  ## <a name="todo"></a>TODO
165
188
  * support streaming requests/responses
166
189
  * better stubbing API
167
- * Add curb, em-http, fast_http
168
190
 
169
191
  ## <a name="pulls"></a>Note on Patches/Pull Requests
170
192
  1. Fork the project.
@@ -174,7 +196,11 @@ stubs.verify_stubbed_calls
174
196
  4. Commit, do not mess with rakefile, version, or history. (if you want to have
175
197
  your own version, that is fine but bump version in a commit by itself I can
176
198
  ignore when I pull)
177
- 5. Send me a pull request. Bonus points for topic branches.
199
+ 5. Send us a pull request. Bonus points for topic branches.
200
+
201
+ We are pushing towards a 1.0 release, when we will have to follow [Semantic
202
+ Versioning](http://semver.org/). If your patch includes changes to break
203
+ compatiblitity, note that so we can add it to the [Changelog](https://github.com/technoweenie/faraday/wiki/Changelog).
178
204
 
179
205
  ## <a name="versions"></a>Supported Ruby Versions
180
206
  This library aims to support and is [tested against][travis] the following Ruby
@@ -185,11 +211,9 @@ implementations:
185
211
  * Ruby 1.9.3
186
212
  * JRuby[]
187
213
  * [Rubinius][]
188
- * [Ruby Enterprise Edition][ree]
189
214
 
190
215
  [jruby]: http://jruby.org/
191
216
  [rubinius]: http://rubini.us/
192
- [ree]: http://www.rubyenterpriseedition.com/
193
217
 
194
218
  If something doesn't work on one of these interpreters, it should be considered
195
219
  a bug.
@@ -206,6 +230,7 @@ timely fashion. If critical issues for a particular implementation exist at the
206
230
  time of a major release, support for that Ruby version may be dropped.
207
231
 
208
232
  ## <a name="copyright"></a>Copyright
209
- Copyright (c) 2009 rick olson, zack hobson. See [LICENSE][] for details.
233
+ Copyright (c) 2009-12 [Rick Olson](mailto:technoweenie@gmail.com), zack hobson.
234
+ See [LICENSE][] for details.
210
235
 
211
236
  [license]: https://github.com/technoweenie/faraday/blob/master/LICENSE.md
data/Rakefile CHANGED
@@ -51,7 +51,7 @@ desc "Commit, create tag v#{version} and build and push #{gem_file} to Rubygems"
51
51
  task :release => :build do
52
52
  sh "git commit --allow-empty -a -m 'Release #{version}'"
53
53
  sh "git tag v#{version}"
54
- sh "git push"
54
+ sh "git push origin master"
55
55
  sh "git push origin v#{version}"
56
56
  sh "gem push pkg/#{gem_file}"
57
57
  end
@@ -3,7 +3,7 @@ Gem::Specification.new do |s|
3
3
  s.required_rubygems_version = Gem::Requirement.new(">= 1.3.5") if s.respond_to? :required_rubygems_version=
4
4
 
5
5
  s.name = 'faraday'
6
- s.version = '0.8.0.rc2'
6
+ s.version = '0.8.0'
7
7
 
8
8
  s.summary = "HTTP/REST API client library."
9
9
  # TODO: s.description
@@ -14,6 +14,7 @@ Gem::Specification.new do |s|
14
14
 
15
15
  s.add_dependency 'multipart-post', '~> 1.1'
16
16
  s.add_development_dependency 'rake'
17
+ s.add_development_dependency 'simplecov'
17
18
  s.add_development_dependency 'test-unit'
18
19
  s.add_development_dependency 'webmock'
19
20
 
@@ -27,13 +28,14 @@ Gem::Specification.new do |s|
27
28
  faraday.gemspec
28
29
  lib/faraday.rb
29
30
  lib/faraday/adapter.rb
30
- lib/faraday/adapter/action_dispatch.rb
31
31
  lib/faraday/adapter/em_http.rb
32
32
  lib/faraday/adapter/em_synchrony.rb
33
33
  lib/faraday/adapter/em_synchrony/parallel_manager.rb
34
34
  lib/faraday/adapter/excon.rb
35
35
  lib/faraday/adapter/net_http.rb
36
+ lib/faraday/adapter/net_http_persistent.rb
36
37
  lib/faraday/adapter/patron.rb
38
+ lib/faraday/adapter/rack.rb
37
39
  lib/faraday/adapter/test.rb
38
40
  lib/faraday/adapter/typhoeus.rb
39
41
  lib/faraday/builder.rb
@@ -41,6 +43,7 @@ Gem::Specification.new do |s|
41
43
  lib/faraday/error.rb
42
44
  lib/faraday/middleware.rb
43
45
  lib/faraday/request.rb
46
+ lib/faraday/request/authorization.rb
44
47
  lib/faraday/request/basic_authentication.rb
45
48
  lib/faraday/request/multipart.rb
46
49
  lib/faraday/request/retry.rb
@@ -51,9 +54,16 @@ Gem::Specification.new do |s|
51
54
  lib/faraday/response/raise_error.rb
52
55
  lib/faraday/upload_io.rb
53
56
  lib/faraday/utils.rb
54
- test/adapters/live_test.rb
57
+ test/adapters/default_test.rb
58
+ test/adapters/em_http_test.rb
59
+ test/adapters/em_synchrony_test.rb
60
+ test/adapters/excon_test.rb
61
+ test/adapters/integration.rb
55
62
  test/adapters/logger_test.rb
63
+ test/adapters/net_http_persistent_test.rb
56
64
  test/adapters/net_http_test.rb
65
+ test/adapters/patron_test.rb
66
+ test/adapters/rack_test.rb
57
67
  test/adapters/test_middleware_test.rb
58
68
  test/adapters/typhoeus_test.rb
59
69
  test/authentication_middleware_test.rb
@@ -1,5 +1,5 @@
1
1
  module Faraday
2
- VERSION = "0.8.0.rc2"
2
+ VERSION = "0.8.0"
3
3
 
4
4
  class << self
5
5
  attr_accessor :root_path, :lib_path
@@ -33,6 +33,15 @@ module Faraday
33
33
  @default_connection ||= Connection.new
34
34
  end
35
35
 
36
+ begin
37
+ require RUBY_VERSION < '1.9' ? 'system_timer' : 'timeout'
38
+ rescue LoadError
39
+ require 'timeout'
40
+ warn "Faraday: you may want to install system_timer for reliable timeouts"
41
+ ensure
42
+ Timer = defined?(::SystemTimer) ? ::SystemTimer : ::Timeout
43
+ end
44
+
36
45
  module MiddlewareRegistry
37
46
  # Internal: Register middleware class(es) on the current module.
38
47
  #
@@ -116,7 +125,7 @@ module Faraday
116
125
  end
117
126
 
118
127
 
119
- # not pulling in active-support JUST for this method.
128
+ # not pulling in active-support JUST for this method. And I love this method.
120
129
  class Object
121
130
  # Yields <code>x</code> to the block, and then returns <code>x</code>.
122
131
  # The primary purpose of this method is to "tap into" a method chain,
@@ -6,24 +6,26 @@ module Faraday
6
6
  extend MiddlewareRegistry
7
7
 
8
8
  autoload_all 'faraday/adapter',
9
- :ActionDispatch => 'action_dispatch',
10
- :NetHttp => 'net_http',
11
- :Typhoeus => 'typhoeus',
12
- :EMSynchrony => 'em_synchrony',
13
- :EMHttp => 'em_http',
14
- :Patron => 'patron',
15
- :Excon => 'excon',
16
- :Test => 'test'
9
+ :NetHttp => 'net_http',
10
+ :NetHttpPersistent => 'net_http_persistent',
11
+ :Typhoeus => 'typhoeus',
12
+ :EMSynchrony => 'em_synchrony',
13
+ :EMHttp => 'em_http',
14
+ :Patron => 'patron',
15
+ :Excon => 'excon',
16
+ :Test => 'test',
17
+ :Rack => 'rack'
17
18
 
18
19
  register_middleware \
19
- :action_dispatch => :ActionDispatch,
20
- :test => :Test,
21
- :net_http => :NetHttp,
22
- :typhoeus => :Typhoeus,
23
- :patron => :Patron,
24
- :em_synchrony => :EMSynchrony,
25
- :em_http => :EMHttp,
26
- :excon => :Excon
20
+ :test => :Test,
21
+ :net_http => :NetHttp,
22
+ :net_http_persistent => :NetHttpPersistent,
23
+ :typhoeus => :Typhoeus,
24
+ :patron => :Patron,
25
+ :em_synchrony => :EMSynchrony,
26
+ :em_http => :EMHttp,
27
+ :excon => :Excon,
28
+ :rack => :Rack
27
29
 
28
30
  module Parallelism
29
31
  attr_writer :supports_parallel
@@ -8,59 +8,32 @@ end
8
8
  module Faraday
9
9
  class Adapter
10
10
  class NetHttp < Faraday::Adapter
11
- def call(env)
12
- super
13
- url = env[:url]
14
- req = env[:request]
11
+ NET_HTTP_EXCEPTIONS = [
12
+ EOFError,
13
+ Errno::ECONNABORTED,
14
+ Errno::ECONNREFUSED,
15
+ Errno::ECONNRESET,
16
+ Errno::EINVAL,
17
+ Net::HTTPBadResponse,
18
+ Net::HTTPHeaderSyntaxError,
19
+ Net::ProtocolError,
20
+ SocketError
21
+ ]
15
22
 
16
- http = net_http_class(env).new(url.host, url.port)
17
-
18
- if http.use_ssl = (url.scheme == 'https' && (ssl = env[:ssl]) && true)
19
- http.verify_mode = ssl[:verify_mode] || begin
20
- if ssl.fetch(:verify, true)
21
- # Use the default cert store by default, i.e. system ca certs
22
- store = OpenSSL::X509::Store.new
23
- store.set_default_paths
24
- http.cert_store = store
25
- OpenSSL::SSL::VERIFY_PEER
26
- else
27
- OpenSSL::SSL::VERIFY_NONE
28
- end
29
- end
23
+ NET_HTTP_EXCEPTIONS << OpenSSL::SSL::SSLError if defined?(OpenSSL)
30
24
 
31
- http.cert = ssl[:client_cert] if ssl[:client_cert]
32
- http.key = ssl[:client_key] if ssl[:client_key]
33
- http.ca_file = ssl[:ca_file] if ssl[:ca_file]
34
- http.ca_path = ssl[:ca_path] if ssl[:ca_path]
35
- http.cert_store = ssl[:cert_store] if ssl[:cert_store]
36
- http.verify_depth = ssl[:verify_depth] if ssl[:verify_depth]
37
- end
25
+ def call(env)
26
+ super
27
+ http = net_http_connection(env)
28
+ configure_ssl(http, env[:ssl]) if env[:url].scheme == 'https' and env[:ssl]
38
29
 
30
+ req = env[:request]
39
31
  http.read_timeout = http.open_timeout = req[:timeout] if req[:timeout]
40
32
  http.open_timeout = req[:open_timeout] if req[:open_timeout]
41
33
 
42
- if :get != env[:method] or env[:body]
43
- http_request = Net::HTTPGenericRequest.new \
44
- env[:method].to_s.upcase, # request method
45
- !!env[:body], # is there request body
46
- :head != env[:method], # is there response body
47
- url.request_uri, # request uri path
48
- env[:request_headers] # request headers
49
-
50
- if env[:body].respond_to?(:read)
51
- http_request.body_stream = env[:body]
52
- env[:body] = nil
53
- end
54
- end
55
-
56
34
  begin
57
- http_response = if :get == env[:method] and env[:body].nil?
58
- # prefer `get` to `request` because the former handles gzip (ruby 1.9)
59
- http.get url.request_uri, env[:request_headers]
60
- else
61
- http.request http_request, env[:body]
62
- end
63
- rescue Errno::ECONNREFUSED
35
+ http_response = perform_request(http, env)
36
+ rescue *NET_HTTP_EXCEPTIONS
64
37
  raise Error::ConnectionFailed, $!
65
38
  end
66
39
 
@@ -75,11 +48,67 @@ module Faraday
75
48
  raise Faraday::Error::TimeoutError, err
76
49
  end
77
50
 
78
- def net_http_class(env)
51
+ def create_request(env)
52
+ request = Net::HTTPGenericRequest.new \
53
+ env[:method].to_s.upcase, # request method
54
+ !!env[:body], # is there request body
55
+ :head != env[:method], # is there response body
56
+ env[:url].request_uri, # request uri path
57
+ env[:request_headers] # request headers
58
+
59
+ if env[:body].respond_to?(:read)
60
+ request.body_stream = env[:body]
61
+ else
62
+ request.body = env[:body]
63
+ end
64
+ request
65
+ end
66
+
67
+ def perform_request(http, env)
68
+ if :get == env[:method] and !env[:body]
69
+ # prefer `get` to `request` because the former handles gzip (ruby 1.9)
70
+ http.get env[:url].request_uri, env[:request_headers]
71
+ else
72
+ http.request create_request(env)
73
+ end
74
+ end
75
+
76
+ def net_http_connection(env)
79
77
  if proxy = env[:request][:proxy]
80
78
  Net::HTTP::Proxy(proxy[:uri].host, proxy[:uri].port, proxy[:user], proxy[:password])
81
79
  else
82
80
  Net::HTTP
81
+ end.new(env[:url].host, env[:url].port)
82
+ end
83
+
84
+ def configure_ssl(http, ssl)
85
+ http.use_ssl = true
86
+ http.verify_mode = ssl_verify_mode(ssl)
87
+ http.cert_store = ssl_cert_store(ssl)
88
+
89
+ http.cert = ssl[:client_cert] if ssl[:client_cert]
90
+ http.key = ssl[:client_key] if ssl[:client_key]
91
+ http.ca_file = ssl[:ca_file] if ssl[:ca_file]
92
+ http.ca_path = ssl[:ca_path] if ssl[:ca_path]
93
+ http.verify_depth = ssl[:verify_depth] if ssl[:verify_depth]
94
+ http.ssl_version = ssl[:version] if ssl[:version]
95
+ end
96
+
97
+ def ssl_cert_store(ssl)
98
+ return ssl[:cert_store] if ssl[:cert_store]
99
+ # Use the default cert store by default, i.e. system ca certs
100
+ cert_store = OpenSSL::X509::Store.new
101
+ cert_store.set_default_paths
102
+ cert_store
103
+ end
104
+
105
+ def ssl_verify_mode(ssl)
106
+ ssl[:verify_mode] || begin
107
+ if ssl.fetch(:verify, true)
108
+ OpenSSL::SSL::VERIFY_PEER
109
+ else
110
+ OpenSSL::SSL::VERIFY_NONE
111
+ end
83
112
  end
84
113
  end
85
114
  end
@@ -0,0 +1,37 @@
1
+ require 'faraday/adapter/net_http'
2
+
3
+ module Faraday
4
+ class Adapter
5
+ # Experimental adapter for net-http-persistent
6
+ class NetHttpPersistent < NetHttp
7
+ dependency 'net/http/persistent'
8
+
9
+ # TODO: investigate is it safe to create a new Persistent instance for
10
+ # every request, or does it defy the purpose of persistent connections
11
+ def net_http_connection(env)
12
+ Net::HTTP::Persistent.new 'Faraday',
13
+ env[:request][:proxy] ? env[:request][:proxy][:uri] : nil
14
+ end
15
+
16
+ def perform_request(http, env)
17
+ http.request env[:url], create_request(env)
18
+ rescue Net::HTTP::Persistent::Error => error
19
+ if error.message.include? 'Timeout::Error'
20
+ raise Faraday::Error::TimeoutError, error
21
+ else
22
+ raise
23
+ end
24
+ end
25
+
26
+ def configure_ssl(http, ssl)
27
+ http.verify_mode = ssl_verify_mode(ssl)
28
+ http.cert_store = ssl_cert_store(ssl)
29
+
30
+ http.certificate = ssl[:client_cert] if ssl[:client_cert]
31
+ http.private_key = ssl[:client_key] if ssl[:client_key]
32
+ http.ca_file = ssl[:ca_file] if ssl[:ca_file]
33
+ http.ssl_version = ssl[:version] if ssl[:version]
34
+ end
35
+ end
36
+ end
37
+ end