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,89 @@
1
+ require 'uri'
2
+
3
+ module Faraday
4
+ class Adapter
5
+ class EMSynchrony < Faraday::Adapter
6
+ include EMHttp::Options
7
+
8
+ dependency do
9
+ require 'em-synchrony/em-http'
10
+ require 'em-synchrony/em-multi'
11
+ require 'fiber'
12
+ end
13
+
14
+ self.supports_parallel = true
15
+
16
+ def self.setup_parallel_manager(options = {})
17
+ ParallelManager.new
18
+ end
19
+
20
+ def call(env)
21
+ super
22
+ request = EventMachine::HttpRequest.new(URI::parse(env[:url].to_s), connection_config(env))
23
+
24
+ http_method = env[:method].to_s.downcase.to_sym
25
+
26
+ # Queue requests for parallel execution.
27
+ if env[:parallel_manager]
28
+ env[:parallel_manager].add(request, http_method, request_config(env)) do |resp|
29
+ save_response(env, resp.response_header.status, resp.response) do |resp_headers|
30
+ resp.response_header.each do |name, value|
31
+ resp_headers[name.to_sym] = value
32
+ end
33
+ end
34
+
35
+ # Finalize the response object with values from `env`.
36
+ env[:response].finish(env)
37
+ end
38
+
39
+ # Execute single request.
40
+ else
41
+ client = nil
42
+ block = lambda { request.send(http_method, request_config(env)) }
43
+
44
+ if !EM.reactor_running?
45
+ EM.run do
46
+ Fiber.new {
47
+ client = block.call
48
+ EM.stop
49
+ }.resume
50
+ end
51
+ else
52
+ client = block.call
53
+ end
54
+
55
+ save_response(env, client.response_header.status, client.response) do |resp_headers|
56
+ client.response_header.each do |name, value|
57
+ resp_headers[name.to_sym] = value
58
+ end
59
+ end
60
+ end
61
+
62
+ @app.call env
63
+ rescue Errno::ECONNREFUSED
64
+ raise Error::ConnectionFailed, $!
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ require 'faraday/adapter/em_synchrony/parallel_manager'
71
+
72
+ # add missing patch(), options() methods
73
+ EventMachine::HTTPMethods.module_eval do
74
+ ([:patch, :options] - instance_methods).each do |type|
75
+ module_eval %[
76
+ def #{type}(options = {}, &blk)
77
+ f = Fiber.current
78
+ conn = setup_request(:#{type}, options, &blk)
79
+ if conn.error.nil?
80
+ conn.callback { f.resume(conn) }
81
+ conn.errback { f.resume(conn) }
82
+ Fiber.yield
83
+ else
84
+ conn
85
+ end
86
+ end
87
+ ]
88
+ end
89
+ end
@@ -0,0 +1,66 @@
1
+ module Faraday
2
+ class Adapter
3
+ class EMSynchrony < Faraday::Adapter
4
+ class ParallelManager
5
+
6
+ # Add requests to queue. The `request` argument should be a
7
+ # `EM::HttpRequest` object.
8
+ def add(request, method, *args, &block)
9
+ queue << {
10
+ :request => request,
11
+ :method => method,
12
+ :args => args,
13
+ :block => block
14
+ }
15
+ end
16
+
17
+ # Run all requests on queue with `EM::Synchrony::Multi`, wrapping
18
+ # it in a reactor and fiber if needed.
19
+ def run
20
+ result = nil
21
+ if !EM.reactor_running?
22
+ EM.run {
23
+ Fiber.new do
24
+ result = perform
25
+ EM.stop
26
+ end.resume
27
+ }
28
+ else
29
+ result = perform
30
+ end
31
+ result
32
+ end
33
+
34
+
35
+ private
36
+
37
+ # The request queue.
38
+ def queue
39
+ @queue ||= []
40
+ end
41
+
42
+ # Main `EM::Synchrony::Multi` performer.
43
+ def perform
44
+ multi = ::EM::Synchrony::Multi.new
45
+
46
+ queue.each do |item|
47
+ method = "a#{item[:method]}".to_sym
48
+
49
+ req = item[:request].send(method, *item[:args])
50
+ req.callback(&item[:block])
51
+
52
+ req_name = "req_#{multi.requests.size}".to_sym
53
+ multi.add(req_name, req)
54
+ end
55
+
56
+ # Clear the queue, so parallel manager objects can be reused.
57
+ @queue = []
58
+
59
+ # Block fiber until all requests have returned.
60
+ multi.perform
61
+ end
62
+
63
+ end # ParallelManager
64
+ end # EMSynchrony
65
+ end # Adapter
66
+ end # Faraday
@@ -0,0 +1,59 @@
1
+ module Faraday
2
+ class Adapter
3
+ class Excon < Faraday::Adapter
4
+ dependency 'excon'
5
+
6
+ def initialize(app, connection_options = {})
7
+ @connection_options = connection_options
8
+ super(app)
9
+ end
10
+
11
+ def call(env)
12
+ super
13
+
14
+ opts = {}
15
+ if env[:url].scheme == 'https' && ssl = env[:ssl]
16
+ opts[:ssl_verify_peer] = !!ssl.fetch(:verify, true)
17
+ opts[:ssl_ca_path] = ssl[:ca_file] if ssl[:ca_file]
18
+ end
19
+
20
+ if ( req = env[:request] )
21
+ if req[:timeout]
22
+ opts[:read_timeout] = req[:timeout]
23
+ opts[:connect_timeout] = req[:timeout]
24
+ opts[:write_timeout] = req[:timeout]
25
+ end
26
+
27
+ if req[:open_timeout]
28
+ opts[:connect_timeout] = req[:open_timeout]
29
+ opts[:write_timeout] = req[:open_timeout]
30
+ end
31
+ end
32
+
33
+ conn = ::Excon.new(env[:url].to_s, opts.merge(@connection_options))
34
+
35
+ resp = conn.request \
36
+ :method => env[:method].to_s.upcase,
37
+ :headers => env[:request_headers],
38
+ :body => read_body(env)
39
+
40
+ save_response(env, resp.status.to_i, resp.body, resp.headers)
41
+
42
+ @app.call env
43
+ rescue ::Excon::Errors::SocketError => err
44
+ if err.message =~ /\btimeout\b/
45
+ raise Error::TimeoutError, err
46
+ else
47
+ raise Error::ConnectionFailed, err
48
+ end
49
+ rescue ::Excon::Errors::Timeout => err
50
+ raise Error::TimeoutError, err
51
+ end
52
+
53
+ # TODO: support streaming requests
54
+ def read_body(env)
55
+ env[:body].respond_to?(:read) ? env[:body].read : env[:body]
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,92 @@
1
+ module Faraday
2
+ class Adapter
3
+ class HTTPClient < Faraday::Adapter
4
+ dependency 'httpclient'
5
+
6
+ def client
7
+ @client ||= ::HTTPClient.new
8
+ end
9
+
10
+ def call(env)
11
+ super
12
+
13
+ if req = env[:request]
14
+ if proxy = req[:proxy]
15
+ configure_proxy proxy
16
+ end
17
+
18
+ if bind = req[:bind]
19
+ configure_socket bind
20
+ end
21
+
22
+ configure_timeouts req
23
+ end
24
+
25
+ if env[:url].scheme == 'https' && ssl = env[:ssl]
26
+ configure_ssl ssl
27
+ end
28
+
29
+ # TODO Don't stream yet.
30
+ # https://github.com/nahi/httpclient/pull/90
31
+ env[:body] = env[:body].read if env[:body].respond_to? :read
32
+
33
+ resp = client.request env[:method], env[:url],
34
+ :body => env[:body],
35
+ :header => env[:request_headers]
36
+
37
+ save_response env, resp.status, resp.body, resp.headers
38
+
39
+ @app.call env
40
+ rescue ::HTTPClient::TimeoutError
41
+ raise Faraday::Error::TimeoutError, $!
42
+ end
43
+
44
+ def configure_socket(bind)
45
+ client.socket_local.host = bind[:host]
46
+ client.socket_local.port = bind[:port]
47
+ end
48
+
49
+ def configure_proxy(proxy)
50
+ client.proxy = proxy[:uri]
51
+ if proxy[:user] && proxy[:password]
52
+ client.set_proxy_auth proxy[:user], proxy[:password]
53
+ end
54
+ end
55
+
56
+ def configure_ssl(ssl)
57
+ ssl_config = client.ssl_config
58
+
59
+ ssl_config.add_trust_ca ssl[:ca_file] if ssl[:ca_file]
60
+ ssl_config.add_trust_ca ssl[:ca_path] if ssl[:ca_path]
61
+ ssl_config.cert_store = ssl[:cert_store] if ssl[:cert_store]
62
+ ssl_config.client_cert = ssl[:client_cert] if ssl[:client_cert]
63
+ ssl_config.client_key = ssl[:client_key] if ssl[:client_key]
64
+ ssl_config.verify_depth = ssl[:verify_depth] if ssl[:verify_depth]
65
+ ssl_config.verify_mode = ssl_verify_mode(ssl)
66
+ end
67
+
68
+ def configure_timeouts(req)
69
+ if req[:timeout]
70
+ client.connect_timeout = req[:timeout]
71
+ client.receive_timeout = req[:timeout]
72
+ client.send_timeout = req[:timeout]
73
+ end
74
+
75
+ if req[:open_timeout]
76
+ client.connect_timeout = req[:open_timeout]
77
+ client.send_timeout = req[:open_timeout]
78
+ end
79
+ end
80
+
81
+ def ssl_verify_mode(ssl)
82
+ ssl[:verify_mode] || begin
83
+ if ssl.fetch(:verify, true)
84
+ OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
85
+ else
86
+ OpenSSL::SSL::VERIFY_NONE
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,116 @@
1
+ begin
2
+ require 'net/https'
3
+ rescue LoadError
4
+ warn "Warning: no such file to load -- net/https. Make sure openssl is installed if you want ssl support"
5
+ require 'net/http'
6
+ end
7
+
8
+ module Faraday
9
+ class Adapter
10
+ class NetHttp < Faraday::Adapter
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
+ ]
22
+
23
+ NET_HTTP_EXCEPTIONS << OpenSSL::SSL::SSLError if defined?(OpenSSL)
24
+
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]
29
+
30
+ req = env[:request]
31
+ http.read_timeout = http.open_timeout = req[:timeout] if req[:timeout]
32
+ http.open_timeout = req[:open_timeout] if req[:open_timeout]
33
+
34
+ begin
35
+ http_response = perform_request(http, env)
36
+ rescue *NET_HTTP_EXCEPTIONS
37
+ raise Error::ConnectionFailed, $!
38
+ end
39
+
40
+ save_response(env, http_response.code.to_i, http_response.body) do |response_headers|
41
+ http_response.each_header do |key, value|
42
+ response_headers[key] = value
43
+ end
44
+ end
45
+
46
+ @app.call env
47
+ rescue Timeout::Error => err
48
+ raise Faraday::Error::TimeoutError, err
49
+ end
50
+
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)
77
+ if proxy = env[:request][:proxy]
78
+ Net::HTTP::Proxy(proxy[:uri].host, proxy[:uri].port, proxy[:user], proxy[:password])
79
+ else
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
112
+ end
113
+ end
114
+ end
115
+ end
116
+ 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