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