Skip to content

Commit 654087c

Browse files
Fix head request handling. (#29)
1 parent 4ae402a commit 654087c

File tree

3 files changed

+56
-9
lines changed

3 files changed

+56
-9
lines changed

lib/protocol/rack/body.rb

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
require_relative "body/streaming"
77
require_relative "body/enumerable"
88
require_relative "constants"
9+
910
require "protocol/http/body/completable"
11+
require "protocol/http/body/head"
1012

1113
module Protocol
1214
module Rack
@@ -37,8 +39,9 @@ def self.no_content?(status)
3739
# @parameter headers [Hash] The response headers.
3840
# @parameter body [Object] The response body to wrap.
3941
# @parameter input [Object] Optional input for streaming bodies.
42+
# @parameter head [Boolean] Indicates if this is a HEAD request, which should not have a body.
4043
# @returns [Protocol::HTTP::Body] The wrapped response body.
41-
def self.wrap(env, status, headers, body, input = nil)
44+
def self.wrap(env, status, headers, body, input = nil, head = false)
4245
# In no circumstance do we want this header propagating out:
4346
if length = headers.delete(CONTENT_LENGTH)
4447
# We don't really trust the user to provide the right length to the transport.
@@ -84,6 +87,19 @@ def self.wrap(env, status, headers, body, input = nil)
8487
end
8588
end
8689

90+
# There are two main situations we need to handle:
91+
# 1. The application has the `Rack::Head` middleware in the stack, which means we should not return a body, and the application is also responsible for setting the content-length header. `Rack::Head` will result in an empty enumerable body.
92+
# 2. The application does not have `Rack::Head`, in which case it will return a body and we need to extract the length.
93+
# In both cases, we need to ensure that the body is wrapped correctly. If there is no body and we don't know the length, we also just return `nil`.
94+
if head
95+
if body
96+
body = ::Protocol::HTTP::Body::Head.for(body)
97+
elsif length
98+
body = ::Protocol::HTTP::Body::Head.new(length)
99+
end
100+
# Otherwise, body is `nil` and we don't know the length either.
101+
end
102+
87103
return body
88104
end
89105

lib/protocol/rack/response.rb

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,7 @@ def self.wrap(env, status, headers, meta, body, request = nil)
5050
body = hijack_body
5151
end
5252

53-
body = Body.wrap(env, status, headers, body, request&.body)
54-
55-
if request&.head?
56-
# I thought about doing this in Output.wrap, but decided the semantics are too tricky. Specifically, the various ways a rack response body can be wrapped, and the need to invoke #close at the right point.
57-
body = ::Protocol::HTTP::Body::Head.for(body)
58-
end
53+
body = Body.wrap(env, status, headers, body, request&.body, request&.head?)
5954

6055
protocol = meta[RACK_PROTOCOL]
6156

test/protocol/rack/body.rb

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
end
1919

2020
with "#wrap" do
21-
let(:env) { {} }
22-
let(:headers) { {} }
21+
let(:env) {Hash.new}
22+
let(:headers) {Hash.new}
2323

2424
it "handles nil body" do
2525
expect(Console).to receive(:warn).and_return(nil)
@@ -28,6 +28,42 @@
2828
expect(result).to be_nil
2929
end
3030

31+
with "head request" do
32+
it "handles head request with content-length and empty body" do
33+
headers["content-length"] = "123"
34+
35+
result = subject.wrap(env, 200, headers, [], nil, true)
36+
37+
expect(result).to be_a(Protocol::HTTP::Body::Head)
38+
expect(result.length).to be == 123
39+
end
40+
41+
it "handles head request with no content-length and empty body" do
42+
result = subject.wrap(env, 200, headers, [], nil, true)
43+
44+
expect(result).to be_a(Protocol::HTTP::Body::Head)
45+
expect(result.length).to be == 0
46+
end
47+
48+
it "handles head request with content-length and nil body" do
49+
headers["content-length"] = "123"
50+
51+
expect(Console).to receive(:warn).and_return(nil)
52+
result = subject.wrap(env, 200, headers, nil, nil, true)
53+
54+
expect(result).to be_a(Protocol::HTTP::Body::Head)
55+
expect(result.length).to be == 123
56+
end
57+
58+
it "handles head request with no content-length and nil body" do
59+
expect(Console).to receive(:warn).and_return(nil)
60+
61+
result = subject.wrap(env, 200, headers, nil, nil, true)
62+
63+
expect(result).to be_nil
64+
end
65+
end
66+
3167
with "non-empty body and no-content status" do
3268
let(:mock_body) do
3369
Protocol::HTTP::Body::Buffered.new(["content"])

0 commit comments

Comments
 (0)