http-protocol 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,85 @@
|
|
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 HTTP
|
24
|
+
module Protocol
|
25
|
+
module HTTP2
|
26
|
+
module FlowControl
|
27
|
+
def available_frame_size
|
28
|
+
maximum_frame_size = self.maximum_frame_size
|
29
|
+
available_size = @remote_window.available
|
30
|
+
|
31
|
+
if available_size < maximum_frame_size
|
32
|
+
return available_size
|
33
|
+
else
|
34
|
+
return maximum_frame_size
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Keep track of the amount of data sent, and fail if is too much.
|
39
|
+
def consume_remote_window(frame)
|
40
|
+
amount = frame.length
|
41
|
+
|
42
|
+
# 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.
|
43
|
+
if amount.zero? and frame.end_stream?
|
44
|
+
# It's okay, we can send. No need to consume, it's empty anyway.
|
45
|
+
elsif amount >= 0 and amount <= @remote_window.available
|
46
|
+
@remote_window.consume(amount)
|
47
|
+
else
|
48
|
+
raise FlowControlError, "Trying to send #{frame.inspect}, exceeded window size: #{@remote_window.available}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def consume_local_window(frame)
|
53
|
+
amount = frame.length
|
54
|
+
|
55
|
+
@local_window.consume(amount)
|
56
|
+
|
57
|
+
if @local_window.limited?
|
58
|
+
self.send_window_update(@local_window.used)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Notify the remote end that we are prepared to receive more data:
|
63
|
+
def send_window_update(window_increment)
|
64
|
+
frame = WindowUpdateFrame.new(self.id)
|
65
|
+
frame.pack window_increment
|
66
|
+
|
67
|
+
write_frame(frame)
|
68
|
+
|
69
|
+
@local_window.used -= window_increment
|
70
|
+
end
|
71
|
+
|
72
|
+
def receive_window_update(frame)
|
73
|
+
was_full = @remote_window.full?
|
74
|
+
|
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
|
85
|
+
end
|
@@ -0,0 +1,187 @@
|
|
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 HTTP
|
25
|
+
module Protocol
|
26
|
+
module HTTP2
|
27
|
+
END_STREAM = 0x1
|
28
|
+
END_HEADERS = 0x4
|
29
|
+
PADDED = 0x8
|
30
|
+
PRIORITY = 0x20
|
31
|
+
|
32
|
+
class Frame
|
33
|
+
include Comparable
|
34
|
+
|
35
|
+
# Stream Identifier cannot be bigger than this:
|
36
|
+
# https://http2.github.io/http2-spec/#rfc.section.4.1
|
37
|
+
VALID_STREAM_ID = 0..0x7fffffff
|
38
|
+
|
39
|
+
# The absolute maximum bounds for the length field:
|
40
|
+
VALID_LENGTH = 0..0xffffff
|
41
|
+
|
42
|
+
# Used for generating 24-bit frame length:
|
43
|
+
LENGTH_HISHIFT = 16
|
44
|
+
LENGTH_LOMASK = 0xFFFF
|
45
|
+
|
46
|
+
# @param length [Integer] the length of the payload, or nil if the header has not been read yet.
|
47
|
+
def initialize(stream_id = 0, flags = 0, type = self.class.const_get(:TYPE), length = nil, payload = nil)
|
48
|
+
@length = length
|
49
|
+
@type = type
|
50
|
+
@flags = flags
|
51
|
+
@stream_id = stream_id
|
52
|
+
@payload = payload
|
53
|
+
end
|
54
|
+
|
55
|
+
def <=> other
|
56
|
+
to_ary <=> other.to_ary
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_ary
|
60
|
+
[@length, @type, @flags, @stream_id, @payload]
|
61
|
+
end
|
62
|
+
|
63
|
+
# The generic frame header uses the following binary representation:
|
64
|
+
#
|
65
|
+
# +-----------------------------------------------+
|
66
|
+
# | Length (24) |
|
67
|
+
# +---------------+---------------+---------------+
|
68
|
+
# | Type (8) | Flags (8) |
|
69
|
+
# +-+-------------+---------------+-------------------------------+
|
70
|
+
# |R| Stream Identifier (31) |
|
71
|
+
# +=+=============================================================+
|
72
|
+
# | Frame Payload (0...) ...
|
73
|
+
# +---------------------------------------------------------------+
|
74
|
+
|
75
|
+
attr_accessor :length
|
76
|
+
attr_accessor :type
|
77
|
+
attr_accessor :flags
|
78
|
+
attr_accessor :stream_id
|
79
|
+
attr_accessor :payload
|
80
|
+
|
81
|
+
def unpack
|
82
|
+
@payload
|
83
|
+
end
|
84
|
+
|
85
|
+
def pack(payload, maximum_size: nil)
|
86
|
+
@payload = payload
|
87
|
+
@length = payload.bytesize
|
88
|
+
|
89
|
+
if maximum_size and @length > maximum_size
|
90
|
+
raise ProtocolError, "Frame length #{@length} bigger than maximum allowed: #{maximum_size}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def set_flags(mask)
|
95
|
+
@flags |= mask
|
96
|
+
end
|
97
|
+
|
98
|
+
def clear_flags(mask)
|
99
|
+
@flags &= ~mask
|
100
|
+
end
|
101
|
+
|
102
|
+
def flag_set?(mask)
|
103
|
+
@flags & mask != 0
|
104
|
+
end
|
105
|
+
|
106
|
+
# Check if frame is a connection frame: SETTINGS, PING, GOAWAY, and any
|
107
|
+
# frame addressed to stream ID = 0.
|
108
|
+
#
|
109
|
+
# @return [Boolean]
|
110
|
+
def connection?
|
111
|
+
@stream_id.zero?
|
112
|
+
end
|
113
|
+
|
114
|
+
HEADER_FORMAT = 'CnCCN'.freeze
|
115
|
+
STREAM_ID_MASK = 0x7fffffff
|
116
|
+
|
117
|
+
# Generates common 9-byte frame header.
|
118
|
+
# - http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-4.1
|
119
|
+
#
|
120
|
+
# @return [String]
|
121
|
+
def header
|
122
|
+
unless VALID_LENGTH.include? @length
|
123
|
+
raise ProtocolError, "Invalid frame size: #{@length.inspect}"
|
124
|
+
end
|
125
|
+
|
126
|
+
unless VALID_STREAM_ID.include? @stream_id
|
127
|
+
raise ProtocolError, "Invalid stream identifier: #{@stream_id.inspect}"
|
128
|
+
end
|
129
|
+
|
130
|
+
[
|
131
|
+
# These are guaranteed correct due to the length check above.
|
132
|
+
@length >> LENGTH_HISHIFT,
|
133
|
+
@length & LENGTH_LOMASK,
|
134
|
+
@type,
|
135
|
+
@flags,
|
136
|
+
@stream_id
|
137
|
+
].pack(HEADER_FORMAT)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Decodes common 9-byte header.
|
141
|
+
#
|
142
|
+
# @param buffer [String]
|
143
|
+
def self.parse_header(buffer)
|
144
|
+
length_hi, length_lo, type, flags, stream_id = buffer.unpack(HEADER_FORMAT)
|
145
|
+
length = (length_hi << LENGTH_HISHIFT) | length_lo
|
146
|
+
stream_id = stream_id & STREAM_ID_MASK
|
147
|
+
|
148
|
+
return length, type, flags, stream_id
|
149
|
+
end
|
150
|
+
|
151
|
+
def read_header(io)
|
152
|
+
@length, @type, @flags, @stream_id = Frame.parse_header(io.read(9))
|
153
|
+
end
|
154
|
+
|
155
|
+
def read_payload(io)
|
156
|
+
@payload = io.read(@length)
|
157
|
+
end
|
158
|
+
|
159
|
+
def read(io)
|
160
|
+
read_header(io) unless @length
|
161
|
+
read_payload(io)
|
162
|
+
end
|
163
|
+
|
164
|
+
def write_header(io)
|
165
|
+
io.write self.header
|
166
|
+
end
|
167
|
+
|
168
|
+
def write_payload(io)
|
169
|
+
io.write(@payload) if @payload
|
170
|
+
end
|
171
|
+
|
172
|
+
def write(io)
|
173
|
+
if @payload and @length != @payload.bytesize
|
174
|
+
raise ProtocolError, "Invalid payload size: #{@length} != #{@payload.bytesize}"
|
175
|
+
end
|
176
|
+
|
177
|
+
self.write_header(io)
|
178
|
+
self.write_payload(io)
|
179
|
+
end
|
180
|
+
|
181
|
+
def apply(connection)
|
182
|
+
connection.receive_frame(self)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,105 @@
|
|
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 HTTP
|
36
|
+
module Protocol
|
37
|
+
module HTTP2
|
38
|
+
# HTTP/2 frame type mapping as defined by the spec
|
39
|
+
FRAMES = [
|
40
|
+
DataFrame,
|
41
|
+
HeadersFrame,
|
42
|
+
PriorityFrame,
|
43
|
+
ResetStreamFrame,
|
44
|
+
SettingsFrame,
|
45
|
+
PushPromiseFrame,
|
46
|
+
PingFrame,
|
47
|
+
GoawayFrame,
|
48
|
+
WindowUpdateFrame,
|
49
|
+
ContinuationFrame,
|
50
|
+
].freeze
|
51
|
+
|
52
|
+
# Default connection "fast-fail" preamble string as defined by the spec.
|
53
|
+
CONNECTION_PREFACE_MAGIC = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".freeze
|
54
|
+
|
55
|
+
class Framer
|
56
|
+
def initialize(io, frames = FRAMES)
|
57
|
+
@io = io
|
58
|
+
@frames = frames
|
59
|
+
|
60
|
+
@buffer = String.new.b
|
61
|
+
end
|
62
|
+
|
63
|
+
def write_connection_preface
|
64
|
+
@io.write(CONNECTION_PREFACE_MAGIC)
|
65
|
+
end
|
66
|
+
|
67
|
+
def read_connection_preface
|
68
|
+
string = @io.read(CONNECTION_PREFACE_MAGIC.bytesize)
|
69
|
+
|
70
|
+
unless string == CONNECTION_PREFACE_MAGIC
|
71
|
+
raise ProtocolError, "Invalid connection preface: #{string.inspect}"
|
72
|
+
end
|
73
|
+
|
74
|
+
return string
|
75
|
+
end
|
76
|
+
|
77
|
+
def read_frame
|
78
|
+
# Read the header:
|
79
|
+
length, type, flags, stream_id = read_header
|
80
|
+
|
81
|
+
# puts "framer: read_frame #{type} #{length}"
|
82
|
+
|
83
|
+
# Allocate the frame:
|
84
|
+
klass = @frames[type] || Frame
|
85
|
+
frame = klass.new(stream_id, flags, type, length)
|
86
|
+
|
87
|
+
# Read the payload:
|
88
|
+
frame.read(@io)
|
89
|
+
|
90
|
+
return frame
|
91
|
+
end
|
92
|
+
|
93
|
+
def write_frame(frame)
|
94
|
+
# puts "framer: write_frame #{frame.inspect}"
|
95
|
+
frame.write(@io)
|
96
|
+
@io.flush
|
97
|
+
end
|
98
|
+
|
99
|
+
def read_header
|
100
|
+
return Frame.parse_header(@io.read(9))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,62 @@
|
|
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
|
+
# 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.
|
27
|
+
#
|
28
|
+
# +-+-------------------------------------------------------------+
|
29
|
+
# |R| Last-Stream-ID (31) |
|
30
|
+
# +-+-------------------------------------------------------------+
|
31
|
+
# | Error Code (32) |
|
32
|
+
# +---------------------------------------------------------------+
|
33
|
+
# | Additional Debug Data (*) |
|
34
|
+
# +---------------------------------------------------------------+
|
35
|
+
#
|
36
|
+
class GoawayFrame < Frame
|
37
|
+
TYPE = 0x7
|
38
|
+
FORMAT = "NN"
|
39
|
+
|
40
|
+
def connection?
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def unpack
|
45
|
+
data = super
|
46
|
+
|
47
|
+
last_stream_id, error_code = data.unpack(FORMAT)
|
48
|
+
|
49
|
+
return last_stream_id, error_code, data.slice(8, data.bytesize-8)
|
50
|
+
end
|
51
|
+
|
52
|
+
def pack(last_stream_id, error_code, data)
|
53
|
+
super [last_stream_id, error_code].pack(FORMAT) + data
|
54
|
+
end
|
55
|
+
|
56
|
+
def apply(connection)
|
57
|
+
connection.receive_goaway(self)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,95 @@
|
|
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
|
+
require_relative 'continuation_frame'
|
24
|
+
require_relative 'priority_frame'
|
25
|
+
|
26
|
+
module HTTP
|
27
|
+
module Protocol
|
28
|
+
module HTTP2
|
29
|
+
# The HEADERS frame is used to open a stream, and additionally carries a header block fragment. HEADERS frames can be sent on a stream in the "idle", "reserved (local)", "open", or "half-closed (remote)" state.
|
30
|
+
#
|
31
|
+
# +---------------+
|
32
|
+
# |Pad Length? (8)|
|
33
|
+
# +-+-------------+-----------------------------------------------+
|
34
|
+
# |E| Stream Dependency? (31) |
|
35
|
+
# +-+-------------+-----------------------------------------------+
|
36
|
+
# | Weight? (8) |
|
37
|
+
# +-+-------------+-----------------------------------------------+
|
38
|
+
# | Header Block Fragment (*) ...
|
39
|
+
# +---------------------------------------------------------------+
|
40
|
+
# | Padding (*) ...
|
41
|
+
# +---------------------------------------------------------------+
|
42
|
+
#
|
43
|
+
class HeadersFrame < Frame
|
44
|
+
include Continued, Padded
|
45
|
+
|
46
|
+
TYPE = 0x1
|
47
|
+
|
48
|
+
def initialize(*)
|
49
|
+
super
|
50
|
+
|
51
|
+
@priority = nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def priority?
|
55
|
+
flag_set?(PRIORITY)
|
56
|
+
end
|
57
|
+
|
58
|
+
def end_headers?
|
59
|
+
flag_set?(END_HEADERS)
|
60
|
+
end
|
61
|
+
|
62
|
+
def end_stream?
|
63
|
+
flag_set?(END_STREAM)
|
64
|
+
end
|
65
|
+
|
66
|
+
def unpack
|
67
|
+
data = super
|
68
|
+
|
69
|
+
if priority?
|
70
|
+
priority = Priority.unpack(data)
|
71
|
+
data = data.byteslice(5, data.bytesize - 5)
|
72
|
+
end
|
73
|
+
|
74
|
+
return priority, data
|
75
|
+
end
|
76
|
+
|
77
|
+
def pack(priority, data, *args)
|
78
|
+
buffer = String.new.b
|
79
|
+
|
80
|
+
if priority
|
81
|
+
buffer << priority.pack
|
82
|
+
end
|
83
|
+
|
84
|
+
buffer << data
|
85
|
+
|
86
|
+
super(buffer, *args)
|
87
|
+
end
|
88
|
+
|
89
|
+
def apply(connection)
|
90
|
+
connection.receive_headers(self)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|