1+ require "concurrent/atomics"
12require "json"
23require "net/http/persistent"
3- require "faraday/http_cache"
44
55module LaunchDarkly
66 # @private
77 class UnexpectedResponseError < StandardError
88 def initialize ( status )
99 @status = status
10+ super ( "HTTP error #{ status } " )
1011 end
1112
1213 def status
@@ -16,14 +17,15 @@ def status
1617
1718 # @private
1819 class Requestor
20+ CacheEntry = Struct . new ( :etag , :body )
21+
1922 def initialize ( sdk_key , config )
2023 @sdk_key = sdk_key
2124 @config = config
22- @client = Faraday . new do |builder |
23- builder . use :http_cache , store : @config . cache_store , serializer : Marshal
24-
25- builder . adapter :net_http_persistent
26- end
25+ @client = Net ::HTTP ::Persistent . new
26+ @client . open_timeout = @config . connect_timeout
27+ @client . read_timeout = @config . read_timeout
28+ @cache = @config . cache_store
2729 end
2830
2931 def request_flag ( key )
@@ -38,27 +40,59 @@ def request_all_data()
3840 make_request ( "/sdk/latest-all" )
3941 end
4042
43+ def stop
44+ @client . shutdown
45+ end
46+
47+ private
48+
4149 def make_request ( path )
42- uri = @config . base_uri + path
43- res = @client . get ( uri ) do |req |
44- req . headers [ "Authorization" ] = @sdk_key
45- req . headers [ "User-Agent" ] = "RubyClient/" + LaunchDarkly ::VERSION
46- req . options . timeout = @config . read_timeout
47- req . options . open_timeout = @config . connect_timeout
48- if @config . proxy
49- req . options . proxy = Faraday ::ProxyOptions . from @config . proxy
50- end
50+ uri = URI ( @config . base_uri + path )
51+ req = Net ::HTTP ::Get . new ( uri )
52+ req [ "Authorization" ] = @sdk_key
53+ req [ "User-Agent" ] = "RubyClient/" + LaunchDarkly ::VERSION
54+ cached = @cache . read ( uri )
55+ if !cached . nil?
56+ req [ "If-None-Match" ] = cached . etag
5157 end
58+ res = @client . request ( uri , req )
59+ status = res . code . to_i
60+ @config . logger . debug { "[LDClient] Got response from uri: #{ uri } \n \t status code: #{ status } \n \t headers: #{ res . to_hash } \n \t body: #{ res . body } " }
5261
53- @config . logger . debug { "[LDClient] Got response from uri: #{ uri } \n \t status code: #{ res . status } \n \t headers: #{ res . headers } \n \t body: #{ res . body } " }
54-
55- if res . status < 200 || res . status >= 300
56- raise UnexpectedResponseError . new ( res . status )
62+ if status == 304 && !cached . nil?
63+ body = cached . body
64+ else
65+ @cache . delete ( uri )
66+ if status < 200 || status >= 300
67+ raise UnexpectedResponseError . new ( status )
68+ end
69+ body = fix_encoding ( res . body , res [ "content-type" ] )
70+ etag = res [ "etag" ]
71+ @cache . write ( uri , CacheEntry . new ( etag , body ) ) if !etag . nil?
5772 end
73+ JSON . parse ( body , symbolize_names : true )
74+ end
5875
59- JSON . parse ( res . body , symbolize_names : true )
76+ def fix_encoding ( body , content_type )
77+ return body if content_type . nil?
78+ media_type , charset = parse_content_type ( content_type )
79+ return body if charset . nil?
80+ body . force_encoding ( Encoding ::find ( charset ) ) . encode ( Encoding ::UTF_8 )
6081 end
6182
62- private :make_request
83+ def parse_content_type ( value )
84+ return [ nil , nil ] if value . nil? || value == ''
85+ parts = value . split ( /; */ )
86+ return [ value , nil ] if parts . count < 2
87+ charset = nil
88+ parts . each do |part |
89+ fields = part . split ( '=' )
90+ if fields . count >= 2 && fields [ 0 ] == 'charset'
91+ charset = fields [ 1 ]
92+ break
93+ end
94+ end
95+ return [ parts [ 0 ] , charset ]
96+ end
6397 end
6498end
0 commit comments