Skip to content

Commit 091418a

Browse files
Prefer to use a buffered body where possible.
1 parent 5881050 commit 091418a

File tree

3 files changed

+57
-3
lines changed

3 files changed

+57
-3
lines changed

lib/protocol/rack/body/enumerable.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# Copyright, 2022-2025, by Samuel Williams.
55

66
require "protocol/http/body/readable"
7+
require "protocol/http/body/buffered"
78
require "protocol/http/body/file"
89

910
module Protocol
@@ -23,9 +24,9 @@ class Enumerable < ::Protocol::HTTP::Body::Readable
2324
# @parameter length [Integer] Optional content length of the response body.
2425
# @returns [Enumerable] A new enumerable body instance.
2526
def self.wrap(body, length = nil)
26-
if body.is_a?(Array)
27-
length ||= body.sum(&:bytesize)
28-
return self.new(body, length)
27+
if body.respond_to?(:to_ary)
28+
# This avoids allocating an enumerator, which is more efficient:
29+
return ::Protocol::HTTP::Body::Buffered.new(body.to_ary)
2930
else
3031
return self.new(body, length)
3132
end

test/protocol/rack/body.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,41 @@ def body.each
126126
end
127127
end
128128

129+
with "array body" do
130+
it "wraps array body and returns chunks via #read" do
131+
result = subject.wrap(env, 200, headers, ["Hello", " ", "World"])
132+
133+
expect(result).not.to be_nil
134+
expect(result.read).to be == "Hello"
135+
expect(result.read).to be == " "
136+
expect(result.read).to be == "World"
137+
expect(result.read).to be_nil
138+
end
139+
140+
it "wraps array body and yields chunks via #each" do
141+
result = subject.wrap(env, 200, headers, ["chunk1", "chunk2"])
142+
chunks = []
143+
result.each { |chunk| chunks << chunk }
144+
145+
expect(chunks).to be == ["chunk1", "chunk2"]
146+
end
147+
148+
it "wraps empty array body" do
149+
result = subject.wrap(env, 200, headers, [])
150+
151+
expect(result).not.to be_nil
152+
expect(result).to be(:empty?)
153+
expect(result.read).to be_nil
154+
end
155+
156+
it "uses content-length when provided" do
157+
headers["content-length"] = "11"
158+
result = subject.wrap(env, 200, headers, ["Hello", " World"])
159+
160+
expect(result.length).to be == 11
161+
end
162+
end
163+
129164
with "response finished callback" do
130165
it "returns a body that calls the callback when closed" do
131166
called = false

test/protocol/rack/body/enumerable.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,24 @@
2222
end
2323
end
2424

25+
with "#read" do
26+
let(:body) {subject.new(["Hello", " ", "World"], 11)}
27+
28+
it "returns chunks in order" do
29+
expect(body.read).to be == "Hello"
30+
expect(body.read).to be == " "
31+
expect(body.read).to be == "World"
32+
expect(body.read).to be_nil
33+
end
34+
35+
it "returns nil when exhausted" do
36+
body.read
37+
body.read
38+
body.read
39+
expect(body.read).to be_nil
40+
end
41+
end
42+
2543
with "#each" do
2644
let(:bad_enumerable) do
2745
Enumerator.new do |yielder|

0 commit comments

Comments
 (0)