ld-eventsource 1.0.3 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.circleci/config.yml +28 -81
- data/.gitignore +2 -0
- data/.ldrelease/circleci/linux/execute.sh +18 -0
- data/.ldrelease/circleci/mac/execute.sh +18 -0
- data/.ldrelease/circleci/template/build.sh +19 -0
- data/.ldrelease/circleci/template/gems-setup.sh +16 -0
- data/.ldrelease/circleci/template/prepare.sh +17 -0
- data/.ldrelease/circleci/template/publish.sh +19 -0
- data/.ldrelease/circleci/template/test.sh +10 -0
- data/.ldrelease/circleci/template/update-version.sh +8 -0
- data/.ldrelease/circleci/windows/execute.ps1 +19 -0
- data/.ldrelease/config.yml +13 -0
- data/CHANGELOG.md +19 -0
- data/README.md +2 -2
- data/ld-eventsource.gemspec +3 -3
- data/lib/ld-eventsource/client.rb +95 -21
- data/lib/ld-eventsource/version.rb +1 -1
- data/spec/backoff_spec.rb +2 -0
- data/spec/client_spec.rb +81 -6
- data/spec/http_stub.rb +2 -0
- metadata +34 -24
- data/Gemfile.lock +0 -46
- data/lib/ld-eventsource/impl/streaming_http.rb +0 -222
- data/spec/streaming_http_spec.rb +0 -263
data/spec/streaming_http_spec.rb
DELETED
@@ -1,263 +0,0 @@
|
|
1
|
-
require "ld-eventsource/impl/streaming_http"
|
2
|
-
require "socketry"
|
3
|
-
require "http_stub"
|
4
|
-
|
5
|
-
#
|
6
|
-
# End-to-end tests of HTTP requests against a real server
|
7
|
-
#
|
8
|
-
describe SSE::Impl::StreamingHTTPConnection do
|
9
|
-
subject { SSE::Impl::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"), headers: headers)) 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"))) 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"))) 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"))) 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"))) 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, read_timeout: 0.25) }.to raise_error(SSE::Errors::ReadTimeoutError)
|
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: proxy.base_uri)) 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: proxy.base_uri) }.to raise_error(SSE::Errors::HTTPProxyError)
|
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"))) 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: proxy.base_uri)) 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::Impl::HTTPResponseReader do
|
158
|
-
subject { SSE::Impl::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(SSE::Errors::ReadTimeoutError)
|
262
|
-
end
|
263
|
-
end
|