1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
# frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2019-2025, by Samuel Williams.
require "protocol/http1/body/chunked"
require "connection_context"
describe Protocol::HTTP1::Body::Chunked do
let(:content) {"Hello World"}
let(:postfix) {nil}
let(:headers) {Protocol::HTTP::Headers.new}
let(:buffer) {StringIO.new("#{content.bytesize.to_s(16)}\r\n#{content}\r\n0\r\n#{postfix}\r\n")}
let(:connection) {Protocol::HTTP1::Connection.new(buffer, state: :open)}
let(:body) {subject.new(connection, headers)}
with "#inspect" do
it "can be inspected" do
expect(body.inspect).to be =~ /0 bytes read in 0 chunks, reading/
end
end
with "#as_json" do
it "returns JSON representation" do
expect(body.as_json).to have_keys(
class: be == "Protocol::HTTP1::Body::Chunked",
length: be_nil, # Not finished yet
stream: be == false,
ready: be == false,
empty: be == false,
count: be == 0,
finished: be == false,
state: be == "open"
)
end
it "shows finished state after reading all chunks" do
body.read # Read the chunk
body.read # Read the end (returns nil)
expect(body.as_json).to have_keys(
length: be == 11,
count: be == 1,
finished: be == true,
empty: be == true,
state: be == "closed"
)
end
end
with "#empty?" do
it "returns whether EOF was reached" do
expect(body.empty?).to be == false
end
end
with "#close" do
it "invokes close_read on the stream if closing without reading all chunks" do
expect(buffer).to receive(:close_read)
body.close
expect(body).to be(:empty?)
expect(connection).to be(:half_closed_remote?)
end
it "invokes close_read on the stream if closing with an error" do
expect(buffer).to receive(:close_read)
body.close(EOFError)
expect(body).to be(:empty?)
expect(connection).to be(:half_closed_remote?)
end
end
with "#read" do
it "retrieves chunks of content" do
expect(body.read).to be == "Hello World"
expect(body.read).to be == nil
expect(body.read).to be == nil
expect(connection).to be(:half_closed_remote?)
end
it "updates number of bytes retrieved" do
expect(body).to have_attributes(length: be_nil, count: be == 0)
expect(body.read).to be == "Hello World"
expect(body.read).to be_nil # there are no more chunks
expect(body).to have_attributes(length: be == 11, count: be == 1)
expect(body).to be(:empty?)
expect(connection).to be(:half_closed_remote?)
end
with "trailer" do
let(:postfix) {"ETag: abcd\r\n"}
it "can read trailing etag" do
headers.add("trailer", "etag")
expect(body.read).to be == "Hello World"
expect(headers["etag"]).to be_nil
expect(body.read).to be == nil
expect(headers["etag"]).to be == "abcd"
expect(connection).to be(:half_closed_remote?)
end
end
with "bad trailers" do
let(:postfix) {":ETag abcd\r\n"}
it "raises error" do
headers.add("trailer", "etag")
expect(body.read).to be == "Hello World"
expect(headers["etag"]).to be_nil
expect{body.read}.to raise_exception(Protocol::HTTP1::BadHeader)
body.close
expect(connection).to be(:half_closed_remote?)
end
end
with "invalid content length" do
let(:buffer) {StringIO.new("#{(content.bytesize + 1).to_s(16)}\r\n#{content}")}
it "raises error" do
expect{body.read}.to raise_exception(EOFError)
expect(connection).to be(:half_closed_remote?)
end
end
end
end
|