faraday 0.6.0 → 0.9.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.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/.document +6 -0
  3. data/CHANGELOG.md +15 -0
  4. data/CONTRIBUTING.md +36 -0
  5. data/Gemfile +25 -15
  6. data/{LICENSE → LICENSE.md} +1 -1
  7. data/README.md +187 -137
  8. data/Rakefile +38 -101
  9. data/faraday.gemspec +26 -81
  10. data/lib/faraday/adapter/em_http.rb +237 -0
  11. data/lib/faraday/adapter/em_http_ssl_patch.rb +56 -0
  12. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +66 -0
  13. data/lib/faraday/adapter/em_synchrony.rb +65 -38
  14. data/lib/faraday/adapter/excon.rb +60 -9
  15. data/lib/faraday/adapter/httpclient.rb +106 -0
  16. data/lib/faraday/adapter/net_http.rb +89 -38
  17. data/lib/faraday/adapter/net_http_persistent.rb +47 -0
  18. data/lib/faraday/adapter/patron.rb +46 -9
  19. data/lib/faraday/adapter/rack.rb +58 -0
  20. data/lib/faraday/adapter/test.rb +68 -28
  21. data/lib/faraday/adapter/typhoeus.rb +94 -16
  22. data/lib/faraday/adapter.rb +33 -25
  23. data/lib/faraday/autoload.rb +85 -0
  24. data/lib/faraday/connection.rb +342 -134
  25. data/lib/faraday/error.rb +44 -29
  26. data/lib/faraday/middleware.rb +18 -10
  27. data/lib/faraday/options.rb +350 -0
  28. data/lib/faraday/parameters.rb +193 -0
  29. data/lib/faraday/rack_builder.rb +212 -0
  30. data/lib/faraday/request/authorization.rb +42 -0
  31. data/lib/faraday/request/basic_authentication.rb +13 -0
  32. data/lib/faraday/request/instrumentation.rb +36 -0
  33. data/lib/faraday/request/multipart.rb +15 -15
  34. data/lib/faraday/request/retry.rb +118 -0
  35. data/lib/faraday/request/token_authentication.rb +15 -0
  36. data/lib/faraday/request/url_encoded.rb +8 -9
  37. data/lib/faraday/request.rb +46 -45
  38. data/lib/faraday/response/logger.rb +4 -4
  39. data/lib/faraday/response/raise_error.rb +8 -3
  40. data/lib/faraday/response.rb +20 -25
  41. data/lib/faraday/upload_io.rb +51 -7
  42. data/lib/faraday/utils.rb +240 -44
  43. data/lib/faraday.rb +218 -38
  44. data/script/console +7 -0
  45. data/script/generate_certs +42 -0
  46. data/script/package +7 -0
  47. data/script/proxy-server +42 -0
  48. data/script/release +17 -0
  49. data/script/server +36 -0
  50. data/script/test +172 -0
  51. data/test/adapters/default_test.rb +14 -0
  52. data/test/adapters/em_http_test.rb +20 -0
  53. data/test/adapters/em_synchrony_test.rb +20 -0
  54. data/test/adapters/excon_test.rb +20 -0
  55. data/test/adapters/httpclient_test.rb +21 -0
  56. data/test/adapters/integration.rb +254 -0
  57. data/test/adapters/logger_test.rb +3 -3
  58. data/test/adapters/net_http_persistent_test.rb +20 -0
  59. data/test/adapters/net_http_test.rb +6 -25
  60. data/test/adapters/patron_test.rb +20 -0
  61. data/test/adapters/rack_test.rb +31 -0
  62. data/test/adapters/test_middleware_test.rb +74 -3
  63. data/test/adapters/typhoeus_test.rb +28 -0
  64. data/test/authentication_middleware_test.rb +65 -0
  65. data/test/composite_read_io_test.rb +111 -0
  66. data/test/connection_test.rb +381 -104
  67. data/test/env_test.rb +116 -40
  68. data/test/helper.rb +61 -25
  69. data/test/live_server.rb +55 -29
  70. data/test/middleware/instrumentation_test.rb +88 -0
  71. data/test/middleware/retry_test.rb +109 -0
  72. data/test/middleware_stack_test.rb +87 -5
  73. data/test/multibyte.txt +1 -0
  74. data/test/options_test.rb +252 -0
  75. data/test/request_middleware_test.rb +78 -21
  76. data/test/response_middleware_test.rb +32 -7
  77. data/test/strawberry.rb +2 -0
  78. data/test/utils_test.rb +58 -0
  79. metadata +116 -117
  80. data/lib/faraday/adapter/action_dispatch.rb +0 -30
  81. data/lib/faraday/builder.rb +0 -137
  82. data/lib/faraday/request/json.rb +0 -31
  83. data/test/adapters/live_test.rb +0 -186
data/faraday.gemspec CHANGED
@@ -1,89 +1,34 @@
1
- ## This is the rakegem gemspec template. Make sure you read and understand
2
- ## all of the comments. Some sections require modification, and others can
3
- ## be deleted if you don't need them. Once you understand the contents of
4
- ## this file, feel free to delete any comments that begin with two hash marks.
5
- ## You can find comprehensive Gem::Specification documentation, at
6
- ## http://docs.rubygems.org/read/chapter/20
7
- Gem::Specification.new do |s|
8
- s.specification_version = 2 if s.respond_to? :specification_version=
9
- s.required_rubygems_version = Gem::Requirement.new(">= 1.3.5") if s.respond_to? :required_rubygems_version=
1
+ lib = "faraday"
2
+ lib_file = File.expand_path("../lib/#{lib}.rb", __FILE__)
3
+ File.read(lib_file) =~ /\bVERSION\s*=\s*["'](.+?)["']/
4
+ version = $1
10
5
 
11
- ## Leave these as is they will be modified for you by the rake gemspec task.
12
- ## If your rubyforge_project name is different, then edit it and comment out
13
- ## the sub! line in the Rakefile
14
- s.name = 'faraday'
15
- s.version = '0.6.0'
16
- s.date = '2011-03-31'
17
- s.rubyforge_project = 'faraday'
6
+ Gem::Specification.new do |spec|
7
+ spec.specification_version = 2 if spec.respond_to? :specification_version=
8
+ spec.required_rubygems_version = '>= 1.3.5'
18
9
 
19
- ## Make sure your summary is short. The description may be as long
20
- ## as you like.
21
- s.summary = "HTTP/REST API client library."
22
- s.description = "HTTP/REST API client library."
10
+ spec.name = lib
11
+ spec.version = version
23
12
 
24
- ## List the primary authors. If there are a bunch of authors, it's probably
25
- ## better to set the email to an email list or something. If you don't have
26
- ## a custom homepage, consider using your GitHub URL or the like.
27
- s.authors = ["Rick Olson"]
28
- s.email = 'technoweenie@gmail.com'
29
- s.homepage = 'http://github.com/technoweenie/faraday'
13
+ spec.summary = "HTTP/REST API client library."
30
14
 
31
- ## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as
32
- ## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'
33
- s.require_paths = %w[lib]
15
+ spec.authors = ["Rick Olson"]
16
+ spec.email = 'technoweenie@gmail.com'
17
+ spec.homepage = 'https://github.com/lostisland/faraday'
18
+ spec.licenses = ['MIT']
34
19
 
35
- s.add_development_dependency('rake', '~> 0.8')
36
- s.add_runtime_dependency('addressable', '~> 2.2.4')
37
- s.add_runtime_dependency('multipart-post', '~> 1.1.0')
38
- s.add_runtime_dependency('rack', ['>= 1.1.0', "< 2"])
20
+ spec.add_dependency 'multipart-post', '>= 1.2', '< 3'
21
+ spec.add_development_dependency 'bundler', '~> 1.0'
39
22
 
40
- ## Leave this section as-is. It will be automatically generated from the
41
- ## contents of your Git repository via the gemspec task. DO NOT REMOVE
42
- ## THE MANIFEST COMMENTS, they are used as delimiters by the task.
43
- # = MANIFEST =
44
- s.files = %w[
45
- Gemfile
46
- LICENSE
47
- README.md
48
- Rakefile
49
- faraday.gemspec
50
- lib/faraday.rb
51
- lib/faraday/adapter.rb
52
- lib/faraday/adapter/action_dispatch.rb
53
- lib/faraday/adapter/em_synchrony.rb
54
- lib/faraday/adapter/excon.rb
55
- lib/faraday/adapter/net_http.rb
56
- lib/faraday/adapter/patron.rb
57
- lib/faraday/adapter/test.rb
58
- lib/faraday/adapter/typhoeus.rb
59
- lib/faraday/builder.rb
60
- lib/faraday/connection.rb
61
- lib/faraday/error.rb
62
- lib/faraday/middleware.rb
63
- lib/faraday/request.rb
64
- lib/faraday/request/json.rb
65
- lib/faraday/request/multipart.rb
66
- lib/faraday/request/url_encoded.rb
67
- lib/faraday/response.rb
68
- lib/faraday/response/logger.rb
69
- lib/faraday/response/raise_error.rb
70
- lib/faraday/upload_io.rb
71
- lib/faraday/utils.rb
72
- test/adapters/live_test.rb
73
- test/adapters/logger_test.rb
74
- test/adapters/net_http_test.rb
75
- test/adapters/test_middleware_test.rb
76
- test/connection_test.rb
77
- test/env_test.rb
78
- test/helper.rb
79
- test/live_server.rb
80
- test/middleware_stack_test.rb
81
- test/request_middleware_test.rb
82
- test/response_middleware_test.rb
83
- ]
84
- # = MANIFEST =
23
+ spec.files = %w(.document CHANGELOG.md CONTRIBUTING.md Gemfile LICENSE.md README.md Rakefile)
24
+ spec.files << "#{lib}.gemspec"
25
+ spec.files += Dir.glob("lib/**/*.rb")
26
+ spec.files += Dir.glob("test/**/*.{rb,txt}")
27
+ spec.files += Dir.glob("script/*")
85
28
 
86
- ## Test files will be grabbed from the file list. Make sure the path glob
87
- ## matches what you actually use.
88
- s.test_files = s.files.select { |path| path =~ /^test\/.*_test\.rb/ }
29
+ dev_null = File.exist?('/dev/null') ? '/dev/null' : 'NUL'
30
+ git_files = `git ls-files -z 2>#{dev_null}`
31
+ spec.files &= git_files.split("\0") if $?.success?
32
+
33
+ spec.test_files = Dir.glob("test/**/*.rb")
89
34
  end
@@ -0,0 +1,237 @@
1
+ module Faraday
2
+ class Adapter
3
+ # EventMachine adapter is useful for either asynchronous requests
4
+ # when in EM reactor loop or for making parallel requests in
5
+ # synchronous code.
6
+ class EMHttp < Faraday::Adapter
7
+ module Options
8
+ def connection_config(env)
9
+ options = {}
10
+ configure_proxy(options, env)
11
+ configure_timeout(options, env)
12
+ configure_socket(options, env)
13
+ configure_ssl(options, env)
14
+ options
15
+ end
16
+
17
+ def request_config(env)
18
+ options = {
19
+ :body => read_body(env),
20
+ :head => env[:request_headers],
21
+ # :keepalive => true,
22
+ # :file => 'path/to/file', # stream data off disk
23
+ }
24
+ configure_compression(options, env)
25
+ options
26
+ end
27
+
28
+ def read_body(env)
29
+ body = env[:body]
30
+ body.respond_to?(:read) ? body.read : body
31
+ end
32
+
33
+ def configure_proxy(options, env)
34
+ if proxy = request_options(env)[:proxy]
35
+ options[:proxy] = {
36
+ :host => proxy[:uri].host,
37
+ :port => proxy[:uri].port,
38
+ :authorization => [proxy[:user], proxy[:password]]
39
+ }
40
+ end
41
+ end
42
+
43
+ def configure_socket(options, env)
44
+ if bind = request_options(env)[:bind]
45
+ options[:bind] = {
46
+ :host => bind[:host],
47
+ :port => bind[:port]
48
+ }
49
+ end
50
+ end
51
+
52
+ def configure_ssl(options, env)
53
+ if env[:url].scheme == 'https' && env[:ssl]
54
+ options[:ssl] = {
55
+ :cert_chain_file => env[:ssl][:ca_file],
56
+ :verify_peer => env[:ssl].fetch(:verify, true)
57
+ }
58
+ end
59
+ end
60
+
61
+ def configure_timeout(options, env)
62
+ timeout, open_timeout = request_options(env).values_at(:timeout, :open_timeout)
63
+ options[:connect_timeout] = options[:inactivity_timeout] = timeout
64
+ options[:connect_timeout] = open_timeout if open_timeout
65
+ end
66
+
67
+ def configure_compression(options, env)
68
+ if env[:method] == :get and not options[:head].key? 'accept-encoding'
69
+ options[:head]['accept-encoding'] = 'gzip, compressed'
70
+ end
71
+ end
72
+
73
+ def request_options(env)
74
+ env[:request]
75
+ end
76
+ end
77
+
78
+ include Options
79
+
80
+ dependency 'em-http'
81
+
82
+ self.supports_parallel = true
83
+
84
+ def self.setup_parallel_manager(options = nil)
85
+ Manager.new
86
+ end
87
+
88
+ def call(env)
89
+ super
90
+ perform_request env
91
+ @app.call env
92
+ end
93
+
94
+ def perform_request(env)
95
+ if parallel?(env)
96
+ manager = env[:parallel_manager]
97
+ manager.add {
98
+ perform_single_request(env).
99
+ callback { env[:response].finish(env) }
100
+ }
101
+ else
102
+ unless EventMachine.reactor_running?
103
+ error = nil
104
+ # start EM, block until request is completed
105
+ EventMachine.run do
106
+ perform_single_request(env).
107
+ callback { EventMachine.stop }.
108
+ errback { |client|
109
+ error = error_message(client)
110
+ EventMachine.stop
111
+ }
112
+ end
113
+ raise_error(error) if error
114
+ else
115
+ # EM is running: instruct upstream that this is an async request
116
+ env[:parallel_manager] = true
117
+ perform_single_request(env).
118
+ callback { env[:response].finish(env) }.
119
+ errback {
120
+ # TODO: no way to communicate the error in async mode
121
+ raise NotImplementedError
122
+ }
123
+ end
124
+ end
125
+ rescue EventMachine::Connectify::CONNECTError => err
126
+ if err.message.include?("Proxy Authentication Required")
127
+ raise Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
128
+ else
129
+ raise Error::ConnectionFailed, err
130
+ end
131
+ rescue => err
132
+ if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
133
+ raise Faraday::SSLError, err
134
+ else
135
+ raise
136
+ end
137
+ end
138
+
139
+ # TODO: reuse the connection to support pipelining
140
+ def perform_single_request(env)
141
+ req = EventMachine::HttpRequest.new(env[:url], connection_config(env))
142
+ req.setup_request(env[:method], request_config(env)).callback { |client|
143
+ save_response(env, client.response_header.status, client.response) do |resp_headers|
144
+ client.response_header.each do |name, value|
145
+ resp_headers[name.to_sym] = value
146
+ end
147
+ end
148
+ }
149
+ end
150
+
151
+ def error_message(client)
152
+ client.error or "request failed"
153
+ end
154
+
155
+ def raise_error(msg)
156
+ errklass = Faraday::Error::ClientError
157
+ if msg == Errno::ETIMEDOUT
158
+ errklass = Faraday::Error::TimeoutError
159
+ msg = "request timed out"
160
+ elsif msg == Errno::ECONNREFUSED
161
+ errklass = Faraday::Error::ConnectionFailed
162
+ msg = "connection refused"
163
+ elsif msg == "connection closed by server"
164
+ errklass = Faraday::Error::ConnectionFailed
165
+ end
166
+ raise errklass, msg
167
+ end
168
+
169
+ def parallel?(env)
170
+ !!env[:parallel_manager]
171
+ end
172
+
173
+ # The parallel manager is designed to start an EventMachine loop
174
+ # and block until all registered requests have been completed.
175
+ class Manager
176
+ def initialize
177
+ reset
178
+ end
179
+
180
+ def reset
181
+ @registered_procs = []
182
+ @num_registered = 0
183
+ @num_succeeded = 0
184
+ @errors = []
185
+ @running = false
186
+ end
187
+
188
+ def running?() @running end
189
+
190
+ def add
191
+ if running?
192
+ perform_request { yield }
193
+ else
194
+ @registered_procs << Proc.new
195
+ end
196
+ @num_registered += 1
197
+ end
198
+
199
+ def run
200
+ if @num_registered > 0
201
+ @running = true
202
+ EventMachine.run do
203
+ @registered_procs.each do |proc|
204
+ perform_request(&proc)
205
+ end
206
+ end
207
+ if @errors.size > 0
208
+ raise Faraday::Error::ClientError, @errors.first || "connection failed"
209
+ end
210
+ end
211
+ ensure
212
+ reset
213
+ end
214
+
215
+ def perform_request
216
+ client = yield
217
+ client.callback { @num_succeeded += 1; check_finished }
218
+ client.errback { @errors << client.error; check_finished }
219
+ end
220
+
221
+ def check_finished
222
+ if @num_succeeded + @errors.size == @num_registered
223
+ EventMachine.stop
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end
230
+
231
+ begin
232
+ require 'openssl'
233
+ rescue LoadError
234
+ warn "Warning: no such file to load -- openssl. Make sure it is installed if you want HTTPS support"
235
+ else
236
+ require 'faraday/adapter/em_http_ssl_patch'
237
+ end if Faraday::Adapter::EMHttp.loaded?
@@ -0,0 +1,56 @@
1
+ require 'openssl'
2
+ require 'em-http'
3
+
4
+ module EmHttpSslPatch
5
+ def ssl_verify_peer(cert_string)
6
+ cert = nil
7
+ begin
8
+ cert = OpenSSL::X509::Certificate.new(cert_string)
9
+ rescue OpenSSL::X509::CertificateError
10
+ return false
11
+ end
12
+
13
+ @last_seen_cert = cert
14
+
15
+ if certificate_store.verify(@last_seen_cert)
16
+ begin
17
+ certificate_store.add_cert(@last_seen_cert)
18
+ rescue OpenSSL::X509::StoreError => e
19
+ raise e unless e.message == 'cert already in hash table'
20
+ end
21
+ true
22
+ else
23
+ raise OpenSSL::SSL::SSLError.new(%(unable to verify the server certificate for "#{host}"))
24
+ end
25
+ end
26
+
27
+ def ssl_handshake_completed
28
+ return true unless verify_peer?
29
+
30
+ unless OpenSSL::SSL.verify_certificate_identity(@last_seen_cert, host)
31
+ raise OpenSSL::SSL::SSLError.new(%(host "#{host}" does not match the server certificate))
32
+ else
33
+ true
34
+ end
35
+ end
36
+
37
+ def verify_peer?
38
+ parent.connopts.tls[:verify_peer]
39
+ end
40
+
41
+ def host
42
+ parent.connopts.host
43
+ end
44
+
45
+ def certificate_store
46
+ @certificate_store ||= begin
47
+ store = OpenSSL::X509::Store.new
48
+ store.set_default_paths
49
+ ca_file = parent.connopts.tls[:cert_chain_file]
50
+ store.add_file(ca_file) if ca_file
51
+ store
52
+ end
53
+ end
54
+ end
55
+
56
+ EventMachine::HttpStubConnection.send(:include, EmHttpSslPatch)
@@ -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
@@ -1,65 +1,92 @@
1
+ require 'uri'
2
+
1
3
  module Faraday
2
4
  class Adapter
3
5
  class EMSynchrony < Faraday::Adapter
6
+ include EMHttp::Options
7
+
4
8
  dependency do
5
9
  require 'em-synchrony/em-http'
10
+ require 'em-synchrony/em-multi'
6
11
  require 'fiber'
7
12
  end
8
13
 
14
+ self.supports_parallel = true
15
+
16
+ def self.setup_parallel_manager(options = {})
17
+ ParallelManager.new
18
+ end
19
+
9
20
  def call(env)
10
21
  super
11
- request = EventMachine::HttpRequest.new(URI::parse(env[:url].to_s))
12
- options = {:head => env[:request_headers]}
13
- options[:ssl] = env[:ssl] if env[:ssl]
22
+ request = EventMachine::HttpRequest.new(Utils::URI(env[:url].to_s), connection_config(env))
14
23
 
15
- if env[:body]
16
- if env[:body].respond_to? :read
17
- options[:body] = env[:body].read
18
- else
19
- options[:body] = env[:body]
20
- end
21
- end
24
+ http_method = env[:method].to_s.downcase.to_sym
22
25
 
23
- if req = env[:request]
24
- if proxy = req[:proxy]
25
- uri = Addressable::URI.parse(proxy[:uri])
26
- options[:proxy] = {
27
- :host => uri.host,
28
- :port => uri.inferred_port
29
- }
30
- if proxy[:username] && proxy[:password]
31
- options[:proxy][:authorization] = [proxy[:username], proxy[:password]]
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
32
33
  end
33
- end
34
34
 
35
- # only one timeout currently supported by em http request
36
- if req[:timeout] or req[:open_timeout]
37
- options[:timeout] = [req[:timeout] || 0, req[:open_timeout] || 0].max
35
+ # Finalize the response object with values from `env`.
36
+ env[:response].finish(env)
38
37
  end
39
- end
40
38
 
41
- client = nil
42
- block = lambda { request.send env[:method].to_s.downcase.to_sym, options }
43
- if !EM.reactor_running?
44
- EM.run {
45
- Fiber.new do
46
- client = block.call
47
- EM.stop
48
- end.resume
49
- }
39
+ # Execute single request.
50
40
  else
51
- client = block.call
52
- end
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
53
54
 
54
- client.response_header.each do |name, value|
55
- response_headers(env)[name.to_sym] = value
55
+ raise client.error if client.error
56
+
57
+ save_response(env, client.response_header.status, client.response) do |resp_headers|
58
+ client.response_header.each do |name, value|
59
+ resp_headers[name.to_sym] = value
60
+ end
61
+ end
56
62
  end
57
- env.update :status => client.response_header.status, :body => client.response
58
63
 
59
64
  @app.call env
60
65
  rescue Errno::ECONNREFUSED
61
66
  raise Error::ConnectionFailed, $!
67
+ rescue EventMachine::Connectify::CONNECTError => err
68
+ if err.message.include?("Proxy Authentication Required")
69
+ raise Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
70
+ else
71
+ raise Error::ConnectionFailed, err
72
+ end
73
+ rescue => err
74
+ if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
75
+ raise Faraday::SSLError, err
76
+ else
77
+ raise
78
+ end
62
79
  end
63
80
  end
64
81
  end
65
82
  end
83
+
84
+ require 'faraday/adapter/em_synchrony/parallel_manager'
85
+
86
+ begin
87
+ require 'openssl'
88
+ rescue LoadError
89
+ warn "Warning: no such file to load -- openssl. Make sure it is installed if you want HTTPS support"
90
+ else
91
+ require 'faraday/adapter/em_http_ssl_patch'
92
+ end if Faraday::Adapter::EMSynchrony.loaded?
@@ -3,26 +3,77 @@ module Faraday
3
3
  class Excon < Faraday::Adapter
4
4
  dependency 'excon'
5
5
 
6
+ def initialize(app, connection_options = {})
7
+ @connection_options = connection_options
8
+ super(app)
9
+ end
10
+
6
11
  def call(env)
7
12
  super
8
13
 
9
- conn = ::Excon.new(env[:url].to_s)
10
- if ssl = (env[:url].scheme == 'https' && env[:ssl])
11
- ::Excon.ssl_verify_peer = !!ssl.fetch(:verify, true)
12
- ::Excon.ssl_ca_path = ssl[:ca_file] if ssl[:ca_file]
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_path] if ssl[:ca_path]
18
+ opts[:ssl_ca_file] = ssl[:ca_file] if ssl[:ca_file]
19
+ opts[:client_cert] = ssl[:client_cert] if ssl[:client_cert]
20
+ opts[:client_key] = ssl[:client_key] if ssl[:client_key]
21
+ opts[:certificate] = ssl[:certificate] if ssl[:certificate]
22
+ opts[:private_key] = ssl[:private_key] if ssl[:private_key]
23
+
24
+ # https://github.com/geemus/excon/issues/106
25
+ # https://github.com/jruby/jruby-ossl/issues/19
26
+ opts[:nonblock] = false
13
27
  end
14
28
 
29
+ if ( req = env[:request] )
30
+ if req[:timeout]
31
+ opts[:read_timeout] = req[:timeout]
32
+ opts[:connect_timeout] = req[:timeout]
33
+ opts[:write_timeout] = req[:timeout]
34
+ end
35
+
36
+ if req[:open_timeout]
37
+ opts[:connect_timeout] = req[:open_timeout]
38
+ opts[:write_timeout] = req[:open_timeout]
39
+ end
40
+
41
+ if req[:proxy]
42
+ opts[:proxy] = {
43
+ :host => req[:proxy][:uri].host,
44
+ :port => req[:proxy][:uri].port,
45
+ :scheme => req[:proxy][:uri].scheme,
46
+ :user => req[:proxy][:user],
47
+ :password => req[:proxy][:password]
48
+ }
49
+ end
50
+ end
51
+
52
+ conn = ::Excon.new(env[:url].to_s, opts.merge(@connection_options))
53
+
15
54
  resp = conn.request \
16
55
  :method => env[:method].to_s.upcase,
17
56
  :headers => env[:request_headers],
18
- :body => env[:body]
57
+ :body => read_body(env)
19
58
 
20
- env.update :status => resp.status.to_i, :body => resp.body
21
- response_headers(env).update resp.headers
59
+ save_response(env, resp.status.to_i, resp.body, resp.headers)
22
60
 
23
61
  @app.call env
24
- rescue ::Excon::Errors::SocketError
25
- raise Error::ConnectionFailed, $!
62
+ rescue ::Excon::Errors::SocketError => err
63
+ if err.message =~ /\btimeout\b/
64
+ raise Error::TimeoutError, err
65
+ elsif err.message =~ /\bcertificate\b/
66
+ raise Faraday::SSLError, err
67
+ else
68
+ raise Error::ConnectionFailed, err
69
+ end
70
+ rescue ::Excon::Errors::Timeout => err
71
+ raise Error::TimeoutError, err
72
+ end
73
+
74
+ # TODO: support streaming requests
75
+ def read_body(env)
76
+ env[:body].respond_to?(:read) ? env[:body].read : env[:body]
26
77
  end
27
78
  end
28
79
  end