ld-eventsource 2.5.1 → 3.0.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.
- checksums.yaml +4 -4
- data/lib/ld-eventsource/client.rb +48 -24
- data/lib/ld-eventsource/errors.rb +10 -0
- data/lib/ld-eventsource/version.rb +1 -1
- metadata +18 -18
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dcf2b9358b588b57f4bba285ed1a91bd399fe04c74622a0d944fd3fa0c56e9bb
|
|
4
|
+
data.tar.gz: 22514811554b69e1225aebebb18bb531b65daf27c8f0d2284717791f21aeb771
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b20450c7187809e03706778149944c6268782c0b307b58874f569af2c6eb635ec125c65ed2d1c3077438f63a95e1dc86411743deb432f7da71aa4945bbfc12c1
|
|
7
|
+
data.tar.gz: 06dc4097880991e04e3ad8a8c3c3f0d01488849d84f0cc98444cbb8cb558b7a9949fc56169e3b18e569b2fe18b1900cd25404bf882f114c9816f0a715b1f9480
|
|
@@ -52,6 +52,15 @@ module SSE
|
|
|
52
52
|
# The default HTTP method for requests.
|
|
53
53
|
DEFAULT_HTTP_METHOD = "GET"
|
|
54
54
|
|
|
55
|
+
# TODO(breaking): Remove this filtering once we have updated to the next major breaking version.
|
|
56
|
+
# HTTP v6 requires keyword arguments instead of an options hash, so we filter to only known valid
|
|
57
|
+
# arguments to avoid passing unsupported options.
|
|
58
|
+
VALID_HTTP_CLIENT_OPTIONS = %i[
|
|
59
|
+
base_uri body encoding features follow form headers json keep_alive_timeout
|
|
60
|
+
nodelay params persistent proxy response retriable socket_class ssl_context
|
|
61
|
+
ssl ssl_socket_class timeout_class timeout_options
|
|
62
|
+
].freeze
|
|
63
|
+
|
|
55
64
|
#
|
|
56
65
|
# Creates a new SSE client.
|
|
57
66
|
#
|
|
@@ -114,19 +123,19 @@ module SSE
|
|
|
114
123
|
retry_enabled: true,
|
|
115
124
|
http_client_options: nil)
|
|
116
125
|
@uri = URI(uri)
|
|
117
|
-
@
|
|
126
|
+
@stop_event = Concurrent::Event.new
|
|
118
127
|
@retry_enabled = retry_enabled
|
|
119
128
|
|
|
120
129
|
@headers = headers.clone
|
|
121
|
-
@connect_timeout = connect_timeout
|
|
122
|
-
@read_timeout = read_timeout
|
|
130
|
+
@connect_timeout = connect_timeout&.to_f
|
|
131
|
+
@read_timeout = read_timeout&.to_f
|
|
123
132
|
@method = method.to_s.upcase
|
|
124
133
|
@payload = payload
|
|
125
134
|
@logger = logger || default_logger
|
|
126
135
|
|
|
127
136
|
base_http_client_options = {}
|
|
128
137
|
if socket_factory
|
|
129
|
-
base_http_client_options[
|
|
138
|
+
base_http_client_options[:socket_class] = socket_factory
|
|
130
139
|
end
|
|
131
140
|
|
|
132
141
|
if proxy
|
|
@@ -139,22 +148,29 @@ module SSE
|
|
|
139
148
|
end
|
|
140
149
|
|
|
141
150
|
if @proxy
|
|
142
|
-
base_http_client_options[
|
|
151
|
+
base_http_client_options[:proxy] = {
|
|
143
152
|
:proxy_address => @proxy.host,
|
|
144
153
|
:proxy_port => @proxy.port,
|
|
145
154
|
}
|
|
146
|
-
base_http_client_options[
|
|
147
|
-
base_http_client_options[
|
|
155
|
+
base_http_client_options[:proxy][:proxy_username] = @proxy.user unless @proxy.user.nil?
|
|
156
|
+
base_http_client_options[:proxy][:proxy_password] = @proxy.password unless @proxy.password.nil?
|
|
148
157
|
end
|
|
149
158
|
|
|
150
159
|
options = http_client_options.is_a?(Hash) ? base_http_client_options.merge(http_client_options) : base_http_client_options
|
|
160
|
+
options = options.transform_keys(&:to_sym)
|
|
161
|
+
options = options.select do |key, _|
|
|
162
|
+
included = VALID_HTTP_CLIENT_OPTIONS.include?(key)
|
|
163
|
+
@logger.warn { "Ignoring unsupported HTTP client option: #{key}" } unless included
|
|
164
|
+
included
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
timeout_options = {}
|
|
168
|
+
timeout_options[:connect] = @connect_timeout if @connect_timeout
|
|
169
|
+
timeout_options[:read] = @read_timeout if @read_timeout
|
|
151
170
|
|
|
152
|
-
@http_client = HTTP::Client.new(options)
|
|
171
|
+
@http_client = HTTP::Client.new(**options)
|
|
153
172
|
.follow
|
|
154
|
-
|
|
155
|
-
read: read_timeout,
|
|
156
|
-
connect: connect_timeout,
|
|
157
|
-
})
|
|
173
|
+
@http_client = @http_client.timeout(timeout_options) unless timeout_options.empty?
|
|
158
174
|
@cxn = nil
|
|
159
175
|
@lock = Mutex.new
|
|
160
176
|
|
|
@@ -262,7 +278,7 @@ module SSE
|
|
|
262
278
|
# has no effect if called a second time.
|
|
263
279
|
#
|
|
264
280
|
def close
|
|
265
|
-
if @
|
|
281
|
+
if @stop_event.try?
|
|
266
282
|
reset_http
|
|
267
283
|
end
|
|
268
284
|
end
|
|
@@ -273,7 +289,7 @@ module SSE
|
|
|
273
289
|
# @return [Boolean] true if the client has been shut down
|
|
274
290
|
#
|
|
275
291
|
def closed?
|
|
276
|
-
@
|
|
292
|
+
@stop_event.set?
|
|
277
293
|
end
|
|
278
294
|
|
|
279
295
|
private
|
|
@@ -298,7 +314,7 @@ module SSE
|
|
|
298
314
|
end
|
|
299
315
|
|
|
300
316
|
def run_stream
|
|
301
|
-
until @
|
|
317
|
+
until @stop_event.set?
|
|
302
318
|
close_connection
|
|
303
319
|
begin
|
|
304
320
|
resp = connect
|
|
@@ -307,12 +323,16 @@ module SSE
|
|
|
307
323
|
end
|
|
308
324
|
# There's a potential race if close was called in the middle of the previous line, i.e. after we
|
|
309
325
|
# connected but before @cxn was set. Checking the variable again is a bit clunky but avoids that.
|
|
310
|
-
|
|
326
|
+
if @stop_event.set?
|
|
327
|
+
# close did not see this connection, so close it here.
|
|
328
|
+
reset_http
|
|
329
|
+
return
|
|
330
|
+
end
|
|
311
331
|
read_stream(resp) unless resp.nil?
|
|
312
332
|
rescue => e
|
|
313
333
|
# When we deliberately close the connection, it will usually trigger an exception. The exact type
|
|
314
|
-
# of exception depends on the specific Ruby runtime. But
|
|
315
|
-
if @
|
|
334
|
+
# of exception depends on the specific Ruby runtime. But the client will always be closed in this case.
|
|
335
|
+
if @stop_event.set?
|
|
316
336
|
@logger.info { "Stream connection closed" }
|
|
317
337
|
else
|
|
318
338
|
log_and_dispatch_error(e, "Unexpected error from event source")
|
|
@@ -331,18 +351,20 @@ module SSE
|
|
|
331
351
|
# Try to establish a streaming connection. Returns the StreamingHTTPConnection object if successful.
|
|
332
352
|
def connect
|
|
333
353
|
loop do
|
|
334
|
-
return if @
|
|
354
|
+
return if @stop_event.set?
|
|
335
355
|
interval = @first_attempt ? 0 : @backoff.next_interval
|
|
336
356
|
@first_attempt = false
|
|
337
357
|
if interval > 0
|
|
338
358
|
@logger.info { "Will retry connection after #{'%.3f' % interval} seconds" }
|
|
339
|
-
|
|
359
|
+
# This wait ends early when close sets the event.
|
|
360
|
+
@stop_event.wait(interval)
|
|
361
|
+
return if @stop_event.set?
|
|
340
362
|
end
|
|
341
363
|
cxn = nil
|
|
342
364
|
begin
|
|
343
365
|
uri = build_uri_with_query_params
|
|
344
366
|
@logger.info { "Connecting to event stream at #{uri}" }
|
|
345
|
-
cxn = @http_client.request(@method, uri, build_opts)
|
|
367
|
+
cxn = @http_client.request(@method, uri, **build_opts)
|
|
346
368
|
headers = cxn.headers
|
|
347
369
|
if cxn.status.code == 200
|
|
348
370
|
content_type = cxn.content_type.mime_type
|
|
@@ -379,7 +401,7 @@ module SSE
|
|
|
379
401
|
|
|
380
402
|
chunks = Enumerator.new do |gen|
|
|
381
403
|
loop do
|
|
382
|
-
if @
|
|
404
|
+
if @stop_event.set?
|
|
383
405
|
break
|
|
384
406
|
else
|
|
385
407
|
begin
|
|
@@ -390,8 +412,10 @@ module SSE
|
|
|
390
412
|
rescue HTTP::TimeoutError
|
|
391
413
|
# For historical reasons, we rethrow this as our own type
|
|
392
414
|
raise Errors::ReadTimeoutError.new(@read_timeout)
|
|
415
|
+
rescue EOFError
|
|
416
|
+
raise Errors::StreamClosedByServerError
|
|
393
417
|
end
|
|
394
|
-
|
|
418
|
+
raise Errors::StreamClosedByServerError if data.nil? # keep for v5 compat
|
|
395
419
|
gen.yield data
|
|
396
420
|
end
|
|
397
421
|
end
|
|
@@ -399,7 +423,7 @@ module SSE
|
|
|
399
423
|
event_parser = Impl::EventParser.new(Impl::BufferedLineReader.lines_from(chunks), @last_id)
|
|
400
424
|
|
|
401
425
|
event_parser.items.each do |item|
|
|
402
|
-
return if @
|
|
426
|
+
return if @stop_event.set?
|
|
403
427
|
case item
|
|
404
428
|
when StreamEvent
|
|
405
429
|
dispatch_event(item)
|
|
@@ -79,5 +79,15 @@ module SSE
|
|
|
79
79
|
super("no data received in #{interval} seconds")
|
|
80
80
|
end
|
|
81
81
|
end
|
|
82
|
+
|
|
83
|
+
#
|
|
84
|
+
# An exception class indicating that the server ended the stream without an error. This is
|
|
85
|
+
# passed to the error handler specified in {Client#on_error}, and the client then reconnects.
|
|
86
|
+
#
|
|
87
|
+
class StreamClosedByServerError < StandardError
|
|
88
|
+
def initialize
|
|
89
|
+
super("stream closed by server")
|
|
90
|
+
end
|
|
91
|
+
end
|
|
82
92
|
end
|
|
83
93
|
end
|
metadata
CHANGED
|
@@ -1,29 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ld-eventsource
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- LaunchDarkly
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
-
- !ruby/object:Gem::Dependency
|
|
14
|
-
name: logger
|
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
|
16
|
-
requirements:
|
|
17
|
-
- - "~>"
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '1.5'
|
|
20
|
-
type: :development
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - "~>"
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '1.5'
|
|
27
13
|
- !ruby/object:Gem::Dependency
|
|
28
14
|
name: rspec
|
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -117,7 +103,7 @@ dependencies:
|
|
|
117
103
|
version: 4.4.1
|
|
118
104
|
- - "<"
|
|
119
105
|
- !ruby/object:Gem::Version
|
|
120
|
-
version:
|
|
106
|
+
version: 7.0.0
|
|
121
107
|
type: :runtime
|
|
122
108
|
prerelease: false
|
|
123
109
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -127,7 +113,21 @@ dependencies:
|
|
|
127
113
|
version: 4.4.1
|
|
128
114
|
- - "<"
|
|
129
115
|
- !ruby/object:Gem::Version
|
|
130
|
-
version:
|
|
116
|
+
version: 7.0.0
|
|
117
|
+
- !ruby/object:Gem::Dependency
|
|
118
|
+
name: logger
|
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - "~>"
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '1.5'
|
|
124
|
+
type: :runtime
|
|
125
|
+
prerelease: false
|
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - "~>"
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '1.5'
|
|
131
131
|
description: LaunchDarkly SSE client for Ruby
|
|
132
132
|
email:
|
|
133
133
|
- team@launchdarkly.com
|