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 +4 -4
- data/lib/plum.rb +2 -0
- data/lib/plum/client.rb +2 -1
- data/lib/plum/client/client_session.rb +1 -1
- data/lib/plum/client/decoders.rb +51 -0
- data/lib/plum/client/legacy_client_session.rb +1 -1
- data/lib/plum/client/response.rb +18 -8
- data/lib/plum/errors.rb +16 -6
- data/lib/plum/version.rb +1 -1
- data/test/plum/client/test_decoders.rb +54 -0
- data/test/plum/client/test_response.rb +5 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7edaa74a52403fc469e096512924e275a1e0b2dc
|
4
|
+
data.tar.gz: b0e40798462d83baff094cbb4664401afe711694
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1fda3ee328f9de55f22d575cbdde788cd388fb1df15ead696cc4f86f8ce02532c458b50dc24866d3e9032345b752dccedbb6d58c5b1bfe70606c85adeeb529ff
|
7
|
+
data.tar.gz: fa4810540a9d8f81a4611cd91fb24d5de3d38543b2c366474388959e6006f99bce46846abe4834b8566a6e6470d4ec2ab3215c1ee3020401d76b79af6665b819
|
data/lib/plum.rb
CHANGED
@@ -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"
|
data/lib/plum/client.rb
CHANGED
@@ -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!
|
@@ -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
|
data/lib/plum/client/response.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
68
|
-
|
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(
|
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
|
data/lib/plum/errors.rb
CHANGED
@@ -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
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
data/lib/plum/version.rb
CHANGED
@@ -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.
|
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-
|
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
|