plum 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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