protocol-http2 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.
@@ -0,0 +1,83 @@
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 Protocol
27
+ module HTTP2
28
+ # 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.
29
+ #
30
+ # +---------------+
31
+ # |Pad Length? (8)|
32
+ # +-+-------------+-----------------------------------------------+
33
+ # |E| Stream Dependency? (31) |
34
+ # +-+-------------+-----------------------------------------------+
35
+ # | Weight? (8) |
36
+ # +-+-------------+-----------------------------------------------+
37
+ # | Header Block Fragment (*) ...
38
+ # +---------------------------------------------------------------+
39
+ # | Padding (*) ...
40
+ # +---------------------------------------------------------------+
41
+ #
42
+ class HeadersFrame < Frame
43
+ include Continued, Padded
44
+
45
+ TYPE = 0x1
46
+
47
+ def priority?
48
+ flag_set?(PRIORITY)
49
+ end
50
+
51
+ def end_stream?
52
+ flag_set?(END_STREAM)
53
+ end
54
+
55
+ def unpack
56
+ data = super
57
+
58
+ if priority?
59
+ priority = Priority.unpack(data)
60
+ data = data.byteslice(5, data.bytesize - 5)
61
+ end
62
+
63
+ return priority, data
64
+ end
65
+
66
+ def pack(priority, data, *args)
67
+ buffer = String.new.b
68
+
69
+ if priority
70
+ buffer << priority.pack
71
+ end
72
+
73
+ buffer << data
74
+
75
+ super(buffer, *args)
76
+ end
77
+
78
+ def apply(connection)
79
+ connection.receive_headers(self)
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,79 @@
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 Protocol
25
+ module HTTP2
26
+ # Certain frames can have padding:
27
+ # https://http2.github.io/http2-spec/#padding
28
+ #
29
+ # +---------------+
30
+ # |Pad Length? (8)|
31
+ # +---------------+-----------------------------------------------+
32
+ # | Data (*) ...
33
+ # +---------------------------------------------------------------+
34
+ # | Padding (*) ...
35
+ # +---------------------------------------------------------------+
36
+ #
37
+ module Padded
38
+ def padded?
39
+ flag_set?(PADDED)
40
+ end
41
+
42
+ def pack(data, padding_size: nil, maximum_size: nil)
43
+ if padding_size
44
+ set_flags(PADDED)
45
+
46
+ buffer = String.new.b
47
+
48
+ buffer << padding_size.chr
49
+ buffer << data
50
+
51
+ if padding_size > 1
52
+ buffer << "\0" * (padding_size - 1)
53
+ end
54
+
55
+ super buffer
56
+ else
57
+ clear_flags(PADDED)
58
+
59
+ super data
60
+ end
61
+ end
62
+
63
+ def unpack
64
+ if padded?
65
+ padding_size = @payload[0].ord
66
+ data_size = (@payload.bytesize - 1) - padding_size
67
+
68
+ if data_size < 0
69
+ raise ProtocolError, "Invalid padding length: #{padding_size}"
70
+ end
71
+
72
+ return @payload.byteslice(1, data_size)
73
+ else
74
+ return @payload
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,80 @@
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
+ ACKNOWLEDGEMENT = 0x1
26
+
27
+ module Acknowledgement
28
+ def acknowledgement?
29
+ flag_set?(ACKNOWLEDGEMENT)
30
+ end
31
+
32
+ def acknowledge
33
+ frame = self.class.new
34
+
35
+ frame.length = 0
36
+ frame.set_flags(ACKNOWLEDGEMENT)
37
+
38
+ return frame
39
+ end
40
+ end
41
+
42
+ # 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.
43
+ #
44
+ # +---------------------------------------------------------------+
45
+ # | |
46
+ # | Opaque Data (64) |
47
+ # | |
48
+ # +---------------------------------------------------------------+
49
+ #
50
+ class PingFrame < Frame
51
+ TYPE = 0x6
52
+
53
+ include Acknowledgement
54
+
55
+ def connection?
56
+ true
57
+ end
58
+
59
+ def apply(connection)
60
+ connection.receive_ping(self)
61
+ end
62
+
63
+ def acknowledge
64
+ frame = super
65
+
66
+ frame.pack self.unpack
67
+
68
+ return frame
69
+ end
70
+
71
+ def read_payload(stream)
72
+ super
73
+
74
+ if @length > 8
75
+ raise FrameSizeError, "Invalid frame length"
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,80 @@
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
+ Priority = Struct.new(:exclusive, :stream_dependency, :weight) do
26
+ FORMAT = "NC".freeze
27
+ EXCLUSIVE = 1 << 31
28
+
29
+ def self.unpack(data)
30
+ stream_dependency, weight = data.unpack(FORMAT)
31
+
32
+ return self.new(stream_dependency & EXCLUSIVE != 0, stream_dependency & ~EXCLUSIVE, weight)
33
+ end
34
+
35
+ def pack
36
+ if exclusive
37
+ stream_dependency = self.stream_dependency | EXCLUSIVE
38
+ end
39
+
40
+ return [stream_dependency, self.weight].pack(FORMAT)
41
+ end
42
+ end
43
+
44
+ # The PRIORITY frame specifies the sender-advised priority of a stream. It can be sent in any stream state, including idle or closed streams.
45
+ #
46
+ # +-+-------------------------------------------------------------+
47
+ # |E| Stream Dependency (31) |
48
+ # +-+-------------+-----------------------------------------------+
49
+ # | Weight (8) |
50
+ # +-+-------------+
51
+ #
52
+ class PriorityFrame < Frame
53
+ TYPE = 0x2
54
+
55
+ def priority
56
+ Priority.unpack(@payload)
57
+ end
58
+
59
+ def pack priority
60
+ super priority.pack
61
+ end
62
+
63
+ def unpack
64
+ Priority.unpack(super)
65
+ end
66
+
67
+ def apply(connection)
68
+ connection.receive_priority(self)
69
+ end
70
+
71
+ def read_payload(stream)
72
+ super
73
+
74
+ if @length != 5
75
+ raise FrameSizeError, "Invalid frame length"
76
+ end
77
+ end
78
+ end
79
+ end
80
+ 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
+ require_relative 'padded'
23
+ require_relative 'continuation_frame'
24
+
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 unpack
46
+ data = super
47
+
48
+ stream_id = data.unpack(FORMAT).first
49
+
50
+ return stream_id, data.byteslice(4, data.bytesize - 4)
51
+ end
52
+
53
+ def pack(stream_id, data, *args)
54
+ super([stream_id].pack(FORMAT) + data, *args)
55
+ end
56
+
57
+ def apply(connection)
58
+ connection.receive_push_promise(self)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,72 @@
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
+ NO_ERROR = 0
26
+ PROTOCOL_ERROR = 1
27
+ INTERNAL_ERROR = 2
28
+ FLOW_CONTROL_ERROR = 3
29
+ TIMEOUT = 4
30
+ STREAM_CLOSED = 5
31
+ FRAME_SIZE_ERROR = 6
32
+ REFUSED_STREAM = 7
33
+ CANCEL = 8
34
+ COMPRESSION_ERROR = 9
35
+ CONNECT_ERROR = 10
36
+ ENHANCE_YOUR_CALM = 11
37
+ INADEQUATE_SECURITY = 12
38
+ HTTP_1_1_REQUIRED = 13
39
+
40
+ # 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.
41
+ #
42
+ # +---------------------------------------------------------------+
43
+ # | Error Code (32) |
44
+ # +---------------------------------------------------------------+
45
+ #
46
+ class ResetStreamFrame < Frame
47
+ TYPE = 0x3
48
+ FORMAT = "N".freeze
49
+
50
+ def unpack
51
+ @payload.unpack(FORMAT).first
52
+ end
53
+
54
+ def pack(error_code = NO_ERROR)
55
+ @payload = [error_code].pack(FORMAT)
56
+ @length = @payload.bytesize
57
+ end
58
+
59
+ def apply(connection)
60
+ connection.receive_reset_stream(self)
61
+ end
62
+
63
+ def read_payload(stream)
64
+ super
65
+
66
+ if @length != 4
67
+ raise FrameSizeError, "Invalid frame length"
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,53 @@
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 Protocol
24
+ module HTTP2
25
+ class Server < Connection
26
+ def initialize(framer)
27
+ super(framer, 2)
28
+ end
29
+
30
+ def read_connection_preface(settings = [])
31
+ if @state == :new
32
+ @framer.read_connection_preface
33
+
34
+ send_settings(settings)
35
+
36
+ read_frame do |frame|
37
+ raise ProtocolError, "First frame must be SettingsFrame, but got #{frame.class}" unless frame.is_a? SettingsFrame
38
+ end
39
+ else
40
+ raise ProtocolError, "Cannot send connection preface in state #{@state}"
41
+ end
42
+ end
43
+
44
+ def enable_push?
45
+ @remote_settings.enable_push?
46
+ end
47
+
48
+ def receive_push_promise
49
+ raise ProtocolError, "Server cannot receive push promises."
50
+ end
51
+ end
52
+ end
53
+ end