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.
@@ -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").unpack("B*").first[-l..-1] }.each(&:freeze).freeze
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
@@ -79,7 +79,8 @@ module HTTP2Next
79
79
  receive(CONNECTION_PREFACE_MAGIC)
80
80
 
81
81
  # Process received HTTP2-Settings payload
82
- buf = HTTP2Next::Buffer.new Base64.urlsafe_decode64(settings.to_s)
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] = [:end_stream]
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 args [Array]
142
+ # @param parent [Stream]
143
+ # @param headers [Enumerable[String, String]]
144
+ # @param flags [Array[Symbol]]
141
145
  # @param callback [Proc]
142
- def promise(*args)
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,
@@ -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
- stream_error(:protocol_error, msg: "already received data")
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 if trailers.is_a?(Buffer)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTP2Next
4
- VERSION = "0.2.5"
4
+ VERSION = "0.4.2"
5
5
  end
@@ -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
- attr_accessor :transitions
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
@@ -0,0 +1,9 @@
1
+ module HTTP2Next
2
+ class Client < Connection
3
+ def upgrade: () -> Stream
4
+
5
+ def send_connection_preface: () -> void
6
+
7
+ def self.settings_header: (settings_enum) -> String
8
+ end
9
+ end
@@ -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
@@ -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
@@ -0,0 +1,13 @@
1
+ module HTTP2Next
2
+ class FrameBuffer
3
+ attr_reader bytesize: Integer
4
+
5
+ @buffer: Array[String]
6
+
7
+ def <<: (frame) -> void
8
+
9
+ def empty?: () -> bool
10
+
11
+ def retrieve: (Integer) -> frame?
12
+ end
13
+ 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
@@ -0,0 +1,9 @@
1
+ module HTTP2Next
2
+ module Header
3
+ class Huffman
4
+ def encode: (String str) -> String
5
+
6
+ def decode: (String str) -> String
7
+ end
8
+ end
9
+ end