async-aws 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 015565e72854159af66339152c90b413caa5fc32b419f3ee14b38d19c345a3ae
4
- data.tar.gz: 26f6079d3e6d1de58bd670ae306af9fa4bbfa7f8523253d984bbab6d91c7db6d
3
+ metadata.gz: d4b4634f5e07b319d4e20f927f524e59fea4236693fc38a57d914cba8e616fc8
4
+ data.tar.gz: 57868e373bbe1137a4f5a0fd38f6727f72eb895ce1a732910e3d5debec1e18b0
5
5
  SHA512:
6
- metadata.gz: 6b5bac239f67be456b553f298b1d7f35ec1701e3a4bc511110bbb71a9af0534b846f0bcdf8f66aefa6361263cc9bdb009d5eb4f9ef8faf88c75b5d7e6f03ad77
7
- data.tar.gz: 47cedb33f2cb7d5ea04759b9601e5fd95c76c697f19d96f08b9c0dc8db82773a66cc95540255b91e5c0a96d1859f43a4418ccba143cc406368ee26ea259a49f5
6
+ metadata.gz: 9acbd7be0847428db25bdc766c184d2c10eb9bd5c6facb72b266389bdcc7d276bf07e99da04cfd959d6522ce1aecf247707061d3b8b1b8f537aacd38b093f01e
7
+ data.tar.gz: 72f1485b45e5d2b8cd8be02f5ad96dc7161fd8b94e0e4989a95fcd8786e7fc63dd460533ebb1cc88dc2923ca52320dac6ae5030c643b28cb845b3aababecd5dd
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- async-aws (0.1.0)
4
+ async-aws (0.3.0)
5
5
  async-http (~> 0.48)
6
6
 
7
7
  GEM
@@ -11,14 +11,17 @@ GEM
11
11
  console (~> 1.0)
12
12
  nio4r (~> 2.3)
13
13
  timers (~> 4.1)
14
- async-http (0.48.2)
15
- async (~> 1.19)
16
- async-io (~> 1.25)
17
- protocol-http (~> 0.12.0)
18
- protocol-http1 (~> 0.9.0)
19
- protocol-http2 (~> 0.9.0)
20
- async-io (1.27.0)
14
+ async-http (0.50.0)
15
+ async (~> 1.23)
16
+ async-io (~> 1.27.0)
17
+ async-pool (~> 0.2)
18
+ protocol-http (~> 0.13.0)
19
+ protocol-http1 (~> 0.10.0)
20
+ protocol-http2 (~> 0.10.0)
21
+ async-io (1.27.1)
21
22
  async (~> 1.14)
23
+ async-pool (0.2.0)
24
+ async (~> 1.8)
22
25
  aws-eventstream (1.0.3)
23
26
  aws-partitions (1.223.0)
24
27
  aws-sdk-core (3.68.1)
@@ -43,10 +46,10 @@ GEM
43
46
  jmespath (1.4.0)
44
47
  nio4r (2.5.2)
45
48
  protocol-hpack (1.4.1)
46
- protocol-http (0.12.3)
47
- protocol-http1 (0.9.0)
48
- protocol-http (~> 0.5)
49
- protocol-http2 (0.9.7)
49
+ protocol-http (0.13.0)
50
+ protocol-http1 (0.10.0)
51
+ protocol-http (~> 0.13)
52
+ protocol-http2 (0.10.4)
50
53
  protocol-hpack (~> 1.4)
51
54
  protocol-http (~> 0.2)
52
55
  rake (10.5.0)
@@ -78,4 +81,4 @@ DEPENDENCIES
78
81
  rspec (~> 3.0)
79
82
 
80
83
  BUNDLED WITH
81
- 2.0.2
84
+ 2.1.2
@@ -1,6 +1,6 @@
1
1
  require 'async/http'
2
2
  require 'async/http/internet'
3
- require 'async/aws/connection_pool'
3
+ require 'async/aws/http_client'
4
4
  require 'async/aws/http_handler'
5
5
  require 'async/aws/http_plugin'
6
6
 
@@ -8,12 +8,24 @@ module Async
8
8
  module Aws
9
9
  module_function
10
10
 
11
- def connection_pool=(arg)
12
- @connection_pool = arg
11
+ def keep_alive_timeout=(arg)
12
+ @keep_alive_timeout = arg.to_i
13
13
  end
14
14
 
15
- def connection_pool
16
- @connection_pool ||= ConnectionPool.new
15
+ def keep_alive_timeout
16
+ @keep_alive_timeout || 2
17
+ end
18
+
19
+ def connection_pool_size=(arg)
20
+ @connection_pool_size = arg.to_i
21
+ end
22
+
23
+ def connection_pool_size
24
+ @connection_pool_size || 1
25
+ end
26
+
27
+ def configure(&block)
28
+ instance_exec(&block)
17
29
  end
18
30
  end
19
31
  end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'async/io/endpoint'
4
+ require 'async/io/stream'
5
+ require 'protocol/http/body/streamable'
6
+ require 'protocol/http/methods'
7
+
8
+ module Async
9
+ module Aws
10
+ class HttpClient < ::Protocol::HTTP::Methods
11
+ attr_accessor :keep_alive_timeout
12
+ attr_reader :scheme
13
+
14
+ ConnectionSpec = Struct.new(:connection, :used_at)
15
+
16
+ # Provides a robust interface to a server.
17
+ # * If there are no connections, it will create one.
18
+ # * If there are already connections, it will reuse it.
19
+ # * If a request fails, it will retry it up to N times if it was idempotent.
20
+ # The client object will never become unusable. It internally manages persistent connections (or non-persistent connections if that's required).
21
+ # @param endpoint [Endpoint] the endpoint to connnect to.
22
+ # @param protocol [Protocol::HTTP1 | Protocol::HTTP2 | Protocol::HTTPS] the protocol to use.
23
+ # @param scheme [String] The default scheme to set to requests.
24
+ # @param authority [String] The default authority to set to requests.
25
+ def initialize(endpoint, pool_size: 2, keep_alive_timeout: 5)
26
+ @endpoint = endpoint
27
+ @protocol = endpoint.protocol
28
+ @scheme = endpoint.scheme
29
+ @authority = endpoint.authority
30
+ @keep_alive_timeout = keep_alive_timeout
31
+ @clock_gettime_constant = \
32
+ if defined?(Process::CLOCK_MONOTONIC)
33
+ Process::CLOCK_MONOTONIC
34
+ else
35
+ Process::CLOCK_REALTIME
36
+ end
37
+ @pool = make_pool(pool_size)
38
+ end
39
+
40
+ def close
41
+ until @pool.empty?
42
+ connection_spec = @pool.dequeue
43
+ connection_spec.connection.close
44
+ end
45
+ end
46
+
47
+ def call(request)
48
+ request.scheme ||= self.scheme
49
+ request.authority ||= self.authority
50
+
51
+ attempt = 0
52
+
53
+ # We may retry the request if it is possible to do so. https://tools.ietf.org/html/draft-nottingham-httpbis-retry-01 is a good guide for how retrying requests should work.
54
+ begin
55
+ attempt += 1
56
+
57
+ # As we cache pool, it's possible these pool go bad (e.g. closed by remote host). In this case, we need to try again. It's up to the caller to impose a timeout on this. If this is the last attempt, we force a new connection.
58
+ connection_spec = @pool.dequeue
59
+ if connection_spec.used_at + keep_alive_timeout <= current_time
60
+ connection_spec.connection.close
61
+ connection_spec = create_connection_spec
62
+ end
63
+
64
+ # send request
65
+ response = request.call(connection_spec.connection)
66
+
67
+ # The connection won't be released until the body is completely read/released.
68
+ ::Protocol::HTTP::Body::Streamable.wrap(response) do
69
+ connection_spec.used_at = current_time
70
+ @pool << connection_spec
71
+ end
72
+
73
+ return response
74
+ rescue => e
75
+ @pool << connection_spec if connection_spec
76
+ raise e
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def current_time
83
+ Process.clock_gettime(@clock_gettime_constant)
84
+ end
85
+
86
+ def create_connection_spec
87
+ ConnectionSpec.new(
88
+ @protocol.client(@endpoint.connect),
89
+ current_time
90
+ )
91
+ end
92
+
93
+ def make_pool(size)
94
+ Async::Queue.new.tap do |queue|
95
+ size.times do
96
+ queue << create_connection_spec
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -1,17 +1,53 @@
1
1
  module Async
2
2
  module Aws
3
3
  class HttpHandler < ::Seahorse::Client::Handler
4
+ def self.clients
5
+ @clients ||= {}
6
+ end
7
+
8
+ def self.with_configuration(**kwargs)
9
+ Class.new(self).tap do |klass|
10
+ klass.connection_pool_size = kwargs.fetch(
11
+ :connection_pool_size, Async::Aws.connection_pool_size
12
+ )
13
+ klass.keep_alive_timeout = kwargs.fetch(
14
+ :keep_alive_timeout, Async::Aws.keep_alive_timeout
15
+ )
16
+ end
17
+ end
18
+
19
+ def self.connection_pool_size
20
+ @connection_pool_size ||= Async::Aws.connection_pool_size
21
+ end
22
+
23
+ def self.connection_pool_size=(value)
24
+ @connection_pool_size = value.to_i
25
+ end
26
+
27
+ def self.keep_alive_timeout
28
+ @keep_alive_timeout ||= Async::Aws.keep_alive_timeout
29
+ end
30
+
31
+ def self.keep_alive_timeout=(value)
32
+ @keep_alive_timeout = value.to_i
33
+ end
34
+
4
35
  def call(context)
5
36
  req = context.http_request
6
37
  resp = context.http_response
38
+ endpoint = Async::HTTP::Endpoint.parse(req.endpoint.to_s)
7
39
 
8
40
  begin
41
+ client = client_for(endpoint)
9
42
  headers_arr = req.headers.reject do |k, v|
10
43
  k == 'host' || k == 'content-length'
11
44
  end
12
- response = Async::Aws.connection_pool.call(
13
- req.http_method, req.endpoint.to_s, headers_arr, req.body
45
+ buffered_body = Async::HTTP::Body::Buffered.wrap(req.body)
46
+ request = ::Protocol::HTTP::Request.new(
47
+ client.scheme, endpoint.authority, req.http_method, endpoint.path,
48
+ nil, headers_arr, buffered_body
14
49
  )
50
+ response = client.call(request)
15
51
  body = response.read
16
52
  resp.signal_headers(response.status.to_i, response.headers.to_h)
17
53
  resp.signal_data(body)
@@ -23,6 +59,14 @@ module Async
23
59
 
24
60
  Seahorse::Client::Response.new(context: context)
25
61
  end
62
+
63
+ def client_for(endpoint)
64
+ self.class.clients[endpoint.hostname] ||= ::Async::Aws::HttpClient.new(
65
+ endpoint,
66
+ pool_size: self.class.connection_pool_size,
67
+ keep_alive_timeout: self.class.keep_alive_timeout
68
+ )
69
+ end
26
70
  end
27
71
  end
28
72
  end
@@ -1,5 +1,5 @@
1
1
  module Async
2
2
  module Aws
3
- VERSION = '0.2.0'
3
+ VERSION = '0.3.0'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async-aws
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julien D.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-11 00:00:00.000000000 Z
11
+ date: 2020-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async-http
@@ -130,7 +130,7 @@ files:
130
130
  - lib/async-aws.rb
131
131
  - lib/async/aws.rb
132
132
  - lib/async/aws/all.rb
133
- - lib/async/aws/connection_pool.rb
133
+ - lib/async/aws/http_client.rb
134
134
  - lib/async/aws/http_handler.rb
135
135
  - lib/async/aws/http_plugin.rb
136
136
  - lib/async/aws/version.rb
@@ -157,7 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
157
157
  - !ruby/object:Gem::Version
158
158
  version: '0'
159
159
  requirements: []
160
- rubygems_version: 3.0.3
160
+ rubygems_version: 3.1.2
161
161
  signing_key:
162
162
  specification_version: 4
163
163
  summary: Async AWS SDK adapter for `socketry/async` framework
@@ -1,41 +0,0 @@
1
- module Async
2
- module Aws
3
- class ConnectionPool
4
- def initialize(connection_limit: nil)
5
- @clients = {}
6
- @connection_limit = connection_limit
7
- end
8
-
9
- def call(method, url, headers = [], body = nil)
10
- endpoint = ::Async::HTTP::Endpoint.parse(url)
11
- client = client_for(endpoint)
12
-
13
- request_body = \
14
- case body
15
- when ::Aws::Query::ParamList::IoWrapper
16
- ::Async::HTTP::Body::Buffered.wrap(body.instance_variable_get(:@io))
17
- else
18
- ::Async::HTTP::Body::Buffered.wrap(body)
19
- end
20
- request = ::Protocol::HTTP::Request.new(
21
- endpoint.scheme, endpoint.authority, method, endpoint.path, nil, headers,
22
- request_body
23
- )
24
-
25
- client.call(request)
26
- end
27
-
28
- def client_for(endpoint)
29
- @clients[endpoint] ||= ::Async::HTTP::Client.new(
30
- endpoint, endpoint.protocol, endpoint.scheme, endpoint.authority,
31
- retries: 3, connection_limit: @connection_limit
32
- )
33
- end
34
-
35
- def close
36
- @clients.each_value(&:close)
37
- @clients.clear
38
- end
39
- end
40
- end
41
- end