ld-eventsource 2.3.0 → 2.5.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 +81 -5
- data/lib/ld-eventsource/errors.rb +18 -2
- data/lib/ld-eventsource/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3ba844140cea3837caa7a02aa4676af7483faa11230a353da421e435932553b3
|
|
4
|
+
data.tar.gz: 30619609829908038a63d8302cefdbe8bda6935dc691ceaaabff225f2747da8c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6b0ffc7be77854b053bc561d1389c3cb6327290066697ca40e6d7a96e29ad9192013b733e9b2973d0b1ebcb5aee66e0eb530066f6bbc3762f95fa80d28183b33
|
|
7
|
+
data.tar.gz: 7efe744ebf4c9cb9b4302fbbf5d2348a31a3f99f45de8cbd54346d2ac5eed8de8c8e79d5120888a2103d712afaf71035ebeec712664a797c229694a561b7dbe9
|
|
@@ -162,8 +162,9 @@ module SSE
|
|
|
162
162
|
reconnect_reset_interval: reconnect_reset_interval)
|
|
163
163
|
@first_attempt = true
|
|
164
164
|
|
|
165
|
-
@on = { event: ->(_) {}, error: ->(_) {} }
|
|
165
|
+
@on = { event: ->(_) {}, error: ->(_) {}, connect: ->(_) {} }
|
|
166
166
|
@last_id = last_event_id
|
|
167
|
+
@query_params_callback = nil
|
|
167
168
|
|
|
168
169
|
yield self if block_given?
|
|
169
170
|
|
|
@@ -206,6 +207,56 @@ module SSE
|
|
|
206
207
|
@on[:error] = action
|
|
207
208
|
end
|
|
208
209
|
|
|
210
|
+
#
|
|
211
|
+
# Specifies a block or Proc to be called when a successful connection is established. This will
|
|
212
|
+
# be called with a single parameter containing the HTTP response headers. It is called
|
|
213
|
+
# from the same worker thread that reads the stream, so no more events will be dispatched until
|
|
214
|
+
# it returns.
|
|
215
|
+
#
|
|
216
|
+
# This is called every time a connection is successfully established, including on reconnections
|
|
217
|
+
# after a failure. It allows you to inspect server response headers such as rate limits, custom
|
|
218
|
+
# metadata, or fallback directives (e.g., `X-LD-FD-FALLBACK`).
|
|
219
|
+
#
|
|
220
|
+
# Any previously specified connect handler will be replaced.
|
|
221
|
+
#
|
|
222
|
+
# @yieldparam headers [Hash, nil] the HTTP response headers from the successful connection,
|
|
223
|
+
# or nil if not available. The headers object uses case-insensitive keys (via the http gem's
|
|
224
|
+
# HTTP::Headers).
|
|
225
|
+
#
|
|
226
|
+
def on_connect(&action)
|
|
227
|
+
@on[:connect] = action
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
#
|
|
231
|
+
# Specifies a block or Proc to generate query parameters dynamically. This will be called before
|
|
232
|
+
# each connection attempt (both initial connection and reconnections), allowing you to update
|
|
233
|
+
# query parameters based on the current client state.
|
|
234
|
+
#
|
|
235
|
+
# The block should return a Hash with string keys and string values, which will be merged with
|
|
236
|
+
# any existing query parameters in the base URI. If the callback raises an exception, it will be
|
|
237
|
+
# logged and the connection will proceed with the base URI's query parameters (or no query
|
|
238
|
+
# parameters if none were present).
|
|
239
|
+
#
|
|
240
|
+
# This is useful for scenarios where query parameters need to reflect the current state of the
|
|
241
|
+
# client, such as sending a "basis" parameter that represents what data the client already has.
|
|
242
|
+
#
|
|
243
|
+
# @example Using dynamic query parameters
|
|
244
|
+
# client = SSE::Client.new(base_uri) do |c|
|
|
245
|
+
# c.query_params do
|
|
246
|
+
# {
|
|
247
|
+
# "basis" => (selector.state if selector.defined?),
|
|
248
|
+
# "filter" => filter_key
|
|
249
|
+
# }.compact
|
|
250
|
+
# end
|
|
251
|
+
# c.on_event { |event| handle_event(event) }
|
|
252
|
+
# end
|
|
253
|
+
#
|
|
254
|
+
# @yieldreturn [Hash<String, String>] a hash of query parameter names to values
|
|
255
|
+
#
|
|
256
|
+
def query_params(&action)
|
|
257
|
+
@query_params_callback = action
|
|
258
|
+
end
|
|
259
|
+
|
|
209
260
|
#
|
|
210
261
|
# Permanently shuts down the client and its connection. No further events will be dispatched. This
|
|
211
262
|
# has no effect if called a second time.
|
|
@@ -289,15 +340,18 @@ module SSE
|
|
|
289
340
|
end
|
|
290
341
|
cxn = nil
|
|
291
342
|
begin
|
|
292
|
-
|
|
293
|
-
|
|
343
|
+
uri = build_uri_with_query_params
|
|
344
|
+
@logger.info { "Connecting to event stream at #{uri}" }
|
|
345
|
+
cxn = @http_client.request(@method, uri, build_opts)
|
|
346
|
+
headers = cxn.headers
|
|
294
347
|
if cxn.status.code == 200
|
|
295
348
|
content_type = cxn.content_type.mime_type
|
|
296
349
|
if content_type && content_type.start_with?("text/event-stream")
|
|
350
|
+
@on[:connect].call(headers) # Notify connect callback with headers
|
|
297
351
|
return cxn # we're good to proceed
|
|
298
352
|
else
|
|
299
353
|
reset_http
|
|
300
|
-
err = Errors::HTTPContentTypeError.new(content_type)
|
|
354
|
+
err = Errors::HTTPContentTypeError.new(content_type, headers)
|
|
301
355
|
@on[:error].call(err)
|
|
302
356
|
@logger.warn { "Event source returned unexpected content type '#{content_type}'" }
|
|
303
357
|
end
|
|
@@ -305,7 +359,7 @@ module SSE
|
|
|
305
359
|
body = cxn.to_s # grab the whole response body in case it has error details
|
|
306
360
|
reset_http
|
|
307
361
|
@logger.info { "Server returned error status #{cxn.status.code}" }
|
|
308
|
-
err = Errors::HTTPStatusError.new(cxn.status.code, body)
|
|
362
|
+
err = Errors::HTTPStatusError.new(cxn.status.code, body, headers)
|
|
309
363
|
@on[:error].call(err)
|
|
310
364
|
end
|
|
311
365
|
rescue
|
|
@@ -397,5 +451,27 @@ module SSE
|
|
|
397
451
|
{headers: build_headers, body: resolved_payload.to_s}
|
|
398
452
|
end
|
|
399
453
|
end
|
|
454
|
+
|
|
455
|
+
def build_uri_with_query_params
|
|
456
|
+
uri = @uri.dup
|
|
457
|
+
|
|
458
|
+
if @query_params_callback
|
|
459
|
+
begin
|
|
460
|
+
dynamic_params = @query_params_callback.call
|
|
461
|
+
if dynamic_params.is_a?(Hash) && !dynamic_params.empty?
|
|
462
|
+
existing_params = uri.query ? URI.decode_www_form(uri.query).to_h : {}
|
|
463
|
+
merged_params = existing_params.merge(dynamic_params)
|
|
464
|
+
uri.query = URI.encode_www_form(merged_params)
|
|
465
|
+
elsif !dynamic_params.is_a?(Hash)
|
|
466
|
+
@logger.warn { "query_params callback returned non-Hash value: #{dynamic_params.class}, ignoring" }
|
|
467
|
+
end
|
|
468
|
+
rescue StandardError => e
|
|
469
|
+
@logger.warn { "query_params callback raised an exception: #{e.inspect}, proceeding with base URI" }
|
|
470
|
+
@logger.debug { "Exception trace: #{e.backtrace}" }
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
uri
|
|
475
|
+
end
|
|
400
476
|
end
|
|
401
477
|
end
|
|
@@ -9,9 +9,10 @@ module SSE
|
|
|
9
9
|
# handler specified in {Client#on_error}.
|
|
10
10
|
#
|
|
11
11
|
class HTTPStatusError < StandardError
|
|
12
|
-
def initialize(status, message)
|
|
12
|
+
def initialize(status, message, headers = nil)
|
|
13
13
|
@status = status
|
|
14
14
|
@message = message
|
|
15
|
+
@headers = headers
|
|
15
16
|
super("HTTP error #{status}")
|
|
16
17
|
end
|
|
17
18
|
|
|
@@ -22,6 +23,13 @@ module SSE
|
|
|
22
23
|
# The response body, if any.
|
|
23
24
|
# @return [String]
|
|
24
25
|
attr_reader :message
|
|
26
|
+
|
|
27
|
+
# The HTTP response headers, if any.
|
|
28
|
+
#
|
|
29
|
+
# The headers object uses case-insensitive keys (via the http gem's HTTP::Headers).
|
|
30
|
+
#
|
|
31
|
+
# @return [Hash, nil] the response headers, or nil if not available
|
|
32
|
+
attr_reader :headers
|
|
25
33
|
end
|
|
26
34
|
|
|
27
35
|
#
|
|
@@ -29,14 +37,22 @@ module SSE
|
|
|
29
37
|
# handler specified in {Client#on_error}.
|
|
30
38
|
#
|
|
31
39
|
class HTTPContentTypeError < StandardError
|
|
32
|
-
def initialize(type)
|
|
40
|
+
def initialize(type, headers = nil)
|
|
33
41
|
@content_type = type
|
|
42
|
+
@headers = headers
|
|
34
43
|
super("invalid content type \"#{type}\"")
|
|
35
44
|
end
|
|
36
45
|
|
|
37
46
|
# The HTTP content type.
|
|
38
47
|
# @return [String]
|
|
39
48
|
attr_reader :type
|
|
49
|
+
|
|
50
|
+
# The HTTP response headers, if any.
|
|
51
|
+
#
|
|
52
|
+
# The headers object uses case-insensitive keys (via the http gem's HTTP::Headers).
|
|
53
|
+
#
|
|
54
|
+
# @return [Hash, nil] the response headers, or nil if not available
|
|
55
|
+
attr_reader :headers
|
|
40
56
|
end
|
|
41
57
|
|
|
42
58
|
#
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ld-eventsource
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- LaunchDarkly
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: logger
|