aws-sdk-core 3.39.0 → 3.54.2
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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/aws-sdk-core/async_client_stubs.rb +80 -0
- data/lib/aws-sdk-core/binary/decode_handler.rb +9 -1
- data/lib/aws-sdk-core/binary/encode_handler.rb +32 -0
- data/lib/aws-sdk-core/binary/event_builder.rb +122 -0
- data/lib/aws-sdk-core/binary/event_parser.rb +48 -18
- data/lib/aws-sdk-core/binary/event_stream_decoder.rb +5 -2
- data/lib/aws-sdk-core/binary/event_stream_encoder.rb +53 -0
- data/lib/aws-sdk-core/binary.rb +3 -0
- data/lib/aws-sdk-core/client_side_monitoring/request_metrics.rb +63 -9
- data/lib/aws-sdk-core/client_stubs.rb +1 -1
- data/lib/aws-sdk-core/ecs_credentials.rb +12 -8
- data/lib/aws-sdk-core/errors.rb +38 -2
- data/lib/aws-sdk-core/event_emitter.rb +42 -0
- data/lib/aws-sdk-core/instance_profile_credentials.rb +12 -8
- data/lib/aws-sdk-core/json/error_handler.rb +19 -2
- data/lib/aws-sdk-core/json/handler.rb +19 -1
- data/lib/aws-sdk-core/log/param_filter.rb +1 -1
- data/lib/aws-sdk-core/param_validator.rb +9 -1
- data/lib/aws-sdk-core/plugins/client_metrics_plugin.rb +22 -3
- data/lib/aws-sdk-core/plugins/client_metrics_send_plugin.rb +5 -1
- data/lib/aws-sdk-core/plugins/event_stream_configuration.rb +14 -0
- data/lib/aws-sdk-core/plugins/invocation_id.rb +33 -0
- data/lib/aws-sdk-core/plugins/retry_errors.rb +2 -0
- data/lib/aws-sdk-core/plugins/stub_responses.rb +19 -7
- data/lib/aws-sdk-core/plugins/transfer_encoding.rb +53 -0
- data/lib/aws-sdk-core/plugins/user_agent.rb +6 -0
- data/lib/aws-sdk-core/process_credentials.rb +7 -1
- data/lib/aws-sdk-core/query/handler.rb +6 -1
- data/lib/aws-sdk-core/refreshing_credentials.rb +1 -1
- data/lib/aws-sdk-core/resources/collection.rb +1 -1
- data/lib/aws-sdk-core/structure.rb +6 -2
- data/lib/aws-sdk-core/stubbing/protocols/rest.rb +19 -0
- data/lib/aws-sdk-core/stubbing/stub_data.rb +13 -4
- data/lib/aws-sdk-core/waiters/waiter.rb +2 -2
- data/lib/aws-sdk-core/xml/error_handler.rb +26 -3
- data/lib/aws-sdk-core.rb +1 -0
- data/lib/aws-sdk-sts/client.rb +622 -427
- data/lib/aws-sdk-sts/client_api.rb +35 -0
- data/lib/aws-sdk-sts/errors.rb +128 -0
- data/lib/aws-sdk-sts/types.rb +498 -165
- data/lib/aws-sdk-sts.rb +1 -1
- data/lib/seahorse/client/async_base.rb +50 -0
- data/lib/seahorse/client/async_response.rb +62 -0
- data/lib/seahorse/client/base.rb +1 -1
- data/lib/seahorse/client/configuration.rb +4 -2
- data/lib/seahorse/client/events.rb +1 -1
- data/lib/seahorse/client/h2/connection.rb +244 -0
- data/lib/seahorse/client/h2/handler.rb +151 -0
- data/lib/seahorse/client/http/async_response.rb +42 -0
- data/lib/seahorse/client/http/response.rb +13 -8
- data/lib/seahorse/client/net_http/patches.rb +7 -1
- data/lib/seahorse/client/networking_error.rb +28 -0
- data/lib/seahorse/client/plugin.rb +1 -1
- data/lib/seahorse/client/plugins/content_length.rb +7 -2
- data/lib/seahorse/client/plugins/h2.rb +64 -0
- data/lib/seahorse/model/api.rb +4 -0
- data/lib/seahorse/model/operation.rb +4 -0
- data/lib/seahorse/model/shapes.rb +2 -2
- data/lib/seahorse.rb +9 -0
- metadata +23 -5
data/lib/aws-sdk-sts.rb
CHANGED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Seahorse
|
2
|
+
module Client
|
3
|
+
class AsyncBase < Seahorse::Client::Base
|
4
|
+
|
5
|
+
# default H2 plugins
|
6
|
+
@plugins = PluginList.new([
|
7
|
+
Plugins::Endpoint,
|
8
|
+
Plugins::H2,
|
9
|
+
Plugins::ResponseTarget
|
10
|
+
])
|
11
|
+
|
12
|
+
def initialize(plugins, options)
|
13
|
+
super
|
14
|
+
@connection = H2::Connection.new(options)
|
15
|
+
@options = options
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [H2::Connection]
|
19
|
+
attr_reader :connection
|
20
|
+
|
21
|
+
# @return [Array<Symbol>] Returns a list of valid async request
|
22
|
+
# operation names.
|
23
|
+
def operation_names
|
24
|
+
self.class.api.async_operation_names
|
25
|
+
end
|
26
|
+
|
27
|
+
# Closes the underlying HTTP2 Connection for the client
|
28
|
+
# @return [Symbol] Returns the status of the connection (:closed)
|
29
|
+
def close_connection
|
30
|
+
@connection.close!
|
31
|
+
end
|
32
|
+
|
33
|
+
# Creates a new HTTP2 Connection for the client
|
34
|
+
# @return [Seahorse::Client::H2::Connection]
|
35
|
+
def new_connection
|
36
|
+
if @connection.closed?
|
37
|
+
@connection = H2::Connection.new(@options)
|
38
|
+
else
|
39
|
+
@connection
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def connection_errors
|
44
|
+
@connection.errors
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Seahorse
|
2
|
+
module Client
|
3
|
+
class AsyncResponse
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@response = Response.new(context: options[:context])
|
7
|
+
@stream = options[:stream]
|
8
|
+
@stream_mutex = options[:stream_mutex]
|
9
|
+
@close_condition = options[:close_condition]
|
10
|
+
@sync_queue = options[:sync_queue]
|
11
|
+
end
|
12
|
+
|
13
|
+
def context
|
14
|
+
@response.context
|
15
|
+
end
|
16
|
+
|
17
|
+
def error
|
18
|
+
@response.error
|
19
|
+
end
|
20
|
+
|
21
|
+
def on(range, &block)
|
22
|
+
@response.on(range, &block)
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def on_complete(&block)
|
27
|
+
@response.on_complete(&block)
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def wait
|
32
|
+
if error && context.config.raise_response_errors
|
33
|
+
raise error
|
34
|
+
elsif @stream
|
35
|
+
# have a sync signal that #signal can be blocked on
|
36
|
+
# else, if #signal is called before #wait
|
37
|
+
# will be waiting for a signal never arrives
|
38
|
+
@sync_queue << "sync_signal"
|
39
|
+
# now #signal is unlocked for
|
40
|
+
# signaling close condition when ready
|
41
|
+
@stream_mutex.synchronize {
|
42
|
+
@close_condition.wait(@stream_mutex)
|
43
|
+
}
|
44
|
+
@response
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def join!
|
49
|
+
if error && context.config.raise_response_errors
|
50
|
+
raise error
|
51
|
+
elsif @stream
|
52
|
+
# close callback is waiting
|
53
|
+
# for the "sync_signal"
|
54
|
+
@sync_queue << "sync_signal"
|
55
|
+
@stream.close
|
56
|
+
@response
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/seahorse/client/base.rb
CHANGED
@@ -104,7 +104,7 @@ module Seahorse
|
|
104
104
|
#
|
105
105
|
# @return [self]
|
106
106
|
def add_option(name, default = nil, &block)
|
107
|
-
default = DynamicDefault.new(
|
107
|
+
default = DynamicDefault.new(block) if block_given?
|
108
108
|
@defaults[name.to_sym] << default
|
109
109
|
self
|
110
110
|
end
|
@@ -199,7 +199,9 @@ module Seahorse
|
|
199
199
|
value = @struct[opt_name]
|
200
200
|
if value.is_a?(Defaults)
|
201
201
|
# this config value is used by endpoint discovery
|
202
|
-
|
202
|
+
if opt_name == :endpoint && @struct.members.include?(:regional_endpoint)
|
203
|
+
@struct[:regional_endpoint] = true
|
204
|
+
end
|
203
205
|
resolve_defaults(opt_name, value)
|
204
206
|
else
|
205
207
|
value
|
@@ -0,0 +1,244 @@
|
|
1
|
+
if RUBY_VERSION >= '2.1'
|
2
|
+
begin
|
3
|
+
require 'http/2'
|
4
|
+
rescue LoadError; end
|
5
|
+
end
|
6
|
+
require 'openssl'
|
7
|
+
require 'socket'
|
8
|
+
|
9
|
+
module Seahorse
|
10
|
+
module Client
|
11
|
+
# @api private
|
12
|
+
module H2
|
13
|
+
|
14
|
+
# H2 Connection build on top of `http/2` gem
|
15
|
+
# (requires Ruby >= 2.1)
|
16
|
+
# with TLS layer plus ALPN, requires:
|
17
|
+
# Ruby >= 2.3 and OpenSSL >= 1.0.2
|
18
|
+
class Connection
|
19
|
+
|
20
|
+
OPTIONS = {
|
21
|
+
max_concurrent_streams: 100,
|
22
|
+
connection_timeout: 60,
|
23
|
+
connection_read_timeout: 60,
|
24
|
+
http_wire_trace: false,
|
25
|
+
logger: nil,
|
26
|
+
ssl_verify_peer: true,
|
27
|
+
ssl_ca_bundle: nil,
|
28
|
+
ssl_ca_directory: nil,
|
29
|
+
ssl_ca_store: nil,
|
30
|
+
enable_alpn: false
|
31
|
+
}
|
32
|
+
|
33
|
+
# chunk read size at socket
|
34
|
+
CHUNKSIZE = 1024
|
35
|
+
|
36
|
+
SOCKET_FAMILY = ::Socket::AF_INET
|
37
|
+
|
38
|
+
def initialize(options = {})
|
39
|
+
OPTIONS.each_pair do |opt_name, default_value|
|
40
|
+
value = options[opt_name].nil? ? default_value : options[opt_name]
|
41
|
+
instance_variable_set("@#{opt_name}", value)
|
42
|
+
end
|
43
|
+
@h2_client = HTTP2::Client.new(
|
44
|
+
settings_max_concurrent_streams: max_concurrent_streams
|
45
|
+
)
|
46
|
+
@logger = options[:logger] || Logger.new($stdout) if @http_wire_trace
|
47
|
+
@chunk_size = options[:read_chunk_size] || CHUNKSIZE
|
48
|
+
@errors = []
|
49
|
+
@status = :ready
|
50
|
+
@mutex = Mutex.new # connection can be shared across requests
|
51
|
+
end
|
52
|
+
|
53
|
+
OPTIONS.keys.each do |attr_name|
|
54
|
+
attr_reader(attr_name)
|
55
|
+
end
|
56
|
+
|
57
|
+
alias ssl_verify_peer? ssl_verify_peer
|
58
|
+
|
59
|
+
attr_reader :errors
|
60
|
+
|
61
|
+
attr_accessor :input_signal_thread
|
62
|
+
|
63
|
+
def new_stream
|
64
|
+
begin
|
65
|
+
@h2_client.new_stream
|
66
|
+
rescue => error
|
67
|
+
raise Http2StreamInitializeError.new(error)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def connect(endpoint)
|
72
|
+
@mutex.synchronize {
|
73
|
+
if @status == :ready
|
74
|
+
tcp, addr = _tcp_socket(endpoint)
|
75
|
+
debug_output("opening connection to #{endpoint.host}:#{endpoint.port} ...")
|
76
|
+
_nonblocking_connect(tcp, addr)
|
77
|
+
debug_output("opened")
|
78
|
+
|
79
|
+
@socket = OpenSSL::SSL::SSLSocket.new(tcp, _tls_context)
|
80
|
+
@socket.sync_close = true
|
81
|
+
@socket.hostname = endpoint.host
|
82
|
+
|
83
|
+
debug_output("starting TLS for #{endpoint.host}:#{endpoint.port} ...")
|
84
|
+
@socket.connect
|
85
|
+
debug_output("TLS established")
|
86
|
+
_register_h2_callbacks
|
87
|
+
@status = :active
|
88
|
+
elsif @status == :closed
|
89
|
+
msg = "Async Client HTTP2 Connection is closed, you may"\
|
90
|
+
" use #new_connection to create a new HTTP2 Connection for this client"
|
91
|
+
raise Http2ConnectionClosedError.new(msg)
|
92
|
+
end
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
def start(stream)
|
97
|
+
@mutex.synchronize {
|
98
|
+
return if @socket_thread
|
99
|
+
@socket_thread = Thread.new do
|
100
|
+
while !@socket.closed?
|
101
|
+
begin
|
102
|
+
data = @socket.read_nonblock(@chunk_size)
|
103
|
+
@h2_client << data
|
104
|
+
rescue IO::WaitReadable
|
105
|
+
begin
|
106
|
+
unless IO.select([@socket], nil, nil, connection_read_timeout)
|
107
|
+
self.debug_output("socket connection read time out")
|
108
|
+
self.close!
|
109
|
+
else
|
110
|
+
# available, retry to start reading
|
111
|
+
retry
|
112
|
+
end
|
113
|
+
rescue
|
114
|
+
# error can happen when closing the socket
|
115
|
+
# while it's waiting for read
|
116
|
+
self.close!
|
117
|
+
end
|
118
|
+
rescue EOFError
|
119
|
+
self.close!
|
120
|
+
rescue => error
|
121
|
+
self.debug_output(error.inspect)
|
122
|
+
@errors << error
|
123
|
+
self.close!
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
@socket_thread.abort_on_exception = true
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
131
|
+
def close!
|
132
|
+
@mutex.synchronize {
|
133
|
+
self.debug_output("closing connection ...")
|
134
|
+
if @socket
|
135
|
+
@socket.close
|
136
|
+
@socket = nil
|
137
|
+
end
|
138
|
+
if @socket_thread
|
139
|
+
Thread.kill(@socket_thread)
|
140
|
+
@socket_thread = nil
|
141
|
+
end
|
142
|
+
@status = :closed
|
143
|
+
}
|
144
|
+
end
|
145
|
+
|
146
|
+
def closed?
|
147
|
+
@status == :closed
|
148
|
+
end
|
149
|
+
|
150
|
+
def debug_output(msg, type = nil)
|
151
|
+
prefix = case type
|
152
|
+
when :send then "-> "
|
153
|
+
when :receive then "<- "
|
154
|
+
else
|
155
|
+
""
|
156
|
+
end
|
157
|
+
return unless @logger
|
158
|
+
_debug_entry(prefix + msg)
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
def _debug_entry(str)
|
164
|
+
@logger << str
|
165
|
+
@logger << "\n"
|
166
|
+
end
|
167
|
+
|
168
|
+
def _register_h2_callbacks
|
169
|
+
@h2_client.on(:frame) do |bytes|
|
170
|
+
if @socket.nil?
|
171
|
+
msg = "Connection is closed due to errors, "\
|
172
|
+
"you can find errors at async_client.connection.errors"
|
173
|
+
raise Http2ConnectionClosedError.new(msg)
|
174
|
+
else
|
175
|
+
@socket.print(bytes)
|
176
|
+
@socket.flush
|
177
|
+
end
|
178
|
+
end
|
179
|
+
@h2_client.on(:frame_sent) do |frame|
|
180
|
+
debug_output("frame: #{frame.inspect}", :send)
|
181
|
+
end
|
182
|
+
@h2_client.on(:frame_received) do |frame|
|
183
|
+
debug_output("frame: #{frame.inspect}", :receive)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def _tcp_socket(endpoint)
|
188
|
+
tcp = ::Socket.new(SOCKET_FAMILY, ::Socket::SOCK_STREAM, 0)
|
189
|
+
tcp.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
190
|
+
|
191
|
+
address = ::Socket.getaddrinfo(endpoint.host, nil, SOCKET_FAMILY).first[3]
|
192
|
+
sockaddr = ::Socket.sockaddr_in(endpoint.port, address)
|
193
|
+
|
194
|
+
[tcp, sockaddr]
|
195
|
+
end
|
196
|
+
|
197
|
+
def _nonblocking_connect(tcp, addr)
|
198
|
+
begin
|
199
|
+
tcp.connect_nonblock(addr)
|
200
|
+
rescue IO::WaitWritable
|
201
|
+
unless IO.select(nil, [tcp], nil, connection_timeout)
|
202
|
+
tcp.close
|
203
|
+
raise
|
204
|
+
end
|
205
|
+
begin
|
206
|
+
tcp.connect_nonblock(addr)
|
207
|
+
rescue Errno::EISCONN
|
208
|
+
# tcp socket connected, continue
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def _tls_context
|
214
|
+
ssl_ctx = OpenSSL::SSL::SSLContext.new(:TLSv1_2)
|
215
|
+
if ssl_verify_peer?
|
216
|
+
ssl_ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
217
|
+
ssl_ctx.ca_file = ssl_ca_bundle ? ssl_ca_bundle : _default_ca_bundle
|
218
|
+
ssl_ctx.ca_path = ssl_ca_directory ? ssl_ca_directory : _default_ca_directory
|
219
|
+
ssl_ctx.cert_store = ssl_ca_store if ssl_ca_store
|
220
|
+
else
|
221
|
+
ssl_ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
222
|
+
end
|
223
|
+
if enable_alpn
|
224
|
+
debug_output("enabling ALPN for TLS ...")
|
225
|
+
ssl_ctx.alpn_protocols = ['h2']
|
226
|
+
end
|
227
|
+
ssl_ctx
|
228
|
+
end
|
229
|
+
|
230
|
+
def _default_ca_bundle
|
231
|
+
File.exist?(OpenSSL::X509::DEFAULT_CERT_FILE) ?
|
232
|
+
OpenSSL::X509::DEFAULT_CERT_FILE : nil
|
233
|
+
end
|
234
|
+
|
235
|
+
def _default_ca_directory
|
236
|
+
Dir.exist?(OpenSSL::X509::DEFAULT_CERT_DIR) ?
|
237
|
+
OpenSSL::X509::DEFAULT_CERT_DIR : nil
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
@@ -0,0 +1,151 @@
|
|
1
|
+
if RUBY_VERSION >= '2.1'
|
2
|
+
begin
|
3
|
+
require 'http/2'
|
4
|
+
rescue LoadError; end
|
5
|
+
end
|
6
|
+
require 'securerandom'
|
7
|
+
|
8
|
+
module Seahorse
|
9
|
+
module Client
|
10
|
+
# @api private
|
11
|
+
module H2
|
12
|
+
|
13
|
+
NETWORK_ERRORS = [
|
14
|
+
SocketError, EOFError, IOError, Timeout::Error,
|
15
|
+
Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE,
|
16
|
+
Errno::EINVAL, Errno::ETIMEDOUT, OpenSSL::SSL::SSLError,
|
17
|
+
Errno::EHOSTUNREACH, Errno::ECONNREFUSED,# OpenSSL::SSL::SSLErrorWaitReadable
|
18
|
+
]
|
19
|
+
|
20
|
+
# @api private
|
21
|
+
DNS_ERROR_MESSAGES = [
|
22
|
+
'getaddrinfo: nodename nor servname provided, or not known', # MacOS
|
23
|
+
'getaddrinfo: Name or service not known' # GNU
|
24
|
+
]
|
25
|
+
|
26
|
+
class Handler < Client::Handler
|
27
|
+
|
28
|
+
def call(context)
|
29
|
+
stream = nil
|
30
|
+
begin
|
31
|
+
conn = context.client.connection
|
32
|
+
stream = conn.new_stream
|
33
|
+
|
34
|
+
stream_mutex = Mutex.new
|
35
|
+
close_condition = ConditionVariable.new
|
36
|
+
sync_queue = Queue.new
|
37
|
+
|
38
|
+
conn.connect(context.http_request.endpoint)
|
39
|
+
_register_callbacks(
|
40
|
+
context.http_response,
|
41
|
+
stream,
|
42
|
+
stream_mutex,
|
43
|
+
close_condition,
|
44
|
+
sync_queue
|
45
|
+
)
|
46
|
+
|
47
|
+
conn.debug_output("sending initial request ...")
|
48
|
+
if input_emitter = context[:input_event_emitter]
|
49
|
+
_send_initial_headers(context.http_request, stream)
|
50
|
+
|
51
|
+
# prepare for sending events later
|
52
|
+
input_emitter.stream = stream
|
53
|
+
# request sigv4 serves as the initial #prior_signature
|
54
|
+
input_emitter.encoder.prior_signature =
|
55
|
+
context.http_request.headers['authorization'].split('Signature=').last
|
56
|
+
input_emitter.validate_event = context.config.validate_params
|
57
|
+
else
|
58
|
+
_send_initial_headers(context.http_request, stream)
|
59
|
+
_send_initial_data(context.http_request, stream)
|
60
|
+
end
|
61
|
+
|
62
|
+
conn.start(stream)
|
63
|
+
rescue *NETWORK_ERRORS => error
|
64
|
+
error = NetworkingError.new(
|
65
|
+
error, error_message(context.http_request, error))
|
66
|
+
context.http_response.signal_error(error)
|
67
|
+
rescue => error
|
68
|
+
conn.debug_output(error.inspect)
|
69
|
+
# not retryable
|
70
|
+
context.http_response.signal_error(error)
|
71
|
+
end
|
72
|
+
|
73
|
+
AsyncResponse.new(
|
74
|
+
context: context,
|
75
|
+
stream: stream,
|
76
|
+
stream_mutex: stream_mutex,
|
77
|
+
close_condition: close_condition,
|
78
|
+
sync_queue: sync_queue
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def _register_callbacks(resp, stream, stream_mutex, close_condition, sync_queue)
|
85
|
+
stream.on(:headers) do |headers|
|
86
|
+
resp.signal_headers(headers)
|
87
|
+
end
|
88
|
+
|
89
|
+
stream.on(:data) do |data|
|
90
|
+
resp.signal_data(data)
|
91
|
+
end
|
92
|
+
|
93
|
+
stream.on(:close) do
|
94
|
+
resp.signal_done
|
95
|
+
# block until #wait is ready for signal
|
96
|
+
# else deadlock may happen because #signal happened
|
97
|
+
# eariler than #wait (see AsyncResponse#wait)
|
98
|
+
sync_queue.pop
|
99
|
+
stream_mutex.synchronize {
|
100
|
+
close_condition.signal
|
101
|
+
}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def _send_initial_headers(req, stream)
|
106
|
+
begin
|
107
|
+
headers = _h2_headers(req)
|
108
|
+
stream.headers(headers, end_stream: false)
|
109
|
+
rescue => e
|
110
|
+
raise Http2InitialRequestError.new(e)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def _send_initial_data(req, stream)
|
115
|
+
begin
|
116
|
+
data = req.body.read
|
117
|
+
stream.data(data, end_stream: true)
|
118
|
+
rescue => e
|
119
|
+
raise Http2InitialRequestError.new(e)
|
120
|
+
end
|
121
|
+
data
|
122
|
+
end
|
123
|
+
|
124
|
+
# H2 pseudo headers
|
125
|
+
# https://http2.github.io/http2-spec/#rfc.section.8.1.2.3
|
126
|
+
def _h2_headers(req)
|
127
|
+
headers = {}
|
128
|
+
headers[':method'] = req.http_method.upcase
|
129
|
+
headers[':scheme'] = req.endpoint.scheme
|
130
|
+
headers[':path'] = req.endpoint.path.empty? ? '/' : req.endpoint.path
|
131
|
+
if req.endpoint.query && !req.endpoint.query.empty?
|
132
|
+
headers[':path'] += "?#{req.endpoint.query}"
|
133
|
+
end
|
134
|
+
req.headers.each {|k, v| headers[k.downcase] = v }
|
135
|
+
headers
|
136
|
+
end
|
137
|
+
|
138
|
+
def error_message(req, error)
|
139
|
+
if error.is_a?(SocketError) && DNS_ERROR_MESSAGES.include?(error.message)
|
140
|
+
host = req.endpoint.host
|
141
|
+
"unable to connect to `#{host}`; SocketError: #{error.message}"
|
142
|
+
else
|
143
|
+
error.message
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Seahorse
|
2
|
+
module Client
|
3
|
+
module Http
|
4
|
+
class AsyncResponse < Seahorse::Client::Http::Response
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def signal_headers(headers)
|
11
|
+
# H2 headers arrive as array of pair
|
12
|
+
hash = headers.inject({}) do |h, pair|
|
13
|
+
key, value = pair
|
14
|
+
h[key] = value
|
15
|
+
h
|
16
|
+
end
|
17
|
+
@status_code = hash[":status"].to_i
|
18
|
+
@headers = Headers.new(hash)
|
19
|
+
emit(:headers, @status_code, @headers)
|
20
|
+
end
|
21
|
+
|
22
|
+
def signal_done(options = {})
|
23
|
+
# H2 only has header and body
|
24
|
+
# ':status' header will be sent back
|
25
|
+
if options.keys.sort == [:body, :headers]
|
26
|
+
signal_headers(options[:headers])
|
27
|
+
signal_data(options[:body])
|
28
|
+
signal_done
|
29
|
+
elsif options.empty?
|
30
|
+
@body.rewind if @body.respond_to?(:rewind)
|
31
|
+
@done = true
|
32
|
+
emit(:done)
|
33
|
+
else
|
34
|
+
msg = "options must be empty or must contain :headers and :body"
|
35
|
+
raise ArgumentError, msg
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -40,12 +40,17 @@ module Seahorse
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
# @return [String]
|
43
|
+
# @return [String|Array]
|
44
44
|
def body_contents
|
45
|
-
body.
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
if body.is_a?(Array)
|
46
|
+
# an array of parsed events
|
47
|
+
body
|
48
|
+
else
|
49
|
+
body.rewind
|
50
|
+
contents = body.read
|
51
|
+
body.rewind
|
52
|
+
contents
|
53
|
+
end
|
49
54
|
end
|
50
55
|
|
51
56
|
# @param [Integer] status_code
|
@@ -117,15 +122,15 @@ module Seahorse
|
|
117
122
|
end
|
118
123
|
|
119
124
|
def on_headers(status_code_range = nil, &block)
|
120
|
-
@listeners[:headers] << listener(status_code_range,
|
125
|
+
@listeners[:headers] << listener(status_code_range, block)
|
121
126
|
end
|
122
127
|
|
123
128
|
def on_data(&callback)
|
124
|
-
@listeners[:data] <<
|
129
|
+
@listeners[:data] << callback
|
125
130
|
end
|
126
131
|
|
127
132
|
def on_done(status_code_range = nil, &callback)
|
128
|
-
listener = listener(status_code_range,
|
133
|
+
listener = listener(status_code_range, callback)
|
129
134
|
if @done
|
130
135
|
listener.call
|
131
136
|
else
|
@@ -10,6 +10,12 @@ module Seahorse
|
|
10
10
|
|
11
11
|
def self.apply!
|
12
12
|
return unless RUBY_VERSION < '2.5'
|
13
|
+
if RUBY_VERSION >= '2.3'
|
14
|
+
Net::HTTP::IDEMPOTENT_METHODS_.clear
|
15
|
+
return
|
16
|
+
end
|
17
|
+
# no further patches needed for above versions
|
18
|
+
|
13
19
|
if RUBY_VERSION >= '2.0'
|
14
20
|
Net::HTTP.send(:include, Ruby_2)
|
15
21
|
Net::HTTP::IDEMPOTENT_METHODS_.clear
|
@@ -30,7 +36,7 @@ module Seahorse
|
|
30
36
|
begin
|
31
37
|
res = Net::HTTPResponse.read_new(@socket)
|
32
38
|
res.decode_content = req.decode_content
|
33
|
-
end while res.kind_of?(Net::
|
39
|
+
end while res.kind_of?(Net::HTTPInformation)
|
34
40
|
|
35
41
|
res.uri = req.uri
|
36
42
|
|
@@ -11,5 +11,33 @@ module Seahorse
|
|
11
11
|
attr_reader :original_error
|
12
12
|
|
13
13
|
end
|
14
|
+
|
15
|
+
# Raised when sending initial headers and data failed
|
16
|
+
# for event stream requests over Http2
|
17
|
+
class Http2InitialRequestError < StandardError
|
18
|
+
|
19
|
+
def initialize(error)
|
20
|
+
@original_error = error
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [HTTP2::Error]
|
24
|
+
attr_reader :original_error
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
# Raised when connection failed to initialize a new stream
|
29
|
+
class Http2StreamInitializeError < StandardError
|
30
|
+
|
31
|
+
def initialize(error)
|
32
|
+
@original_error = error
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [HTTP2::Error]
|
36
|
+
attr_reader :original_error
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
# Rasied when trying to use an closed connection
|
41
|
+
class Http2ConnectionClosedError < StandardError; end
|
14
42
|
end
|
15
43
|
end
|