http-protocol 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 +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.travis.yml +23 -0
- data/Gemfile +11 -0
- data/README.md +64 -0
- data/Rakefile +6 -0
- data/http-protocol.gemspec +24 -0
- data/lib/http/protocol.rb +21 -0
- data/lib/http/protocol/error.rb +80 -0
- data/lib/http/protocol/headers.rb +203 -0
- data/lib/http/protocol/http2/client.rb +49 -0
- data/lib/http/protocol/http2/connection.rb +309 -0
- data/lib/http/protocol/http2/continuation_frame.rb +86 -0
- data/lib/http/protocol/http2/data_frame.rb +64 -0
- data/lib/http/protocol/http2/flow_control.rb +85 -0
- data/lib/http/protocol/http2/frame.rb +187 -0
- data/lib/http/protocol/http2/framer.rb +105 -0
- data/lib/http/protocol/http2/goaway_frame.rb +62 -0
- data/lib/http/protocol/http2/headers_frame.rb +95 -0
- data/lib/http/protocol/http2/padded.rb +91 -0
- data/lib/http/protocol/http2/ping_frame.rb +82 -0
- data/lib/http/protocol/http2/priority_frame.rb +82 -0
- data/lib/http/protocol/http2/push_promise_frame.rb +67 -0
- data/lib/http/protocol/http2/reset_stream_frame.rb +74 -0
- data/lib/http/protocol/http2/server.rb +47 -0
- data/lib/http/protocol/http2/settings_frame.rb +242 -0
- data/lib/http/protocol/http2/stream.rb +313 -0
- data/lib/http/protocol/http2/window_update_frame.rb +89 -0
- data/lib/http/protocol/version.rb +25 -0
- metadata +128 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
# Copyright, 2013, by Ilya Grigorik.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
|
22
|
+
require_relative 'frame'
|
23
|
+
|
24
|
+
module HTTP
|
25
|
+
module Protocol
|
26
|
+
module HTTP2
|
27
|
+
# Certain frames can have padding:
|
28
|
+
# https://http2.github.io/http2-spec/#padding
|
29
|
+
#
|
30
|
+
# +---------------+
|
31
|
+
# |Pad Length? (8)|
|
32
|
+
# +---------------+-----------------------------------------------+
|
33
|
+
# | Data (*) ...
|
34
|
+
# +---------------------------------------------------------------+
|
35
|
+
# | Padding (*) ...
|
36
|
+
# +---------------------------------------------------------------+
|
37
|
+
#
|
38
|
+
module Padded
|
39
|
+
def padded?
|
40
|
+
flag_set?(PADDED)
|
41
|
+
end
|
42
|
+
|
43
|
+
# We will round up frames to the given length:
|
44
|
+
MODULUS = 0x0F
|
45
|
+
|
46
|
+
def pack(data, modulus: MODULUS, padding_size: nil, maximum_size: nil)
|
47
|
+
padding_size ||= (MODULUS - data.bytesize) % MODULUS
|
48
|
+
|
49
|
+
if maximum_size
|
50
|
+
maximum_padding_size = maximum_size - data.bytesize
|
51
|
+
|
52
|
+
if padding_size > maximum_padding_size
|
53
|
+
padding_size = maximum_padding_size
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
if padding_size > 0
|
58
|
+
set_flags(PADDED)
|
59
|
+
|
60
|
+
buffer = String.new.b
|
61
|
+
|
62
|
+
buffer << padding_size.chr
|
63
|
+
buffer << data
|
64
|
+
buffer << "\0" * padding_size
|
65
|
+
|
66
|
+
super buffer
|
67
|
+
else
|
68
|
+
clear_flags(PADDED)
|
69
|
+
|
70
|
+
super data
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def unpack
|
75
|
+
if padded?
|
76
|
+
padding_size = @payload[0].ord
|
77
|
+
data_size = @payload.bytesize - 1 - padding_size
|
78
|
+
|
79
|
+
if data_size < 0
|
80
|
+
raise ProtocolError, "Invalid padding length: #{padding_size}"
|
81
|
+
end
|
82
|
+
|
83
|
+
return @payload.byteslice(1, data_size)
|
84
|
+
else
|
85
|
+
return @payload
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require_relative 'frame'
|
22
|
+
|
23
|
+
module HTTP
|
24
|
+
module Protocol
|
25
|
+
module HTTP2
|
26
|
+
ACKNOWLEDGEMENT = 0x1
|
27
|
+
|
28
|
+
module Acknowledgement
|
29
|
+
def acknowledgement?
|
30
|
+
flag_set?(ACKNOWLEDGEMENT)
|
31
|
+
end
|
32
|
+
|
33
|
+
def acknowledge
|
34
|
+
frame = self.class.new
|
35
|
+
|
36
|
+
frame.length = 0
|
37
|
+
frame.set_flags(ACKNOWLEDGEMENT)
|
38
|
+
|
39
|
+
return frame
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# The PING frame is a mechanism for measuring a minimal round-trip time from the sender, as well as determining whether an idle connection is still functional. PING frames can be sent from any endpoint.
|
44
|
+
#
|
45
|
+
# +---------------------------------------------------------------+
|
46
|
+
# | |
|
47
|
+
# | Opaque Data (64) |
|
48
|
+
# | |
|
49
|
+
# +---------------------------------------------------------------+
|
50
|
+
#
|
51
|
+
class PingFrame < Frame
|
52
|
+
TYPE = 0x6
|
53
|
+
|
54
|
+
include Acknowledgement
|
55
|
+
|
56
|
+
def connection?
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
def apply(connection)
|
61
|
+
connection.receive_ping(self)
|
62
|
+
end
|
63
|
+
|
64
|
+
def acknowledge
|
65
|
+
frame = super
|
66
|
+
|
67
|
+
frame.pack self.unpack
|
68
|
+
|
69
|
+
return frame
|
70
|
+
end
|
71
|
+
|
72
|
+
def read_payload(io)
|
73
|
+
super
|
74
|
+
|
75
|
+
if @length > 8
|
76
|
+
raise FrameSizeError, "Invalid frame length"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require_relative 'frame'
|
22
|
+
|
23
|
+
module HTTP
|
24
|
+
module Protocol
|
25
|
+
module HTTP2
|
26
|
+
Priority = Struct.new(:exclusive, :stream_dependency, :weight) do
|
27
|
+
FORMAT = "NC".freeze
|
28
|
+
EXCLUSIVE = 1 << 31
|
29
|
+
|
30
|
+
def self.unpack(data)
|
31
|
+
stream_dependency, weight = data.unpack(FORMAT)
|
32
|
+
|
33
|
+
return self.new(stream_dependency & EXCLUSIVE != 0, stream_dependency & ~EXCLUSIVE, weight)
|
34
|
+
end
|
35
|
+
|
36
|
+
def pack
|
37
|
+
if exclusive
|
38
|
+
stream_dependency = self.stream_dependency | EXCLUSIVE
|
39
|
+
end
|
40
|
+
|
41
|
+
return [stream_dependency, self.weight].pack(FORMAT)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# The PRIORITY frame specifies the sender-advised priority of a stream. It can be sent in any stream state, including idle or closed streams.
|
46
|
+
#
|
47
|
+
# +-+-------------------------------------------------------------+
|
48
|
+
# |E| Stream Dependency (31) |
|
49
|
+
# +-+-------------+-----------------------------------------------+
|
50
|
+
# | Weight (8) |
|
51
|
+
# +-+-------------+
|
52
|
+
#
|
53
|
+
class PriorityFrame < Frame
|
54
|
+
TYPE = 0x2
|
55
|
+
|
56
|
+
def priority
|
57
|
+
Priority.unpack(@payload)
|
58
|
+
end
|
59
|
+
|
60
|
+
def pack priority
|
61
|
+
super priority.pack
|
62
|
+
end
|
63
|
+
|
64
|
+
def unpack
|
65
|
+
Priority.unpack(super)
|
66
|
+
end
|
67
|
+
|
68
|
+
def apply(connection)
|
69
|
+
connection.receive_priority(self)
|
70
|
+
end
|
71
|
+
|
72
|
+
def read_payload(io)
|
73
|
+
super
|
74
|
+
|
75
|
+
if @length != 5
|
76
|
+
raise FrameSizeError, "Invalid frame length"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require_relative 'frame'
|
22
|
+
require_relative 'padded'
|
23
|
+
|
24
|
+
module HTTP
|
25
|
+
module Protocol
|
26
|
+
module HTTP2
|
27
|
+
# The PUSH_PROMISE frame is used to notify the peer endpoint in advance of streams the sender intends to initiate. The PUSH_PROMISE frame includes the unsigned 31-bit identifier of the stream the endpoint plans to create along with a set of headers that provide additional context for the stream.
|
28
|
+
#
|
29
|
+
# +---------------+
|
30
|
+
# |Pad Length? (8)|
|
31
|
+
# +-+-------------+-----------------------------------------------+
|
32
|
+
# |R| Promised Stream ID (31) |
|
33
|
+
# +-+-----------------------------+-------------------------------+
|
34
|
+
# | Header Block Fragment (*) ...
|
35
|
+
# +---------------------------------------------------------------+
|
36
|
+
# | Padding (*) ...
|
37
|
+
# +---------------------------------------------------------------+
|
38
|
+
#
|
39
|
+
class PushPromiseFrame < Frame
|
40
|
+
include Continued, Padded
|
41
|
+
|
42
|
+
TYPE = 0x5
|
43
|
+
FORMAT = "N".freeze
|
44
|
+
|
45
|
+
def end_headers?
|
46
|
+
flag_set?(END_HEADERS)
|
47
|
+
end
|
48
|
+
|
49
|
+
def unpack
|
50
|
+
data = super
|
51
|
+
|
52
|
+
stream_id = data.unpack(FORMAT).first
|
53
|
+
|
54
|
+
return stream_id, data.byteslice(4, data.bytesize - 4)
|
55
|
+
end
|
56
|
+
|
57
|
+
def pack(stream_id, data, *args)
|
58
|
+
super([stream_id].pack(FORMAT) + data, *args)
|
59
|
+
end
|
60
|
+
|
61
|
+
def apply(connection)
|
62
|
+
connection.receive_push_promise(self)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require_relative 'frame'
|
22
|
+
|
23
|
+
module HTTP
|
24
|
+
module Protocol
|
25
|
+
module HTTP2
|
26
|
+
NO_ERROR = 0
|
27
|
+
PROTOCOL_ERROR = 1
|
28
|
+
INTERNAL_ERROR = 2
|
29
|
+
FLOW_CONTROL_ERROR = 3
|
30
|
+
TIMEOUT = 4
|
31
|
+
STREAM_CLOSED = 5
|
32
|
+
FRAME_SIZE_ERROR = 6
|
33
|
+
REFUSED_STREAM = 7
|
34
|
+
CANCEL = 8
|
35
|
+
COMPRESSION_ERROR = 9
|
36
|
+
CONNECT_ERROR = 10
|
37
|
+
ENHANCE_YOUR_CALM = 11
|
38
|
+
INADEQUATE_SECURITY = 12
|
39
|
+
HTTP_1_1_REQUIRED = 13
|
40
|
+
|
41
|
+
# The RST_STREAM frame allows for immediate termination of a stream. RST_STREAM is sent to request cancellation of a stream or to indicate that an error condition has occurred.
|
42
|
+
#
|
43
|
+
# +---------------------------------------------------------------+
|
44
|
+
# | Error Code (32) |
|
45
|
+
# +---------------------------------------------------------------+
|
46
|
+
#
|
47
|
+
class ResetStreamFrame < Frame
|
48
|
+
TYPE = 0x3
|
49
|
+
FORMAT = "N".freeze
|
50
|
+
|
51
|
+
def unpack
|
52
|
+
@payload.unpack(FORMAT).first
|
53
|
+
end
|
54
|
+
|
55
|
+
def pack(error_code = NO_ERROR)
|
56
|
+
@payload = [error_code].pack(FORMAT)
|
57
|
+
@length = @payload.bytesize
|
58
|
+
end
|
59
|
+
|
60
|
+
def apply(connection)
|
61
|
+
connection.receive_reset_stream(self)
|
62
|
+
end
|
63
|
+
|
64
|
+
def read_payload(io)
|
65
|
+
super
|
66
|
+
|
67
|
+
if @length != 4
|
68
|
+
raise FrameSizeError, "Invalid frame length"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require_relative 'connection'
|
22
|
+
|
23
|
+
module HTTP
|
24
|
+
module Protocol
|
25
|
+
module HTTP2
|
26
|
+
class Server < Connection
|
27
|
+
def initialize(framer, *args)
|
28
|
+
super(framer, 2, *args)
|
29
|
+
end
|
30
|
+
|
31
|
+
def read_connection_preface(settings)
|
32
|
+
if @state == :new
|
33
|
+
@framer.read_connection_preface
|
34
|
+
|
35
|
+
send_settings(settings)
|
36
|
+
|
37
|
+
read_frame do |frame|
|
38
|
+
raise ProtocolError, "First frame must be SettingsFrame, but got #{frame.class}" unless frame.is_a? SettingsFrame
|
39
|
+
end
|
40
|
+
else
|
41
|
+
raise ProtocolError, "Cannot send connection preface in state #{@state}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|