plum 0.0.3 → 0.1.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/Gemfile +1 -1
- data/README.md +35 -6
- data/bin/plum +7 -0
- data/examples/rack.ru +22 -0
- data/lib/plum/binary_string.rb +5 -5
- data/lib/plum/connection.rb +24 -25
- data/lib/plum/connection_utils.rb +1 -4
- data/lib/plum/errors.rb +1 -1
- data/lib/plum/event_emitter.rb +7 -5
- data/lib/plum/flow_control.rb +19 -12
- data/lib/plum/frame.rb +60 -50
- data/lib/plum/frame_factory.rb +39 -5
- data/lib/plum/frame_utils.rb +0 -3
- data/lib/plum/hpack/constants.rb +49 -49
- data/lib/plum/hpack/context.rb +4 -3
- data/lib/plum/hpack/decoder.rb +55 -45
- data/lib/plum/hpack/encoder.rb +15 -20
- data/lib/plum/http_connection.rb +19 -3
- data/lib/plum/https_connection.rb +21 -5
- data/lib/plum/rack/cli.rb +130 -0
- data/lib/plum/rack/config.rb +28 -0
- data/lib/plum/rack/connection.rb +169 -0
- data/lib/plum/rack/dsl.rb +44 -0
- data/lib/plum/rack/listener.rb +122 -0
- data/lib/plum/rack/server.rb +68 -0
- data/lib/plum/rack.rb +10 -0
- data/lib/plum/stream.rb +35 -25
- data/lib/plum/stream_utils.rb +0 -4
- data/lib/plum/version.rb +1 -1
- data/lib/plum.rb +1 -0
- data/lib/rack/handler/plum.rb +49 -0
- data/plum.gemspec +1 -0
- data/test/plum/hpack/test_decoder.rb +8 -8
- data/test/plum/hpack/test_encoder.rb +3 -3
- data/test/plum/stream/test_handle_frame.rb +6 -6
- data/test/plum/test_frame.rb +4 -1
- data/test/plum/test_http_connection.rb +1 -3
- data/test/plum/test_https_connection.rb +9 -12
- data/test/utils/server.rb +3 -3
- metadata +28 -3
data/lib/plum/frame_factory.rb
CHANGED
@@ -2,11 +2,19 @@ using Plum::BinaryString
|
|
2
2
|
|
3
3
|
module Plum
|
4
4
|
module FrameFactory
|
5
|
+
# Creates a RST_STREAM frame.
|
6
|
+
# @param stream_id [Integer] The stream ID.
|
7
|
+
# @param error_type [Symbol] The error type defined in RFC 7540 Section 7.
|
5
8
|
def rst_stream(stream_id, error_type)
|
6
9
|
payload = "".push_uint32(HTTPError::ERROR_CODES[error_type])
|
7
10
|
Frame.new(type: :rst_stream, stream_id: stream_id, payload: payload)
|
8
11
|
end
|
9
12
|
|
13
|
+
# Creates a GOAWAY frame.
|
14
|
+
# @param last_id [Integer] The biggest processed stream ID.
|
15
|
+
# @param error_type [Symbol] The error type defined in RFC 7540 Section 7.
|
16
|
+
# @param message [String] Additional debug data.
|
17
|
+
# @see RFC 7540 Section 6.8
|
10
18
|
def goaway(last_id, error_type, message = "")
|
11
19
|
payload = "".push_uint32((last_id || 0) | (0 << 31))
|
12
20
|
.push_uint32(HTTPError::ERROR_CODES[error_type])
|
@@ -14,15 +22,24 @@ module Plum
|
|
14
22
|
Frame.new(type: :goaway, stream_id: 0, payload: payload)
|
15
23
|
end
|
16
24
|
|
25
|
+
# Creates a SETTINGS frame.
|
26
|
+
# @param ack [Symbol] Pass :ack to create an ACK frame.
|
27
|
+
# @param args [Hash<Symbol, Integer>] The settings values to send.
|
17
28
|
def settings(ack = nil, **args)
|
18
29
|
payload = args.inject("") {|payload, (key, value)|
|
19
30
|
id = Frame::SETTINGS_TYPE[key] or raise ArgumentError.new("invalid settings type")
|
20
31
|
payload.push_uint16(id)
|
21
32
|
payload.push_uint32(value)
|
22
33
|
}
|
23
|
-
Frame.new(type: :settings, stream_id: 0, flags: [ack]
|
34
|
+
Frame.new(type: :settings, stream_id: 0, flags: [ack], payload: payload)
|
24
35
|
end
|
25
36
|
|
37
|
+
# Creates a PING frame.
|
38
|
+
# @overload ping(ack, payload)
|
39
|
+
# @param ack [Symbol] Pass :ack to create an ACK frame.
|
40
|
+
# @param payload [String] 8 bytes length data to send.
|
41
|
+
# @overload ping(payload = "plum\x00\x00\x00\x00")
|
42
|
+
# @param payload [String] 8 bytes length data to send.
|
26
43
|
def ping(arg1 = "plum\x00\x00\x00\x00", arg2 = nil)
|
27
44
|
if !arg2
|
28
45
|
raise ArgumentError.new("data must be 8 octets") if arg1.bytesize != 8
|
@@ -32,22 +49,39 @@ module Plum
|
|
32
49
|
end
|
33
50
|
end
|
34
51
|
|
52
|
+
# Creates a DATA frame.
|
53
|
+
# @param stream_id [Integer] The stream ID.
|
54
|
+
# @param payload [String] Payload.
|
55
|
+
# @param flags [Array<Symbol>] Flags.
|
35
56
|
def data(stream_id, payload, *flags)
|
36
|
-
Frame.new(type: :data, stream_id: stream_id, flags: flags
|
57
|
+
Frame.new(type: :data, stream_id: stream_id, flags: flags, payload: payload)
|
37
58
|
end
|
38
59
|
|
60
|
+
# Creates a DATA frame.
|
61
|
+
# @param stream_id [Integer] The stream ID.
|
62
|
+
# @param encoded [String] Headers.
|
63
|
+
# @param flags [Array<Symbol>] Flags.
|
39
64
|
def headers(stream_id, encoded, *flags)
|
40
|
-
Frame.new(type: :headers, stream_id: stream_id, flags: flags
|
65
|
+
Frame.new(type: :headers, stream_id: stream_id, flags: flags, payload: encoded)
|
41
66
|
end
|
42
67
|
|
68
|
+
# Creates a PUSH_PROMISE frame.
|
69
|
+
# @param stream_id [Integer] The stream ID.
|
70
|
+
# @param new_id [Integer] The stream ID to create.
|
71
|
+
# @param encoded [String] Request headers.
|
72
|
+
# @param flags [Array<Symbol>] Flags.
|
43
73
|
def push_promise(stream_id, new_id, encoded, *flags)
|
44
74
|
payload = "".push_uint32(0 << 31 | new_id)
|
45
75
|
.push(encoded)
|
46
|
-
Frame.new(type: :push_promise, stream_id: stream_id, flags: flags
|
76
|
+
Frame.new(type: :push_promise, stream_id: stream_id, flags: flags, payload: payload)
|
47
77
|
end
|
48
78
|
|
79
|
+
# Creates a CONTINUATION frame.
|
80
|
+
# @param stream_id [Integer] The stream ID.
|
81
|
+
# @param payload [String] Payload.
|
82
|
+
# @param flags [Array<Symbol>] Flags.
|
49
83
|
def continuation(stream_id, payload, *flags)
|
50
|
-
Frame.new(type: :continuation, stream_id: stream_id, flags: flags
|
84
|
+
Frame.new(type: :continuation, stream_id: stream_id, flags: flags, payload: payload)
|
51
85
|
end
|
52
86
|
end
|
53
87
|
end
|
data/lib/plum/frame_utils.rb
CHANGED
@@ -3,7 +3,6 @@ using Plum::BinaryString
|
|
3
3
|
module Plum
|
4
4
|
module FrameUtils
|
5
5
|
# Splits the DATA frame into multiple frames if the payload size exceeds max size.
|
6
|
-
#
|
7
6
|
# @param max [Integer] The maximum size of a frame payload.
|
8
7
|
# @return [Array<Frame>] The splitted frames.
|
9
8
|
def split_data(max)
|
@@ -18,7 +17,6 @@ module Plum
|
|
18
17
|
end
|
19
18
|
|
20
19
|
# Splits the HEADERS or PUSH_PROMISE frame into multiple frames if the payload size exceeds max size.
|
21
|
-
#
|
22
20
|
# @param max [Integer] The maximum size of a frame payload.
|
23
21
|
# @return [Array<Frame>] The splitted frames.
|
24
22
|
def split_headers(max)
|
@@ -34,7 +32,6 @@ module Plum
|
|
34
32
|
end
|
35
33
|
|
36
34
|
# Parses SETTINGS frame payload. Ignores unknown settings type (see RFC7540 6.5.2).
|
37
|
-
#
|
38
35
|
# @return [Hash<Symbol, Integer>] The parsed strings.
|
39
36
|
def parse_settings
|
40
37
|
settings = {}
|
data/lib/plum/hpack/constants.rb
CHANGED
@@ -3,7 +3,7 @@ module Plum
|
|
3
3
|
# RFC7541 Appendix A
|
4
4
|
# index is starting from 0
|
5
5
|
STATIC_TABLE = [
|
6
|
-
[":authority"],
|
6
|
+
[":authority", ""],
|
7
7
|
[":method", "GET"],
|
8
8
|
[":method", "POST"],
|
9
9
|
[":path", "/"],
|
@@ -19,52 +19,52 @@ module Plum
|
|
19
19
|
[":status", "500"],
|
20
20
|
["accept-charset"],
|
21
21
|
["accept-encoding", "gzip, deflate"],
|
22
|
-
["accept-language"],
|
23
|
-
["accept-ranges"],
|
24
|
-
["accept"],
|
25
|
-
["access-control-allow-origin"],
|
26
|
-
["age"],
|
27
|
-
["allow"],
|
28
|
-
["authorization"],
|
29
|
-
["cache-control"],
|
30
|
-
["content-disposition"],
|
31
|
-
["content-encoding"],
|
32
|
-
["content-language"],
|
33
|
-
["content-length"],
|
34
|
-
["content-location"],
|
35
|
-
["content-range"],
|
36
|
-
["content-type"],
|
37
|
-
["cookie"],
|
38
|
-
["date"],
|
39
|
-
["etag"],
|
40
|
-
["expect"],
|
41
|
-
["expires"],
|
42
|
-
["from"],
|
43
|
-
["host"],
|
44
|
-
["if-match"],
|
45
|
-
["if-modified-since"],
|
46
|
-
["if-none-match"],
|
47
|
-
["if-range"],
|
48
|
-
["if-unmodified-since"],
|
49
|
-
["last-modified"],
|
50
|
-
["link"],
|
51
|
-
["location"],
|
52
|
-
["max-forwards"],
|
53
|
-
["proxy-authenticate"],
|
54
|
-
["proxy-authorization"],
|
55
|
-
["range"],
|
56
|
-
["referer"],
|
57
|
-
["refresh"],
|
58
|
-
["retry-after"],
|
59
|
-
["server"],
|
60
|
-
["set-cookie"],
|
61
|
-
["strict-transport-security"],
|
62
|
-
["transfer-encoding"],
|
63
|
-
["user-agent"],
|
64
|
-
["vary"],
|
65
|
-
["via"],
|
66
|
-
["www-authenticate"],
|
67
|
-
]
|
22
|
+
["accept-language", ""],
|
23
|
+
["accept-ranges", ""],
|
24
|
+
["accept", ""],
|
25
|
+
["access-control-allow-origin", ""],
|
26
|
+
["age", ""],
|
27
|
+
["allow", ""],
|
28
|
+
["authorization", ""],
|
29
|
+
["cache-control", ""],
|
30
|
+
["content-disposition", ""],
|
31
|
+
["content-encoding", ""],
|
32
|
+
["content-language", ""],
|
33
|
+
["content-length", ""],
|
34
|
+
["content-location", ""],
|
35
|
+
["content-range", ""],
|
36
|
+
["content-type", ""],
|
37
|
+
["cookie", ""],
|
38
|
+
["date", ""],
|
39
|
+
["etag", ""],
|
40
|
+
["expect", ""],
|
41
|
+
["expires", ""],
|
42
|
+
["from", ""],
|
43
|
+
["host", ""],
|
44
|
+
["if-match", ""],
|
45
|
+
["if-modified-since", ""],
|
46
|
+
["if-none-match", ""],
|
47
|
+
["if-range", ""],
|
48
|
+
["if-unmodified-since", ""],
|
49
|
+
["last-modified", ""],
|
50
|
+
["link", ""],
|
51
|
+
["location", ""],
|
52
|
+
["max-forwards", ""],
|
53
|
+
["proxy-authenticate", ""],
|
54
|
+
["proxy-authorization", ""],
|
55
|
+
["range", ""],
|
56
|
+
["referer", ""],
|
57
|
+
["refresh", ""],
|
58
|
+
["retry-after", ""],
|
59
|
+
["server", ""],
|
60
|
+
["set-cookie", ""],
|
61
|
+
["strict-transport-security", ""],
|
62
|
+
["transfer-encoding", ""],
|
63
|
+
["user-agent", ""],
|
64
|
+
["vary", ""],
|
65
|
+
["via", ""],
|
66
|
+
["www-authenticate", ""],
|
67
|
+
].freeze
|
68
68
|
|
69
69
|
HUFFMAN_TABLE = [
|
70
70
|
"1111111111000",
|
@@ -324,8 +324,8 @@ module Plum
|
|
324
324
|
"111111111111111111111110000",
|
325
325
|
"11111111111111111111101110",
|
326
326
|
"111111111111111111111111111111"
|
327
|
-
]
|
327
|
+
].freeze
|
328
328
|
|
329
|
-
HUFFMAN_TABLE_INVERSED = HUFFMAN_TABLE.each_with_index.to_h
|
329
|
+
HUFFMAN_TABLE_INVERSED = HUFFMAN_TABLE.each_with_index.to_h.freeze
|
330
330
|
end
|
331
331
|
end
|
data/lib/plum/hpack/context.rb
CHANGED
@@ -16,8 +16,9 @@ module Plum
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def store(name, value)
|
19
|
+
value = value.to_s
|
19
20
|
@dynamic_table.unshift([name, value])
|
20
|
-
@size += name.bytesize + value.
|
21
|
+
@size += name.bytesize + value.bytesize + 32
|
21
22
|
evict
|
22
23
|
end
|
23
24
|
|
@@ -34,7 +35,7 @@ module Plum
|
|
34
35
|
end
|
35
36
|
|
36
37
|
def search(name, value)
|
37
|
-
pr = proc {|n, v|
|
38
|
+
pr = proc { |n, v|
|
38
39
|
n == name && (!value || v == value)
|
39
40
|
}
|
40
41
|
|
@@ -47,7 +48,7 @@ module Plum
|
|
47
48
|
def evict
|
48
49
|
while @limit && @size > @limit
|
49
50
|
name, value = @dynamic_table.pop
|
50
|
-
@size -= name.bytesize + value.
|
51
|
+
@size -= name.bytesize + value.bytesize + 32
|
51
52
|
end
|
52
53
|
end
|
53
54
|
end
|
data/lib/plum/hpack/decoder.rb
CHANGED
@@ -10,42 +10,47 @@ module Plum
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def decode(str)
|
13
|
-
str = str.dup
|
14
13
|
headers = []
|
15
|
-
|
16
|
-
|
14
|
+
pos = 0
|
15
|
+
lpos = str.bytesize
|
16
|
+
while pos < lpos
|
17
|
+
l, succ = parse(str, pos)
|
18
|
+
pos += succ
|
19
|
+
headers << l if l
|
20
|
+
end
|
21
|
+
headers
|
17
22
|
end
|
18
23
|
|
19
24
|
private
|
20
|
-
def parse
|
21
|
-
first_byte = str.
|
22
|
-
if first_byte >=
|
23
|
-
parse_indexed
|
24
|
-
elsif first_byte >=
|
25
|
-
parse_indexing
|
26
|
-
elsif first_byte >=
|
27
|
-
self.limit = read_integer
|
28
|
-
nil
|
25
|
+
def parse(str, pos)
|
26
|
+
first_byte = str.getbyte(pos)
|
27
|
+
if first_byte >= 0x80 # 0b1XXXXXXX
|
28
|
+
parse_indexed(str, pos)
|
29
|
+
elsif first_byte >= 0x40 # 0b01XXXXXX
|
30
|
+
parse_indexing(str, pos)
|
31
|
+
elsif first_byte >= 0x20 # 0b001XXXXX
|
32
|
+
self.limit, succ = read_integer(str, pos, 5)
|
33
|
+
[nil, succ]
|
29
34
|
else # 0b0000XXXX (without indexing) or 0b0001XXXX (never indexing)
|
30
|
-
parse_no_indexing
|
35
|
+
parse_no_indexing(str, pos)
|
31
36
|
end
|
32
37
|
end
|
33
38
|
|
34
|
-
def read_integer
|
35
|
-
|
36
|
-
|
39
|
+
def read_integer(str, pos, prefix_length)
|
40
|
+
raise HPACKError.new("integer: end of buffer") if str.empty?
|
41
|
+
first_byte = str.getbyte(pos)
|
37
42
|
|
38
|
-
mask =
|
39
|
-
ret = first_byte
|
40
|
-
return ret if ret
|
43
|
+
mask = 1 << prefix_length
|
44
|
+
ret = first_byte % mask
|
45
|
+
return [ret, 1] if ret != mask - 1
|
41
46
|
|
42
47
|
octets = 0
|
43
|
-
while next_value = str.
|
44
|
-
ret += (next_value
|
48
|
+
while next_value = str.uint8(pos + octets + 1)
|
49
|
+
ret += (next_value % 0x80) << (7 * octets)
|
45
50
|
octets += 1
|
46
51
|
|
47
|
-
if next_value <
|
48
|
-
return ret
|
52
|
+
if next_value < 0x80
|
53
|
+
return [ret, 1 + octets]
|
49
54
|
elsif octets == 4 # RFC 7541 5.1 tells us that we MUST have limitation. at least > 2 ** 28
|
50
55
|
raise HPACKError.new("integer: too large integer")
|
51
56
|
end
|
@@ -54,29 +59,32 @@ module Plum
|
|
54
59
|
raise HPACKError.new("integer: end of buffer")
|
55
60
|
end
|
56
61
|
|
57
|
-
def read_string
|
58
|
-
|
59
|
-
|
62
|
+
def read_string(str, pos)
|
63
|
+
raise HPACKError.new("string: end of buffer") if str.empty?
|
64
|
+
first_byte = str.uint8(pos)
|
60
65
|
|
61
|
-
huffman =
|
62
|
-
length = read_integer
|
63
|
-
|
66
|
+
huffman = first_byte > 0x80
|
67
|
+
length, ilen = read_integer(str, pos, 7)
|
68
|
+
raise HTTPError.new("string: end of buffer") if str.bytesize < length
|
64
69
|
|
65
|
-
|
66
|
-
|
67
|
-
|
70
|
+
bin = str.byteslice(pos + ilen, length)
|
71
|
+
if huffman
|
72
|
+
[Huffman.decode(bin), ilen + length]
|
73
|
+
else
|
74
|
+
[bin, ilen + length]
|
75
|
+
end
|
68
76
|
end
|
69
77
|
|
70
|
-
def parse_indexed
|
78
|
+
def parse_indexed(str, pos)
|
71
79
|
# indexed
|
72
80
|
# +---+---+---+---+---+---+---+---+
|
73
81
|
# | 1 | Index (7+) |
|
74
82
|
# +---+---------------------------+
|
75
|
-
index = read_integer
|
76
|
-
fetch(index)
|
83
|
+
index, succ = read_integer(str, pos, 7)
|
84
|
+
[fetch(index), succ]
|
77
85
|
end
|
78
86
|
|
79
|
-
def parse_indexing
|
87
|
+
def parse_indexing(str, pos)
|
80
88
|
# +---+---+---+---+---+---+---+---+
|
81
89
|
# | 0 | 1 | Index (6+) |
|
82
90
|
# +---+---+-----------------------+
|
@@ -96,20 +104,21 @@ module Plum
|
|
96
104
|
# +---+---------------------------+
|
97
105
|
# | Value String (Length octets) |
|
98
106
|
# +-------------------------------+
|
99
|
-
index = read_integer
|
107
|
+
index, ilen = read_integer(str, pos, 6)
|
100
108
|
if index == 0
|
101
|
-
name = read_string
|
109
|
+
name, nlen = read_string(str, pos + ilen)
|
102
110
|
else
|
103
111
|
name, = fetch(index)
|
112
|
+
nlen = 0
|
104
113
|
end
|
105
114
|
|
106
|
-
val = read_string
|
115
|
+
val, vlen = read_string(str, pos + ilen + nlen)
|
107
116
|
store(name, val)
|
108
117
|
|
109
|
-
[name, val]
|
118
|
+
[[name, val], ilen + nlen + vlen]
|
110
119
|
end
|
111
120
|
|
112
|
-
def parse_no_indexing
|
121
|
+
def parse_no_indexing(str, pos)
|
113
122
|
# +---+---+---+---+---+---+---+---+
|
114
123
|
# | 0 | 0 | 0 |0,1| Index (4+) |
|
115
124
|
# +---+---+-----------------------+
|
@@ -129,16 +138,17 @@ module Plum
|
|
129
138
|
# +---+---------------------------+
|
130
139
|
# | Value String (Length octets) |
|
131
140
|
# +-------------------------------+
|
132
|
-
index = read_integer
|
141
|
+
index, ilen = read_integer(str, pos, 4)
|
133
142
|
if index == 0
|
134
|
-
name = read_string
|
143
|
+
name, nlen = read_string(str, pos + ilen)
|
135
144
|
else
|
136
145
|
name, = fetch(index)
|
146
|
+
nlen = 0
|
137
147
|
end
|
138
148
|
|
139
|
-
val = read_string
|
149
|
+
val, vlen = read_string(str, pos + ilen + nlen)
|
140
150
|
|
141
|
-
[name, val]
|
151
|
+
[[name, val], ilen + nlen + vlen]
|
142
152
|
end
|
143
153
|
end
|
144
154
|
end
|
data/lib/plum/hpack/encoder.rb
CHANGED
@@ -14,8 +14,8 @@ module Plum
|
|
14
14
|
def encode(headers)
|
15
15
|
out = ""
|
16
16
|
headers.each do |name, value|
|
17
|
-
name = name.to_s
|
18
|
-
value = value.to_s
|
17
|
+
name = name.to_s
|
18
|
+
value = value.to_s
|
19
19
|
if index = search(name, value)
|
20
20
|
out << encode_indexed(index)
|
21
21
|
elsif index = search(name, nil)
|
@@ -59,10 +59,9 @@ module Plum
|
|
59
59
|
def encode_half_indexed(index, value)
|
60
60
|
if @indexing
|
61
61
|
store(fetch(index)[0], value)
|
62
|
-
fb = encode_integer(index, 6)
|
63
|
-
fb.setbyte(0, fb.uint8 | 0b01000000)
|
62
|
+
fb = encode_integer(index, 6, 0b01000000)
|
64
63
|
else
|
65
|
-
fb = encode_integer(index, 4)
|
64
|
+
fb = encode_integer(index, 4, 0b00000000)
|
66
65
|
end
|
67
66
|
fb << encode_string(value)
|
68
67
|
end
|
@@ -71,27 +70,24 @@ module Plum
|
|
71
70
|
# | 1 | Index (7+) |
|
72
71
|
# +---+---------------------------+
|
73
72
|
def encode_indexed(index)
|
74
|
-
|
75
|
-
s.setbyte(0, s.uint8 | 0b10000000)
|
76
|
-
s
|
73
|
+
encode_integer(index, 7, 0b10000000)
|
77
74
|
end
|
78
75
|
|
79
|
-
def encode_integer(value, prefix_length)
|
76
|
+
def encode_integer(value, prefix_length, hmask)
|
80
77
|
mask = (1 << prefix_length) - 1
|
81
|
-
out = ""
|
82
78
|
|
83
79
|
if value < mask
|
84
|
-
|
80
|
+
(value + hmask).chr.force_encoding(Encoding::BINARY)
|
85
81
|
else
|
82
|
+
vals = [mask + hmask]
|
86
83
|
value -= mask
|
87
|
-
out.push_uint8(mask)
|
88
84
|
while value >= mask
|
89
|
-
|
90
|
-
value
|
85
|
+
vals << (value % 0x80) + 0x80
|
86
|
+
value /= 0x80
|
91
87
|
end
|
92
|
-
|
88
|
+
vals << value
|
89
|
+
vals.pack("C*")
|
93
90
|
end
|
94
|
-
out.force_encoding(Encoding::BINARY)
|
95
91
|
end
|
96
92
|
|
97
93
|
def encode_string(str)
|
@@ -105,14 +101,13 @@ module Plum
|
|
105
101
|
end
|
106
102
|
|
107
103
|
def encode_string_plain(str)
|
108
|
-
encode_integer(str.bytesize, 7) << str
|
104
|
+
encode_integer(str.bytesize, 7, 0b00000000) << str
|
109
105
|
end
|
110
106
|
|
111
107
|
def encode_string_huffman(str)
|
112
108
|
huffman_str = Huffman.encode(str)
|
113
|
-
lenstr = encode_integer(huffman_str.bytesize, 7)
|
114
|
-
lenstr
|
115
|
-
lenstr.force_encoding(Encoding::BINARY) << huffman_str
|
109
|
+
lenstr = encode_integer(huffman_str.bytesize, 7, 0b10000000)
|
110
|
+
lenstr << huffman_str
|
116
111
|
end
|
117
112
|
end
|
118
113
|
end
|
data/lib/plum/http_connection.rb
CHANGED
@@ -2,12 +2,28 @@ using Plum::BinaryString
|
|
2
2
|
|
3
3
|
module Plum
|
4
4
|
class HTTPConnection < Connection
|
5
|
-
|
5
|
+
attr_reader :sock
|
6
|
+
|
7
|
+
def initialize(sock, local_settings = {})
|
6
8
|
require "http/parser"
|
7
|
-
super
|
8
9
|
@_headers = nil
|
9
10
|
@_body = ""
|
10
11
|
@_http_parser = setup_parser
|
12
|
+
@sock = sock
|
13
|
+
super(@sock.method(:write), local_settings)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Starts communication with the peer. It blocks until the io is closed, or reaches EOF.
|
17
|
+
def run
|
18
|
+
while !@sock.closed? && !@sock.eof?
|
19
|
+
self << @sock.readpartial(1024)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Closes the socket.
|
24
|
+
def close
|
25
|
+
super
|
26
|
+
@sock.close
|
11
27
|
end
|
12
28
|
|
13
29
|
private
|
@@ -56,7 +72,7 @@ module Plum
|
|
56
72
|
resp << "Server: plum/#{Plum::VERSION}\r\n"
|
57
73
|
resp << "\r\n"
|
58
74
|
|
59
|
-
|
75
|
+
@sock.write(resp)
|
60
76
|
end
|
61
77
|
|
62
78
|
def process_first_request
|
@@ -1,15 +1,31 @@
|
|
1
1
|
module Plum
|
2
2
|
class HTTPSConnection < Connection
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
attr_reader :sock
|
4
|
+
|
5
|
+
def initialize(sock, local_settings = {})
|
6
|
+
@sock = sock
|
7
|
+
super(@sock.method(:write), local_settings)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Starts communication with the peer. It blocks until the io is closed, or reaches EOF.
|
11
|
+
def run
|
12
|
+
if @sock.respond_to?(:cipher) # OpenSSL::SSL::SSLSocket-like
|
13
|
+
if CIPHER_BLACKLIST.include?(@sock.cipher.first) # [cipher-suite, ssl-version, keylen, alglen]
|
14
|
+
on(:negotiated) {
|
7
15
|
raise ConnectionError.new(:inadequate_security)
|
8
16
|
}
|
9
17
|
end
|
10
18
|
end
|
11
19
|
|
20
|
+
while !@sock.closed? && !@sock.eof?
|
21
|
+
self << @sock.readpartial(1024)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Closes the socket.
|
26
|
+
def close
|
12
27
|
super
|
28
|
+
@sock.close
|
13
29
|
end
|
14
30
|
|
15
31
|
CIPHER_BLACKLIST = %w(
|
@@ -26,6 +42,6 @@ module Plum
|
|
26
42
|
AECDH-NULL-SHA AECDH-RC4-SHA AECDH-DES-CBC3-SHA AECDH-AES128-SHA AECDH-AES256-SHA SRP-3DES-EDE-CBC-SHA SRP-RSA-3DES-EDE-CBC-SHA SRP-DSS-3DES-EDE-CBC-SHA SRP-AES-128-CBC-SHA SRP-RSA-AES-128-CBC-SHA
|
27
43
|
SRP-DSS-AES-128-CBC-SHA SRP-AES-256-CBC-SHA SRP-RSA-AES-256-CBC-SHA SRP-DSS-AES-256-CBC-SHA ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES256-SHA384 ECDH-ECDSA-AES128-SHA256 ECDH-ECDSA-AES256-SHA384 ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES256-SHA384
|
28
44
|
ECDH-RSA-AES128-SHA256 ECDH-RSA-AES256-SHA384 ECDH-ECDSA-AES128-GCM-SHA256 ECDH-ECDSA-AES256-GCM-SHA384 ECDH-RSA-AES128-GCM-SHA256 ECDH-RSA-AES256-GCM-SHA384
|
29
|
-
)
|
45
|
+
).freeze
|
30
46
|
end
|
31
47
|
end
|