protocol-http2 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,98 @@
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 Protocol
24
+ module HTTP2
25
+ module Continued
26
+ def initialize(*)
27
+ super
28
+
29
+ @continuation = nil
30
+ end
31
+
32
+ def end_headers?
33
+ flag_set?(END_HEADERS)
34
+ end
35
+
36
+ def read(stream, maximum_frame_size)
37
+ super
38
+
39
+ unless end_headers?
40
+ @continuation = ContinuationFrame.new
41
+
42
+ @continuation.read(stream, maximum_frame_size)
43
+ end
44
+ end
45
+
46
+ def write(stream)
47
+ super
48
+
49
+ if continuation = self.continuation
50
+ continuation.write(stream)
51
+ end
52
+ end
53
+
54
+ attr_accessor :continuation
55
+
56
+ def pack(data, **options)
57
+ maximum_size = options[:maximum_size]
58
+
59
+ if maximum_size and data.bytesize > maximum_size
60
+ clear_flags(END_HEADERS)
61
+
62
+ super(data.byteslice(0, maximum_size), **options)
63
+
64
+ remainder = data.byteslice(maximum_size, data.bytesize-maximum_size)
65
+
66
+ @continuation = ContinuationFrame.new
67
+ @continuation.pack(remainder, maximum_size: maximum_size)
68
+ else
69
+ set_flags(END_HEADERS)
70
+
71
+ super data, **options
72
+
73
+ @continuation = nil
74
+ end
75
+ end
76
+
77
+ def unpack
78
+ if @continuation.nil?
79
+ super
80
+ else
81
+ super + @continuation.unpack
82
+ end
83
+ end
84
+ end
85
+
86
+ # The CONTINUATION frame is used to continue a sequence of header block fragments. Any number of CONTINUATION frames can be sent, as long as the preceding frame is on the same stream and is a HEADERS, PUSH_PROMISE, or CONTINUATION frame without the END_HEADERS flag set.
87
+ #
88
+ # +---------------------------------------------------------------+
89
+ # | Header Block Fragment (*) ...
90
+ # +---------------------------------------------------------------+
91
+ #
92
+ class ContinuationFrame < Frame
93
+ include Continued
94
+
95
+ TYPE = 0x9
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,62 @@
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
+ require_relative 'padded'
24
+
25
+ module Protocol
26
+ module HTTP2
27
+ # DATA frames convey arbitrary, variable-length sequences of octets associated with a stream. One or more DATA frames are used, for instance, to carry HTTP request or response payloads.
28
+ #
29
+ # DATA frames MAY also contain padding. Padding can be added to DATA frames to obscure the size of messages.
30
+ #
31
+ # +---------------+
32
+ # |Pad Length? (8)|
33
+ # +---------------+-----------------------------------------------+
34
+ # | Data (*) ...
35
+ # +---------------------------------------------------------------+
36
+ # | Padding (*) ...
37
+ # +---------------------------------------------------------------+
38
+ #
39
+ class DataFrame < Frame
40
+ include Padded
41
+
42
+ TYPE = 0x0
43
+
44
+ def end_stream?
45
+ flag_set?(END_STREAM)
46
+ end
47
+
48
+ def pack(data, *)
49
+ if data
50
+ super
51
+ else
52
+ @length = 0
53
+ set_flags(END_STREAM)
54
+ end
55
+ end
56
+
57
+ def apply(connection)
58
+ connection.receive_data(self)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,36 @@
1
+ # Copyright, 2019, 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 'protocol/http/error'
22
+
23
+ module Protocol
24
+ module HTTP2
25
+ class ProtocolError < HTTP::ProtocolError
26
+ end
27
+
28
+ # When the frame payload does not match expectations.
29
+ class FrameSizeError < ProtocolError
30
+ end
31
+
32
+ # Raised on invalid flow control frame or command.
33
+ class FlowControlError < ProtocolError
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,84 @@
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 'window_update_frame'
22
+
23
+ module Protocol
24
+ module HTTP2
25
+ module FlowControl
26
+ def available_frame_size
27
+ maximum_frame_size = self.maximum_frame_size
28
+ available_size = @remote_window.available
29
+
30
+ if available_size < maximum_frame_size
31
+ return available_size
32
+ else
33
+ return maximum_frame_size
34
+ end
35
+ end
36
+
37
+ # Keep track of the amount of data sent, and fail if is too much.
38
+ def consume_remote_window(frame)
39
+ amount = frame.length
40
+
41
+ # Frames with zero length with the END_STREAM flag set (that is, an empty DATA frame) MAY be sent if there is no available space in either flow-control window.
42
+ if amount.zero? and frame.end_stream?
43
+ # It's okay, we can send. No need to consume, it's empty anyway.
44
+ elsif amount >= 0 and amount <= @remote_window.available
45
+ @remote_window.consume(amount)
46
+ else
47
+ raise FlowControlError, "Trying to send #{frame.length} bytes, exceeded window size: #{@remote_window.available} (#{@remote_window})"
48
+ end
49
+ end
50
+
51
+ def consume_local_window(frame)
52
+ amount = frame.length
53
+
54
+ @local_window.consume(amount)
55
+
56
+ if @local_window.limited?
57
+ self.send_window_update(@local_window.used)
58
+ end
59
+ end
60
+
61
+ # Notify the remote end that we are prepared to receive more data:
62
+ def send_window_update(window_increment)
63
+ frame = WindowUpdateFrame.new(self.id)
64
+ frame.pack window_increment
65
+
66
+ write_frame(frame)
67
+
68
+ @local_window.expand(window_increment)
69
+ end
70
+
71
+ def receive_window_update(frame)
72
+ was_full = @remote_window.full?
73
+
74
+ # puts "expand remote_window=#{@remote_window} by #{frame.unpack}"
75
+ @remote_window.expand(frame.unpack)
76
+
77
+ self.window_updated if was_full
78
+ end
79
+
80
+ def window_updated
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,193 @@
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 'error'
23
+
24
+ module Protocol
25
+ module HTTP2
26
+ END_STREAM = 0x1
27
+ END_HEADERS = 0x4
28
+ PADDED = 0x8
29
+ PRIORITY = 0x20
30
+
31
+ MAXIMUM_ALLOWED_WINDOW_SIZE = 0x7FFFFFFF
32
+ MAXIMUM_ALLOWED_FRAME_SIZE = 0xFFFFFF
33
+
34
+ class Frame
35
+ include Comparable
36
+
37
+ # Stream Identifier cannot be bigger than this:
38
+ # https://http2.github.stream/http2-spec/#rfc.section.4.1
39
+ VALID_STREAM_ID = 0..0x7fffffff
40
+
41
+ # The absolute maximum bounds for the length field:
42
+ VALID_LENGTH = 0..0xffffff
43
+
44
+ # Used for generating 24-bit frame length:
45
+ LENGTH_HISHIFT = 16
46
+ LENGTH_LOMASK = 0xFFFF
47
+
48
+ # @param length [Integer] the length of the payload, or nil if the header has not been read yet.
49
+ def initialize(stream_id = 0, flags = 0, type = self.class.const_get(:TYPE), length = nil, payload = nil)
50
+ @length = length
51
+ @type = type
52
+ @flags = flags
53
+ @stream_id = stream_id
54
+ @payload = payload
55
+ end
56
+
57
+ def <=> other
58
+ to_ary <=> other.to_ary
59
+ end
60
+
61
+ def to_ary
62
+ [@length, @type, @flags, @stream_id, @payload]
63
+ end
64
+
65
+ # The generic frame header uses the following binary representation:
66
+ #
67
+ # +-----------------------------------------------+
68
+ # | Length (24) |
69
+ # +---------------+---------------+---------------+
70
+ # | Type (8) | Flags (8) |
71
+ # +-+-------------+---------------+-------------------------------+
72
+ # |R| Stream Identifier (31) |
73
+ # +=+=============================================================+
74
+ # | Frame Payload (0...) ...
75
+ # +---------------------------------------------------------------+
76
+
77
+ attr_accessor :length
78
+ attr_accessor :type
79
+ attr_accessor :flags
80
+ attr_accessor :stream_id
81
+ attr_accessor :payload
82
+
83
+ def unpack
84
+ @payload
85
+ end
86
+
87
+ def pack(payload, maximum_size: nil)
88
+ @payload = payload
89
+ @length = payload.bytesize
90
+
91
+ if maximum_size and @length > maximum_size
92
+ raise ProtocolError, "Frame length #{@length} bigger than maximum allowed: #{maximum_size}"
93
+ end
94
+ end
95
+
96
+ def set_flags(mask)
97
+ @flags |= mask
98
+ end
99
+
100
+ def clear_flags(mask)
101
+ @flags &= ~mask
102
+ end
103
+
104
+ def flag_set?(mask)
105
+ @flags & mask != 0
106
+ end
107
+
108
+ # Check if frame is a connection frame: SETTINGS, PING, GOAWAY, and any
109
+ # frame addressed to stream ID = 0.
110
+ #
111
+ # @return [Boolean]
112
+ def connection?
113
+ @stream_id.zero?
114
+ end
115
+
116
+ HEADER_FORMAT = 'CnCCN'.freeze
117
+ STREAM_ID_MASK = 0x7fffffff
118
+
119
+ # Generates common 9-byte frame header.
120
+ # - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-4.1
121
+ #
122
+ # @return [String]
123
+ def header
124
+ unless VALID_LENGTH.include? @length
125
+ raise ProtocolError, "Invalid frame size: #{@length.inspect}"
126
+ end
127
+
128
+ unless VALID_STREAM_ID.include? @stream_id
129
+ raise ProtocolError, "Invalid stream identifier: #{@stream_id.inspect}"
130
+ end
131
+
132
+ [
133
+ # These are guaranteed correct due to the length check above.
134
+ @length >> LENGTH_HISHIFT,
135
+ @length & LENGTH_LOMASK,
136
+ @type,
137
+ @flags,
138
+ @stream_id
139
+ ].pack(HEADER_FORMAT)
140
+ end
141
+
142
+ # Decodes common 9-byte header.
143
+ #
144
+ # @param buffer [String]
145
+ def self.parse_header(buffer)
146
+ length_hi, length_lo, type, flags, stream_id = buffer.unpack(HEADER_FORMAT)
147
+ length = (length_hi << LENGTH_HISHIFT) | length_lo
148
+ stream_id = stream_id & STREAM_ID_MASK
149
+
150
+ return length, type, flags, stream_id
151
+ end
152
+
153
+ def read_header(stream)
154
+ @length, @type, @flags, @stream_id = Frame.parse_header(stream.read(9))
155
+ end
156
+
157
+ def read_payload(stream)
158
+ @payload = stream.read(@length)
159
+ end
160
+
161
+ def read(stream, maximum_frame_size = MAXIMUM_ALLOWED_FRAME_SIZE)
162
+ read_header(stream) unless @length
163
+
164
+ if @length > maximum_frame_size
165
+ raise FrameSizeError, "#{self.class} (type=#{@type}) frame length #{@length} exceeds maximum frame size #{maximum_frame_size}!"
166
+ end
167
+
168
+ read_payload(stream)
169
+ end
170
+
171
+ def write_header(stream)
172
+ stream.write self.header
173
+ end
174
+
175
+ def write_payload(stream)
176
+ stream.write(@payload) if @payload
177
+ end
178
+
179
+ def write(stream)
180
+ if @payload and @length != @payload.bytesize
181
+ raise ProtocolError, "Invalid payload size: #{@length} != #{@payload.bytesize}"
182
+ end
183
+
184
+ self.write_header(stream)
185
+ self.write_payload(stream)
186
+ end
187
+
188
+ def apply(connection)
189
+ connection.receive_frame(self)
190
+ end
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,113 @@
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 'error'
23
+
24
+ require_relative 'data_frame'
25
+ require_relative 'headers_frame'
26
+ require_relative 'priority_frame'
27
+ require_relative 'reset_stream_frame'
28
+ require_relative 'settings_frame'
29
+ require_relative 'push_promise_frame'
30
+ require_relative 'ping_frame'
31
+ require_relative 'goaway_frame'
32
+ require_relative 'window_update_frame'
33
+ require_relative 'continuation_frame'
34
+
35
+ module Protocol
36
+ module HTTP2
37
+ # HTTP/2 frame type mapping as defined by the spec
38
+ FRAMES = [
39
+ DataFrame,
40
+ HeadersFrame,
41
+ PriorityFrame,
42
+ ResetStreamFrame,
43
+ SettingsFrame,
44
+ PushPromiseFrame,
45
+ PingFrame,
46
+ GoawayFrame,
47
+ WindowUpdateFrame,
48
+ ContinuationFrame,
49
+ ].freeze
50
+
51
+ # Default connection "fast-fail" preamble string as defined by the spec.
52
+ CONNECTION_PREFACE_MAGIC = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".freeze
53
+
54
+ class Framer
55
+ def initialize(stream, frames = FRAMES)
56
+ @stream = stream
57
+ @frames = frames
58
+ end
59
+
60
+ def close
61
+ @stream.close
62
+ end
63
+
64
+ def write_connection_preface
65
+ @stream.write(CONNECTION_PREFACE_MAGIC)
66
+ end
67
+
68
+ def read_connection_preface
69
+ string = @stream.read(CONNECTION_PREFACE_MAGIC.bytesize)
70
+
71
+ unless string == CONNECTION_PREFACE_MAGIC
72
+ raise ProtocolError, "Invalid connection preface: #{string.inspect}"
73
+ end
74
+
75
+ return string
76
+ end
77
+
78
+ def read_frame(maximum_frame_size = MAXIMUM_ALLOWED_FRAME_SIZE)
79
+ # Read the header:
80
+ length, type, flags, stream_id = read_header
81
+
82
+ # puts "framer: read_frame #{type} #{length}"
83
+
84
+ # Allocate the frame:
85
+ klass = @frames[type] || Frame
86
+ frame = klass.new(stream_id, flags, type, length)
87
+
88
+ # Read the payload:
89
+ frame.read(@stream, maximum_frame_size)
90
+
91
+ return frame
92
+ end
93
+
94
+ def write_frame(frame)
95
+ # puts "framer: write_frame #{frame.inspect}"
96
+ frame.write(@stream)
97
+
98
+ @stream.flush
99
+
100
+ return frame
101
+ end
102
+
103
+ def read_header
104
+ if buffer = @stream.read(9)
105
+ return Frame.parse_header(buffer)
106
+ else
107
+ # TODO: Is this necessary? I thought the IO would throw this.
108
+ raise EOFError
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,60 @@
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 Protocol
24
+ module HTTP2
25
+ # The GOAWAY frame is used to initiate shutdown of a connection or to signal serious error conditions. GOAWAY allows an endpoint to gracefully stop accepting new streams while still finishing processing of previously established streams. This enables administrative actions, like server maintenance.
26
+ #
27
+ # +-+-------------------------------------------------------------+
28
+ # |R| Last-Stream-ID (31) |
29
+ # +-+-------------------------------------------------------------+
30
+ # | Error Code (32) |
31
+ # +---------------------------------------------------------------+
32
+ # | Additional Debug Data (*) |
33
+ # +---------------------------------------------------------------+
34
+ #
35
+ class GoawayFrame < Frame
36
+ TYPE = 0x7
37
+ FORMAT = "NN"
38
+
39
+ def connection?
40
+ true
41
+ end
42
+
43
+ def unpack
44
+ data = super
45
+
46
+ last_stream_id, error_code = data.unpack(FORMAT)
47
+
48
+ return last_stream_id, error_code, data.slice(8, data.bytesize-8)
49
+ end
50
+
51
+ def pack(last_stream_id, error_code, data)
52
+ super [last_stream_id, error_code].pack(FORMAT) + data
53
+ end
54
+
55
+ def apply(connection)
56
+ connection.receive_goaway(self)
57
+ end
58
+ end
59
+ end
60
+ end