Skip to content
Closed
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
13 changes: 10 additions & 3 deletions lib/async/http/protocol/http1/finishable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module Async
module HTTP
module Protocol
module HTTP1
# Keeps track of whether a body is being read, and if so, waits for it to be closed.
# Keeps track of whether a body is being read, and releases it once the response has been sent.
class Finishable < ::Protocol::HTTP::Body::Wrapper
# Initialize the finishable wrapper.
# @parameter body [Protocol::HTTP::Body::Readable] The body to wrap.
Expand Down Expand Up @@ -47,11 +47,18 @@ def close(error = nil)
end
end

# Wait for the body to be fully consumed or discard it.
# Discard or close the body once the response has been sent.
# @parameter persistent [Boolean] Whether the connection will be reused.
def wait(persistent = true)
if @reading
@closed.wait
# The response has been sent, so release a body the application stopped reading:
unless @closed.resolved?
begin
self.discard if persistent
ensure
self.close
end
end
elsif persistent
# If the connection can be reused, let's gracefully discard the body:
self.discard
Expand Down
48 changes: 48 additions & 0 deletions test/async/http/protocol/http1/finishable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2026, by Andrew Fader.

require "async/http/protocol/http1/finishable"

require "protocol/http/body/buffered"
require "sus/fixtures/async/reactor_context"

describe Async::HTTP::Protocol::HTTP1::Finishable do
include Sus::Fixtures::Async::ReactorContext

let(:source) {Protocol::HTTP::Body::Buffered.new(["Hello", " ", "World"])}
let(:finishable) {subject.new(source)}

with "a partially read body" do
it "discards the rest when the connection is persistent" do
expect(finishable.read).to be == "Hello"

Async::Task.current.with_timeout(1) do
finishable.wait(true)
end

expect(source.read).to be_nil
end

it "closes it when the connection is not persistent" do
expect(finishable.read).to be == "Hello"

expect(source).to receive(:close)

Async::Task.current.with_timeout(1) do
finishable.wait(false)
end
end
end

with "a fully read body" do
it "returns once it has been closed" do
expect(finishable.join).to be == "Hello World"

Async::Task.current.with_timeout(1) do
finishable.wait(true)
end
end
end
end