plum 0.2.0 → 0.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4194e9fa59a660fc839ea356770a05a627dba684
4
- data.tar.gz: 5b4f22bd734f7d7df5dca01667716ecc3dc4e51e
3
+ metadata.gz: 7edaa74a52403fc469e096512924e275a1e0b2dc
4
+ data.tar.gz: b0e40798462d83baff094cbb4664401afe711694
5
5
  SHA512:
6
- metadata.gz: 987380963b7702bd5a6aa0f4ab817ae515f34cbd1b74ba48ea2b46f5e27169173c80821af49f353bc61363a634107290bc4a43abe599a21707cbaadbc6446f0f
7
- data.tar.gz: bd645808edbcf763c896e00085ab3d69d89639f44b1de09c3ac39f7ed2c79d6c5f313f2791dad57c43d31d68eeb9b59e745ba3e4040835e5670543f9284c905e
6
+ metadata.gz: 1fda3ee328f9de55f22d575cbdde788cd388fb1df15ead696cc4f86f8ce02532c458b50dc24866d3e9032345b752dccedbb6d58c5b1bfe70606c85adeeb529ff
7
+ data.tar.gz: fa4810540a9d8f81a4611cd91fb24d5de3d38543b2c366474388959e6006f99bce46846abe4834b8566a6e6470d4ec2ab3215c1ee3020401d76b79af6665b819
@@ -2,6 +2,7 @@ require "openssl"
2
2
  require "socket"
3
3
  require "base64"
4
4
  require "set"
5
+ require "zlib"
5
6
  require "plum/version"
6
7
  require "plum/errors"
7
8
  require "plum/binary_string"
@@ -24,6 +25,7 @@ require "plum/server/https_connection"
24
25
  require "plum/server/http_connection"
25
26
  require "plum/client"
26
27
  require "plum/client/response"
28
+ require "plum/client/decoders"
27
29
  require "plum/client/connection"
28
30
  require "plum/client/client_session"
29
31
  require "plum/client/legacy_client_session"
@@ -9,6 +9,7 @@ module Plum
9
9
  ssl_context: nil,
10
10
  http2_settings: {},
11
11
  user_agent: "plum/#{Plum::VERSION}",
12
+ auto_decode: true,
12
13
  }.freeze
13
14
 
14
15
  attr_reader :host, :port, :config
@@ -79,7 +80,7 @@ module Plum
79
80
  # @param block [Proc] if passed, it will be called when received response headers.
80
81
  def request(headers, body, options = {}, &block)
81
82
  raise ArgumentError, ":method and :path headers are required" unless headers[":method"] && headers[":path"]
82
- @session.request(headers, body, options, &block)
83
+ @session.request(headers, body, @config.merge(options), &block)
83
84
  end
84
85
 
85
86
  # @!method get!
@@ -42,7 +42,7 @@ module Plum
42
42
  ":scheme" => @config[:scheme]
43
43
  }.merge(headers)
44
44
 
45
- response = Response.new
45
+ response = Response.new(**options)
46
46
  @responses << response
47
47
  stream = @plum.open_stream
48
48
  stream.send_headers(headers, end_stream: !body)
@@ -0,0 +1,51 @@
1
+ module Plum
2
+ module Decoders
3
+ class Base
4
+ def decode(chunk)
5
+ chunk
6
+ end
7
+
8
+ def finish
9
+ end
10
+ end
11
+
12
+ # `deflate` is not just deflate, wrapped by zlib format (RFC 1950)
13
+ class Deflate < Base
14
+ def initialize
15
+ @inflate = Zlib::Inflate.new(Zlib::MAX_WBITS)
16
+ end
17
+
18
+ def decode(chunk)
19
+ @inflate.inflate(chunk)
20
+ rescue Zlib::Error => e
21
+ raise DecoderError.new("failed to decode chunk", e)
22
+ end
23
+
24
+ def finish
25
+ @inflate.finish
26
+ rescue Zlib::Error => e
27
+ raise DecoderError.new("failed to finalize", e)
28
+ end
29
+ end
30
+
31
+ class GZip < Base
32
+ def initialize
33
+ @stream = Zlib::Inflate.new(Zlib::MAX_WBITS + 16)
34
+ end
35
+
36
+ def decode(chunk)
37
+ @stream.inflate(chunk)
38
+ rescue Zlib::Error => e
39
+ raise DecoderError.new("failed to decode chunk", e)
40
+ end
41
+
42
+ def finish
43
+ @stream.finish
44
+ rescue Zlib::Error => e
45
+ raise DecoderError.new("failed to finalize", e)
46
+ end
47
+ end
48
+
49
+ DECODERS = { "gzip" => GZip, "deflate" => Deflate }.freeze
50
+ end
51
+ end
@@ -40,7 +40,7 @@ module Plum
40
40
  end
41
41
  end
42
42
 
43
- response = Response.new
43
+ response = Response.new(**options)
44
44
  @requests << [response, headers, body, chunked, headers_cb]
45
45
  consume_queue
46
46
  response
@@ -6,11 +6,12 @@ module Plum
6
6
  attr_reader :headers
7
7
 
8
8
  # @api private
9
- def initialize
9
+ def initialize(auto_decode: true, **options)
10
10
  @body = Queue.new
11
11
  @finished = false
12
12
  @failed = false
13
13
  @body = []
14
+ @auto_decode = auto_decode
14
15
  end
15
16
 
16
17
  # Returns the HTTP status code.
@@ -54,7 +55,7 @@ module Plum
54
55
  def on_finish(&block)
55
56
  raise ArgumentError, "block must be given" unless block_given?
56
57
  if finished?
57
- block.call
58
+ yield
58
59
  else
59
60
  @on_finish = block
60
61
  end
@@ -64,21 +65,20 @@ module Plum
64
65
  # @return [String] the whole response body
65
66
  def body
66
67
  raise "Body already read" if @on_chunk
67
- if finished?
68
- @body.join
69
- else
70
- raise "Response body is not complete"
71
- end
68
+ raise "Response body is not complete" unless finished?
69
+ @body.join
72
70
  end
73
71
 
74
72
  # @api private
75
73
  def _headers(raw_headers)
76
74
  # response headers should not have duplicates
77
75
  @headers = raw_headers.to_h.freeze
76
+ @decoder = setup_decoder
78
77
  end
79
78
 
80
79
  # @api private
81
- def _chunk(chunk)
80
+ def _chunk(encoded)
81
+ chunk = @decoder.decode(encoded)
82
82
  if @on_chunk
83
83
  @on_chunk.call(chunk)
84
84
  else
@@ -89,6 +89,7 @@ module Plum
89
89
  # @api private
90
90
  def _finish
91
91
  @finished = true
92
+ @decoder.finish
92
93
  @on_finish.call if @on_finish
93
94
  end
94
95
 
@@ -96,5 +97,14 @@ module Plum
96
97
  def _fail
97
98
  @failed = true
98
99
  end
100
+
101
+ private
102
+ def setup_decoder
103
+ if @auto_decode
104
+ klass = Decoders::DECODERS[@headers["content-encoding"]]
105
+ end
106
+ klass ||= Decoders::Base
107
+ klass.new
108
+ end
99
109
  end
100
110
  end
@@ -31,6 +31,14 @@ module Plum
31
31
  ERROR_CODES[@http2_error_type]
32
32
  end
33
33
  end
34
+
35
+ class RemoteHTTPError < HTTPError; end
36
+ class RemoteConnectionError < RemoteHTTPError; end
37
+ class RemoteStreamError < RemoteHTTPError; end
38
+ class LocalHTTPError < HTTPError; end
39
+ class LocalConnectionError < LocalHTTPError; end
40
+ class LocalStreamError < LocalHTTPError; end
41
+
34
42
  class LegacyHTTPError < Error
35
43
  attr_reader :headers, :data, :parser
36
44
 
@@ -41,10 +49,12 @@ module Plum
41
49
  end
42
50
  end
43
51
 
44
- class RemoteHTTPError < HTTPError; end
45
- class RemoteConnectionError < RemoteHTTPError; end
46
- class RemoteStreamError < RemoteHTTPError; end
47
- class LocalHTTPError < HTTPError; end
48
- class LocalConnectionError < LocalHTTPError; end
49
- class LocalStreamError < LocalHTTPError; end
52
+ class DecoderError < Error
53
+ attr_reader :inner_error
54
+
55
+ def initialize(message, inner_error = nil)
56
+ super(message)
57
+ @inner_error = inner_error
58
+ end
59
+ end
50
60
  end
@@ -1,4 +1,4 @@
1
1
  # -*- frozen-string-literal: true -*-
2
2
  module Plum
3
- VERSION = "0.2.0"
3
+ VERSION = "0.2.1"
4
4
  end
@@ -0,0 +1,54 @@
1
+ require "test_helper"
2
+
3
+ using Plum::BinaryString
4
+ class DecodersTest < Minitest::Test
5
+ def test_base_decode
6
+ decoder = Decoders::Base.new
7
+ assert_equal("abc", decoder.decode("abc"))
8
+ end
9
+
10
+ def test_base_finish
11
+ decoder = Decoders::Base.new
12
+ decoder.finish
13
+ end
14
+
15
+ def test_deflate_decode
16
+ decoder = Decoders::Deflate.new
17
+ assert_equal("hello", decoder.decode("\x78\x9c\xcb\x48\xcd\xc9\xc9\x07\x00\x06\x2c\x02\x15"))
18
+ end
19
+
20
+ def test_deflate_decode_error
21
+ decoder = Decoders::Deflate.new
22
+ assert_raises(DecoderError) {
23
+ decoder.decode("\x79\x9c\xcb\x48\xcd\xc9\xc9\x07\x00\x06\x2c\x02\x15")
24
+ }
25
+ end
26
+
27
+ def test_deflate_finish_error
28
+ decoder = Decoders::Deflate.new
29
+ decoder.decode("\x78\x9c\xcb\x48\xcd\xc9\xc9\x07\x00\x06\x2c\x02")
30
+ assert_raises(DecoderError) {
31
+ decoder.finish
32
+ }
33
+ end
34
+
35
+ def test_gzip_decode
36
+ decoder = Decoders::GZip.new
37
+ assert_equal("hello", decoder.decode("\x1f\x8b\x08\x00\x1a\x96\xe0\x4c\x00\x03\xcb\x48\xcd\xc9\xc9\x07\x00\x86\xa6\x10\x36\x05\x00\x00\x00"))
38
+ end
39
+
40
+ def test_gzip_decode_error
41
+ decoder = Decoders::GZip.new
42
+ assert_raises(DecoderError) {
43
+ decoder.decode("\x2f\x8b\x08\x00\x1a\x96\xe0\x4c\x00\x03\xcb\x48\xcd\xc9\xc9\x07\x00\x86\xa6\x10\x36\x05\x00\x00\x00")
44
+ }
45
+ end
46
+
47
+ def test_gzip_finish_error
48
+ decoder = Decoders::GZip.new
49
+ decoder.decode("\x1f\x8b\x08\x00\x1a\x96")
50
+ assert_raises(DecoderError) {
51
+ decoder.finish
52
+ }
53
+ end
54
+ end
@@ -4,6 +4,7 @@ using Plum::BinaryString
4
4
  class ResponseTest < Minitest::Test
5
5
  def test_finished
6
6
  resp = Response.new
7
+ resp._headers({})
7
8
  assert_equal(false, resp.finished?)
8
9
  resp._finish
9
10
  assert_equal(true, resp.finished?)
@@ -34,6 +35,7 @@ class ResponseTest < Minitest::Test
34
35
 
35
36
  def test_body
36
37
  resp = Response.new
38
+ resp._headers({})
37
39
  resp._chunk("a")
38
40
  resp._chunk("b")
39
41
  resp._finish
@@ -42,6 +44,7 @@ class ResponseTest < Minitest::Test
42
44
 
43
45
  def test_body_not_finished
44
46
  resp = Response.new
47
+ resp._headers({})
45
48
  resp._chunk("a")
46
49
  resp._chunk("b")
47
50
  assert_raises { # TODO
@@ -51,6 +54,7 @@ class ResponseTest < Minitest::Test
51
54
 
52
55
  def test_on_chunk
53
56
  resp = Response.new
57
+ resp._headers({})
54
58
  res = []
55
59
  resp._chunk("a")
56
60
  resp._chunk("b")
@@ -63,6 +67,7 @@ class ResponseTest < Minitest::Test
63
67
 
64
68
  def test_on_finish
65
69
  resp = Response.new
70
+ resp._headers({})
66
71
  ran = false
67
72
  resp.on_finish { ran = true }
68
73
  resp._finish
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plum
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - rhenium
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-09 00:00:00.000000000 Z
11
+ date: 2015-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -178,6 +178,7 @@ files:
178
178
  - lib/plum/client.rb
179
179
  - lib/plum/client/client_session.rb
180
180
  - lib/plum/client/connection.rb
181
+ - lib/plum/client/decoders.rb
181
182
  - lib/plum/client/legacy_client_session.rb
182
183
  - lib/plum/client/response.rb
183
184
  - lib/plum/client/upgrade_client_session.rb
@@ -211,6 +212,7 @@ files:
211
212
  - plum.gemspec
212
213
  - test/plum/client/test_client.rb
213
214
  - test/plum/client/test_connection.rb
215
+ - test/plum/client/test_decoders.rb
214
216
  - test/plum/client/test_legacy_client_session.rb
215
217
  - test/plum/client/test_response.rb
216
218
  - test/plum/client/test_upgrade_client_session.rb
@@ -268,6 +270,7 @@ summary: An HTTP/2 Library for Ruby
268
270
  test_files:
269
271
  - test/plum/client/test_client.rb
270
272
  - test/plum/client/test_connection.rb
273
+ - test/plum/client/test_decoders.rb
271
274
  - test/plum/client/test_legacy_client_session.rb
272
275
  - test/plum/client/test_response.rb
273
276
  - test/plum/client/test_upgrade_client_session.rb