Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/protocol/http/body/readable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
# Copyright, 2019-2024, by Samuel Williams.
# Copyright, 2023, by Bruno Sutic.

require_relative "stream"

module Protocol
module HTTP
module Body
Expand Down Expand Up @@ -186,6 +188,12 @@ def as_json(...)
def to_json(...)
as_json.to_json(...)
end

# Return an IO-compatible stream for reading this body.
# @returns [Stream] The IO-compatible stream adapter.
def to_io
return Stream.new(self)
end
end
end
end
Expand Down
22 changes: 13 additions & 9 deletions lib/protocol/http/body/stream.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
# Copyright, 2023, by Genki Takiuchi.
# Copyright, 2025, by William T. Nelson.

require_relative "buffered"

module Protocol
module HTTP
module Body
Expand All @@ -17,13 +15,15 @@ class Stream

# Initialize the stream with the given input and output.
#
# @parameter input [Readable] The input stream.
# @parameter output [Writable] The output stream.
def initialize(input = nil, output = Buffered.new)
# @parameter input [Readable | Nil] The input stream.
# @parameter output [Writable | Nil] The output stream.
def initialize(input = nil, output = nil)
@input = input
@output = output

raise ArgumentError, "Non-writable output!" unless output.respond_to?(:write)
if @output
raise ArgumentError, "Non-writable output!" unless output.respond_to?(:write)
end

# Will hold remaining data in `#read`.
@buffer = nil
Expand All @@ -32,10 +32,10 @@ def initialize(input = nil, output = Buffered.new)
@closed_read = false
end

# @attribute [Readable] The input stream.
# @attribute [Readable | Nil] The input stream.
attr :input

# @attribute [Writable] The output stream.
# @attribute [Writable | Nil] The output stream.
attr :output

# This provides a read-only interface for data, which is surprisingly tricky to implement correctly.
Expand Down Expand Up @@ -409,7 +409,11 @@ def inspect

# @returns [Boolean] Whether there are any output chunks remaining.
def empty?
@output.empty?
if @output
return @output.empty?
else
return true
end
end

private
Expand Down
5 changes: 5 additions & 0 deletions releases.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Releases

## Unreleased

- Add `Protocol::HTTP::Body::Readable#to_io` for obtaining an IO-compatible stream adapter.
- Avoid creating an implicit buffered output for `Protocol::HTTP::Body::Stream`.

## v0.68.0

- Add HTTP status descriptions.
Expand Down
16 changes: 16 additions & 0 deletions test/protocol/http/body/readable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@
describe Protocol::HTTP::Body::Readable do
let(:body) {subject.new}

with "#to_io" do
let(:body) {Protocol::HTTP::Body::Buffered.new(["Hello", "World"])}

it "returns an IO-compatible stream" do
stream = body.to_io

expect(stream).to be_a(Protocol::HTTP::Body::Stream)
expect(stream.output).to be_nil
expect(body.to_io).not.to be_equal(stream)
expect(stream.read(5)).to be == "Hello"
expect(stream.read(5)).to be == "World"
expect(stream.read(5)).to be_nil
expect{stream.write("!")}.to raise_exception(IOError)
end
end

it "might not be empty" do
expect(body).not.to be(:empty?)
end
Expand Down
Loading