biryani 0.0.5 → 0.0.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be854e5993fe96b137d1d4db219551155b3103b213a97058c64f756ad2971ba3
4
- data.tar.gz: 2256eea9aee63134060398084d904651a1d0fde583564dc211e13ddb5b134e78
3
+ metadata.gz: 2a6614c6a74a069a08281c0f3846da8512f379e347ce77e3812c7cd22f2506bc
4
+ data.tar.gz: 5a0f61a32f576c0a693abeb2773490ddcbbdcc573b82dac16a6d3a3e82bf2e0e
5
5
  SHA512:
6
- metadata.gz: 0abdc8a1ce8d2b0a4d149830e14c0435e08b884950c026ab7d67af0726aeff53eff9d8dc06da83c4819badd967d6e1f0f2fb0b5833432c508ec3874c117ceaf8
7
- data.tar.gz: e11b1fdde4debd807fb8ddfd7c9b7eaec3be56510be8758e1c42b815589418c5711848ae2ded681247578509aad0f20951558ee54a0b0d6d92825f7704bb3256
6
+ metadata.gz: 0c0add40f15c4e1fa9110a295f9fda392aae21a6ad4dd062a0d3a31fd5dd208f8a384fbcf9009a6fc29e6a940f4fc755f9da0f3e244f68292ee41323cd10e82f
7
+ data.tar.gz: 1af73e5c917c095dfcb635fd7c09548b4330114125e00df539356fb270d8f083e76c0d25262652f6781e4b8415fc3c2da91083b0af19b20ac7175a70b5c28bc1
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 4.0.0
1
+ 4.0.1
data/Rakefile CHANGED
@@ -5,4 +5,11 @@ require 'rubocop/rake_task'
5
5
  RuboCop::RakeTask.new
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
+ desc 'conformance test using h2spec'
9
+ RSpec::Core::RakeTask.new(:conformance) do |t|
10
+ t.pattern = 'conformance/server_spec.rb'
11
+ t.rspec_opts = %w[--out /dev/null]
12
+ t.verbose = false
13
+ end
14
+
8
15
  task default: %i[rubocop spec]
@@ -0,0 +1,31 @@
1
+ require_relative 'spec_helper'
2
+
3
+ RSpec.describe Server do
4
+ before do
5
+ @tcpserver = TCPServer.open(PORT)
6
+
7
+ Ractor.new(@tcpserver) do |socket|
8
+ server = Server.new(
9
+ Ractor.shareable_proc do |_req, res|
10
+ res.status = 200
11
+ res.content = 'OK'
12
+ end
13
+ )
14
+
15
+ server.run(socket)
16
+ end
17
+ end
18
+
19
+ let(:client) do
20
+ which('h2spec')
21
+ "h2spec --port #{PORT} --verbose"
22
+ end
23
+
24
+ after do
25
+ @tcpserver.close
26
+ end
27
+
28
+ it 'should run' do
29
+ system(client)
30
+ end
31
+ end
@@ -0,0 +1,14 @@
1
+ require 'open3'
2
+ require 'socket'
3
+ require 'biryani'
4
+
5
+ # rubocop: disable Style/MixinUsage
6
+ include Biryani
7
+ # rubocop: enable Style/MixinUsage
8
+
9
+ PORT = 8888
10
+
11
+ def which(cmd)
12
+ o, = Open3.capture3("which #{cmd}")
13
+ warn "conformance task require `#{cmd}`. Install `#{cmd}`." if o.empty?
14
+ end
data/example/echo.rb CHANGED
@@ -13,7 +13,6 @@ server = Biryani::Server.new(
13
13
  # @param res [Biryani::HTTPResponse]
14
14
  Ractor.shareable_proc do |req, res|
15
15
  res.status = 200
16
-
17
16
  res.content = if req.method.upcase == 'POST'
18
17
  req.content
19
18
  else
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << "#{__dir__}/../lib"
4
+
5
+ require 'socket'
6
+ require 'biryani'
7
+
8
+ port = ARGV[0] || 8888
9
+ socket = TCPServer.new(port)
10
+
11
+ server = Biryani::Server.new(
12
+ Ractor.shareable_proc do
13
+ raise 'error'
14
+ end
15
+ )
16
+ server.run(socket)
17
+
18
+ # $ bundle exec ruby example/raise_error.rb
19
+ # $ curl -v --http2-prior-knowledge http://localhost:8888
@@ -44,10 +44,11 @@ module Biryani
44
44
 
45
45
  recv_loop(io.clone)
46
46
  send_loop(io)
47
- rescue StandardError
47
+ rescue StandardError => e
48
+ puts e.backtrace
48
49
  self.class.do_send(io, Frame::Goaway.new(0, @streams_ctx.last_stream_id, ErrorCode::INTERNAL_ERROR, 'internal error'), true)
49
50
  ensure
50
- io&.close_write
51
+ io.close_write
51
52
  end
52
53
 
53
54
  # @param io [IO]
@@ -57,7 +58,7 @@ module Biryani
57
58
  obj = Frame.read(io_)
58
59
  break if obj.nil?
59
60
 
60
- sock_ << obj
61
+ sock_.send(obj, move: true)
61
62
  end
62
63
  end
63
64
  end
@@ -71,15 +72,14 @@ module Biryani
71
72
  def send_loop(io)
72
73
  loop do
73
74
  ports = @streams_ctx.txs
74
- ports << @sock
75
- next if ports.empty?
75
+ break if ports.empty? && @sock.closed?
76
76
 
77
- port, obj = Ractor.select(*ports)
77
+ port, obj = Ractor.select(@sock, *ports)
78
78
  if port == @sock
79
79
  if Biryani.err?(obj)
80
80
  reply_frame = Biryani.unwrap(obj, @streams_ctx.last_stream_id)
81
81
  self.class.do_send(io, reply_frame, true)
82
- close if self.class.transition_state_send(reply_frame, @streams_ctx)
82
+ close if self.class.transition_stream_state_send(reply_frame, @streams_ctx)
83
83
  elsif obj.length > @settings[SettingsID::SETTINGS_MAX_FRAME_SIZE]
84
84
  self.class.do_send(io, Frame::Goaway.new(0, @streams_ctx.last_stream_id, ErrorCode::FRAME_SIZE_ERROR, 'payload length greater than SETTINGS_MAX_FRAME_SIZE'), true)
85
85
  close
@@ -93,7 +93,7 @@ module Biryani
93
93
  @streams_ctx[reply_frame.stream_id].recv_window.increase!(reply_frame.window_size_increment)
94
94
  end
95
95
 
96
- close if self.class.transition_state_send(reply_frame, @streams_ctx)
96
+ close if self.class.transition_stream_state_send(reply_frame, @streams_ctx)
97
97
  end
98
98
  end
99
99
  else
@@ -129,8 +129,12 @@ module Biryani
129
129
  #
130
130
  # @return [Array<Object>, Array<ConnectionError>, Array<StreamError>] frames or errors
131
131
  # rubocop: disable Metrics/CyclomaticComplexity
132
+ # rubocop: disable Metrics/PerceivedComplexity
132
133
  def handle_connection_frame(frame)
133
134
  typ = frame.f_type
135
+ return [ConnectionError.new(ErrorCode::PROTOCOL_ERROR, "invalid frame type #{format('0x%02x', typ)} for stream identifier #{format('0x%02x', stream_id)}")] \
136
+ if @streams_ctx.receiving_continuation? && ([FrameType::SETTINGS, FrameType::PING, FrameType::WINDOW_UPDATE].include?(typ) || FrameType.unknown?(typ))
137
+
134
138
  case typ
135
139
  when FrameType::DATA, FrameType::HEADERS, FrameType::PRIORITY, FrameType::RST_STREAM, FrameType::PUSH_PROMISE, FrameType::CONTINUATION
136
140
  [ConnectionError.new(ErrorCode::PROTOCOL_ERROR, "invalid frame type #{format('0x%02x', typ)} for stream identifier 0x00")]
@@ -163,6 +167,7 @@ module Biryani
163
167
  end
164
168
  end
165
169
  # rubocop: enable Metrics/CyclomaticComplexity
170
+ # rubocop: enable Metrics/PerceivedComplexity
166
171
 
167
172
  # @param frame [Object]
168
173
  #
@@ -179,7 +184,7 @@ module Biryani
179
184
  max_streams = @peer_settings[SettingsID::SETTINGS_MAX_CONCURRENT_STREAMS]
180
185
  send_initial_window_size = @peer_settings[SettingsID::SETTINGS_INITIAL_WINDOW_SIZE]
181
186
  recv_initial_window_size = @settings[SettingsID::SETTINGS_INITIAL_WINDOW_SIZE]
182
- obj = self.class.transition_state_recv(frame, @streams_ctx, stream_id, max_streams, send_initial_window_size, recv_initial_window_size)
187
+ obj = self.class.transition_stream_state_recv(frame, @streams_ctx, stream_id, max_streams, send_initial_window_size, recv_initial_window_size)
183
188
  return [obj] if Biryani.err?(obj)
184
189
 
185
190
  ctx = obj
@@ -241,7 +246,7 @@ module Biryani
241
246
  # @return [StreamContext, StreamError, ConnectionError]
242
247
  # rubocop: disable Metrics/CyclomaticComplexity
243
248
  # rubocop: disable Metrics/PerceivedComplexity
244
- def self.transition_state_recv(recv_frame, streams_ctx, stream_id, max_streams, send_initial_window_size, recv_initial_window_size)
249
+ def self.transition_stream_state_recv(recv_frame, streams_ctx, stream_id, max_streams, send_initial_window_size, recv_initial_window_size)
245
250
  ctx = streams_ctx[stream_id]
246
251
  return StreamError.new(ErrorCode::PROTOCOL_ERROR, stream_id, 'exceed max concurrent streams') if ctx.nil? && streams_ctx.count_active + 1 > max_streams
247
252
  return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'even-numbered stream identifier') if ctx.nil? && stream_id.even?
@@ -260,7 +265,7 @@ module Biryani
260
265
  # @param streams_ctx [StreamsContext]
261
266
  #
262
267
  # @return [Boolean] should close connection?
263
- def self.transition_state_send(send_frame, streams_ctx)
268
+ def self.transition_stream_state_send(send_frame, streams_ctx)
264
269
  stream_id = send_frame.stream_id
265
270
  typ = send_frame.f_type
266
271
  case typ
@@ -297,7 +302,7 @@ module Biryani
297
302
  do_send(io, frame, false)
298
303
  send_window.consume!(frame.length)
299
304
  streams_ctx[stream_id].send_window.consume!(frame.length)
300
- transition_state_send(frame, streams_ctx)
305
+ transition_stream_state_send(frame, streams_ctx)
301
306
  end
302
307
 
303
308
  data_buffer.store(stream_id, remains) unless remains.empty?
@@ -322,7 +327,7 @@ module Biryani
322
327
 
323
328
  frames.each do |frame|
324
329
  do_send(io, frame, false)
325
- transition_state_send(frame, streams_ctx)
330
+ transition_stream_state_send(frame, streams_ctx)
326
331
  end
327
332
  end
328
333
 
@@ -352,7 +357,7 @@ module Biryani
352
357
  obj = http_request(ctx.fragment, ctx.content, decoder)
353
358
  return obj if Biryani.err?(obj)
354
359
 
355
- ctx.stream.rx << obj
360
+ ctx.stream.rx.send(obj, move: true)
356
361
  end
357
362
 
358
363
  window_updates = []
@@ -371,9 +376,9 @@ module Biryani
371
376
  ctx.fragment << headers.fragment
372
377
  if ctx.half_closed_remote?
373
378
  obj = http_request(ctx.fragment, ctx.content, decoder)
374
- return [obj] if Biryani.err?(obj)
379
+ return obj if Biryani.err?(obj)
375
380
 
376
- ctx.stream.rx << obj
381
+ ctx.stream.rx.send(obj, move: true)
377
382
  end
378
383
 
379
384
  nil
@@ -0,0 +1,8 @@
1
+ module Biryani
2
+ module Error
3
+ # Generic error, common for all classes under Biryani::Error module.
4
+ class Error < StandardError; end
5
+
6
+ class InvalidHTTPResponseError < Error; end
7
+ end
8
+ end
@@ -21,9 +21,7 @@ module Biryani
21
21
 
22
22
  # @return [String]
23
23
  def to_binary_s
24
- payload_length = length
25
-
26
- Frame.to_binary_s_header(payload_length, @f_type, flags, @stream_id) + @payload
24
+ Frame.to_binary_s_header(length, @f_type, @flags, @stream_id) + @payload
27
25
  end
28
26
  end
29
27
  end
data/lib/biryani/frame.rb CHANGED
@@ -10,6 +10,11 @@ module Biryani
10
10
  GOAWAY = 0x07
11
11
  WINDOW_UPDATE = 0x08
12
12
  CONTINUATION = 0x09
13
+
14
+ # @return [Boolean]
15
+ def self.unknown?(typ)
16
+ typ.negative? || typ > 0x09
17
+ end
13
18
  end
14
19
 
15
20
  module ErrorCode
@@ -65,7 +65,8 @@ module Biryani
65
65
  #
66
66
  # @return [HTTPRequest]
67
67
  def build(s)
68
- self.class.http_request(@h, s)
68
+ h = @h.transform_values(&:dup)
69
+ self.class.http_request(h, s)
69
70
  end
70
71
 
71
72
  # @param fields [Hash<String, String>]
@@ -1,15 +1,37 @@
1
1
  module Biryani
2
2
  class HTTPResponse
3
+ FORBIDDEN_KEY_CHARS = (0x00..0x20).chain([0x3a]).chain(0x41..0x5a).chain(0x7f..0xff).to_a.freeze
4
+ FORBIDDEN_VALUE_CHARS = [0x00, 0x0a, 0x0d].freeze
5
+
3
6
  attr_accessor :status, :fields, :content
4
7
 
5
8
  # @param status [Integer]
6
9
  # @param fields [Hash]
7
- # @param content [String]
10
+ # @param content [String, nil]
8
11
  def initialize(status, fields, content)
9
12
  @status = status
10
13
  @fields = fields
11
14
  @content = content
12
15
  end
16
+
17
+ # @raise [InvalidHTTPResponseError]
18
+ # rubocop: disable Metrics/CyclomaticComplexity
19
+ def validate
20
+ # https://datatracker.ietf.org/doc/html/rfc9113#section-8.2.1
21
+ raise Error::InvalidHTTPResponseError, 'invalid HTTP status' if @status < 100 || @status >= 600
22
+ raise Error::InvalidHTTPResponseError, 'HTTP field name contains invalid characters' if (@fields.keys.join.downcase.bytes.uniq & FORBIDDEN_KEY_CHARS).any?
23
+ raise Error::InvalidHTTPResponseError, 'HTTP field value contains NUL, LF or CR' if (@fields.values.join.bytes.uniq & FORBIDDEN_VALUE_CHARS).any?
24
+ raise Error::InvalidHTTPResponseError, 'HTTP field value starts/ends with SP or HTAB' if @fields.values.filter { |s| s.start_with?("\t", ' ') || s.end_with?("\t", ' ') }.any?
25
+ end
26
+ # rubocop: enable Metrics/CyclomaticComplexity
27
+
28
+ def self.default
29
+ HTTPResponse.new(0, {}, nil)
30
+ end
31
+
32
+ def self.internal_server_error
33
+ HTTPResponse.new(500, {}, 'Internal Server Error')
34
+ end
13
35
  end
14
36
 
15
37
  class HTTPResponseParser
@@ -22,7 +44,6 @@ module Biryani
22
44
  def fields
23
45
  fields = [[':status', @res.status.to_s]]
24
46
  @res.fields.each do |name, value|
25
- # TODO: validate fields
26
47
  fields << [name.to_s.downcase, value.to_s]
27
48
  end
28
49
 
@@ -11,7 +11,7 @@ module Biryani
11
11
  Ractor.new(socket.accept, @proc) do |io, proc|
12
12
  conn = Connection.new(proc)
13
13
  conn.serve(io)
14
- io&.close
14
+ io.close
15
15
  end
16
16
  end
17
17
  end
data/lib/biryani/state.rb CHANGED
@@ -65,7 +65,7 @@ module Biryani
65
65
  in [:idle, FrameType::HEADERS, :recv] if frame.end_stream?
66
66
  :receiving_continuation
67
67
  in [:idle, FrameType::HEADERS, :recv]
68
- :receiving_continuation_data
68
+ :receiving_continuation_and_data
69
69
  in [:idle, FrameType::PRIORITY, :recv]
70
70
  state
71
71
  in [:idle, FrameType::PUSH_PROMISE, :send]
@@ -73,16 +73,16 @@ module Biryani
73
73
  in [:idle, _, _]
74
74
  unexpected(ErrorCode::PROTOCOL_ERROR, state, typ, direction)
75
75
 
76
- # receiving_continuation_data
77
- in [:receiving_continuation_data, FrameType::RST_STREAM, _]
76
+ # receiving_continuation_and_data
77
+ in [:receiving_continuation_and_data, FrameType::RST_STREAM, _]
78
78
  :closed
79
- in [:receiving_continuation_data, FrameType::WINDOW_UPDATE, :recv]
79
+ in [:receiving_continuation_and_data, FrameType::WINDOW_UPDATE, :recv]
80
80
  state
81
- in [:receiving_continuation_data, FrameType::CONTINUATION, :recv] if frame.end_headers?
81
+ in [:receiving_continuation_and_data, FrameType::CONTINUATION, :recv] if frame.end_headers?
82
82
  :receiving_data
83
- in [:receiving_continuation_data, FrameType::CONTINUATION, :recv]
83
+ in [:receiving_continuation_and_data, FrameType::CONTINUATION, :recv]
84
84
  state
85
- in [:receiving_continuation_data, _, _]
85
+ in [:receiving_continuation_and_data, _, _]
86
86
  unexpected(ErrorCode::PROTOCOL_ERROR, state, typ, direction)
87
87
 
88
88
  # receiving_continuation
@@ -234,5 +234,10 @@ module Biryani
234
234
  def half_closed_remote?
235
235
  @state == :half_closed_remote
236
236
  end
237
+
238
+ # @return [Boolean]
239
+ def receiving_continuation?
240
+ %i[receiving_continuation receiving_continuation_and_data].include?(@state)
241
+ end
237
242
  end
238
243
  end
@@ -8,10 +8,17 @@ module Biryani
8
8
  def initialize(tx, stream_id, proc)
9
9
  @rx = Ractor.new(tx, stream_id, proc) do |tx, stream_id, proc|
10
10
  unless (req = Ractor.recv).nil?
11
- res = HTTPResponse.new(0, {}, '')
11
+ res = HTTPResponse.default
12
12
 
13
- proc.call(req, res)
14
- tx << [res, stream_id]
13
+ begin
14
+ proc.call(req, res)
15
+ res.validate
16
+ rescue StandardError => e
17
+ puts e.backtrace
18
+ res = HTTPResponse.internal_server_error
19
+ end
20
+
21
+ tx.send([res, stream_id], move: true)
15
22
  end
16
23
  end
17
24
  end
@@ -47,6 +47,11 @@ module Biryani
47
47
  @h.values.filter(&:active?).length
48
48
  end
49
49
 
50
+ # @return [Boolean]
51
+ def receiving_continuation?
52
+ @h.values.any?(&:receiving_continuation?)
53
+ end
54
+
50
55
  # @return [Array<Integer>]
51
56
  def closed_stream_ids
52
57
  @h.filter { |_, ctx| ctx.closed? }.keys
@@ -131,5 +136,10 @@ module Biryani
131
136
  def half_closed_remote?
132
137
  @state.half_closed_remote?
133
138
  end
139
+
140
+ # @return [Boolean]
141
+ def receiving_continuation?
142
+ @state.receiving_continuation?
143
+ end
134
144
  end
135
145
  end
@@ -1,3 +1,3 @@
1
1
  module Biryani
2
- VERSION = '0.0.5'.freeze
2
+ VERSION = '0.0.6'.freeze
3
3
  end
data/lib/biryani.rb CHANGED
@@ -3,6 +3,7 @@ require 'uri'
3
3
  require_relative 'biryani/connection_error'
4
4
  require_relative 'biryani/connection'
5
5
  require_relative 'biryani/data_buffer'
6
+ require_relative 'biryani/error'
6
7
  require_relative 'biryani/frame'
7
8
  require_relative 'biryani/hpack'
8
9
  require_relative 'biryani/http_request'
@@ -1,7 +1,7 @@
1
1
  require_relative '../spec_helper'
2
2
 
3
3
  RSpec.describe Connection do
4
- context 'transition_state_send' do
4
+ context 'transition_stream_state_send' do
5
5
  let(:streams_ctx) do
6
6
  streams_ctx = StreamsContext.new(do_nothing_proc)
7
7
  streams_ctx.new_context(1, 65_535, 65_535)
@@ -14,7 +14,7 @@ RSpec.describe Connection do
14
14
  end
15
15
  it 'should transition' do
16
16
  streams_ctx[2].state.transition!(headers, :recv)
17
- Connection.transition_state_send(headers, streams_ctx)
17
+ Connection.transition_stream_state_send(headers, streams_ctx)
18
18
  expect(streams_ctx.length).to eq 2
19
19
  end
20
20
 
@@ -31,7 +31,7 @@ RSpec.describe Connection do
31
31
  end
32
32
  it 'should transition' do
33
33
  streams_ctx[2].state.transition!(headers, :recv)
34
- Connection.transition_state_send(rst_stream, streams_ctx)
34
+ Connection.transition_stream_state_send(rst_stream, streams_ctx)
35
35
  expect { streams_ctx[1].tx << nil }.to_not raise_error
36
36
  expect { streams_ctx[2].tx << nil }.to_not raise_error
37
37
  end
@@ -0,0 +1,12 @@
1
+ require_relative 'spec_helper'
2
+
3
+ RSpec.describe HTTPResponse do
4
+ it 'should validate' do
5
+ expect { HTTPResponse.new(600, {}, nil).validate }.to raise_error Error::InvalidHTTPResponseError
6
+ expect { HTTPResponse.new(200, { "\x00key" => 'value' }, nil).validate }.to raise_error Error::InvalidHTTPResponseError
7
+ expect { HTTPResponse.new(200, { 'key:' => 'value' }, nil).validate }.to raise_error Error::InvalidHTTPResponseError
8
+ expect { HTTPResponse.new(200, { 'key' => "one\ntwo" }, nil).validate }.to raise_error Error::InvalidHTTPResponseError
9
+ expect { HTTPResponse.new(200, { 'key' => ' value' }, nil).validate }.to raise_error Error::InvalidHTTPResponseError
10
+ expect { HTTPResponse.new(200, { 'key' => "value\t" }, nil).validate }.to raise_error Error::InvalidHTTPResponseError
11
+ end
12
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: biryani
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - thekuwayama
@@ -39,12 +39,16 @@ files:
39
39
  - README.md
40
40
  - Rakefile
41
41
  - biryani.gemspec
42
+ - conformance/server_spec.rb
43
+ - conformance/spec_helper.rb
42
44
  - example/echo.rb
43
45
  - example/hello_world.rb
46
+ - example/raise_error.rb
44
47
  - lib/biryani.rb
45
48
  - lib/biryani/connection.rb
46
49
  - lib/biryani/connection_error.rb
47
50
  - lib/biryani/data_buffer.rb
51
+ - lib/biryani/error.rb
48
52
  - lib/biryani/frame.rb
49
53
  - lib/biryani/frame/continuation.rb
50
54
  - lib/biryani/frame/data.rb
@@ -87,7 +91,7 @@ files:
87
91
  - spec/connection/handle_stream_window_update_spec.rb
88
92
  - spec/connection/read_http2_magic_spec.rb
89
93
  - spec/connection/send_spec.rb
90
- - spec/connection/transition_state_send_spec.rb
94
+ - spec/connection/transition_stream_state_send_spec.rb
91
95
  - spec/data_buffer_spec.rb
92
96
  - spec/frame/continuation_spec.rb
93
97
  - spec/frame/data_spec.rb
@@ -108,6 +112,7 @@ files:
108
112
  - spec/hpack/integer_spec.rb
109
113
  - spec/hpack/string_spec.rb
110
114
  - spec/http_request_builder_spec.rb
115
+ - spec/http_response_spec.rb
111
116
  - spec/spec_helper.rb
112
117
  - spec/streams_context_spec.rb
113
118
  - spec/utils_spec.rb
@@ -142,7 +147,7 @@ test_files:
142
147
  - spec/connection/handle_stream_window_update_spec.rb
143
148
  - spec/connection/read_http2_magic_spec.rb
144
149
  - spec/connection/send_spec.rb
145
- - spec/connection/transition_state_send_spec.rb
150
+ - spec/connection/transition_stream_state_send_spec.rb
146
151
  - spec/data_buffer_spec.rb
147
152
  - spec/frame/continuation_spec.rb
148
153
  - spec/frame/data_spec.rb
@@ -163,6 +168,7 @@ test_files:
163
168
  - spec/hpack/integer_spec.rb
164
169
  - spec/hpack/string_spec.rb
165
170
  - spec/http_request_builder_spec.rb
171
+ - spec/http_response_spec.rb
166
172
  - spec/spec_helper.rb
167
173
  - spec/streams_context_spec.rb
168
174
  - spec/utils_spec.rb