faraday-em_http 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8982a4cb78e29659a4903e779305b1d8b949991b60bd5fc9c39f3d9caaa4bcc3
4
+ data.tar.gz: 65343a1e259389675dc9ef4c5b50772e7a87ad8bbaa601b7433f8697154e9a78
5
+ SHA512:
6
+ metadata.gz: da88bf5433fef291af85674490b017c8f6356030304a37e6156c2409d776b9e0712a05fe13656029bf400bef0404239d6132a964bdc85a2097321b4051a96494
7
+ data.tar.gz: ff7dc9115defddc26448b5dce034c56e7abf33375987a4083dd7bdb1f5fab6d0dc1d0e4b24884681cb1d03207717e83692ca2f0d106f5f16656211748750c1f7
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Jan van der Pas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # Faraday Em::Http adapter
2
+
3
+ This gem is a [Faraday][faraday] adapter for the [Em::Http::Request][em_http_request] library.
4
+ Faraday is an HTTP client library that provides a common interface over many adapters.
5
+ Every adapter is defined into its own gem. This gem defines the adapter for Em::Http::Request.
6
+
7
+ ## Installation
8
+
9
+ Add these lines to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'em-http-request', '>= 1.1'
13
+ gem 'faraday-em_http'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle install
19
+
20
+ Or install them yourself as:
21
+
22
+ $ gem install em-http-request -v '>= 1.1'
23
+ $ gem install faraday-em_http
24
+
25
+ ## Usage
26
+
27
+ Configure your Faraday connection to use this adapter like this:
28
+
29
+ ```ruby
30
+ connection = Faraday.new(url, conn_options) do |conn|
31
+ conn.adapter(:em_http)
32
+ end
33
+ ```
34
+
35
+ For more information on how to setup your Faraday connection and adapters usage, please refer to the [Faraday Website][faraday-website].
36
+
37
+ ## Development
38
+
39
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
40
+
41
+ To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](rubygems).
42
+
43
+ ## Contributing
44
+
45
+ Bug reports and pull requests are welcome on [GitHub][repo].
46
+
47
+ ## License
48
+
49
+ The gem is available as open source under the terms of the [license][license].
50
+
51
+ ## Code of Conduct
52
+
53
+ Everyone interacting in the Faraday Em::Http adapter project's codebase, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct][code-of-conduct].
54
+
55
+ [faraday]: https://github.com/lostisland/faraday
56
+ [faraday-website]: https://lostisland.github.io/faraday
57
+ [em_http_request]: https://github.com/igrigorik/em-http-request
58
+ [rubygems]: https://rubygems.org
59
+ [repo]: https://github.com/lostisland/faraday-em_http
60
+ [license]: https://github.com/lostisland/faraday-em_http/blob/main/LICENSE.md
61
+ [code-of-conduct]: https://github.com/lostisland/faraday-em_http/blob/main/CODE_OF_CONDUCT.md
@@ -0,0 +1,289 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ class Adapter
5
+ # EventMachine adapter. This adapter is useful for either asynchronous
6
+ # requests when in an EM reactor loop, or for making parallel requests in
7
+ # synchronous code.
8
+ class EMHttp < Faraday::Adapter
9
+ # Options is a module containing helpers to convert the Faraday env object
10
+ # into options hashes for EMHTTP method calls.
11
+ module Options
12
+ # @return [Hash]
13
+ def connection_config(env)
14
+ options = {}
15
+ configure_proxy(options, env)
16
+ configure_timeout(options, env)
17
+ configure_socket(options, env)
18
+ configure_ssl(options, env)
19
+ options
20
+ end
21
+
22
+ def request_config(env)
23
+ options = {
24
+ body: read_body(env),
25
+ head: env[:request_headers]
26
+ # keepalive: true,
27
+ # file: 'path/to/file', # stream data off disk
28
+ }
29
+ configure_compression(options, env)
30
+ options
31
+ end
32
+
33
+ def read_body(env)
34
+ body = env[:body]
35
+ body.respond_to?(:read) ? body.read : body
36
+ end
37
+
38
+ # Reads out proxy settings from env into options
39
+ def configure_proxy(options, env)
40
+ proxy = request_options(env)[:proxy]
41
+ return unless proxy
42
+
43
+ options[:proxy] = {
44
+ host: proxy[:uri].host,
45
+ port: proxy[:uri].port,
46
+ authorization: [proxy[:user], proxy[:password]]
47
+ }
48
+ end
49
+
50
+ # Reads out host and port settings from env into options
51
+ def configure_socket(options, env)
52
+ bind = request_options(env)[:bind]
53
+ return unless bind
54
+
55
+ options[:bind] = {
56
+ host: bind[:host],
57
+ port: bind[:port]
58
+ }
59
+ end
60
+
61
+ # Reads out SSL certificate settings from env into options
62
+ def configure_ssl(options, env)
63
+ return unless env[:url].scheme == 'https' && env[:ssl]
64
+
65
+ options[:ssl] = {
66
+ cert_chain_file: env[:ssl][:ca_file],
67
+ verify_peer: env[:ssl].fetch(:verify, true)
68
+ }
69
+ end
70
+
71
+ # Reads out timeout settings from env into options
72
+ def configure_timeout(options, env)
73
+ req = request_options(env)
74
+ options[:inactivity_timeout] = request_timeout(:read, req)
75
+ options[:connect_timeout] = request_timeout(:open, req)
76
+ end
77
+
78
+ # Reads out compression header settings from env into options
79
+ def configure_compression(options, env)
80
+ return unless (env[:method] == :get) &&
81
+ !options[:head].key?('accept-encoding')
82
+
83
+ options[:head]['accept-encoding'] = 'gzip, compressed'
84
+ end
85
+
86
+ def request_options(env)
87
+ env[:request]
88
+ end
89
+ end
90
+
91
+ include Options
92
+
93
+ dependency do
94
+ require 'em-http'
95
+
96
+ begin
97
+ require 'openssl'
98
+ rescue LoadError
99
+ warn 'Warning: no such file to load -- openssl. ' \
100
+ 'Make sure it is installed if you want HTTPS support'
101
+ else
102
+ require 'em-http/version'
103
+ if EventMachine::HttpRequest::VERSION < '1.1.6'
104
+ require 'faraday/adapter/em_http_ssl_patch'
105
+ end
106
+ end
107
+ end
108
+
109
+ self.supports_parallel = true
110
+
111
+ # @return [Manager]
112
+ def self.setup_parallel_manager(_options = nil)
113
+ Manager.new
114
+ end
115
+
116
+ def call(env)
117
+ super
118
+ perform_request env
119
+ @app.call env
120
+ end
121
+
122
+ def perform_request(env)
123
+ if parallel?(env)
124
+ manager = env[:parallel_manager]
125
+ manager.add do
126
+ perform_single_request(env)
127
+ .callback { env[:response].finish(env) }
128
+ end
129
+ elsif EventMachine.reactor_running?
130
+ # EM is running: instruct upstream that this is an async request
131
+ env[:parallel_manager] = true
132
+ perform_single_request(env)
133
+ .callback { env[:response].finish(env) }
134
+ .errback do
135
+ # TODO: no way to communicate the error in async mode
136
+ raise NotImplementedError
137
+ end
138
+ else
139
+ error = nil
140
+ # start EM, block until request is completed
141
+ EventMachine.run do
142
+ perform_single_request(env)
143
+ .callback { EventMachine.stop }
144
+ .errback do |client|
145
+ error = error_message(client)
146
+ EventMachine.stop
147
+ end
148
+ end
149
+ raise_error(error) if error
150
+ end
151
+ rescue EventMachine::Connectify::CONNECTError => e
152
+ if e.message.include?('Proxy Authentication Required')
153
+ raise Faraday::ConnectionFailed,
154
+ %(407 "Proxy Authentication Required ")
155
+ end
156
+
157
+ raise Faraday::ConnectionFailed, e
158
+ rescue StandardError => e
159
+ if defined?(::OpenSSL::SSL::SSLError) && \
160
+ e.is_a?(::OpenSSL::SSL::SSLError)
161
+ raise Faraday::SSLError, e
162
+ end
163
+
164
+ raise
165
+ end
166
+
167
+ # TODO: reuse the connection to support pipelining
168
+ def perform_single_request(env)
169
+ req = create_request(env)
170
+ req = req.setup_request(env[:method], request_config(env))
171
+ req.callback do |client|
172
+ if env[:request].stream_response?
173
+ warn "Streaming downloads for #{self.class.name} " \
174
+ 'are not yet implemented.'
175
+ env[:request].on_data.call(
176
+ client.response,
177
+ client.response.bytesize
178
+ )
179
+ end
180
+ status = client.response_header.status
181
+ reason = client.response_header.http_reason
182
+ save_response(env, status, client.response, nil, reason) do |headers|
183
+ client.response_header.each do |name, value|
184
+ headers[name.to_sym] = value
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+ def create_request(env)
191
+ EventMachine::HttpRequest.new(
192
+ env[:url], connection_config(env).merge(@connection_options)
193
+ )
194
+ end
195
+
196
+ def error_message(client)
197
+ client.error || 'request failed'
198
+ end
199
+
200
+ def raise_error(msg)
201
+ error_class = Faraday::ClientError
202
+ if timeout_message?(msg)
203
+ error_class = Faraday::TimeoutError
204
+ msg = 'request timed out'
205
+ elsif msg == Errno::ECONNREFUSED
206
+ error_class = Faraday::ConnectionFailed
207
+ msg = 'connection refused'
208
+ elsif msg == 'connection closed by server'
209
+ error_class = Faraday::ConnectionFailed
210
+ end
211
+ raise error_class, msg
212
+ end
213
+
214
+ def timeout_message?(msg)
215
+ msg == Errno::ETIMEDOUT ||
216
+ (msg.is_a?(String) && msg.include?('timeout error'))
217
+ end
218
+
219
+ # @return [Boolean]
220
+ def parallel?(env)
221
+ !!env[:parallel_manager]
222
+ end
223
+
224
+ # This parallel manager is designed to start an EventMachine loop
225
+ # and block until all registered requests have been completed.
226
+ class Manager
227
+ # @see reset
228
+ def initialize
229
+ reset
230
+ end
231
+
232
+ # Re-initializes instance variables
233
+ def reset
234
+ @registered_procs = []
235
+ @num_registered = 0
236
+ @num_succeeded = 0
237
+ @errors = []
238
+ @running = false
239
+ end
240
+
241
+ # @return [Boolean]
242
+ def running?
243
+ @running
244
+ end
245
+
246
+ def add(&block)
247
+ if running?
248
+ perform_request(&block)
249
+ else
250
+ @registered_procs << block
251
+ end
252
+ @num_registered += 1
253
+ end
254
+
255
+ def run
256
+ if @num_registered.positive?
257
+ @running = true
258
+ EventMachine.run do
259
+ @registered_procs.each do |proc|
260
+ perform_request(&proc)
261
+ end
262
+ end
263
+ unless @errors.empty?
264
+ raise Faraday::ClientError, @errors.first || 'connection failed'
265
+ end
266
+ end
267
+ ensure
268
+ reset
269
+ end
270
+
271
+ def perform_request
272
+ client = yield
273
+ client.callback do
274
+ @num_succeeded += 1
275
+ check_finished
276
+ end
277
+ client.errback do
278
+ @errors << client.error
279
+ check_finished
280
+ end
281
+ end
282
+
283
+ def check_finished
284
+ EventMachine.stop if @num_succeeded + @errors.size == @num_registered
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+ require 'em-http'
5
+
6
+ # EventMachine patch to make SSL work.
7
+ module EmHttpSslPatch
8
+ def ssl_verify_peer(cert_string)
9
+ begin
10
+ @last_seen_cert = OpenSSL::X509::Certificate.new(cert_string)
11
+ rescue OpenSSL::X509::CertificateError
12
+ return false
13
+ end
14
+
15
+ unless certificate_store.verify(@last_seen_cert)
16
+ raise OpenSSL::SSL::SSLError,
17
+ %(unable to verify the server certificate for "#{host}")
18
+ end
19
+
20
+ begin
21
+ certificate_store.add_cert(@last_seen_cert)
22
+ rescue OpenSSL::X509::StoreError => e
23
+ raise e unless e.message == 'cert already in hash table'
24
+ end
25
+ true
26
+ end
27
+
28
+ def ssl_handshake_completed
29
+ return true unless verify_peer?
30
+
31
+ unless verified_cert_identity?
32
+ raise OpenSSL::SSL::SSLError,
33
+ %(host "#{host}" does not match the server certificate)
34
+ end
35
+
36
+ true
37
+ end
38
+
39
+ def verify_peer?
40
+ parent.connopts.tls[:verify_peer]
41
+ end
42
+
43
+ def verified_cert_identity?
44
+ OpenSSL::SSL.verify_certificate_identity(@last_seen_cert, host)
45
+ end
46
+
47
+ def host
48
+ parent.uri.host
49
+ end
50
+
51
+ def certificate_store
52
+ @certificate_store ||= begin
53
+ store = OpenSSL::X509::Store.new
54
+ store.set_default_paths
55
+ ca_file = parent.connopts.tls[:cert_chain_file]
56
+ store.add_file(ca_file) if ca_file
57
+ store
58
+ end
59
+ end
60
+ end
61
+
62
+ EventMachine::HttpStubConnection.include(EmHttpSslPatch)
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'adapter/em_http'
4
+ require_relative 'em_http/version'
5
+
6
+ module Faraday
7
+ # Main Faraday::EmHttp module
8
+ module EmHttp
9
+ Faraday::Adapter.register_middleware(em_http: Faraday::Adapter::EMHttp)
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ module EmHttp
5
+ VERSION = '1.0.0'
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,206 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: faraday-em_http
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - "@iMacTia"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-04-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: em-http-request
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '1.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '13.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '13.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.19.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.19.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: multipart-parser
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.1.1
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.1.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: webmock
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.4'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.4'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.91.1
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.91.1
139
+ - !ruby/object:Gem::Dependency
140
+ name: rubocop-packaging
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '0.5'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '0.5'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rubocop-performance
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '1.0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '1.0'
167
+ description: Faraday adapter for Em::Http
168
+ email:
169
+ - giuffrida.mattia@gmail.com
170
+ executables: []
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - LICENSE.md
175
+ - README.md
176
+ - lib/faraday/adapter/em_http.rb
177
+ - lib/faraday/adapter/em_http_ssl_patch.rb
178
+ - lib/faraday/em_http.rb
179
+ - lib/faraday/em_http/version.rb
180
+ homepage: https://github.com/lostisland/faraday-em_http
181
+ licenses:
182
+ - MIT
183
+ metadata:
184
+ homepage_uri: https://github.com/lostisland/faraday-em_http
185
+ source_code_uri: https://github.com/lostisland/faraday-em_http
186
+ changelog_uri: https://github.com/lostisland/faraday-em_http
187
+ post_install_message:
188
+ rdoc_options: []
189
+ require_paths:
190
+ - lib
191
+ required_ruby_version: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - ">="
194
+ - !ruby/object:Gem::Version
195
+ version: 2.4.0
196
+ required_rubygems_version: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: '0'
201
+ requirements: []
202
+ rubygems_version: 3.1.6
203
+ signing_key:
204
+ specification_version: 4
205
+ summary: Faraday adapter for Em::Http
206
+ test_files: []