launchdarkly-server-sdk 8.11.1 → 8.11.3

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ldclient-rb/config.rb +66 -3
  3. data/lib/ldclient-rb/context.rb +1 -1
  4. data/lib/ldclient-rb/data_system.rb +243 -0
  5. data/lib/ldclient-rb/events.rb +35 -20
  6. data/lib/ldclient-rb/flags_state.rb +1 -1
  7. data/lib/ldclient-rb/impl/big_segments.rb +4 -4
  8. data/lib/ldclient-rb/impl/cache_store.rb +44 -0
  9. data/lib/ldclient-rb/impl/data_source/null_processor.rb +52 -0
  10. data/lib/ldclient-rb/impl/data_source/polling.rb +108 -0
  11. data/lib/ldclient-rb/impl/data_source/requestor.rb +106 -0
  12. data/lib/ldclient-rb/impl/data_source/status_provider.rb +78 -0
  13. data/lib/ldclient-rb/impl/data_source/stream.rb +198 -0
  14. data/lib/ldclient-rb/impl/data_source.rb +3 -3
  15. data/lib/ldclient-rb/impl/data_store/data_kind.rb +108 -0
  16. data/lib/ldclient-rb/impl/data_store/feature_store_client_wrapper.rb +187 -0
  17. data/lib/ldclient-rb/impl/data_store/in_memory_feature_store.rb +130 -0
  18. data/lib/ldclient-rb/impl/data_store/status_provider.rb +82 -0
  19. data/lib/ldclient-rb/impl/data_store/store.rb +371 -0
  20. data/lib/ldclient-rb/impl/data_store.rb +11 -97
  21. data/lib/ldclient-rb/impl/data_system/fdv1.rb +178 -0
  22. data/lib/ldclient-rb/impl/data_system/fdv2.rb +471 -0
  23. data/lib/ldclient-rb/impl/data_system/polling.rb +601 -0
  24. data/lib/ldclient-rb/impl/data_system/protocolv2.rb +264 -0
  25. data/lib/ldclient-rb/impl/data_system.rb +298 -0
  26. data/lib/ldclient-rb/impl/dependency_tracker.rb +21 -9
  27. data/lib/ldclient-rb/impl/evaluator.rb +3 -2
  28. data/lib/ldclient-rb/impl/event_sender.rb +4 -3
  29. data/lib/ldclient-rb/impl/expiring_cache.rb +79 -0
  30. data/lib/ldclient-rb/impl/integrations/file_data_source.rb +9 -9
  31. data/lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb +0 -1
  32. data/lib/ldclient-rb/impl/integrations/test_data/test_data_source_v2.rb +288 -0
  33. data/lib/ldclient-rb/impl/memoized_value.rb +34 -0
  34. data/lib/ldclient-rb/impl/migrations/migrator.rb +2 -1
  35. data/lib/ldclient-rb/impl/migrations/tracker.rb +2 -1
  36. data/lib/ldclient-rb/impl/model/serialization.rb +6 -6
  37. data/lib/ldclient-rb/impl/non_blocking_thread_pool.rb +48 -0
  38. data/lib/ldclient-rb/impl/repeating_task.rb +2 -2
  39. data/lib/ldclient-rb/impl/simple_lru_cache.rb +27 -0
  40. data/lib/ldclient-rb/impl/util.rb +65 -0
  41. data/lib/ldclient-rb/impl.rb +1 -2
  42. data/lib/ldclient-rb/in_memory_store.rb +1 -18
  43. data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +9 -9
  44. data/lib/ldclient-rb/integrations/test_data.rb +11 -11
  45. data/lib/ldclient-rb/integrations/test_data_v2/flag_builder_v2.rb +582 -0
  46. data/lib/ldclient-rb/integrations/test_data_v2.rb +248 -0
  47. data/lib/ldclient-rb/integrations/util/store_wrapper.rb +3 -2
  48. data/lib/ldclient-rb/interfaces/data_system.rb +755 -0
  49. data/lib/ldclient-rb/interfaces/feature_store.rb +3 -0
  50. data/lib/ldclient-rb/ldclient.rb +55 -149
  51. data/lib/ldclient-rb/util.rb +11 -70
  52. data/lib/ldclient-rb/version.rb +1 -1
  53. data/lib/ldclient-rb.rb +8 -17
  54. metadata +52 -17
  55. data/lib/ldclient-rb/cache_store.rb +0 -45
  56. data/lib/ldclient-rb/expiring_cache.rb +0 -77
  57. data/lib/ldclient-rb/memoized_value.rb +0 -32
  58. data/lib/ldclient-rb/non_blocking_thread_pool.rb +0 -46
  59. data/lib/ldclient-rb/polling.rb +0 -102
  60. data/lib/ldclient-rb/requestor.rb +0 -102
  61. data/lib/ldclient-rb/simple_lru_cache.rb +0 -25
  62. data/lib/ldclient-rb/stream.rb +0 -196
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: launchdarkly-server-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.11.1
4
+ version: 8.11.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - LaunchDarkly
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-10-10 00:00:00.000000000 Z
11
+ date: 2026-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-dynamodb
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.57'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bigdecimal
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 3.1.1
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 3.1.1
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -284,22 +298,22 @@ dependencies:
284
298
  name: openssl
285
299
  requirement: !ruby/object:Gem::Requirement
286
300
  requirements:
287
- - - "~>"
288
- - !ruby/object:Gem::Version
289
- version: '3.1'
290
301
  - - ">="
291
302
  - !ruby/object:Gem::Version
292
303
  version: 3.1.2
304
+ - - "<"
305
+ - !ruby/object:Gem::Version
306
+ version: '5.0'
293
307
  type: :runtime
294
308
  prerelease: false
295
309
  version_requirements: !ruby/object:Gem::Requirement
296
310
  requirements:
297
- - - "~>"
298
- - !ruby/object:Gem::Version
299
- version: '3.1'
300
311
  - - ">="
301
312
  - !ruby/object:Gem::Version
302
313
  version: 3.1.2
314
+ - - "<"
315
+ - !ruby/object:Gem::Version
316
+ version: '5.0'
303
317
  - !ruby/object:Gem::Dependency
304
318
  name: semantic
305
319
  requirement: !ruby/object:Gem::Requirement
@@ -373,20 +387,35 @@ files:
373
387
  - README.md
374
388
  - lib/launchdarkly-server-sdk.rb
375
389
  - lib/ldclient-rb.rb
376
- - lib/ldclient-rb/cache_store.rb
377
390
  - lib/ldclient-rb/config.rb
378
391
  - lib/ldclient-rb/context.rb
392
+ - lib/ldclient-rb/data_system.rb
379
393
  - lib/ldclient-rb/evaluation_detail.rb
380
394
  - lib/ldclient-rb/events.rb
381
- - lib/ldclient-rb/expiring_cache.rb
382
395
  - lib/ldclient-rb/flags_state.rb
383
396
  - lib/ldclient-rb/impl.rb
384
397
  - lib/ldclient-rb/impl/big_segments.rb
385
398
  - lib/ldclient-rb/impl/broadcaster.rb
399
+ - lib/ldclient-rb/impl/cache_store.rb
386
400
  - lib/ldclient-rb/impl/context.rb
387
401
  - lib/ldclient-rb/impl/context_filter.rb
388
402
  - lib/ldclient-rb/impl/data_source.rb
403
+ - lib/ldclient-rb/impl/data_source/null_processor.rb
404
+ - lib/ldclient-rb/impl/data_source/polling.rb
405
+ - lib/ldclient-rb/impl/data_source/requestor.rb
406
+ - lib/ldclient-rb/impl/data_source/status_provider.rb
407
+ - lib/ldclient-rb/impl/data_source/stream.rb
389
408
  - lib/ldclient-rb/impl/data_store.rb
409
+ - lib/ldclient-rb/impl/data_store/data_kind.rb
410
+ - lib/ldclient-rb/impl/data_store/feature_store_client_wrapper.rb
411
+ - lib/ldclient-rb/impl/data_store/in_memory_feature_store.rb
412
+ - lib/ldclient-rb/impl/data_store/status_provider.rb
413
+ - lib/ldclient-rb/impl/data_store/store.rb
414
+ - lib/ldclient-rb/impl/data_system.rb
415
+ - lib/ldclient-rb/impl/data_system/fdv1.rb
416
+ - lib/ldclient-rb/impl/data_system/fdv2.rb
417
+ - lib/ldclient-rb/impl/data_system/polling.rb
418
+ - lib/ldclient-rb/impl/data_system/protocolv2.rb
390
419
  - lib/ldclient-rb/impl/dependency_tracker.rb
391
420
  - lib/ldclient-rb/impl/diagnostic_events.rb
392
421
  - lib/ldclient-rb/impl/evaluation_with_hook_result.rb
@@ -397,12 +426,15 @@ files:
397
426
  - lib/ldclient-rb/impl/event_sender.rb
398
427
  - lib/ldclient-rb/impl/event_summarizer.rb
399
428
  - lib/ldclient-rb/impl/event_types.rb
429
+ - lib/ldclient-rb/impl/expiring_cache.rb
400
430
  - lib/ldclient-rb/impl/flag_tracker.rb
401
431
  - lib/ldclient-rb/impl/integrations/consul_impl.rb
402
432
  - lib/ldclient-rb/impl/integrations/dynamodb_impl.rb
403
433
  - lib/ldclient-rb/impl/integrations/file_data_source.rb
404
434
  - lib/ldclient-rb/impl/integrations/redis_impl.rb
405
435
  - lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb
436
+ - lib/ldclient-rb/impl/integrations/test_data/test_data_source_v2.rb
437
+ - lib/ldclient-rb/impl/memoized_value.rb
406
438
  - lib/ldclient-rb/impl/migrations/migrator.rb
407
439
  - lib/ldclient-rb/impl/migrations/tracker.rb
408
440
  - lib/ldclient-rb/impl/model/clause.rb
@@ -410,8 +442,10 @@ files:
410
442
  - lib/ldclient-rb/impl/model/preprocessed_data.rb
411
443
  - lib/ldclient-rb/impl/model/segment.rb
412
444
  - lib/ldclient-rb/impl/model/serialization.rb
445
+ - lib/ldclient-rb/impl/non_blocking_thread_pool.rb
413
446
  - lib/ldclient-rb/impl/repeating_task.rb
414
447
  - lib/ldclient-rb/impl/sampler.rb
448
+ - lib/ldclient-rb/impl/simple_lru_cache.rb
415
449
  - lib/ldclient-rb/impl/store_client_wrapper.rb
416
450
  - lib/ldclient-rb/impl/store_data_set_sorter.rb
417
451
  - lib/ldclient-rb/impl/unbounded_pool.rb
@@ -424,31 +458,32 @@ files:
424
458
  - lib/ldclient-rb/integrations/redis.rb
425
459
  - lib/ldclient-rb/integrations/test_data.rb
426
460
  - lib/ldclient-rb/integrations/test_data/flag_builder.rb
461
+ - lib/ldclient-rb/integrations/test_data_v2.rb
462
+ - lib/ldclient-rb/integrations/test_data_v2/flag_builder_v2.rb
427
463
  - lib/ldclient-rb/integrations/util/store_wrapper.rb
428
464
  - lib/ldclient-rb/interfaces.rb
429
465
  - lib/ldclient-rb/interfaces/big_segment_store.rb
430
466
  - lib/ldclient-rb/interfaces/data_source.rb
431
467
  - lib/ldclient-rb/interfaces/data_store.rb
468
+ - lib/ldclient-rb/interfaces/data_system.rb
432
469
  - lib/ldclient-rb/interfaces/feature_store.rb
433
470
  - lib/ldclient-rb/interfaces/flag_tracker.rb
434
471
  - lib/ldclient-rb/interfaces/hooks.rb
435
472
  - lib/ldclient-rb/interfaces/migrations.rb
436
473
  - lib/ldclient-rb/interfaces/plugins.rb
437
474
  - lib/ldclient-rb/ldclient.rb
438
- - lib/ldclient-rb/memoized_value.rb
439
475
  - lib/ldclient-rb/migrations.rb
440
- - lib/ldclient-rb/non_blocking_thread_pool.rb
441
- - lib/ldclient-rb/polling.rb
442
476
  - lib/ldclient-rb/reference.rb
443
- - lib/ldclient-rb/requestor.rb
444
- - lib/ldclient-rb/simple_lru_cache.rb
445
- - lib/ldclient-rb/stream.rb
446
477
  - lib/ldclient-rb/util.rb
447
478
  - lib/ldclient-rb/version.rb
448
479
  homepage: https://github.com/launchdarkly/ruby-server-sdk
449
480
  licenses:
450
481
  - Apache-2.0
451
- metadata: {}
482
+ metadata:
483
+ bug_tracker_uri: https://github.com/launchdarkly/ruby-server-sdk/issues
484
+ changelog_uri: https://github.com/launchdarkly/ruby-server-sdk/blob/main/CHANGELOG.md
485
+ homepage_uri: https://github.com/launchdarkly/ruby-server-sdk
486
+ source_code_uri: https://github.com/launchdarkly/ruby-server-sdk
452
487
  post_install_message:
453
488
  rdoc_options: []
454
489
  require_paths:
@@ -1,45 +0,0 @@
1
- require "concurrent/map"
2
-
3
- module LaunchDarkly
4
- #
5
- # A thread-safe in-memory store that uses the same semantics that Faraday would expect, although we
6
- # no longer use Faraday. This is used by Requestor, when we are not in a Rails environment.
7
- #
8
- # @private
9
- #
10
- class ThreadSafeMemoryStore
11
- #
12
- # Default constructor
13
- #
14
- # @return [ThreadSafeMemoryStore] a new store
15
- def initialize
16
- @cache = Concurrent::Map.new
17
- end
18
-
19
- #
20
- # Read a value from the cache
21
- # @param key [Object] the cache key
22
- #
23
- # @return [Object] the cache value
24
- def read(key)
25
- @cache[key]
26
- end
27
-
28
- #
29
- # Store a value in the cache
30
- # @param key [Object] the cache key
31
- # @param value [Object] the value to associate with the key
32
- #
33
- # @return [Object] the value
34
- def write(key, value)
35
- @cache[key] = value
36
- end
37
-
38
- #
39
- # Delete a value in the cache
40
- # @param key [Object] the cache key
41
- def delete(key)
42
- @cache.delete(key)
43
- end
44
- end
45
- end
@@ -1,77 +0,0 @@
1
-
2
- module LaunchDarkly
3
- # A thread-safe cache with maximum number of entries and TTL.
4
- # Adapted from https://github.com/SamSaffron/lru_redux/blob/master/lib/lru_redux/ttl/cache.rb
5
- # under MIT license with the following changes:
6
- # * made thread-safe
7
- # * removed many unused methods
8
- # * reading a key does not reset its expiration time, only writing
9
- # @private
10
- class ExpiringCache
11
- def initialize(max_size, ttl)
12
- @max_size = max_size
13
- @ttl = ttl
14
- @data_lru = {}
15
- @data_ttl = {}
16
- @lock = Mutex.new
17
- end
18
-
19
- def [](key)
20
- @lock.synchronize do
21
- ttl_evict
22
- @data_lru[key]
23
- end
24
- end
25
-
26
- def []=(key, val)
27
- @lock.synchronize do
28
- ttl_evict
29
-
30
- @data_lru.delete(key)
31
- @data_ttl.delete(key)
32
-
33
- @data_lru[key] = val
34
- @data_ttl[key] = Time.now.to_f
35
-
36
- if @data_lru.size > @max_size
37
- key, _ = @data_lru.first # hashes have a FIFO ordering in Ruby
38
-
39
- @data_ttl.delete(key)
40
- @data_lru.delete(key)
41
- end
42
-
43
- val
44
- end
45
- end
46
-
47
- def delete(key)
48
- @lock.synchronize do
49
- ttl_evict
50
-
51
- @data_lru.delete(key)
52
- @data_ttl.delete(key)
53
- end
54
- end
55
-
56
- def clear
57
- @lock.synchronize do
58
- @data_lru.clear
59
- @data_ttl.clear
60
- end
61
- end
62
-
63
- private
64
-
65
- def ttl_evict
66
- ttl_horizon = Time.now.to_f - @ttl
67
- key, time = @data_ttl.first
68
-
69
- until time.nil? || time > ttl_horizon
70
- @data_ttl.delete(key)
71
- @data_lru.delete(key)
72
-
73
- key, time = @data_ttl.first
74
- end
75
- end
76
- end
77
- end
@@ -1,32 +0,0 @@
1
-
2
- module LaunchDarkly
3
- # Simple implementation of a thread-safe memoized value whose generator function will never be
4
- # run more than once, and whose value can be overridden by explicit assignment.
5
- # Note that we no longer use this class and it will be removed in a future version.
6
- # @private
7
- class MemoizedValue
8
- def initialize(&generator)
9
- @generator = generator
10
- @mutex = Mutex.new
11
- @inited = false
12
- @value = nil
13
- end
14
-
15
- def get
16
- @mutex.synchronize do
17
- unless @inited
18
- @value = @generator.call
19
- @inited = true
20
- end
21
- end
22
- @value
23
- end
24
-
25
- def set(value)
26
- @mutex.synchronize do
27
- @value = value
28
- @inited = true
29
- end
30
- end
31
- end
32
- end
@@ -1,46 +0,0 @@
1
- require "concurrent"
2
- require "concurrent/atomics"
3
- require "concurrent/executors"
4
- require "thread"
5
-
6
- module LaunchDarkly
7
- # Simple wrapper for a FixedThreadPool that rejects new jobs if all the threads are busy, rather
8
- # than blocking. Also provides a way to wait for all jobs to finish without shutting down.
9
- # @private
10
- class NonBlockingThreadPool
11
- def initialize(capacity, name = 'LD/NonBlockingThreadPool')
12
- @capacity = capacity
13
- @pool = Concurrent::FixedThreadPool.new(capacity, name: name)
14
- @semaphore = Concurrent::Semaphore.new(capacity)
15
- end
16
-
17
- # Attempts to submit a job, but only if a worker is available. Unlike the regular post method,
18
- # this returns a value: true if the job was submitted, false if all workers are busy.
19
- def post
20
- unless @semaphore.try_acquire(1)
21
- return
22
- end
23
- @pool.post do
24
- begin
25
- yield
26
- ensure
27
- @semaphore.release(1)
28
- end
29
- end
30
- end
31
-
32
- # Waits until no jobs are executing, without shutting down the pool.
33
- def wait_all
34
- @semaphore.acquire(@capacity)
35
- @semaphore.release(@capacity)
36
- end
37
-
38
- def shutdown
39
- @pool.shutdown
40
- end
41
-
42
- def wait_for_termination
43
- @pool.wait_for_termination
44
- end
45
- end
46
- end
@@ -1,102 +0,0 @@
1
- require "ldclient-rb/impl/repeating_task"
2
-
3
- require "concurrent/atomics"
4
- require "json"
5
- require "thread"
6
-
7
- module LaunchDarkly
8
- # @private
9
- class PollingProcessor
10
- def initialize(config, requestor)
11
- @config = config
12
- @requestor = requestor
13
- @initialized = Concurrent::AtomicBoolean.new(false)
14
- @started = Concurrent::AtomicBoolean.new(false)
15
- @ready = Concurrent::Event.new
16
- @task = Impl::RepeatingTask.new(@config.poll_interval, 0, -> { self.poll }, @config.logger, 'LD/PollingDataSource')
17
- end
18
-
19
- def initialized?
20
- @initialized.value
21
- end
22
-
23
- def start
24
- return @ready unless @started.make_true
25
- @config.logger.info { "[LDClient] Initializing polling connection" }
26
- @task.start
27
- @ready
28
- end
29
-
30
- def stop
31
- stop_with_error_info
32
- end
33
-
34
- def poll
35
- begin
36
- all_data = @requestor.request_all_data
37
- if all_data
38
- update_sink_or_data_store.init(all_data)
39
- if @initialized.make_true
40
- @config.logger.info { "[LDClient] Polling connection initialized" }
41
- @ready.set
42
- end
43
- end
44
- @config.data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::VALID, nil)
45
- rescue JSON::ParserError => e
46
- @config.logger.error { "[LDClient] JSON parsing failed for polling response." }
47
- error_info = LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(
48
- LaunchDarkly::Interfaces::DataSource::ErrorInfo::INVALID_DATA,
49
- 0,
50
- e.to_s,
51
- Time.now
52
- )
53
- @config.data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED, error_info)
54
- rescue UnexpectedResponseError => e
55
- error_info = LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(
56
- LaunchDarkly::Interfaces::DataSource::ErrorInfo::ERROR_RESPONSE, e.status, nil, Time.now)
57
- message = Util.http_error_message(e.status, "polling request", "will retry")
58
- @config.logger.error { "[LDClient] #{message}" }
59
-
60
- if Util.http_error_recoverable?(e.status)
61
- @config.data_source_update_sink&.update_status(
62
- LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED,
63
- error_info
64
- )
65
- else
66
- @ready.set # if client was waiting on us, make it stop waiting - has no effect if already set
67
- stop_with_error_info error_info
68
- end
69
- rescue StandardError => e
70
- Util.log_exception(@config.logger, "Exception while polling", e)
71
- @config.data_source_update_sink&.update_status(
72
- LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED,
73
- LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(LaunchDarkly::Interfaces::DataSource::ErrorInfo::UNKNOWN, 0, e.to_s, Time.now)
74
- )
75
- end
76
- end
77
-
78
- #
79
- # The original implementation of this class relied on the feature store
80
- # directly, which we are trying to move away from. Customers who might have
81
- # instantiated this directly for some reason wouldn't know they have to set
82
- # the config's sink manually, so we have to fall back to the store if the
83
- # sink isn't present.
84
- #
85
- # The next major release should be able to simplify this structure and
86
- # remove the need for fall back to the data store because the update sink
87
- # should always be present.
88
- #
89
- private def update_sink_or_data_store
90
- @config.data_source_update_sink || @config.feature_store
91
- end
92
-
93
- #
94
- # @param [LaunchDarkly::Interfaces::DataSource::ErrorInfo, nil] error_info
95
- #
96
- private def stop_with_error_info(error_info = nil)
97
- @task.stop
98
- @config.logger.info { "[LDClient] Polling connection stopped" }
99
- @config.data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::OFF, error_info)
100
- end
101
- end
102
- end
@@ -1,102 +0,0 @@
1
- require "ldclient-rb/impl/model/serialization"
2
-
3
- require "concurrent/atomics"
4
- require "json"
5
- require "uri"
6
- require "http"
7
-
8
- module LaunchDarkly
9
- # @private
10
- class UnexpectedResponseError < StandardError
11
- def initialize(status)
12
- @status = status
13
- super("HTTP error #{status}")
14
- end
15
-
16
- def status
17
- @status
18
- end
19
- end
20
-
21
- # @private
22
- class Requestor
23
- CacheEntry = Struct.new(:etag, :body)
24
-
25
- def initialize(sdk_key, config)
26
- @sdk_key = sdk_key
27
- @config = config
28
- @http_client = LaunchDarkly::Util.new_http_client(config.base_uri, config)
29
- .use(:auto_inflate)
30
- .headers("Accept-Encoding" => "gzip")
31
- @cache = @config.cache_store
32
- end
33
-
34
- def request_all_data()
35
- all_data = JSON.parse(make_request("/sdk/latest-all"), symbolize_names: true)
36
- Impl::Model.make_all_store_data(all_data, @config.logger)
37
- end
38
-
39
- def stop
40
- begin
41
- @http_client.close
42
- rescue
43
- end
44
- end
45
-
46
- private
47
-
48
- def make_request(path)
49
- uri = URI(
50
- Util.add_payload_filter_key(@config.base_uri + path, @config)
51
- )
52
- headers = {}
53
- Impl::Util.default_http_headers(@sdk_key, @config).each { |k, v| headers[k] = v }
54
- headers["Connection"] = "keep-alive"
55
- cached = @cache.read(uri)
56
- unless cached.nil?
57
- headers["If-None-Match"] = cached.etag
58
- end
59
- response = @http_client.request("GET", uri, {
60
- headers: headers,
61
- })
62
- status = response.status.code
63
- # must fully read body for persistent connections
64
- body = response.to_s
65
- @config.logger.debug { "[LDClient] Got response from uri: #{uri}\n\tstatus code: #{status}\n\theaders: #{response.headers.to_h}\n\tbody: #{body}" }
66
- if status == 304 && !cached.nil?
67
- body = cached.body
68
- else
69
- @cache.delete(uri)
70
- if status < 200 || status >= 300
71
- raise UnexpectedResponseError.new(status)
72
- end
73
- body = fix_encoding(body, response.headers["content-type"])
74
- etag = response.headers["etag"]
75
- @cache.write(uri, CacheEntry.new(etag, body)) unless etag.nil?
76
- end
77
- body
78
- end
79
-
80
- def fix_encoding(body, content_type)
81
- return body if content_type.nil?
82
- media_type, charset = parse_content_type(content_type)
83
- return body if charset.nil?
84
- body.force_encoding(Encoding::find(charset)).encode(Encoding::UTF_8)
85
- end
86
-
87
- def parse_content_type(value)
88
- return [nil, nil] if value.nil? || value == ''
89
- parts = value.split(/; */)
90
- return [value, nil] if parts.count < 2
91
- charset = nil
92
- parts.each do |part|
93
- fields = part.split('=')
94
- if fields.count >= 2 && fields[0] == 'charset'
95
- charset = fields[1]
96
- break
97
- end
98
- end
99
- [parts[0], charset]
100
- end
101
- end
102
- end
@@ -1,25 +0,0 @@
1
-
2
- module LaunchDarkly
3
- # A non-thread-safe implementation of a LRU cache set with only add and reset methods.
4
- # Based on https://github.com/SamSaffron/lru_redux/blob/master/lib/lru_redux/cache.rb
5
- # @private
6
- class SimpleLRUCacheSet
7
- def initialize(capacity)
8
- @values = {}
9
- @capacity = capacity
10
- end
11
-
12
- # Adds a value to the cache or marks it recent if it was already there. Returns true if already there.
13
- def add(value)
14
- found = true
15
- @values.delete(value) { found = false }
16
- @values[value] = true
17
- @values.shift if @values.length > @capacity
18
- found
19
- end
20
-
21
- def clear
22
- @values = {}
23
- end
24
- end
25
- end