http-2-next 0.2.3 → 0.4.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 +4 -4
- data/README.md +2 -2
- data/lib/http/2/next.rb +1 -4
- data/lib/http/2/next/connection.rb +49 -31
- data/lib/http/2/next/emitter.rb +2 -3
- data/lib/http/2/next/error.rb +1 -0
- data/lib/http/2/next/extensions.rb +26 -1
- data/lib/http/2/next/flow_buffer.rb +8 -4
- data/lib/http/2/next/framer.rb +25 -17
- data/lib/http/2/next/header.rb +35 -0
- data/lib/http/2/next/header/compressor.rb +137 -0
- data/lib/http/2/next/header/decompressor.rb +142 -0
- data/lib/http/2/next/{compressor.rb → header/encoding_context.rb} +7 -298
- data/lib/http/2/next/{huffman.rb → header/huffman.rb} +5 -2
- data/lib/http/2/next/{huffman_statemachine.rb → header/huffman_statemachine.rb} +0 -0
- data/lib/http/2/next/server.rb +8 -5
- data/lib/http/2/next/stream.rb +4 -5
- data/lib/http/2/next/version.rb +1 -1
- data/lib/tasks/generate_huffman_table.rb +4 -4
- data/sig/client.rbs +9 -0
- data/sig/connection.rbs +73 -0
- data/sig/emitter.rbs +13 -0
- data/sig/flow_buffer.rbs +23 -0
- data/sig/frame_buffer.rbs +13 -0
- data/sig/framer.rbs +25 -0
- data/sig/header.rbs +15 -0
- data/sig/header/compressor.rbs +23 -0
- data/sig/header/decompressor.rbs +22 -0
- data/sig/header/encoding_context.rbs +34 -0
- data/sig/header/huffman.rbs +9 -0
- data/sig/next.rbs +95 -0
- data/sig/server.rbs +12 -0
- data/sig/stream.rbs +77 -0
- metadata +26 -10
- data/lib/http/2/next/buffer.rb +0 -80
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "error"
|
3
|
+
require_relative "../error"
|
4
|
+
require_relative "../extensions"
|
4
5
|
|
5
6
|
module HTTP2Next
|
6
7
|
# Implementation of huffman encoding for HPACK
|
@@ -9,6 +10,8 @@ module HTTP2Next
|
|
9
10
|
module Header
|
10
11
|
# Huffman encoder/decoder
|
11
12
|
class Huffman
|
13
|
+
using StringExtensions
|
14
|
+
|
12
15
|
include Error
|
13
16
|
|
14
17
|
BITS_AT_ONCE = 4
|
@@ -319,7 +322,7 @@ module HTTP2Next
|
|
319
322
|
[0x3fffffff, 30]
|
320
323
|
].each(&:freeze).freeze
|
321
324
|
|
322
|
-
ENCODE_TABLE = CODES.map { |c, l| [c].pack("N").
|
325
|
+
ENCODE_TABLE = CODES.map { |c, l| [c].pack("N").unpack1("B*")[-l..-1] }.each(&:freeze).freeze
|
323
326
|
end
|
324
327
|
end
|
325
328
|
end
|
File without changes
|
data/lib/http/2/next/server.rb
CHANGED
@@ -79,7 +79,8 @@ module HTTP2Next
|
|
79
79
|
receive(CONNECTION_PREFACE_MAGIC)
|
80
80
|
|
81
81
|
# Process received HTTP2-Settings payload
|
82
|
-
buf =
|
82
|
+
buf = "".b
|
83
|
+
buf << Base64.urlsafe_decode64(settings.to_s)
|
83
84
|
header = @framer.common_header(
|
84
85
|
length: buf.bytesize,
|
85
86
|
type: :settings,
|
@@ -95,6 +96,7 @@ module HTTP2Next
|
|
95
96
|
|
96
97
|
headers_frame = {
|
97
98
|
type: :headers,
|
99
|
+
flags: [:end_headers],
|
98
100
|
stream: 1,
|
99
101
|
weight: DEFAULT_WEIGHT,
|
100
102
|
dependency: 0,
|
@@ -103,7 +105,7 @@ module HTTP2Next
|
|
103
105
|
}
|
104
106
|
|
105
107
|
if body.empty?
|
106
|
-
headers_frame[:flags]
|
108
|
+
headers_frame[:flags] << [:end_stream]
|
107
109
|
stream << headers_frame
|
108
110
|
else
|
109
111
|
stream << headers_frame
|
@@ -137,10 +139,11 @@ module HTTP2Next
|
|
137
139
|
|
138
140
|
# Handle locally initiated server-push event emitted by the stream.
|
139
141
|
#
|
140
|
-
# @param
|
142
|
+
# @param parent [Stream]
|
143
|
+
# @param headers [Enumerable[String, String]]
|
144
|
+
# @param flags [Array[Symbol]]
|
141
145
|
# @param callback [Proc]
|
142
|
-
def promise(
|
143
|
-
parent, headers, flags = *args
|
146
|
+
def promise(parent, headers, flags)
|
144
147
|
promise = new_stream(parent: parent)
|
145
148
|
promise.send(
|
146
149
|
type: :push_promise,
|
data/lib/http/2/next/stream.rb
CHANGED
@@ -51,11 +51,10 @@ module HTTP2Next
|
|
51
51
|
|
52
52
|
# Stream priority as set by initiator.
|
53
53
|
attr_reader :weight
|
54
|
-
attr_reader :dependency
|
54
|
+
attr_reader :dependency, :remote_window
|
55
55
|
|
56
56
|
# Size of current stream flow control window.
|
57
57
|
attr_reader :local_window
|
58
|
-
attr_reader :remote_window
|
59
58
|
alias window local_window
|
60
59
|
|
61
60
|
# Reason why connection was closed.
|
@@ -75,7 +74,7 @@ module HTTP2Next
|
|
75
74
|
# @param parent [Stream]
|
76
75
|
# @param state [Symbol]
|
77
76
|
def initialize(connection:, id:, weight: 16, dependency: 0, exclusive: false, parent: nil, state: :idle)
|
78
|
-
stream_error(:protocol_error, "stream can't depend on itself") if id == dependency
|
77
|
+
stream_error(:protocol_error, msg: "stream can't depend on itself") if id == dependency
|
79
78
|
|
80
79
|
@connection = connection
|
81
80
|
@id = id
|
@@ -164,7 +163,7 @@ module HTTP2Next
|
|
164
163
|
return unless @_trailers
|
165
164
|
|
166
165
|
trailers = frame[:payload]
|
167
|
-
return
|
166
|
+
return unless trailers.respond_to?(:each)
|
168
167
|
|
169
168
|
trailers.each do |field, _|
|
170
169
|
@_trailers.delete(field)
|
@@ -174,7 +173,7 @@ module HTTP2Next
|
|
174
173
|
end
|
175
174
|
|
176
175
|
def calculate_content_length(data_length)
|
177
|
-
return unless @_content_length
|
176
|
+
return unless @_content_length && data_length
|
178
177
|
|
179
178
|
@_content_length -= data_length
|
180
179
|
return if @_content_length >= 0
|
data/lib/http/2/next/version.rb
CHANGED
@@ -5,7 +5,7 @@ task :generate_table do
|
|
5
5
|
HuffmanTable::Node.generate_state_table
|
6
6
|
end
|
7
7
|
|
8
|
-
require_relative "../http/2/next/huffman"
|
8
|
+
require_relative "../http/2/next/header/huffman"
|
9
9
|
|
10
10
|
# @private
|
11
11
|
module HuffmanTable
|
@@ -13,9 +13,8 @@ module HuffmanTable
|
|
13
13
|
EOS = 256
|
14
14
|
|
15
15
|
class Node
|
16
|
-
attr_accessor :next, :emit, :final, :depth
|
17
|
-
|
18
|
-
attr_accessor :id
|
16
|
+
attr_accessor :next, :emit, :final, :depth, :transitions, :id
|
17
|
+
|
19
18
|
@@id = 0 # rubocop:disable Style/ClassVars
|
20
19
|
def initialize(depth)
|
21
20
|
@next = [nil, nil]
|
@@ -38,6 +37,7 @@ module HuffmanTable
|
|
38
37
|
|
39
38
|
class Transition
|
40
39
|
attr_accessor :emit, :node
|
40
|
+
|
41
41
|
def initialize(emit, node)
|
42
42
|
@emit = emit
|
43
43
|
@node = node
|
data/sig/client.rbs
ADDED
data/sig/connection.rbs
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
module HTTP2Next
|
2
|
+
class Connection
|
3
|
+
include FlowBuffer
|
4
|
+
include Emitter
|
5
|
+
|
6
|
+
attr_reader state: Symbol
|
7
|
+
|
8
|
+
attr_reader local_window: Integer
|
9
|
+
attr_reader remote_window: Integer
|
10
|
+
|
11
|
+
alias window local_window
|
12
|
+
|
13
|
+
attr_reader remote_settings: settings_hash
|
14
|
+
attr_reader local_settings: settings_hash
|
15
|
+
attr_reader pending_settings: settings_hash
|
16
|
+
|
17
|
+
attr_reader active_stream_count: Integer
|
18
|
+
|
19
|
+
attr_writer max_streams: Integer
|
20
|
+
|
21
|
+
def closed?: () -> bool
|
22
|
+
|
23
|
+
def new_stream: (
|
24
|
+
?weight: Integer,
|
25
|
+
?dependency: Integer,
|
26
|
+
?exclusive: bool,
|
27
|
+
?parent: Stream?,
|
28
|
+
?state: Symbol
|
29
|
+
) -> Stream
|
30
|
+
|
31
|
+
def ping: (String) -> void
|
32
|
+
| (String) { () -> void } -> void
|
33
|
+
|
34
|
+
def goaway: (Symbol, String?) -> void
|
35
|
+
| (Symbol) -> void
|
36
|
+
| () -> void
|
37
|
+
|
38
|
+
def window_update: (Integer increment) -> void
|
39
|
+
|
40
|
+
def settings: (settings_enum) -> void
|
41
|
+
|
42
|
+
def receive: (string data) -> void
|
43
|
+
alias << receive
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def initialize: (settings_hash) -> untyped
|
48
|
+
|
49
|
+
def send: (frame) -> void
|
50
|
+
|
51
|
+
def encode: (frame) -> Array[String]
|
52
|
+
|
53
|
+
def connection_frame?: (frame) -> bool
|
54
|
+
|
55
|
+
def connection_management: (frame) -> void
|
56
|
+
|
57
|
+
def ping_management: (frame) -> void
|
58
|
+
|
59
|
+
def validate_settings: (:client | :server, settings_enum) -> void
|
60
|
+
|
61
|
+
def connection_settings: (frame) -> void
|
62
|
+
|
63
|
+
def decode_headers: (frame) -> void
|
64
|
+
|
65
|
+
def encode_headers: (frame) -> Array[frame]
|
66
|
+
|
67
|
+
def activate_stream: (id: Integer, **untyped) -> void
|
68
|
+
|
69
|
+
def verify_stream_order: (Integer id) -> void
|
70
|
+
|
71
|
+
def verify_pseudo_headers: (frame, Array[String]) -> void
|
72
|
+
end
|
73
|
+
end
|
data/sig/emitter.rbs
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module HTTP2Next
|
2
|
+
module Emitter
|
3
|
+
def on: (Symbol event) { (*untyped) -> void } -> void
|
4
|
+
|
5
|
+
def once: (Symbol event) { (*untyped) -> void } -> void
|
6
|
+
|
7
|
+
def emit: (Symbol event, *untyped args) ?{ (*untyped) -> void } -> void
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def listeners: (Symbol event) -> Array[Proc]
|
12
|
+
end
|
13
|
+
end
|
data/sig/flow_buffer.rbs
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module HTTP2Next
|
2
|
+
module FlowBuffer
|
3
|
+
MAX_WINDOW_SIZE: Integer
|
4
|
+
|
5
|
+
def buffered_amount: () -> Integer
|
6
|
+
|
7
|
+
def flush: () -> void
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def send_buffer: () -> FrameBuffer
|
12
|
+
|
13
|
+
def update_local_window: (frame) -> void
|
14
|
+
|
15
|
+
def calculate_window_update: (Integer) -> void
|
16
|
+
|
17
|
+
def send_data: () -> void
|
18
|
+
| (frame?) -> void
|
19
|
+
| (frame?, bool) -> void
|
20
|
+
|
21
|
+
def process_window_update: (frame: frame, ?encode: bool) -> void
|
22
|
+
end
|
23
|
+
end
|
data/sig/framer.rbs
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module HTTP2Next
|
2
|
+
class Framer
|
3
|
+
attr_accessor local_max_frame_size: Integer
|
4
|
+
|
5
|
+
attr_accessor remote_max_frame_size: Integer
|
6
|
+
|
7
|
+
def common_header: (frame) -> String
|
8
|
+
|
9
|
+
def read_common_frame: (String) -> frame
|
10
|
+
|
11
|
+
def generate: (frame) -> String
|
12
|
+
|
13
|
+
def parse: (String) -> frame?
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def initialize: (Integer, Integer) -> untyped
|
18
|
+
| (Integer) -> untyped
|
19
|
+
| () -> untyped
|
20
|
+
|
21
|
+
def pack_error: (Integer | Symbol e) -> String
|
22
|
+
|
23
|
+
def unpack_error: (Integer) -> (Symbol | Integer)
|
24
|
+
end
|
25
|
+
end
|
data/sig/header.rbs
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module HTTP2Next
|
2
|
+
module Header
|
3
|
+
type header_key = :type | :name | :value | :index
|
4
|
+
type header_value = Integer | String | :indexed | :changetablesize | :incremental | :noindex | :neverindexed
|
5
|
+
|
6
|
+
#type context_hash = {
|
7
|
+
# huffman?: (:always | :never | :shorter),
|
8
|
+
# index?: (:all | :static | :never),
|
9
|
+
# table_size?: Integer
|
10
|
+
#}
|
11
|
+
type context_hash = Hash[Symbol, Symbol | Integer]
|
12
|
+
|
13
|
+
type header_command = Hash[header_key, header_value]
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module HTTP2Next
|
2
|
+
module Header
|
3
|
+
class Compressor
|
4
|
+
@cc: EncodingContext
|
5
|
+
|
6
|
+
def table_size=: (Integer) -> void
|
7
|
+
|
8
|
+
def integer: (Integer, Integer) -> String
|
9
|
+
|
10
|
+
def string: (String) -> String
|
11
|
+
|
12
|
+
def header: (header_command, String) -> String
|
13
|
+
| (header_command) -> String
|
14
|
+
|
15
|
+
def encode: (Enumerable[header_pair]) -> String
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def initialize: (context_hash options) -> untyped
|
20
|
+
| () -> untyped
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module HTTP2Next
|
2
|
+
module Header
|
3
|
+
class Decompressor
|
4
|
+
@cc: EncodingContext
|
5
|
+
|
6
|
+
def table_size=: (Integer) -> void
|
7
|
+
|
8
|
+
def integer: (String, Integer) -> Integer
|
9
|
+
|
10
|
+
def string: (String) -> String
|
11
|
+
|
12
|
+
def header: (String) -> header_command
|
13
|
+
|
14
|
+
def decode: (String, frame?) -> Array[header_pair]
|
15
|
+
| (String) -> Array[header_pair]
|
16
|
+
private
|
17
|
+
|
18
|
+
def initialize: (context_hash options) -> untyped
|
19
|
+
| () -> untyped
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module HTTP2Next
|
2
|
+
module Header
|
3
|
+
class EncodingContext
|
4
|
+
STATIC_TABLE: Array[header_pair]
|
5
|
+
|
6
|
+
attr_reader table: Array[header_pair]
|
7
|
+
|
8
|
+
attr_reader options: context_hash
|
9
|
+
|
10
|
+
def dup: () -> EncodingContext
|
11
|
+
|
12
|
+
def dereference: (Integer) -> header_pair
|
13
|
+
|
14
|
+
def process: (header_command cmd) -> header_pair?
|
15
|
+
|
16
|
+
def encode: (_Each[header_pair]) -> Array[header_command]
|
17
|
+
|
18
|
+
def addcmd: (String name, String value) -> header_command
|
19
|
+
|
20
|
+
def table_size=: (Integer) -> void
|
21
|
+
|
22
|
+
def current_table_size: () -> Integer
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def initialize: (context_hash options) -> untyped
|
27
|
+
| () -> untyped
|
28
|
+
|
29
|
+
def add_to_table: (header_pair) -> void
|
30
|
+
|
31
|
+
def size_check: (header_pair?) -> bool
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/sig/next.rbs
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
module HTTP2Next
|
2
|
+
DEFAULT_FLOW_WINDOW: Integer
|
3
|
+
|
4
|
+
DEFAULT_HEADER_SIZE: Integer
|
5
|
+
|
6
|
+
DEFAULT_MAX_CONCURRENT_STREAMS: Integer
|
7
|
+
|
8
|
+
type settings_hash = Hash[Symbol, Integer]
|
9
|
+
|
10
|
+
type settings_enum = Enumerable[Symbol, Integer]
|
11
|
+
|
12
|
+
SPEC_DEFAULT_CONNECTION_SETTINGS: settings_hash
|
13
|
+
|
14
|
+
DEFAULT_CONNECTION_SETTINGS: settings_hash
|
15
|
+
|
16
|
+
DEFAULT_WEIGHT: Integer
|
17
|
+
|
18
|
+
CONNECTION_PREFACE_MAGIC: String
|
19
|
+
|
20
|
+
REQUEST_MANDATORY_HEADERS: Array[Symbol]
|
21
|
+
|
22
|
+
RESPONSE_MANDATORY_HEADERS: Array[Symbol]
|
23
|
+
|
24
|
+
type header_pair = [String, String]
|
25
|
+
|
26
|
+
# # FRAMES
|
27
|
+
# type frame_control_flags = Array[:end_headers | :end_stream]
|
28
|
+
|
29
|
+
# # HEADERS
|
30
|
+
# type headers_frame = {
|
31
|
+
# type: :headers, flags: frame_control_flags, stream: Integer, payload: Enumerable[header_pair],
|
32
|
+
# ?method: Symbol, ?trailer: Array[String], ?content_length: Integer, ?padding: Integer
|
33
|
+
# }
|
34
|
+
|
35
|
+
# # DATA
|
36
|
+
# type data_frame = { type: :data, flags: frame_control_flags, stream: Integer, length: Integer, payload: String, ?padding: Integer }
|
37
|
+
|
38
|
+
# # PUSH_PROMISE
|
39
|
+
# type push_promise_frame = { type: :push_promise, promise_stream: Integer, flags: frame_control_flags, stream: Integer, ?method: Symbol, ?trailer: Array[String], ?content_length: Integer, payload: Enumerable[header_pair], ?padding: Integer }
|
40
|
+
|
41
|
+
# # SETTINGS
|
42
|
+
# type settings_frame = { type: :settings, stream: 0, payload: Array[[Symbol | Integer, Integer]] }
|
43
|
+
|
44
|
+
# # WINDOW_UPDATE
|
45
|
+
# type window_update_frame = { type: :window_update, stream: Integer, increment: Integer }
|
46
|
+
|
47
|
+
# # PRIORITY
|
48
|
+
# type priority_frame = { type: :priority, stream: Integer, dependency: Integer, exclusive: bool, weight: Integer }
|
49
|
+
|
50
|
+
# # ALTSVC
|
51
|
+
# type altsvc_frame = { type: :altsvc, stream: 0, max_age: Integer, port: Integer, proto: "String", host: String }
|
52
|
+
|
53
|
+
# # ORIGIN
|
54
|
+
# type origin_frame = { type: :origin, stream: 0, origin: Array[String] }
|
55
|
+
|
56
|
+
# # PING
|
57
|
+
# type ping_frame = { type: :ping, payload: String, length: Integer }
|
58
|
+
|
59
|
+
# # GOAWAY
|
60
|
+
# type goaway_frame = { type: :goaway, stream: 0, last_stream: Integer, error: Symbol? }
|
61
|
+
|
62
|
+
# type frame = headers_frame | data_frame | push_promise_frame |
|
63
|
+
# settings_frame | window_update_frame | priority_frame | altsvc_frame |
|
64
|
+
# origin_frame | ping_frame | goaway_frame
|
65
|
+
|
66
|
+
type frame_key = :type | :flags | :stream | :padding | :ignore |
|
67
|
+
# headers
|
68
|
+
:method | :trailer | :content_length |
|
69
|
+
# data, settings, ping
|
70
|
+
:payload | :length |
|
71
|
+
# promise
|
72
|
+
:promise_stream |
|
73
|
+
# window_update
|
74
|
+
:increment |
|
75
|
+
# priority
|
76
|
+
:dependency | :exclusive | :weight |
|
77
|
+
# altsvc
|
78
|
+
:max_age | :port | :proto | :host |
|
79
|
+
# origin
|
80
|
+
:origin |
|
81
|
+
# goaway
|
82
|
+
:last_stream | :error
|
83
|
+
|
84
|
+
type frame_value = Integer |
|
85
|
+
Symbol | # type (:data, :headers)
|
86
|
+
Array[Symbol] |
|
87
|
+
String |
|
88
|
+
bool |
|
89
|
+
Array[String] |
|
90
|
+
Array[[Symbol | Integer, Integer]] |
|
91
|
+
Enumerable[header_pair] |
|
92
|
+
nil
|
93
|
+
|
94
|
+
type frame = Hash[frame_key, frame_value]
|
95
|
+
end
|