http_streaming_client 0.8.11 → 0.9.0
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 +4 -4
- data/CHANGELOG +5 -0
- data/README.md +1 -1
- data/lib/http_streaming_client/client.rb +21 -12
- data/lib/http_streaming_client/decoders/chunked.rb +98 -0
- data/lib/http_streaming_client/decoders/gzip.rb +46 -27
- data/lib/http_streaming_client/version.rb +1 -1
- data/spec/reconnect_spec.rb +4 -3
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5b7c831d1e6fc8a96a13ddf7966f34da78fd5d2
|
4
|
+
data.tar.gz: b73177edd8c134f63d5cc45fd7e0607a6e6d792a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db040d0205a52db9e8677a6d1ff66c226fbb7c516f688ba01513ddeb5c6b7d7493981c50ef990cc3b42503583b536482e3efb5b04813cb47ae86c6021d81b057
|
7
|
+
data.tar.gz: 956e0283d6173983c79b364b212fc3a403c958ca409b8b2c4545a828dba2dd831ac496b2a451fe081c47416d9110e5efe3bc92e61b568c22d8a489f17c8249d3
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
http_streaming_client 0.9.0 (04.28.2014)
|
2
|
+
========================================
|
3
|
+
* Tested and fixed for MRI ruby-2.0.0-p451 and JRuby jruby-1.7.12
|
4
|
+
* Fixed chunked encoding support handler, support for gzip'd blocks spanning chunks
|
5
|
+
|
1
6
|
http_streaming_client 0.8.11 ()
|
2
7
|
========================================
|
3
8
|
* Fixed warn logging for non-200 HTTP response codes
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ Ruby HTTP client with support for HTTP 1.1 streaming, GZIP and zlib compressed s
|
|
7
7
|
|
8
8
|
## Ruby Version
|
9
9
|
|
10
|
-
MRI ruby-2.0.0-
|
10
|
+
MRI ruby-2.0.0-p451 and JRuby jruby-1.7.12. Install via rvm: https://rvm.io/
|
11
11
|
|
12
12
|
## Installation
|
13
13
|
|
@@ -35,6 +35,7 @@ require "http_streaming_client/version"
|
|
35
35
|
require "http_streaming_client/custom_logger"
|
36
36
|
require "http_streaming_client/errors"
|
37
37
|
require "http_streaming_client/decoders/gzip"
|
38
|
+
require "http_streaming_client/decoders/chunked"
|
38
39
|
|
39
40
|
module HttpStreamingClient
|
40
41
|
|
@@ -231,7 +232,7 @@ module HttpStreamingClient
|
|
231
232
|
response = ""
|
232
233
|
|
233
234
|
if response_compression then
|
234
|
-
logger.debug "
|
235
|
+
logger.debug "chunked transfer encoding with compression detected"
|
235
236
|
if block_given? then
|
236
237
|
decoder = HttpStreamingClient::Decoders::GZip.new { |line|
|
237
238
|
logger.debug "read #{line.size} uncompressed bytes, decoder queue bytes:#{decoder.size}"
|
@@ -241,6 +242,17 @@ module HttpStreamingClient
|
|
241
242
|
logger.debug "read #{line.size} uncompressed bytes, #{response.size} bytes total, decoder queue bytes:#{decoder.size}"
|
242
243
|
response << line unless @interrupted }
|
243
244
|
end
|
245
|
+
else
|
246
|
+
logger.debug "chunked transfer encoding with no compression detected"
|
247
|
+
if block_given? then
|
248
|
+
decoder = HttpStreamingClient::Decoders::Chunked.new { |line|
|
249
|
+
logger.debug "read #{line.size} uncompressed bytes, decoder queue bytes:#{decoder.size}"
|
250
|
+
block.call(line) unless @interrupted }
|
251
|
+
else
|
252
|
+
decoder = HttpStreamingClient::Decoders::Chunked.new { |line|
|
253
|
+
logger.debug "read #{line.size} uncompressed bytes, #{response.size} bytes total, decoder queue bytes:#{decoder.size}"
|
254
|
+
response << line unless @interrupted }
|
255
|
+
end
|
244
256
|
end
|
245
257
|
|
246
258
|
while !socket.eof? && (line = socket.gets)
|
@@ -268,19 +280,16 @@ module HttpStreamingClient
|
|
268
280
|
logger.debug "read #{partial.size} bytes, #{remaining} bytes remaining"
|
269
281
|
end
|
270
282
|
|
271
|
-
|
272
|
-
|
273
|
-
|
283
|
+
decoder << partial
|
284
|
+
|
285
|
+
if !block_given? then
|
286
|
+
logger.debug "no block specified, returning chunk results and halting streaming response"
|
287
|
+
return response
|
274
288
|
else
|
275
|
-
if
|
276
|
-
|
277
|
-
else
|
278
|
-
return response if @interrupted
|
279
|
-
logger.debug "no block specified, returning chunk results and halting streaming response"
|
280
|
-
response << partial
|
281
|
-
return response
|
282
|
-
end
|
289
|
+
return if @interrupted and response_compression
|
290
|
+
return response if @interrupted
|
283
291
|
end
|
292
|
+
|
284
293
|
end
|
285
294
|
|
286
295
|
logger.debug "socket EOF detected" if socket.eof?
|
@@ -0,0 +1,98 @@
|
|
1
|
+
###########################################################################
|
2
|
+
##
|
3
|
+
## http_streaming_client
|
4
|
+
##
|
5
|
+
## Ruby HTTP client with support for HTTP 1.1 streaming, GZIP compressed
|
6
|
+
## streams, and chunked transfer encoding. Includes extensible OAuth
|
7
|
+
## support for the Adobe Analytics Firehose and Twitter Streaming APIs.
|
8
|
+
##
|
9
|
+
## David Tompkins -- 4/25/2014
|
10
|
+
## tompkins@adobe_dot_com
|
11
|
+
##
|
12
|
+
###########################################################################
|
13
|
+
##
|
14
|
+
## Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
|
15
|
+
##
|
16
|
+
## Licensed under the Apache License, Version 2.0 (the "License");
|
17
|
+
## you may not use this file except in compliance with the License.
|
18
|
+
## You may obtain a copy of the License at
|
19
|
+
##
|
20
|
+
## http://www.apache.org/licenses/LICENSE-2.0
|
21
|
+
##
|
22
|
+
## Unless required by applicable law or agreed to in writing, software
|
23
|
+
## distributed under the License is distributed on an "AS IS" BASIS,
|
24
|
+
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
25
|
+
## See the License for the specific language governing permissions and
|
26
|
+
## limitations under the License.
|
27
|
+
##
|
28
|
+
###########################################################################
|
29
|
+
|
30
|
+
require "http_streaming_client/errors"
|
31
|
+
|
32
|
+
module HttpStreamingClient
|
33
|
+
|
34
|
+
module Decoders
|
35
|
+
|
36
|
+
class Chunked
|
37
|
+
|
38
|
+
def logger
|
39
|
+
HttpStreamingClient.logger
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(&packet_callback)
|
43
|
+
logger.debug "Chunked:initialize"
|
44
|
+
@packet_callback = packet_callback
|
45
|
+
end
|
46
|
+
|
47
|
+
def <<(chunk)
|
48
|
+
return unless chunk && chunk.size > 0
|
49
|
+
chunk_io = StringIO.new(chunk)
|
50
|
+
while true
|
51
|
+
line = nonblock_readline(chunk_io)
|
52
|
+
break if line.nil?
|
53
|
+
process_line(line)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def size
|
58
|
+
logger.debug "Chunked:size"
|
59
|
+
return @line_buffer.size unless @line_buffer.nil?
|
60
|
+
return 0
|
61
|
+
end
|
62
|
+
|
63
|
+
def close
|
64
|
+
logger.debug "Chunked:close"
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def nonblock_readline(io)
|
70
|
+
@line_buffer ||= ""
|
71
|
+
ch = nil
|
72
|
+
begin
|
73
|
+
while ch = io.getc
|
74
|
+
@line_buffer += ch
|
75
|
+
if ch == "\n" then
|
76
|
+
result = @line_buffer
|
77
|
+
@line_buffer = ""
|
78
|
+
return result
|
79
|
+
end
|
80
|
+
end
|
81
|
+
rescue => e
|
82
|
+
logger.debug "nonblock_readline:error received:#{e.class}:#{e}"
|
83
|
+
return nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def process_line(line)
|
90
|
+
logger.debug "Chunked:process_line:size:#{line.nil? ? "nil" : line.size}"
|
91
|
+
if line && line.size > 0
|
92
|
+
@packet_callback.call(line) unless @packet_callback.nil?
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
###########################################################################
|
2
4
|
##
|
3
5
|
## http_streaming_client
|
@@ -37,6 +39,14 @@ module HttpStreamingClient
|
|
37
39
|
|
38
40
|
class GZip
|
39
41
|
|
42
|
+
if defined?(JRUBY_VERSION) then
|
43
|
+
# JRuby: pass at least 8k bytes to GzipReader to avoid zlib EOF
|
44
|
+
GZIP_READER_MIN_BUF_SIZE = 8192
|
45
|
+
else
|
46
|
+
# MRI: pass at least 2k bytes to GzipReader to avoid zlib EOF
|
47
|
+
GZIP_READER_MIN_BUF_SIZE = 2048
|
48
|
+
end
|
49
|
+
|
40
50
|
def logger
|
41
51
|
HttpStreamingClient.logger
|
42
52
|
end
|
@@ -46,37 +56,19 @@ module HttpStreamingClient
|
|
46
56
|
@packet_callback = packet_callback
|
47
57
|
end
|
48
58
|
|
49
|
-
def nonblock_readline(io)
|
50
|
-
@line_buffer ||= ""
|
51
|
-
ch = nil
|
52
|
-
begin
|
53
|
-
while ch = io.getc
|
54
|
-
@line_buffer += ch
|
55
|
-
if ch == "\n" then
|
56
|
-
result = @line_buffer
|
57
|
-
@line_buffer = ""
|
58
|
-
return result
|
59
|
-
end
|
60
|
-
end
|
61
|
-
rescue Zlib::GzipFile::Error
|
62
|
-
# this is raised on EOF by ZLib, return nil to indicate EOF and leave partial line in the buffer
|
63
|
-
return nil
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
59
|
def <<(compressed_packet)
|
68
60
|
return unless compressed_packet && compressed_packet.size > 0
|
69
61
|
@buf ||= GZipBufferIO.new
|
70
62
|
@buf << compressed_packet
|
71
63
|
|
72
|
-
# pass at least
|
73
|
-
if @buf.size >
|
64
|
+
# pass at least GZIP_READER_MIN_BUF_SIZE bytes to GzipReader to avoid zlib EOF
|
65
|
+
if @buf.size > GZIP_READER_MIN_BUF_SIZE then
|
74
66
|
|
75
67
|
@gzip ||= Zlib::GzipReader.new @buf
|
76
68
|
|
77
69
|
while true do
|
78
70
|
decompressed_packet = nonblock_readline(@gzip)
|
79
|
-
#logger.debug "decompressed_packet:#{decompressed_packet}"
|
71
|
+
#logger.debug "GZip:<<:decompressed_packet:#{decompressed_packet}"
|
80
72
|
break if decompressed_packet.nil?
|
81
73
|
process_decompressed_packet(decompressed_packet)
|
82
74
|
end
|
@@ -91,7 +83,7 @@ module HttpStreamingClient
|
|
91
83
|
|
92
84
|
while true do
|
93
85
|
decompressed_packet = nonblock_readline(@gzip)
|
94
|
-
#logger.debug "decompressed_packet:#{decompressed_packet}"
|
86
|
+
#logger.debug "GZip:close:decompressed_packet:#{decompressed_packet}"
|
95
87
|
break if decompressed_packet.nil?
|
96
88
|
process_decompressed_packet(decompressed_packet)
|
97
89
|
end
|
@@ -100,14 +92,40 @@ module HttpStreamingClient
|
|
100
92
|
raise HttpStreamingClient::DecoderError.new(e.message)
|
101
93
|
end
|
102
94
|
end
|
103
|
-
|
95
|
+
|
104
96
|
def size
|
105
97
|
@buf.size
|
106
98
|
end
|
107
99
|
|
100
|
+
def nonblock_readline(io)
|
101
|
+
@line_buffer ||= ""
|
102
|
+
ch = nil
|
103
|
+
begin
|
104
|
+
while ch = io.getc
|
105
|
+
@line_buffer += ch
|
106
|
+
if ch == "\n" then
|
107
|
+
result = @line_buffer
|
108
|
+
@line_buffer = ""
|
109
|
+
return result
|
110
|
+
end
|
111
|
+
end
|
112
|
+
rescue Zlib::GzipFile::Error
|
113
|
+
# this is raised on EOF by ZLib in MRI, return nil to indicate EOF and leave partial line in the buffer
|
114
|
+
logger.debug "Gzip:nonblock_readline:Zlib::GzipFile::Error:line_buffer.size:#{@line_buffer.size}"
|
115
|
+
return nil
|
116
|
+
rescue IOError
|
117
|
+
# this is raised on EOF by ZLib in JRuby, return nil to indicate EOF and leave partial line in the buffer
|
118
|
+
logger.debug "Gzip:nonblock_readline:IOError:line_buffer.size:#{@line_buffer.size}"
|
119
|
+
return nil
|
120
|
+
rescue => e
|
121
|
+
logger.debug "Gzip:nonblock_readline:error received:#{e.class}:#{e}"
|
122
|
+
raise e
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
108
126
|
protected
|
109
127
|
|
110
|
-
class GZipBufferIO <
|
128
|
+
class GZipBufferIO < StringIO
|
111
129
|
|
112
130
|
def logger
|
113
131
|
HttpStreamingClient.logger
|
@@ -116,6 +134,7 @@ module HttpStreamingClient
|
|
116
134
|
def initialize(string="")
|
117
135
|
logger.debug "GZipBufferIO:initialize"
|
118
136
|
@packet_stream = string
|
137
|
+
@packet_stream.force_encoding("BINARY")
|
119
138
|
end
|
120
139
|
|
121
140
|
def <<(string)
|
@@ -124,7 +143,7 @@ module HttpStreamingClient
|
|
124
143
|
|
125
144
|
# called by GzipReader
|
126
145
|
def readpartial(length=nil, buffer=nil)
|
127
|
-
logger.debug "GZipBufferIO:readpartial:length:#{length}:@packet_stream:#{@packet_stream.nil? ? 'nil' :
|
146
|
+
logger.debug "GZipBufferIO:readpartial:length:#{length}:@packet_stream:#{@packet_stream.nil? ? 'nil' : @packet_stream.size}"
|
128
147
|
buffer ||= ""
|
129
148
|
|
130
149
|
raise EOFError "" if @packet_stream.size == 0
|
@@ -150,14 +169,14 @@ module HttpStreamingClient
|
|
150
169
|
|
151
170
|
# called by GzipReader
|
152
171
|
def read(length=nil, buffer=nil)
|
153
|
-
logger.debug "read:length:#{length}"
|
172
|
+
logger.debug "GZipBufferIO:read:length:#{length}"
|
154
173
|
return nil if @packet_stream.size == 0
|
155
174
|
readpartial(length, buffer)
|
156
175
|
end
|
157
176
|
|
158
177
|
# called by GzipReader
|
159
178
|
def size
|
160
|
-
logger.debug "size():#{@packet_stream.size}"
|
179
|
+
logger.debug "GZipBufferIO:size():#{@packet_stream.size}"
|
161
180
|
@packet_stream.size
|
162
181
|
end
|
163
182
|
end
|
data/spec/reconnect_spec.rb
CHANGED
@@ -5,8 +5,8 @@ describe HttpStreamingClient do
|
|
5
5
|
|
6
6
|
# currently disabled, requires a server that can be killed to simulate dropped connections
|
7
7
|
|
8
|
-
it "should receive exactly 10 messages, no reconnect" do
|
9
|
-
|
8
|
+
#it "should receive exactly 10 messages, no reconnect" do
|
9
|
+
it "should receive exactly 10 messages, no reconnect", :disabled => true do
|
10
10
|
|
11
11
|
count = 0
|
12
12
|
client = HttpStreamingClient::Client.new(compression: false)
|
@@ -18,7 +18,8 @@ describe HttpStreamingClient do
|
|
18
18
|
expect(count).to be(10)
|
19
19
|
end
|
20
20
|
|
21
|
-
it "should reconnect on any error or EOF" do
|
21
|
+
#it "should reconnect on any error or EOF" do
|
22
|
+
it "should reconnect on any error or EOF", :disabled => true do
|
22
23
|
|
23
24
|
client = HttpStreamingClient::Client.new(compression: false, reconnect: true, reconnect_attempts: 5, reconnect_interval: 1)
|
24
25
|
count = 0
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: http_streaming_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Tompkins
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-04-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -117,6 +117,7 @@ files:
|
|
117
117
|
- lib/http_streaming_client/credentials/adobe.rb.sample
|
118
118
|
- lib/http_streaming_client/credentials/twitter.rb.sample
|
119
119
|
- lib/http_streaming_client/custom_logger.rb
|
120
|
+
- lib/http_streaming_client/decoders/chunked.rb
|
120
121
|
- lib/http_streaming_client/decoders/gzip.rb
|
121
122
|
- lib/http_streaming_client/errors.rb
|
122
123
|
- lib/http_streaming_client/oauth.rb
|