http-2-next 0.2.5 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/http/2/next.rb +1 -4
- data/lib/http/2/next/connection.rb +48 -30
- data/lib/http/2/next/emitter.rb +3 -4
- 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 +7 -3
- data/lib/http/2/next/framer.rb +26 -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 +144 -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 +12 -8
- 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
|
@@ -89,7 +88,7 @@ module HTTP2Next
|
|
89
88
|
@state = state
|
90
89
|
@error = false
|
91
90
|
@closed = false
|
92
|
-
@_method = @_content_length = nil
|
91
|
+
@_method = @_content_length = @_status_code = nil
|
93
92
|
@_waiting_on_trailers = false
|
94
93
|
@received_data = false
|
95
94
|
|
@@ -127,12 +126,17 @@ module HTTP2Next
|
|
127
126
|
stream_error(:stream_closed) if (@state == :closed && @closed != :local_rst) ||
|
128
127
|
@state == :remote_closed
|
129
128
|
@_method ||= frame[:method]
|
129
|
+
@_status_code ||= frame[:status]
|
130
130
|
@_content_length ||= frame[:content_length]
|
131
131
|
@_trailers ||= frame[:trailer]
|
132
132
|
if @_waiting_on_trailers
|
133
133
|
verify_trailers(frame)
|
134
|
-
elsif @received_data
|
135
|
-
|
134
|
+
elsif @received_data &&
|
135
|
+
(!@_status_code || @_status_code >= 200)
|
136
|
+
|
137
|
+
# An endpoint that receives a HEADERS frame without the END_STREAM flag set after receiving a final
|
138
|
+
# (non-informational) status code MUST treat the corresponding request or response as malformed.
|
139
|
+
verify_trailers(frame)
|
136
140
|
end
|
137
141
|
emit(:headers, frame[:payload]) unless frame[:ignore]
|
138
142
|
@_waiting_on_trailers = !@_trailers.nil?
|
@@ -164,7 +168,7 @@ module HTTP2Next
|
|
164
168
|
return unless @_trailers
|
165
169
|
|
166
170
|
trailers = frame[:payload]
|
167
|
-
return
|
171
|
+
return unless trailers.respond_to?(:each)
|
168
172
|
|
169
173
|
trailers.each do |field, _|
|
170
174
|
@_trailers.delete(field)
|
@@ -174,7 +178,7 @@ module HTTP2Next
|
|
174
178
|
end
|
175
179
|
|
176
180
|
def calculate_content_length(data_length)
|
177
|
-
return unless @_content_length
|
181
|
+
return unless @_content_length && data_length
|
178
182
|
|
179
183
|
@_content_length -= data_length
|
180
184
|
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
|