Skip to content

Commit 2980e16

Browse files
authored
Ignore/close response bodies for status codes that don't allow them. (#23)
1 parent e53d4e2 commit 2980e16

File tree

2 files changed

+106
-3
lines changed

2 files changed

+106
-3
lines changed

lib/protocol/rack/body.rb

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,18 @@
55

66
require_relative "body/streaming"
77
require_relative "body/enumerable"
8+
require_relative "constants"
89
require "protocol/http/body/completable"
910

1011
module Protocol
1112
module Rack
1213
module Body
1314
CONTENT_LENGTH = "content-length"
1415

16+
def self.no_content?(status)
17+
status == 204 or status == 205 or status == 304
18+
end
19+
1520
def self.wrap(env, status, headers, body, input = nil)
1621
# In no circumstance do we want this header propagating out:
1722
if length = headers.delete(CONTENT_LENGTH)
@@ -33,12 +38,29 @@ def self.wrap(env, status, headers, body, input = nil)
3338
end
3439
elsif body.respond_to?(:each)
3540
body = Body::Enumerable.wrap(body, length)
36-
else
41+
elsif body
3742
body = Body::Streaming.new(body, input)
43+
else
44+
Console.warn(self, "Rack response body was nil, ignoring!")
3845
end
3946

40-
if response_finished = env[RACK_RESPONSE_FINISHED] and response_finished.any?
41-
body = ::Protocol::HTTP::Body::Completable.new(body, completion_callback(response_finished, env, status, headers))
47+
if body and no_content?(status)
48+
unless body.empty?
49+
Console.warn(self, "Rack response body was not empty, and status code indicates no content!", body: body, status: status)
50+
end
51+
52+
body.close
53+
body = nil
54+
end
55+
56+
response_finished = env[RACK_RESPONSE_FINISHED]
57+
58+
if response_finished&.any?
59+
if body
60+
body = ::Protocol::HTTP::Body::Completable.new(body, completion_callback(response_finished, env, status, headers))
61+
else
62+
completion_callback(response_finished, env, status, headers).call(nil)
63+
end
4264
end
4365

4466
return body

test/protocol/rack/body.rb

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# frozen_string_literal: true
2+
3+
# Released under the MIT License.
4+
# Copyright, 2024, by Samuel Williams.
5+
6+
require "protocol/rack/body"
7+
require "protocol/http/body/readable"
8+
require "console"
9+
10+
describe Protocol::Rack::Body do
11+
with "#no_content?" do
12+
it "returns true for status codes that indicate no content" do
13+
expect(subject.no_content?(204)).to be == true
14+
expect(subject.no_content?(205)).to be == true
15+
expect(subject.no_content?(304)).to be == true
16+
expect(subject.no_content?(200)).to be == false
17+
end
18+
end
19+
20+
with "#wrap" do
21+
let(:env) { {} }
22+
let(:headers) { {} }
23+
24+
it "handles nil body" do
25+
expect(Console).to receive(:warn).and_return(nil)
26+
27+
result = subject.wrap(env, 200, headers, nil)
28+
expect(result).to be_nil
29+
end
30+
31+
with "non-empty body and no-content status" do
32+
let(:mock_body) do
33+
Protocol::HTTP::Body::Buffered.new(["content"])
34+
end
35+
36+
[204, 205, 304].each do |status|
37+
it "closes body and returns nil for status #{status}", unique: status do
38+
expect(Console).to receive(:warn).and_return(nil)
39+
expect(mock_body).to receive(:close)
40+
41+
result = subject.wrap(env, status, headers, mock_body)
42+
43+
expect(result).to be_nil
44+
end
45+
end
46+
end
47+
48+
with "empty body and no-content status" do
49+
let(:mock_body) do
50+
Protocol::HTTP::Body::Buffered.new
51+
end
52+
53+
it "closes body and returns nil for no-content status" do
54+
expect(Console).not.to receive(:warn)
55+
expect(mock_body).to receive(:close)
56+
57+
result = subject.wrap(env, 204, headers, mock_body)
58+
59+
expect(result).to be_nil
60+
end
61+
end
62+
63+
with "body and normal status" do
64+
let(:mock_body) do
65+
body = Object.new
66+
67+
def body.each
68+
yield "content"
69+
end
70+
71+
body
72+
end
73+
74+
it "wraps body properly with status 200" do
75+
result = subject.wrap(env, 200, headers, mock_body)
76+
expect(result).to be_a(Protocol::Rack::Body::Enumerable)
77+
expect(result).not.to be_nil
78+
end
79+
end
80+
end
81+
end

0 commit comments

Comments
 (0)