launchdarkly-server-sdk 8.8.3-java
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 +7 -0
- data/LICENSE.txt +13 -0
- data/README.md +61 -0
- data/lib/launchdarkly-server-sdk.rb +1 -0
- data/lib/ldclient-rb/cache_store.rb +45 -0
- data/lib/ldclient-rb/config.rb +658 -0
- data/lib/ldclient-rb/context.rb +565 -0
- data/lib/ldclient-rb/evaluation_detail.rb +387 -0
- data/lib/ldclient-rb/events.rb +642 -0
- data/lib/ldclient-rb/expiring_cache.rb +77 -0
- data/lib/ldclient-rb/flags_state.rb +88 -0
- data/lib/ldclient-rb/impl/big_segments.rb +117 -0
- data/lib/ldclient-rb/impl/broadcaster.rb +78 -0
- data/lib/ldclient-rb/impl/context.rb +96 -0
- data/lib/ldclient-rb/impl/context_filter.rb +166 -0
- data/lib/ldclient-rb/impl/data_source.rb +188 -0
- data/lib/ldclient-rb/impl/data_store.rb +109 -0
- data/lib/ldclient-rb/impl/dependency_tracker.rb +102 -0
- data/lib/ldclient-rb/impl/diagnostic_events.rb +129 -0
- data/lib/ldclient-rb/impl/evaluation_with_hook_result.rb +34 -0
- data/lib/ldclient-rb/impl/evaluator.rb +539 -0
- data/lib/ldclient-rb/impl/evaluator_bucketing.rb +86 -0
- data/lib/ldclient-rb/impl/evaluator_helpers.rb +50 -0
- data/lib/ldclient-rb/impl/evaluator_operators.rb +131 -0
- data/lib/ldclient-rb/impl/event_sender.rb +100 -0
- data/lib/ldclient-rb/impl/event_summarizer.rb +68 -0
- data/lib/ldclient-rb/impl/event_types.rb +136 -0
- data/lib/ldclient-rb/impl/flag_tracker.rb +58 -0
- data/lib/ldclient-rb/impl/integrations/consul_impl.rb +170 -0
- data/lib/ldclient-rb/impl/integrations/dynamodb_impl.rb +300 -0
- data/lib/ldclient-rb/impl/integrations/file_data_source.rb +229 -0
- data/lib/ldclient-rb/impl/integrations/redis_impl.rb +306 -0
- data/lib/ldclient-rb/impl/integrations/test_data/test_data_source.rb +40 -0
- data/lib/ldclient-rb/impl/migrations/migrator.rb +287 -0
- data/lib/ldclient-rb/impl/migrations/tracker.rb +136 -0
- data/lib/ldclient-rb/impl/model/clause.rb +45 -0
- data/lib/ldclient-rb/impl/model/feature_flag.rb +254 -0
- data/lib/ldclient-rb/impl/model/preprocessed_data.rb +64 -0
- data/lib/ldclient-rb/impl/model/segment.rb +132 -0
- data/lib/ldclient-rb/impl/model/serialization.rb +72 -0
- data/lib/ldclient-rb/impl/repeating_task.rb +46 -0
- data/lib/ldclient-rb/impl/sampler.rb +25 -0
- data/lib/ldclient-rb/impl/store_client_wrapper.rb +141 -0
- data/lib/ldclient-rb/impl/store_data_set_sorter.rb +55 -0
- data/lib/ldclient-rb/impl/unbounded_pool.rb +34 -0
- data/lib/ldclient-rb/impl/util.rb +95 -0
- data/lib/ldclient-rb/impl.rb +13 -0
- data/lib/ldclient-rb/in_memory_store.rb +100 -0
- data/lib/ldclient-rb/integrations/consul.rb +45 -0
- data/lib/ldclient-rb/integrations/dynamodb.rb +92 -0
- data/lib/ldclient-rb/integrations/file_data.rb +108 -0
- data/lib/ldclient-rb/integrations/redis.rb +98 -0
- data/lib/ldclient-rb/integrations/test_data/flag_builder.rb +663 -0
- data/lib/ldclient-rb/integrations/test_data.rb +213 -0
- data/lib/ldclient-rb/integrations/util/store_wrapper.rb +246 -0
- data/lib/ldclient-rb/integrations.rb +6 -0
- data/lib/ldclient-rb/interfaces.rb +974 -0
- data/lib/ldclient-rb/ldclient.rb +822 -0
- data/lib/ldclient-rb/memoized_value.rb +32 -0
- data/lib/ldclient-rb/migrations.rb +230 -0
- data/lib/ldclient-rb/non_blocking_thread_pool.rb +46 -0
- data/lib/ldclient-rb/polling.rb +102 -0
- data/lib/ldclient-rb/reference.rb +295 -0
- data/lib/ldclient-rb/requestor.rb +102 -0
- data/lib/ldclient-rb/simple_lru_cache.rb +25 -0
- data/lib/ldclient-rb/stream.rb +196 -0
- data/lib/ldclient-rb/util.rb +132 -0
- data/lib/ldclient-rb/version.rb +3 -0
- data/lib/ldclient-rb.rb +27 -0
- metadata +400 -0
@@ -0,0 +1,102 @@
|
|
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
|
@@ -0,0 +1,25 @@
|
|
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
|
@@ -0,0 +1,196 @@
|
|
1
|
+
require "ldclient-rb/impl/model/serialization"
|
2
|
+
|
3
|
+
require "concurrent/atomics"
|
4
|
+
require "json"
|
5
|
+
require "ld-eventsource"
|
6
|
+
|
7
|
+
module LaunchDarkly
|
8
|
+
# @private
|
9
|
+
PUT = :put
|
10
|
+
# @private
|
11
|
+
PATCH = :patch
|
12
|
+
# @private
|
13
|
+
DELETE = :delete
|
14
|
+
# @private
|
15
|
+
READ_TIMEOUT_SECONDS = 300 # 5 minutes; the stream should send a ping every 3 minutes
|
16
|
+
|
17
|
+
# @private
|
18
|
+
KEY_PATHS = {
|
19
|
+
FEATURES => "/flags/",
|
20
|
+
SEGMENTS => "/segments/",
|
21
|
+
}
|
22
|
+
|
23
|
+
# @private
|
24
|
+
class StreamProcessor
|
25
|
+
def initialize(sdk_key, config, diagnostic_accumulator = nil)
|
26
|
+
@sdk_key = sdk_key
|
27
|
+
@config = config
|
28
|
+
@data_source_update_sink = config.data_source_update_sink
|
29
|
+
@feature_store = config.feature_store
|
30
|
+
@initialized = Concurrent::AtomicBoolean.new(false)
|
31
|
+
@started = Concurrent::AtomicBoolean.new(false)
|
32
|
+
@stopped = Concurrent::AtomicBoolean.new(false)
|
33
|
+
@ready = Concurrent::Event.new
|
34
|
+
@connection_attempt_start_time = 0
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialized?
|
38
|
+
@initialized.value
|
39
|
+
end
|
40
|
+
|
41
|
+
def start
|
42
|
+
return @ready unless @started.make_true
|
43
|
+
|
44
|
+
@config.logger.info { "[LDClient] Initializing stream connection" }
|
45
|
+
|
46
|
+
headers = Impl::Util.default_http_headers(@sdk_key, @config)
|
47
|
+
opts = {
|
48
|
+
headers: headers,
|
49
|
+
read_timeout: READ_TIMEOUT_SECONDS,
|
50
|
+
logger: @config.logger,
|
51
|
+
socket_factory: @config.socket_factory,
|
52
|
+
reconnect_time: @config.initial_reconnect_delay,
|
53
|
+
}
|
54
|
+
log_connection_started
|
55
|
+
|
56
|
+
uri = Util.add_payload_filter_key(@config.stream_uri + "/all", @config)
|
57
|
+
@es = SSE::Client.new(uri, **opts) do |conn|
|
58
|
+
conn.on_event { |event| process_message(event) }
|
59
|
+
conn.on_error { |err|
|
60
|
+
log_connection_result(false)
|
61
|
+
case err
|
62
|
+
when SSE::Errors::HTTPStatusError
|
63
|
+
status = err.status
|
64
|
+
error_info = LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(
|
65
|
+
LaunchDarkly::Interfaces::DataSource::ErrorInfo::ERROR_RESPONSE, status, nil, Time.now)
|
66
|
+
message = Util.http_error_message(status, "streaming connection", "will retry")
|
67
|
+
@config.logger.error { "[LDClient] #{message}" }
|
68
|
+
|
69
|
+
if Util.http_error_recoverable?(status)
|
70
|
+
@data_source_update_sink&.update_status(
|
71
|
+
LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED,
|
72
|
+
error_info
|
73
|
+
)
|
74
|
+
else
|
75
|
+
@ready.set # if client was waiting on us, make it stop waiting - has no effect if already set
|
76
|
+
stop_with_error_info error_info
|
77
|
+
end
|
78
|
+
when SSE::Errors::HTTPContentTypeError, SSE::Errors::HTTPProxyError, SSE::Errors::ReadTimeoutError
|
79
|
+
@data_source_update_sink&.update_status(
|
80
|
+
LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED,
|
81
|
+
LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(LaunchDarkly::Interfaces::DataSource::ErrorInfo::NETWORK_ERROR, 0, err.to_s, Time.now)
|
82
|
+
)
|
83
|
+
|
84
|
+
else
|
85
|
+
@data_source_update_sink&.update_status(
|
86
|
+
LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED,
|
87
|
+
LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(LaunchDarkly::Interfaces::DataSource::ErrorInfo::UNKNOWN, 0, err.to_s, Time.now)
|
88
|
+
)
|
89
|
+
end
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
@ready
|
94
|
+
end
|
95
|
+
|
96
|
+
def stop
|
97
|
+
stop_with_error_info
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
#
|
103
|
+
# @param [LaunchDarkly::Interfaces::DataSource::ErrorInfo, nil] error_info
|
104
|
+
#
|
105
|
+
def stop_with_error_info(error_info = nil)
|
106
|
+
if @stopped.make_true
|
107
|
+
@es.close
|
108
|
+
@data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::OFF, error_info)
|
109
|
+
@config.logger.info { "[LDClient] Stream connection stopped" }
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# The original implementation of this class relied on the feature store
|
115
|
+
# directly, which we are trying to move away from. Customers who might have
|
116
|
+
# instantiated this directly for some reason wouldn't know they have to set
|
117
|
+
# the config's sink manually, so we have to fall back to the store if the
|
118
|
+
# sink isn't present.
|
119
|
+
#
|
120
|
+
# The next major release should be able to simplify this structure and
|
121
|
+
# remove the need for fall back to the data store because the update sink
|
122
|
+
# should always be present.
|
123
|
+
#
|
124
|
+
def update_sink_or_data_store
|
125
|
+
@data_source_update_sink || @feature_store
|
126
|
+
end
|
127
|
+
|
128
|
+
def process_message(message)
|
129
|
+
log_connection_result(true)
|
130
|
+
method = message.type
|
131
|
+
@config.logger.debug { "[LDClient] Stream received #{method} message: #{message.data}" }
|
132
|
+
|
133
|
+
begin
|
134
|
+
if method == PUT
|
135
|
+
message = JSON.parse(message.data, symbolize_names: true)
|
136
|
+
all_data = Impl::Model.make_all_store_data(message[:data], @config.logger)
|
137
|
+
update_sink_or_data_store.init(all_data)
|
138
|
+
@initialized.make_true
|
139
|
+
@config.logger.info { "[LDClient] Stream initialized" }
|
140
|
+
@ready.set
|
141
|
+
elsif method == PATCH
|
142
|
+
data = JSON.parse(message.data, symbolize_names: true)
|
143
|
+
for kind in [FEATURES, SEGMENTS]
|
144
|
+
key = key_for_path(kind, data[:path])
|
145
|
+
if key
|
146
|
+
item = Impl::Model.deserialize(kind, data[:data], @config.logger)
|
147
|
+
update_sink_or_data_store.upsert(kind, item)
|
148
|
+
break
|
149
|
+
end
|
150
|
+
end
|
151
|
+
elsif method == DELETE
|
152
|
+
data = JSON.parse(message.data, symbolize_names: true)
|
153
|
+
for kind in [FEATURES, SEGMENTS]
|
154
|
+
key = key_for_path(kind, data[:path])
|
155
|
+
if key
|
156
|
+
update_sink_or_data_store.delete(kind, key, data[:version])
|
157
|
+
break
|
158
|
+
end
|
159
|
+
end
|
160
|
+
else
|
161
|
+
@config.logger.warn { "[LDClient] Unknown message received: #{method}" }
|
162
|
+
end
|
163
|
+
|
164
|
+
@data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::VALID, nil)
|
165
|
+
rescue JSON::ParserError => e
|
166
|
+
@config.logger.error { "[LDClient] JSON parsing failed for method #{method}. Ignoring event." }
|
167
|
+
error_info = LaunchDarkly::Interfaces::DataSource::ErrorInfo.new(
|
168
|
+
LaunchDarkly::Interfaces::DataSource::ErrorInfo::INVALID_DATA,
|
169
|
+
0,
|
170
|
+
e.to_s,
|
171
|
+
Time.now
|
172
|
+
)
|
173
|
+
@data_source_update_sink&.update_status(LaunchDarkly::Interfaces::DataSource::Status::INTERRUPTED, error_info)
|
174
|
+
|
175
|
+
# Re-raise the exception so the SSE implementation can catch it and restart the stream.
|
176
|
+
raise
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def key_for_path(kind, path)
|
181
|
+
path.start_with?(KEY_PATHS[kind]) ? path[KEY_PATHS[kind].length..-1] : nil
|
182
|
+
end
|
183
|
+
|
184
|
+
def log_connection_started
|
185
|
+
@connection_attempt_start_time = Impl::Util::current_time_millis
|
186
|
+
end
|
187
|
+
|
188
|
+
def log_connection_result(is_success)
|
189
|
+
if !@diagnostic_accumulator.nil? && @connection_attempt_start_time > 0
|
190
|
+
@diagnostic_accumulator.record_stream_init(@connection_attempt_start_time, !is_success,
|
191
|
+
Impl::Util::current_time_millis - @connection_attempt_start_time)
|
192
|
+
@connection_attempt_start_time = 0
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require "uri"
|
2
|
+
require "http"
|
3
|
+
|
4
|
+
module LaunchDarkly
|
5
|
+
#
|
6
|
+
# A Result is used to reflect the outcome of any operation.
|
7
|
+
#
|
8
|
+
# Results can either be considered a success or a failure.
|
9
|
+
#
|
10
|
+
# In the event of success, the Result will contain an option, nullable value to hold any success value back to the
|
11
|
+
# calling function.
|
12
|
+
#
|
13
|
+
# If the operation fails, the Result will contain an error describing the value.
|
14
|
+
#
|
15
|
+
class Result
|
16
|
+
#
|
17
|
+
# Create a successful result with the provided value.
|
18
|
+
#
|
19
|
+
# @param value [Object, nil]
|
20
|
+
# @return [Result]
|
21
|
+
#
|
22
|
+
def self.success(value)
|
23
|
+
Result.new(value)
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Create a failed result with the provided error description.
|
28
|
+
#
|
29
|
+
# @param error [String]
|
30
|
+
# @param exception [Exception, nil]
|
31
|
+
# @return [Result]
|
32
|
+
#
|
33
|
+
def self.fail(error, exception = nil)
|
34
|
+
Result.new(nil, error, exception)
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Was this result successful or did it encounter an error?
|
39
|
+
#
|
40
|
+
# @return [Boolean]
|
41
|
+
#
|
42
|
+
def success?
|
43
|
+
@error.nil?
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# @return [Object, nil] The value returned from the operation if it was successful; nil otherwise.
|
48
|
+
#
|
49
|
+
attr_reader :value
|
50
|
+
|
51
|
+
#
|
52
|
+
# @return [String, nil] An error description of the failure; nil otherwise
|
53
|
+
#
|
54
|
+
attr_reader :error
|
55
|
+
|
56
|
+
#
|
57
|
+
# @return [Exception, nil] An optional exception which caused the failure
|
58
|
+
#
|
59
|
+
attr_reader :exception
|
60
|
+
|
61
|
+
private def initialize(value, error = nil, exception = nil)
|
62
|
+
@value = value
|
63
|
+
@error = error
|
64
|
+
@exception = exception
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# @private
|
69
|
+
module Util
|
70
|
+
#
|
71
|
+
# Append the payload filter key query parameter to the provided URI.
|
72
|
+
#
|
73
|
+
# @param uri [String]
|
74
|
+
# @param config [Config]
|
75
|
+
# @return [String]
|
76
|
+
#
|
77
|
+
def self.add_payload_filter_key(uri, config)
|
78
|
+
return uri if config.payload_filter_key.nil?
|
79
|
+
|
80
|
+
begin
|
81
|
+
parsed = URI.parse(uri)
|
82
|
+
new_query_params = URI.decode_www_form(String(parsed.query)) << ["filter", config.payload_filter_key]
|
83
|
+
parsed.query = URI.encode_www_form(new_query_params)
|
84
|
+
parsed.to_s
|
85
|
+
rescue URI::InvalidURIError
|
86
|
+
config.logger.warn { "[LDClient] URI could not be parsed. No filtering will be applied." }
|
87
|
+
uri
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.new_http_client(uri_s, config)
|
92
|
+
http_client_options = {}
|
93
|
+
if config.socket_factory
|
94
|
+
http_client_options["socket_class"] = config.socket_factory
|
95
|
+
end
|
96
|
+
proxy = URI.parse(uri_s).find_proxy
|
97
|
+
unless proxy.nil?
|
98
|
+
http_client_options["proxy"] = {
|
99
|
+
proxy_address: proxy.host,
|
100
|
+
proxy_port: proxy.port,
|
101
|
+
proxy_username: proxy.user,
|
102
|
+
proxy_password: proxy.password,
|
103
|
+
}
|
104
|
+
end
|
105
|
+
HTTP::Client.new(http_client_options)
|
106
|
+
.timeout({
|
107
|
+
read: config.read_timeout,
|
108
|
+
connect: config.connect_timeout,
|
109
|
+
})
|
110
|
+
.persistent(uri_s)
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.log_exception(logger, message, exc)
|
114
|
+
logger.error { "[LDClient] #{message}: #{exc.inspect}" }
|
115
|
+
logger.debug { "[LDClient] Exception trace: #{exc.backtrace}" }
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.http_error_recoverable?(status)
|
119
|
+
if status >= 400 && status < 500
|
120
|
+
status == 400 || status == 408 || status == 429
|
121
|
+
else
|
122
|
+
true
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.http_error_message(status, context, recoverable_message)
|
127
|
+
desc = (status == 401 || status == 403) ? " (invalid SDK key)" : ""
|
128
|
+
message = Util.http_error_recoverable?(status) ? recoverable_message : "giving up permanently"
|
129
|
+
"HTTP error #{status}#{desc} for #{context} - #{message}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/lib/ldclient-rb.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Namespace for the LaunchDarkly Ruby SDK.
|
4
|
+
#
|
5
|
+
module LaunchDarkly
|
6
|
+
end
|
7
|
+
|
8
|
+
require "ldclient-rb/version"
|
9
|
+
require "ldclient-rb/interfaces"
|
10
|
+
require "ldclient-rb/util"
|
11
|
+
require "ldclient-rb/flags_state"
|
12
|
+
require "ldclient-rb/migrations"
|
13
|
+
require "ldclient-rb/ldclient"
|
14
|
+
require "ldclient-rb/cache_store"
|
15
|
+
require "ldclient-rb/expiring_cache"
|
16
|
+
require "ldclient-rb/memoized_value"
|
17
|
+
require "ldclient-rb/in_memory_store"
|
18
|
+
require "ldclient-rb/config"
|
19
|
+
require "ldclient-rb/context"
|
20
|
+
require "ldclient-rb/reference"
|
21
|
+
require "ldclient-rb/stream"
|
22
|
+
require "ldclient-rb/polling"
|
23
|
+
require "ldclient-rb/simple_lru_cache"
|
24
|
+
require "ldclient-rb/non_blocking_thread_pool"
|
25
|
+
require "ldclient-rb/events"
|
26
|
+
require "ldclient-rb/requestor"
|
27
|
+
require "ldclient-rb/integrations"
|