Skip to content

Commit 81f38b1

Browse files
committed
Tidy up implementation of stream? and call(stream).
1 parent 7909cff commit 81f38b1

File tree

14 files changed

+94
-112
lines changed

14 files changed

+94
-112
lines changed

lib/protocol/http/body/completable.rb

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,6 @@ def rewind
3232
false
3333
end
3434

35-
def finish
36-
super.tap do
37-
if @callback
38-
@callback.call
39-
@callback = nil
40-
end
41-
end
42-
end
43-
4435
def close(error = nil)
4536
super.tap do
4637
if @callback

lib/protocol/http/body/deflate.rb

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,6 @@ def self.for(body, window_size = GZIP, level = DEFAULT_LEVEL)
6262
self.new(body, Zlib::Deflate.new(level, window_size))
6363
end
6464

65-
def stream?
66-
# We might want to revisit this design choice.
67-
# We could wrap the streaming body in a Deflate stream, but that would require an extra stream wrapper which we don't have right now. See also `Digestable#stream?`.
68-
false
69-
end
70-
7165
def read
7266
return if @stream.finished?
7367

lib/protocol/http/body/digestable.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,6 @@ def etag(weak: false)
3838
end
3939
end
4040

41-
def stream?
42-
false
43-
end
44-
4541
def read
4642
if chunk = super
4743
@digest.update(chunk)

lib/protocol/http/body/file.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,6 @@ def rewind
5656
@remaining = @length
5757
end
5858

59-
def stream?
60-
false
61-
end
62-
6359
def read
6460
if @remaining > 0
6561
amount = [@remaining, @block_size].min
@@ -72,6 +68,14 @@ def read
7268
end
7369
end
7470

71+
def stream?
72+
true
73+
end
74+
75+
def call(stream)
76+
IO.copy_stream(@file, stream, @remaining)
77+
end
78+
7579
def join
7680
return "" if @remaining == 0
7781

lib/protocol/http/body/inflate.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@ def self.for(body, encoding = GZIP)
1515
self.new(body, Zlib::Inflate.new(encoding))
1616
end
1717

18-
def stream?
19-
false
20-
end
21-
2218
def read
2319
return if @stream.finished?
2420

lib/protocol/http/body/readable.rb

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@
77
module Protocol
88
module HTTP
99
module Body
10-
# An interface for reading data from a body.
10+
# Represents a readable input streams.
1111
#
1212
# Typically, you'd override `#read` to return chunks of data.
13+
#
14+
# I n general, you read chunks of data from a body until it is empty and returns `nil`. Upon reading `nil`, the body is considered consumed and should not be read from again.
15+
#
16+
# Reading can also fail, for example if the body represents a streaming upload, and the connection is lost. In this case, the body will raise some kind of error.
17+
#
18+
# 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).
1319
class Readable
1420
# Close the stream immediately.
1521
def close(error = nil)
@@ -29,63 +35,46 @@ def ready?
2935
false
3036
end
3137

38+
# Whether the stream can be rewound using {rewind}.
3239
def rewindable?
3340
false
3441
end
3542

43+
# Rewind the stream to the beginning.
44+
# @returns [Boolean] Whether the stream was successfully rewound.
3645
def rewind
3746
false
3847
end
3948

49+
# The total length of the body, if known.
50+
# @returns [Integer | Nil] The total length of the body, or `nil` if the length is unknown.
4051
def length
4152
nil
4253
end
4354

4455
# Read the next available chunk.
4556
# @returns [String | Nil] The chunk of data, or `nil` if the stream has finished.
57+
# @raises [StandardError] If an error occurs while reading.
4658
def read
4759
nil
4860
end
4961

50-
# Should the internal mechanism prefer to use {call}?
51-
# @returns [Boolean]
52-
def stream?
53-
false
54-
end
55-
56-
# Write the body to the given stream.
57-
def call(stream)
58-
while chunk = self.read
59-
stream.write(chunk)
60-
61-
# Flush the stream unless we are immediately expecting more data:
62-
unless self.ready?
63-
stream.flush
64-
end
65-
end
66-
end
67-
68-
# Read all remaining chunks into a buffered body and close the underlying input.
69-
# @returns [Buffered] The buffered body.
70-
def finish
71-
# Internally, this invokes `self.each` which then invokes `self.close`.
72-
Buffered.read(self)
73-
end
74-
7562
# Enumerate all chunks until finished, then invoke `#close`.
7663
#
64+
# Closes the stream when finished or if an error occurs.
65+
#
7766
# @yields {|chunk| ...} The block to call with each chunk of data.
7867
# @parameter chunk [String | Nil] The chunk of data, or `nil` if the stream has finished.
7968
def each
80-
return to_enum(:each) unless block_given?
69+
return to_enum unless block_given?
8170

82-
begin
83-
while chunk = self.read
84-
yield chunk
85-
end
86-
ensure
87-
self.close($!)
71+
while chunk = self.read
72+
yield chunk
8873
end
74+
rescue => error
75+
raise
76+
ensure
77+
self.close(error)
8978
end
9079

9180
# Read all remaining chunks into a single binary string using `#each`.
@@ -105,6 +94,35 @@ def join
10594
end
10695
end
10796

97+
def stream?
98+
false
99+
end
100+
101+
# Write the body to the given stream.
102+
#
103+
# In some cases, the stream may also be readable, such as when hijacking an HTTP/1 connection. In that case, it may be acceptable to read and write to the stream directly.
104+
#
105+
# If the stream is not ready, it will be flushed after each chunk. Closes the stream when finished or if an error occurs.
106+
#
107+
def call(stream)
108+
self.each do |chunk|
109+
stream.write(chunk)
110+
111+
# Flush the stream unless we are immediately expecting more data:
112+
unless self.ready?
113+
stream.flush
114+
end
115+
end
116+
end
117+
118+
# Read all remaining chunks into a buffered body and close the underlying input.
119+
#
120+
# @returns [Buffered] The buffered body.
121+
def finish
122+
# Internally, this invokes `self.each` which then invokes `self.close`.
123+
Buffered.read(self)
124+
end
125+
108126
def as_json(...)
109127
{
110128
class: self.class.name,

lib/protocol/http/body/rewindable.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,6 @@ def buffered
4343
Buffered.new(@chunks)
4444
end
4545

46-
def stream?
47-
false
48-
end
49-
5046
def read
5147
if @index < @chunks.size
5248
chunk = @chunks[@index]

lib/protocol/http/body/wrapper.rb

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,11 @@ def initialize(body)
2727
# The wrapped body.
2828
attr :body
2929

30-
# Buffer any remaining body.
31-
def finish
32-
@body.finish
33-
end
34-
3530
def close(error = nil)
3631
@body.close(error)
3732

38-
super
33+
# It's a no-op:
34+
# super
3935
end
4036

4137
def empty?
@@ -77,14 +73,6 @@ def to_json(...)
7773
def inspect
7874
@body.inspect
7975
end
80-
81-
def stream?
82-
@body.stream?
83-
end
84-
85-
def call(stream)
86-
@body.call(stream)
87-
end
8876
end
8977
end
9078
end

test/protocol/http/body/deflate.rb

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@
1515
let(:compressed_body) {Protocol::HTTP::Body::Deflate.for(body)}
1616
let(:decompressed_body) {Protocol::HTTP::Body::Inflate.for(compressed_body)}
1717

18-
it "should not be a stream" do
19-
expect(compressed_body).not.to be(:stream?)
20-
expect(decompressed_body).not.to be(:stream?)
21-
end
22-
2318
it "should round-trip data" do
2419
body.write("Hello World!")
2520
body.close

test/protocol/http/body/digestable.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@
1010
let(:source) {Protocol::HTTP::Body::Buffered.new}
1111
let(:body) {subject.new(source)}
1212

13-
it "should not be a stream" do
14-
expect(body).not.to be(:stream?)
15-
end
16-
1713
with '.wrap' do
1814
let(:source) {Protocol::HTTP::Body::Buffered.wrap("HelloWorld")}
1915
let(:message) {Protocol::HTTP::Request.new(nil, nil, 'GET', '/', nil, Protocol::HTTP::Headers.new, body)}

0 commit comments

Comments
 (0)