http_decoders 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3800af55baeac0e36064f9837b9c0aab1bdf0268
4
+ data.tar.gz: ac21d6225c4d5c3275e2ec0d58868b3f7dcf49bc
5
+ SHA512:
6
+ metadata.gz: c0141f3482e8ed76c47caf50f33fb82312adb393c2c7f22828f8c327d05c439cb429eccb3812e42ad90dd4ac2b912395d0e133cb1d33357cdd74541032faabf0
7
+ data.tar.gz: 6b7b1d5c70b8add6369ff3fba33647c573e2353586f37acdeb7485b2e5e238a38ac4857f9c68a914d105d0069902e1a5493e541ae2c9223aa044c3749dc8d196
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,7 @@
1
+ sudo: false
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.3.3
6
+ - 2.4.0
7
+ before_install: gem install bundler -v 1.14.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in http_decoders.gemspec
4
+ gemspec
@@ -0,0 +1,44 @@
1
+ Copyright (c) 2017 Smartly.io Solutions Oy
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+
23
+ This license applies to parts originating from the https://github.com/igrigorik/em-http-request repository:
24
+
25
+ Copyright (c) 2011 Ilya Grigorik
26
+
27
+ Permission is hereby granted, free of charge, to any person obtaining
28
+ a copy of this software and associated documentation files (the
29
+ "Software"), to deal in the Software without restriction, including
30
+ without limitation the rights to use, copy, modify, merge, publish,
31
+ distribute, sublicense, and/or sell copies of the Software, and to
32
+ permit persons to whom the Software is furnished to do so, subject to
33
+ the following conditions:
34
+
35
+ The above copyright notice and this permission notice shall be
36
+ included in all copies or substantial portions of the Software.
37
+
38
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
39
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
40
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
41
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
42
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
43
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
44
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,40 @@
1
+ # HttpDecoders
2
+
3
+ Decompress HTTP responses. Supports deflate and gzip. Extracted from [em-http-request](https://github.com/igrigorik/em-http-request).
4
+
5
+ [![Build Status](https://travis-ci.org/smartlyio/http_decoders.svg?branch=master)](https://travis-ci.org/smartlyio/http_decoders)
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'http_decoders'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install http_decoders
22
+
23
+ ## Usage
24
+
25
+ ```ruby
26
+ decoder = HttpDecoders.decoder_for_encoding('gzip')
27
+ decompressed = decoder << data
28
+ decompressed += decoder.finalize!
29
+
30
+ ```
31
+
32
+ ## Development
33
+
34
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
35
+
36
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
37
+
38
+ ## Contributing
39
+
40
+ Bug reports and pull requests are welcome on GitHub at https://github.com/smartlyio/http_decoders.
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'http_decoders'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'http_decoders/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'http_decoders'
8
+ spec.version = HttpDecoders::VERSION
9
+ spec.authors = ['Ville Lautanala']
10
+ spec.email = ['lautis@gmail.com']
11
+ spec.license = 'MIT'
12
+
13
+ spec.summary = 'Unified callback interface to decompression libraries.'
14
+ spec.description = 'Unified callback interface to decompression libraries.'
15
+ spec.homepage = 'https://github.com/smartlyio/http_decoders'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = 'exe'
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.14'
25
+ spec.add_development_dependency 'rake', '~> 12.0'
26
+ spec.add_development_dependency 'rspec', '~> 3.5'
27
+ end
@@ -0,0 +1,306 @@
1
+ require 'http_decoders/version'
2
+
3
+ require 'zlib'
4
+ require 'stringio'
5
+
6
+ # Unified callback interface to decompression libraries.
7
+ module HttpDecoders
8
+
9
+ class DecoderError < StandardError
10
+ end
11
+
12
+ class << self
13
+ def accepted_encodings
14
+ DECODERS.inject([]) { |r, d| r + d.encoding_names }
15
+ end
16
+
17
+ def decoder_for_encoding(encoding)
18
+ DECODERS.each { |d|
19
+ return d if d.encoding_names.include? encoding
20
+ }
21
+ nil
22
+ end
23
+ end
24
+
25
+ class Base
26
+ def self.encoding_names
27
+ name = to_s.split('::').last.downcase
28
+ [name]
29
+ end
30
+
31
+ ##
32
+ # chunk_callback:: [Block] To handle a decompressed chunk
33
+ def initialize(&chunk_callback)
34
+ @chunk_callback = chunk_callback
35
+ end
36
+
37
+ def <<(compressed)
38
+ return unless compressed && compressed.size > 0
39
+
40
+ decompressed = decompress(compressed)
41
+ receive_decompressed decompressed
42
+ end
43
+
44
+ def finalize!
45
+ decompressed = finalize
46
+ receive_decompressed decompressed
47
+ end
48
+
49
+ private
50
+
51
+ def receive_decompressed(decompressed)
52
+ if decompressed && decompressed.size > 0
53
+ @chunk_callback.call(decompressed)
54
+ end
55
+ end
56
+
57
+ protected
58
+
59
+ ##
60
+ # Must return a part of decompressed
61
+ def decompress(compressed)
62
+ nil
63
+ end
64
+
65
+ ##
66
+ # May return last part
67
+ def finalize
68
+ nil
69
+ end
70
+ end
71
+
72
+ class Deflate < Base
73
+ def decompress(compressed)
74
+ begin
75
+ @zstream ||= Zlib::Inflate.new(-Zlib::MAX_WBITS)
76
+ @zstream.inflate(compressed)
77
+ rescue Zlib::Error
78
+ raise DecoderError
79
+ end
80
+ end
81
+
82
+ def finalize
83
+ return nil unless @zstream
84
+
85
+ begin
86
+ r = @zstream.inflate(nil)
87
+ @zstream.close
88
+ r
89
+ rescue Zlib::Error
90
+ raise DecoderError
91
+ end
92
+ end
93
+ end
94
+
95
+ ##
96
+ # Partial implementation of RFC 1952 to extract the deflate stream from a gzip file
97
+ class GZipHeader
98
+ def initialize
99
+ @state = :begin
100
+ @data = ""
101
+ @pos = 0
102
+ end
103
+
104
+ def finished?
105
+ @state == :finish
106
+ end
107
+
108
+ def read(n, buffer)
109
+ if (@pos + n) <= @data.size
110
+ buffer << @data[@pos..(@pos + n - 1)]
111
+ @pos += n
112
+ return true
113
+ else
114
+ return false
115
+ end
116
+ end
117
+
118
+ def readbyte
119
+ if (@pos + 1) <= @data.size
120
+ @pos += 1
121
+ @data.getbyte(@pos - 1)
122
+ end
123
+ end
124
+
125
+ def eof?
126
+ @pos >= @data.size
127
+ end
128
+
129
+ def extract_stream(compressed)
130
+ @data << compressed
131
+
132
+ while !eof? && !finished?
133
+ buffer = ""
134
+
135
+ case @state
136
+ when :begin
137
+ break if !read(10, buffer)
138
+
139
+ if buffer.getbyte(0) != 0x1f || buffer.getbyte(1) != 0x8b
140
+ raise DecoderError.new("magic header not found")
141
+ end
142
+
143
+ if buffer.getbyte(2) != 0x08
144
+ raise DecoderError.new("unknown compression method")
145
+ end
146
+
147
+ @flags = buffer.getbyte(3)
148
+ if (@flags & 0xe0).nonzero?
149
+ raise DecoderError.new("unknown header flags set")
150
+ end
151
+
152
+ # We don't care about these values, I'm leaving the code for reference
153
+ # @time = buffer[4..7].unpack("V")[0] # little-endian uint32
154
+ # @extra_flags = buffer.getbyte(8)
155
+ # @os = buffer.getbyte(9)
156
+
157
+ @state = :extra_length
158
+
159
+ when :extra_length
160
+ if (@flags & 0x04).nonzero?
161
+ break if !read(2, buffer)
162
+ @extra_length = buffer.unpack("v")[0] # little-endian uint16
163
+ @state = :extra
164
+ else
165
+ @state = :extra
166
+ end
167
+
168
+ when :extra
169
+ if (@flags & 0x04).nonzero?
170
+ break if read(@extra_length, buffer)
171
+ @state = :name
172
+ else
173
+ @state = :name
174
+ end
175
+
176
+ when :name
177
+ if (@flags & 0x08).nonzero?
178
+ while !(buffer = readbyte).nil?
179
+ if buffer == 0
180
+ @state = :comment
181
+ break
182
+ end
183
+ end
184
+ else
185
+ @state = :comment
186
+ end
187
+
188
+ when :comment
189
+ if (@flags & 0x10).nonzero?
190
+ while !(buffer = readbyte).nil?
191
+ if buffer == 0
192
+ @state = :hcrc
193
+ break
194
+ end
195
+ end
196
+ else
197
+ @state = :hcrc
198
+ end
199
+
200
+ when :hcrc
201
+ if (@flags & 0x02).nonzero?
202
+ break if !read(2, buffer)
203
+ @state = :finish
204
+ else
205
+ @state = :finish
206
+ end
207
+ end
208
+ end
209
+
210
+ if finished?
211
+ compressed[(@pos - (@data.length - compressed.length))..-1]
212
+ else
213
+ ""
214
+ end
215
+ end
216
+ end
217
+
218
+ class GZip < Base
219
+ MAGIC_STRING = "\x1F\x8B".force_encoding(Encoding::ASCII_8BIT).freeze
220
+
221
+ def self.encoding_names
222
+ %w(gzip compressed)
223
+ end
224
+
225
+ def initialize
226
+ super
227
+ @buffer = nil
228
+ end
229
+
230
+ def decompress(compressed)
231
+ compressed
232
+ .force_encoding(Encoding::ASCII_8BIT)
233
+ .each_line(MAGIC_STRING)
234
+ .map { |chunk| decompress_chunk(chunk) }
235
+ .join('')
236
+ end
237
+
238
+ def finalize
239
+ if @zstream
240
+ if !@zstream.finished?
241
+ r = @zstream.finish
242
+ end
243
+ @zstream.close
244
+ r
245
+ else
246
+ nil
247
+ end
248
+ rescue Zlib::Error
249
+ raise DecoderError
250
+ end
251
+
252
+ private
253
+
254
+ def decompress_chunk(compressed)
255
+ @header ||= GZipHeader.new
256
+ if !@header.finished?
257
+ compressed = @header.extract_stream(compressed)
258
+ end
259
+
260
+ if @buffer
261
+ @buffer << compressed
262
+ decompress_buffer
263
+ else
264
+ @zstream ||= Zlib::Inflate.new(-Zlib::MAX_WBITS)
265
+ decompressed = @zstream.inflate(compressed)
266
+
267
+ # Gzip tailer with CRC32 and length is included after deflate stream.
268
+ # Buffer input including magic byte
269
+ if @header.finished? && @zstream.finished?
270
+ @buffer = []
271
+ @buffer << MAGIC_STRING if compressed.end_with?(MAGIC_STRING)
272
+ end
273
+ decompressed.to_s
274
+ end
275
+ rescue Zlib::Error
276
+ raise DecoderError
277
+ end
278
+
279
+ def decompress_buffer
280
+ next_stream = find_stream(@buffer.join) if @buffer
281
+ return unless next_stream
282
+
283
+ @buffer = nil
284
+ decompressed = finalize.to_s
285
+ reset
286
+ decompressed + decompress(next_stream)
287
+ end
288
+
289
+ def find_stream(decompressed)
290
+ index = decompressed.force_encoding(Encoding::ASCII_8BIT).index(MAGIC_STRING)
291
+ if index
292
+ decompressed[index..-1]
293
+ else
294
+ nil
295
+ end
296
+ end
297
+
298
+ def reset
299
+ @header = nil
300
+ @zstream = nil
301
+ end
302
+ end
303
+
304
+ DECODERS = [Deflate, GZip]
305
+
306
+ end
@@ -0,0 +1,3 @@
1
+ module HttpDecoders
2
+ VERSION = '0.1.0'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: http_decoders
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ville Lautanala
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-04-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '12.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '12.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.5'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.5'
55
+ description: Unified callback interface to decompression libraries.
56
+ email:
57
+ - lautis@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - bin/console
70
+ - bin/setup
71
+ - http_decoders.gemspec
72
+ - lib/http_decoders.rb
73
+ - lib/http_decoders/version.rb
74
+ homepage: https://github.com/smartlyio/http_decoders
75
+ licenses:
76
+ - MIT
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.5.1
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: Unified callback interface to decompression libraries.
98
+ test_files: []