ldclient-rb 4.0.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,263 @@
1
+ require "spec_helper"
2
+ require "socketry"
3
+ require "sse_client/sse_shared"
4
+
5
+ #
6
+ # End-to-end tests of HTTP requests against a real server
7
+ #
8
+ describe SSE::StreamingHTTPConnection do
9
+ subject { SSE::StreamingHTTPConnection }
10
+
11
+ def with_connection(cxn)
12
+ begin
13
+ yield cxn
14
+ ensure
15
+ cxn.close
16
+ end
17
+ end
18
+
19
+ it "makes HTTP connection and sends request" do
20
+ with_server do |server|
21
+ requests = Queue.new
22
+ server.setup_response("/foo") do |req,res|
23
+ requests << req
24
+ res.status = 200
25
+ end
26
+ headers = {
27
+ "Accept" => "text/plain"
28
+ }
29
+ with_connection(subject.new(server.base_uri.merge("/foo?bar"), nil, headers, 30, 30)) do
30
+ received_req = requests.pop
31
+ expect(received_req.unparsed_uri).to eq("/foo?bar")
32
+ expect(received_req.header).to eq({
33
+ "accept" => ["text/plain"],
34
+ "host" => [server.base_uri.host]
35
+ })
36
+ end
37
+ end
38
+ end
39
+
40
+ it "receives response status" do
41
+ with_server do |server|
42
+ server.setup_response("/foo") do |req,res|
43
+ res.status = 204
44
+ end
45
+ with_connection(subject.new(server.base_uri.merge("/foo"), nil, {}, 30, 30)) do |cxn|
46
+ expect(cxn.status).to eq(204)
47
+ end
48
+ end
49
+ end
50
+
51
+ it "receives response headers" do
52
+ with_server do |server|
53
+ server.setup_response("/foo") do |req,res|
54
+ res["Content-Type"] = "application/json"
55
+ end
56
+ with_connection(subject.new(server.base_uri.merge("/foo"), nil, {}, 30, 30)) do |cxn|
57
+ expect(cxn.headers["content-type"]).to eq("application/json")
58
+ end
59
+ end
60
+ end
61
+
62
+ it "can read response as lines" do
63
+ body = <<-EOT
64
+ This is
65
+ a response
66
+ EOT
67
+ with_server do |server|
68
+ server.setup_response("/foo") do |req,res|
69
+ res.body = body
70
+ end
71
+ with_connection(subject.new(server.base_uri.merge("/foo"), nil, {}, 30, 30)) do |cxn|
72
+ lines = cxn.read_lines
73
+ expect(lines.next).to eq("This is\n")
74
+ expect(lines.next).to eq("a response\n")
75
+ end
76
+ end
77
+ end
78
+
79
+ it "can read entire response body" do
80
+ body = <<-EOT
81
+ This is
82
+ a response
83
+ EOT
84
+ with_server do |server|
85
+ server.setup_response("/foo") do |req,res|
86
+ res.body = body
87
+ end
88
+ with_connection(subject.new(server.base_uri.merge("/foo"), nil, {}, 30, 30)) do |cxn|
89
+ read_body = cxn.read_all
90
+ expect(read_body).to eq("This is\na response\n")
91
+ end
92
+ end
93
+ end
94
+
95
+ it "enforces read timeout" do
96
+ with_server do |server|
97
+ server.setup_response("/") do |req,res|
98
+ sleep(2)
99
+ res.status = 200
100
+ end
101
+ expect { subject.new(server.base_uri, nil, {}, 30, 0.25) }.to raise_error(Socketry::TimeoutError)
102
+ end
103
+ end
104
+
105
+ it "connects to HTTP server through proxy" do
106
+ body = "hi"
107
+ with_server do |server|
108
+ server.setup_response("/") do |req,res|
109
+ res.body = body
110
+ end
111
+ with_server(StubProxyServer.new) do |proxy|
112
+ with_connection(subject.new(server.base_uri, proxy.base_uri, {}, 30, 30)) do |cxn|
113
+ read_body = cxn.read_all
114
+ expect(read_body).to eq("hi")
115
+ expect(proxy.request_count).to eq(1)
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ it "throws error if proxy responds with error status" do
122
+ with_server do |server|
123
+ server.setup_response("/") do |req,res|
124
+ res.body = body
125
+ end
126
+ with_server(StubProxyServer.new) do |proxy|
127
+ proxy.connect_status = 403
128
+ expect { subject.new(server.base_uri, proxy.base_uri, {}, 30, 30) }.to raise_error(SSE::ProxyError)
129
+ end
130
+ end
131
+ end
132
+
133
+ # The following 2 tests were originally written to connect to an embedded HTTPS server made with
134
+ # WEBrick. Unfortunately, some unknown problem prevents WEBrick's self-signed certificate feature
135
+ # from working in JRuby 9.1 (but not in any other Ruby version). Therefore these tests currently
136
+ # hit an external URL.
137
+
138
+ it "connects to HTTPS server" do
139
+ with_connection(subject.new(URI("https://app.launchdarkly.com"), nil, {}, 30, 30)) do |cxn|
140
+ expect(cxn.status).to eq 200
141
+ end
142
+ end
143
+
144
+ it "connects to HTTPS server through proxy" do
145
+ with_server(StubProxyServer.new) do |proxy|
146
+ with_connection(subject.new(URI("https://app.launchdarkly.com"), proxy.base_uri, {}, 30, 30)) do |cxn|
147
+ expect(cxn.status).to eq 200
148
+ expect(proxy.request_count).to eq(1)
149
+ end
150
+ end
151
+ end
152
+ end
153
+
154
+ #
155
+ # Tests of response parsing functionality without a real HTTP request
156
+ #
157
+ describe SSE::HTTPResponseReader do
158
+ subject { SSE::HTTPResponseReader }
159
+
160
+ let(:simple_response) { <<-EOT
161
+ HTTP/1.1 200 OK
162
+ Cache-Control: no-cache
163
+ Content-Type: text/event-stream
164
+
165
+ line1\r
166
+ line2
167
+ \r
168
+ EOT
169
+ }
170
+
171
+ def make_chunks(str)
172
+ # arbitrarily split content into 5-character blocks
173
+ str.scan(/.{1,5}/m).to_enum
174
+ end
175
+
176
+ def mock_socket_without_timeout(chunks)
177
+ mock_socket(chunks) { :eof }
178
+ end
179
+
180
+ def mock_socket_with_timeout(chunks)
181
+ mock_socket(chunks) { raise Socketry::TimeoutError }
182
+ end
183
+
184
+ def mock_socket(chunks)
185
+ sock = double
186
+ allow(sock).to receive(:readpartial) do
187
+ begin
188
+ chunks.next
189
+ rescue StopIteration
190
+ yield
191
+ end
192
+ end
193
+ sock
194
+ end
195
+
196
+ it "parses status code" do
197
+ socket = mock_socket_without_timeout(make_chunks(simple_response))
198
+ reader = subject.new(socket, 0)
199
+ expect(reader.status).to eq(200)
200
+ end
201
+
202
+ it "parses headers" do
203
+ socket = mock_socket_without_timeout(make_chunks(simple_response))
204
+ reader = subject.new(socket, 0)
205
+ expect(reader.headers).to eq({
206
+ 'cache-control' => 'no-cache',
207
+ 'content-type' => 'text/event-stream'
208
+ })
209
+ end
210
+
211
+ it "can read entire response body" do
212
+ socket = mock_socket_without_timeout(make_chunks(simple_response))
213
+ reader = subject.new(socket, 0)
214
+ expect(reader.read_all).to eq("line1\r\nline2\n\r\n")
215
+ end
216
+
217
+ it "can read response body as lines" do
218
+ socket = mock_socket_without_timeout(make_chunks(simple_response))
219
+ reader = subject.new(socket, 0)
220
+ expect(reader.read_lines.to_a).to eq([
221
+ "line1\r\n",
222
+ "line2\n",
223
+ "\r\n"
224
+ ])
225
+ end
226
+
227
+ it "handles chunked encoding" do
228
+ chunked_response = <<-EOT
229
+ HTTP/1.1 200 OK
230
+ Content-Type: text/plain
231
+ Transfer-Encoding: chunked
232
+
233
+ 6\r
234
+ things\r
235
+ A\r
236
+ and stuff\r
237
+ 0\r
238
+ \r
239
+ EOT
240
+ socket = mock_socket_without_timeout(make_chunks(chunked_response))
241
+ reader = subject.new(socket, 0)
242
+ expect(reader.read_all).to eq("things and stuff")
243
+ end
244
+
245
+ it "raises error if response ends without complete headers" do
246
+ malformed_response = <<-EOT
247
+ HTTP/1.1 200 OK
248
+ Cache-Control: no-cache
249
+ EOT
250
+ socket = mock_socket_without_timeout(make_chunks(malformed_response))
251
+ expect { subject.new(socket, 0) }.to raise_error(EOFError)
252
+ end
253
+
254
+ it "throws timeout if thrown by socket read" do
255
+ socket = mock_socket_with_timeout(make_chunks(simple_response))
256
+ reader = subject.new(socket, 0)
257
+ lines = reader.read_lines
258
+ lines.next
259
+ lines.next
260
+ lines.next
261
+ expect { lines.next }.to raise_error(Socketry::TimeoutError)
262
+ end
263
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ldclient-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 5.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: 2018-05-11 00:00:00.000000000 Z
11
+ date: 2018-06-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -253,61 +253,33 @@ dependencies:
253
253
  - !ruby/object:Gem::Version
254
254
  version: '0.2'
255
255
  - !ruby/object:Gem::Dependency
256
- name: ld-celluloid-eventsource
256
+ name: http_tools
257
257
  requirement: !ruby/object:Gem::Requirement
258
258
  requirements:
259
259
  - - "~>"
260
260
  - !ruby/object:Gem::Version
261
- version: 0.11.0
261
+ version: 0.4.5
262
262
  type: :runtime
263
263
  prerelease: false
264
264
  version_requirements: !ruby/object:Gem::Requirement
265
265
  requirements:
266
266
  - - "~>"
267
267
  - !ruby/object:Gem::Version
268
- version: 0.11.0
268
+ version: 0.4.5
269
269
  - !ruby/object:Gem::Dependency
270
- name: celluloid
270
+ name: socketry
271
271
  requirement: !ruby/object:Gem::Requirement
272
272
  requirements:
273
273
  - - "~>"
274
274
  - !ruby/object:Gem::Version
275
- version: 0.18.0.pre
275
+ version: 0.5.1
276
276
  type: :runtime
277
277
  prerelease: false
278
278
  version_requirements: !ruby/object:Gem::Requirement
279
279
  requirements:
280
280
  - - "~>"
281
281
  - !ruby/object:Gem::Version
282
- version: 0.18.0.pre
283
- - !ruby/object:Gem::Dependency
284
- name: nio4r
285
- requirement: !ruby/object:Gem::Requirement
286
- requirements:
287
- - - "<"
288
- - !ruby/object:Gem::Version
289
- version: '3'
290
- type: :runtime
291
- prerelease: false
292
- version_requirements: !ruby/object:Gem::Requirement
293
- requirements:
294
- - - "<"
295
- - !ruby/object:Gem::Version
296
- version: '3'
297
- - !ruby/object:Gem::Dependency
298
- name: waitutil
299
- requirement: !ruby/object:Gem::Requirement
300
- requirements:
301
- - - '='
302
- - !ruby/object:Gem::Version
303
- version: '0.2'
304
- type: :runtime
305
- prerelease: false
306
- version_requirements: !ruby/object:Gem::Requirement
307
- requirements:
308
- - - '='
309
- - !ruby/object:Gem::Version
310
- version: '0.2'
282
+ version: 0.5.1
311
283
  description: Official LaunchDarkly SDK for Ruby
312
284
  email:
313
285
  - team@launchdarkly.com
@@ -349,7 +321,13 @@ files:
349
321
  - lib/ldclient-rb/simple_lru_cache.rb
350
322
  - lib/ldclient-rb/stream.rb
351
323
  - lib/ldclient-rb/user_filter.rb
324
+ - lib/ldclient-rb/util.rb
352
325
  - lib/ldclient-rb/version.rb
326
+ - lib/sse_client.rb
327
+ - lib/sse_client/backoff.rb
328
+ - lib/sse_client/sse_client.rb
329
+ - lib/sse_client/sse_events.rb
330
+ - lib/sse_client/streaming_http.rb
353
331
  - scripts/release.sh
354
332
  - spec/config_spec.rb
355
333
  - spec/evaluation_spec.rb
@@ -365,11 +343,16 @@ files:
365
343
  - spec/in_memory_feature_store_spec.rb
366
344
  - spec/ldclient_spec.rb
367
345
  - spec/newrelic_spec.rb
346
+ - spec/polling_spec.rb
368
347
  - spec/redis_feature_store_spec.rb
369
348
  - spec/requestor_spec.rb
370
349
  - spec/segment_store_spec_base.rb
371
350
  - spec/simple_lru_cache_spec.rb
372
351
  - spec/spec_helper.rb
352
+ - spec/sse_client/sse_client_spec.rb
353
+ - spec/sse_client/sse_events_spec.rb
354
+ - spec/sse_client/sse_shared.rb
355
+ - spec/sse_client/streaming_http_spec.rb
373
356
  - spec/store_spec.rb
374
357
  - spec/stream_spec.rb
375
358
  - spec/user_filter_spec.rb
@@ -413,11 +396,16 @@ test_files:
413
396
  - spec/in_memory_feature_store_spec.rb
414
397
  - spec/ldclient_spec.rb
415
398
  - spec/newrelic_spec.rb
399
+ - spec/polling_spec.rb
416
400
  - spec/redis_feature_store_spec.rb
417
401
  - spec/requestor_spec.rb
418
402
  - spec/segment_store_spec_base.rb
419
403
  - spec/simple_lru_cache_spec.rb
420
404
  - spec/spec_helper.rb
405
+ - spec/sse_client/sse_client_spec.rb
406
+ - spec/sse_client/sse_events_spec.rb
407
+ - spec/sse_client/sse_shared.rb
408
+ - spec/sse_client/streaming_http_spec.rb
421
409
  - spec/store_spec.rb
422
410
  - spec/stream_spec.rb
423
411
  - spec/user_filter_spec.rb