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.
@@ -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