Skip to content

Commit 698a052

Browse files
authored
100% test coverage + minor bug fixes. (#12)
1 parent c270638 commit 698a052

37 files changed

+1580
-180
lines changed

fixtures/frame_examples.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,19 @@
99
let(:stream) {StringIO.new}
1010
let(:frame) {subject.new}
1111

12-
it "can write frame" do
12+
it "is a valid frame type" do
13+
expect(frame).to be(:valid_type?)
14+
end
15+
16+
it "can write the frame" do
1317
frame.write(stream)
1418

1519
expect(stream.string).not.to be(:empty?)
1620
end
1721

1822
let(:framer) {Protocol::HTTP2::Framer.new(stream, {subject::TYPE => subject})}
1923

20-
it "can read frame using framer" do
24+
it "can read the frame using framer" do
2125
frame.write(stream)
2226
stream.seek(0)
2327

lib/protocol/http2/client.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,6 @@ def receive_push_promise(frame)
5353
# This is almost certainly invalid:
5454
promised_stream, request_headers = stream.receive_push_promise(frame)
5555

56-
if stream.closed?
57-
@streams.delete(stream.id)
58-
end
59-
6056
return promised_stream, request_headers
6157
end
6258
end

lib/protocol/http2/connection.rb

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def initialize(framer, local_stream_id)
4141
@decoder = HPACK::Context.new
4242
@encoder = HPACK::Context.new
4343

44-
@local_window = LocalWindow.new
44+
@local_window = LocalWindow.new()
4545
@remote_window = Window.new
4646
end
4747

@@ -287,9 +287,10 @@ def send_ping(data)
287287

288288
def receive_ping(frame)
289289
if @state != :closed
290-
if frame.stream_id != 0
291-
raise ProtocolError, "Ping received for non-zero stream!"
292-
end
290+
# This is handled in `read_payload`:
291+
# if frame.stream_id != 0
292+
# raise ProtocolError, "Ping received for non-zero stream!"
293+
# end
293294

294295
unless frame.acknowledgement?
295296
reply = frame.acknowledge
@@ -313,7 +314,7 @@ def receive_data(frame)
313314
end
314315
end
315316

316-
def valid_remote_stream_id?
317+
def valid_remote_stream_id?(stream_id)
317318
false
318319
end
319320

@@ -338,6 +339,10 @@ def accept_push_promise_stream(stream_id, &block)
338339
# On the client side, we create requests.
339340
# @return [Stream] the created stream.
340341
def create_stream(id = next_stream_id, &block)
342+
if @streams.key?(id)
343+
raise ProtocolError, "Cannot create stream with id #{id}, already exists!"
344+
end
345+
341346
if block_given?
342347
return yield(self, id)
343348
else
@@ -382,24 +387,6 @@ def send_priority(stream_id, priority)
382387
write_frame(frame)
383388
end
384389

385-
def idle_stream_id?(id)
386-
if id.even?
387-
# Server-initiated streams are even.
388-
if @local_stream_id.even?
389-
id >= @local_stream_id
390-
else
391-
id > @remote_stream_id
392-
end
393-
elsif id.odd?
394-
# Client-initiated streams are odd.
395-
if @local_stream_id.odd?
396-
id >= @local_stream_id
397-
else
398-
id > @remote_stream_id
399-
end
400-
end
401-
end
402-
403390
# Sets the priority for an incoming stream.
404391
def receive_priority(frame)
405392
if dependency = @dependencies[frame.stream_id]
@@ -421,27 +408,34 @@ def server_stream_id?(id)
421408
id.even?
422409
end
423410

424-
def closed_stream_id?(id)
425-
if id.zero?
426-
# The connection "stream id" can never be closed:
427-
false
428-
elsif id.even?
411+
def idle_stream_id?(id)
412+
if id.even?
429413
# Server-initiated streams are even.
430414
if @local_stream_id.even?
431-
id < @local_stream_id
415+
id >= @local_stream_id
432416
else
433-
id <= @remote_stream_id
417+
id > @remote_stream_id
434418
end
435419
elsif id.odd?
436420
# Client-initiated streams are odd.
437421
if @local_stream_id.odd?
438-
id < @local_stream_id
422+
id >= @local_stream_id
439423
else
440-
id <= @remote_stream_id
424+
id > @remote_stream_id
441425
end
442426
end
443427
end
444428

429+
# This is only valid if the stream doesn't exist in `@streams`.
430+
def closed_stream_id?(id)
431+
if id.zero?
432+
# The connection "stream id" can never be closed:
433+
false
434+
else
435+
!idle_stream_id?(id)
436+
end
437+
end
438+
445439
def receive_reset_stream(frame)
446440
if frame.connection?
447441
raise ProtocolError, "Cannot reset connection!"

lib/protocol/http2/continuation_frame.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ def initialize(*)
1414
@continuation = nil
1515
end
1616

17+
def continued?
18+
!!@continuation
19+
end
20+
1721
def end_headers?
1822
flag_set?(END_HEADERS)
1923
end
@@ -97,7 +101,7 @@ def apply(connection)
97101
end
98102

99103
def inspect
100-
"\#<#{self.class} stream_id=#{@stream_id} flags=#{@flags} #{@length}b>"
104+
"\#<#{self.class} stream_id=#{@stream_id} flags=#{@flags} length=#{@length || 0}b>"
101105
end
102106
end
103107
end

lib/protocol/http2/data_frame.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def apply(connection)
4343
end
4444

4545
def inspect
46-
"\#<#{self.class} stream_id=#{@stream_id} flags=#{@flags} #{@length}b>"
46+
"\#<#{self.class} stream_id=#{@stream_id} flags=#{@flags} #{@length || 0}b>"
4747
end
4848
end
4949
end

lib/protocol/http2/dependency.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ def receive_priority(frame)
170170
end
171171

172172
def total_weight
173-
self.orderd_children
173+
self.ordered_children
174174

175175
return @total_weight
176176
end
@@ -203,14 +203,14 @@ def consume_window(size)
203203
end
204204
end
205205

206-
def to_s
206+
def inspect
207207
"\#<#{self.class} id=#{@id} parent id=#{@parent&.id} weight=#{@weight} #{@children&.size || 0} children>"
208208
end
209209

210-
def print_hierarchy(buffer, indent: 0)
211-
buffer.puts "#{" " * indent}#{self}"
210+
def print_hierarchy(output = $stderr, indent: 0)
211+
output.puts "#{"\t" * indent}#{self.inspect}"
212212
@children&.each_value do |child|
213-
child.print_hierarchy(buffer, indent: indent+1)
213+
child.print_hierarchy(output, indent: indent+1)
214214
end
215215
end
216216
end

lib/protocol/http2/error.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,6 @@ def initialize(message)
8080
end
8181
end
8282

83-
class HeaderError < StreamClosed
84-
def initialize(message)
85-
super(message)
86-
end
87-
end
88-
8983
class GoawayError < ProtocolError
9084
end
9185

@@ -96,6 +90,12 @@ def initialize(message)
9690
end
9791
end
9892

93+
class HeaderError < StreamClosed
94+
def initialize(message)
95+
super(message)
96+
end
97+
end
98+
9999
# Raised on invalid flow control frame or command.
100100
class FlowControlError < ProtocolError
101101
def initialize(message)

lib/protocol/http2/frame.rb

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@ class Frame
3131
LENGTH_HISHIFT = 16
3232
LENGTH_LOMASK = 0xFFFF
3333

34+
# The base class does not have any specific type index:
35+
TYPE = nil
36+
3437
# @param length [Integer] the length of the payload, or nil if the header has not been read yet.
3538
def initialize(stream_id = 0, flags = 0, type = self.class::TYPE, length = nil, payload = nil)
36-
@length = length
37-
@type = type
38-
@flags = flags
3939
@stream_id = stream_id
40+
@flags = flags
41+
@type = type
42+
@length = length
4043
@payload = payload
4144
end
4245

@@ -79,7 +82,7 @@ def pack(payload, maximum_size: nil)
7982
@length = payload.bytesize
8083

8184
if maximum_size and @length > maximum_size
82-
raise ProtocolError, "Frame length #{@length} bigger than maximum allowed: #{maximum_size}"
85+
raise ProtocolError, "Frame length bigger than maximum allowed: #{@length} > #{maximum_size}"
8386
end
8487
end
8588

@@ -112,7 +115,7 @@ def connection?
112115
# @return [String]
113116
def header
114117
unless VALID_LENGTH.include? @length
115-
raise ProtocolError, "Invalid frame size: #{@length.inspect}"
118+
raise ProtocolError, "Invalid frame length: #{@length.inspect}"
116119
end
117120

118121
unless VALID_STREAM_ID.include? @stream_id
@@ -178,8 +181,15 @@ def write_payload(stream)
178181
end
179182

180183
def write(stream)
181-
if @payload and @length != @payload.bytesize
182-
raise ProtocolError, "Invalid payload size: #{@length} != #{@payload.bytesize}"
184+
# Validate the payload size:
185+
if @payload.nil?
186+
if @length != 0
187+
raise ProtocolError, "Invalid frame length: #{@length} != 0"
188+
end
189+
else
190+
if @length != @payload.bytesize
191+
raise ProtocolError, "Invalid payload size: #{@length} != #{@payload.bytesize}"
192+
end
183193
end
184194

185195
self.write_header(stream)
@@ -191,7 +201,7 @@ def apply(connection)
191201
end
192202

193203
def inspect
194-
"\#<#{self.class} stream_id=#{@stream_id} flags=#{@flags} #{self.unpack}>"
204+
"\#<#{self.class} stream_id=#{@stream_id} flags=#{@flags} payload=#{self.unpack}>"
195205
end
196206
end
197207
end

lib/protocol/http2/framer.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ module HTTP2
3333
].freeze
3434

3535
# Default connection "fast-fail" preamble string as defined by the spec.
36-
CONNECTION_PREFACE_MAGIC = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".freeze
36+
CONNECTION_PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".freeze
3737

3838
class Framer
3939
def initialize(stream, frames = FRAMES)
@@ -50,13 +50,13 @@ def closed?
5050
end
5151

5252
def write_connection_preface
53-
@stream.write(CONNECTION_PREFACE_MAGIC)
53+
@stream.write(CONNECTION_PREFACE)
5454
end
5555

5656
def read_connection_preface
57-
string = @stream.read(CONNECTION_PREFACE_MAGIC.bytesize)
57+
string = @stream.read(CONNECTION_PREFACE.bytesize)
5858

59-
unless string == CONNECTION_PREFACE_MAGIC
59+
unless string == CONNECTION_PREFACE
6060
raise HandshakeError, "Invalid connection preface: #{string.inspect}"
6161
end
6262

lib/protocol/http2/headers_frame.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def apply(connection)
6868
end
6969

7070
def inspect
71-
"\#<#{self.class} stream_id=#{@stream_id} flags=#{@flags} #{@length}b>"
71+
"\#<#{self.class} stream_id=#{@stream_id} flags=#{@flags} #{@length || 0}b>"
7272
end
7373
end
7474
end

0 commit comments

Comments
 (0)