Skip to content

Commit f95fd5b

Browse files
committed
Improving test coverage.
1 parent 527a358 commit f95fd5b

File tree

6 files changed

+49
-27
lines changed

6 files changed

+49
-27
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module Protocol
2+
module HTTP
3+
module Body
4+
AReadableBody = Sus::Shared("a readable body") do
5+
with "#close" do
6+
it "should close the body" do
7+
body.close
8+
expect(body.read).to be_nil
9+
end
10+
end
11+
end
12+
end
13+
end
14+
end

lib/protocol/http/body/buffered.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ def initialize(chunks = [], length = nil)
5252

5353
attr :chunks
5454

55-
def finish
56-
self
55+
def close(error = nil)
56+
@chunks = []
5757
end
5858

5959
def length

lib/protocol/http/body/readable.rb

+5-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ module Body
1717
#
1818
# If you don't want to read from a stream, and instead want to close it immediately, you can call `close` on the body. If the body is already completely consumed, `close` will do nothing, but if there is still data to be read, it will cause the underlying stream to be reset (and possibly closed).
1919
class Readable
20-
# Close the stream immediately.
20+
# Close the stream immediately. After invoking this method, the stream should be considered closed, and all internal resources should be released.
21+
#
22+
# If an error occured while handling the output, it can be passed as an argument. This may be propagated to the client, for example the client may be informed that the stream was not fully read correctly.
23+
#
24+
# Invoking `#read` after `#close` will return `nil`.
2125
def close(error = nil)
2226
end
2327

lib/protocol/http/body/streamable.rb

+16-23
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@ module Body
1717
#
1818
# When invoking `call(stream)`, the stream can be read from and written to, and closed. However, the stream is only guaranteed to be open for the duration of the `call(stream)` call. Once the method returns, the stream **should** be closed by the server.
1919
module Streamable
20+
class ClosedError < StandardError
21+
end
22+
2023
def self.new(*arguments)
2124
if arguments.size == 1
22-
RequestBody.new(*arguments)
25+
DeferredBody.new(*arguments)
2326
else
24-
ResponseBody.new(*arguments)
27+
Body.new(*arguments)
2528
end
2629
end
2730

@@ -39,8 +42,6 @@ def initialize(input, block)
3942
@fiber = Fiber.new do |from|
4043
@from = from
4144
block.call(stream)
42-
rescue Closed
43-
# Ignore.
4445
ensure
4546
@fiber = nil
4647

@@ -58,16 +59,18 @@ def write(chunk)
5859
@from = nil
5960
@from = from.transfer(chunk)
6061
else
61-
raise RuntimeError, "Stream is not being read!"
62+
raise ClosedError, "Stream is not being read!"
6263
end
6364
end
6465

6566
# Can be invoked by the block to close the stream. Closing the output means that no more chunks will be generated.
6667
def close(error = nil)
6768
if from = @from
69+
# We are closing from within the output fiber, so we need to transfer back to `@from`:
6870
@from = nil
6971
from.transfer(nil)
7072
elsif @fiber
73+
# We are closing from outside the output fiber, so we need to resume the fiber appropriately:
7174
@from = Fiber.current
7275

7376
if error
@@ -134,20 +137,21 @@ def call(stream)
134137

135138
# Closing a stream indicates we are no longer interested in reading from it.
136139
def close(error = nil)
137-
if output = @output
138-
@output = nil
139-
output.close(error)
140-
end
141-
140+
$stderr.puts "Closing input: #{@input.inspect}"
142141
if input = @input
143142
@input = nil
144143
input.close(error)
145144
end
145+
146+
if output = @output
147+
@output = nil
148+
output.close(error)
149+
end
146150
end
147151
end
148152

149-
# A request body has an extra `stream` method which can be used to stream data into the body, as the response body won't be available until the request has been sent.
150-
class RequestBody < Body
153+
# A deferred body has an extra `stream` method which can be used to stream data into the body, as the response body won't be available until the request has been sent.
154+
class DeferredBody < Body
151155
def initialize(block)
152156
super(block, Writable.new)
153157
@finishing = false
@@ -172,17 +176,6 @@ def stream(input)
172176
self.close(error)
173177
end
174178
end
175-
176-
def self.request(&block)
177-
RequestBody.new(block)
178-
end
179-
180-
class ResponseBody < Body
181-
end
182-
183-
def self.response(request, &block)
184-
ResponseBody.new(block, request.body)
185-
end
186179
end
187180
end
188181
end

test/protocol/http/body/buffered.rb

+3
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
# Copyright, 2020-2023, by Bruno Sutic.
66

77
require 'protocol/http/body/buffered'
8+
require "protocol/http/body/a_readable_body"
89

910
describe Protocol::HTTP::Body::Buffered do
1011
let(:source) {["Hello", "World"]}
1112
let(:body) {subject.wrap(source)}
1213

14+
it_behaves_like Protocol::HTTP::Body::AReadableBody
15+
1316
with ".wrap" do
1417
with "an instance of Protocol::HTTP::Body::Readable as a source" do
1518
let(:source) {Protocol::HTTP::Body::Readable.new}

test/protocol/http/body/streamable.rb

+9-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262

6363
expect do
6464
@stream.write("!")
65-
end.to raise_exception(RuntimeError, message: be =~ /Stream is not being read!/)
65+
end.to raise_exception(Protocol::HTTP::Body::Streamable::ClosedError, message: be =~ /Stream is not being read!/)
6666
end
6767
end
6868
end
@@ -149,6 +149,7 @@
149149
let(:block) do
150150
proc do |stream|
151151
while chunk = stream.read_partial
152+
$stderr.puts "Got chunk: #{chunk.inspect}"
152153
stream.write(chunk)
153154
end
154155
end
@@ -170,5 +171,12 @@
170171

171172
expect(output.string).to be == "Hello World"
172173
end
174+
175+
with '#close' do
176+
it "can close the body" do
177+
expect(body.read).to be == "Hello"
178+
body.close
179+
end
180+
end
173181
end
174182
end

0 commit comments

Comments
 (0)