mirror of
https://github.com/puma/puma.git
synced 2022-11-09 13:48:40 -05:00
Support responses without entity body
Some HTTP verbs imply that no body is sent with the response. Puma did not respect that. This led to issues like this one: $ curl localhost:9292 curl: (18) transfer closed with outstanding read data remaining This also breaks persistent connections. Example program to program to provoke this issue: proc do [204, {'Conten-Type' => 'text/plain'}, []] end Signed-off-by: Konstantin Haase <konstantin.mailinglists@googlemail.com>
This commit is contained in:
parent
bcef6b82c5
commit
e4a4eb964e
3 changed files with 26 additions and 1 deletions
|
@ -1,3 +1,4 @@
|
|||
require 'set'
|
||||
|
||||
module Puma
|
||||
|
||||
|
@ -44,6 +45,9 @@ module Puma
|
|||
505 => 'HTTP Version not supported'
|
||||
}
|
||||
|
||||
# For some HTTP status codes the client only expects headers.
|
||||
STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 205 << 304)
|
||||
|
||||
# Frequently used constants when constructing requests or responses. Many times
|
||||
# the constant just refers to a string with the same contents. Using these constants
|
||||
# gave about a 3% to 10% performance improvement over using the strings directly.
|
||||
|
|
|
@ -341,6 +341,7 @@ module Puma
|
|||
end
|
||||
|
||||
content_length = nil
|
||||
no_body = STATUS_WITH_NO_ENTITY_BODY.include? status
|
||||
|
||||
if res_body.kind_of? Array and res_body.size == 1
|
||||
content_length = res_body[0].size
|
||||
|
@ -395,6 +396,8 @@ module Puma
|
|||
when TRANSFER_ENCODING
|
||||
allow_chunked = false
|
||||
content_length = nil
|
||||
when CONTENT_TYPE
|
||||
next if no_body
|
||||
end
|
||||
|
||||
vs.split(NEWLINE).each do |v|
|
||||
|
@ -405,6 +408,11 @@ module Puma
|
|||
end
|
||||
end
|
||||
|
||||
if no_body
|
||||
client.write line_ending
|
||||
return keep_alive
|
||||
end
|
||||
|
||||
if include_keepalive_header
|
||||
client.write CONNECTION_KEEP_ALIVE
|
||||
elsif !keep_alive
|
||||
|
|
|
@ -10,6 +10,7 @@ class TestPersistent < Test::Unit::TestCase
|
|||
@keep_request = "GET / HTTP/1.0\r\nHost: test.com\r\nContent-Type: text/plain\r\nConnection: Keep-Alive\r\n\r\n"
|
||||
|
||||
@valid_post = "POST / HTTP/1.1\r\nHost: test.com\r\nContent-Type: text/plain\r\nContent-Length: 5\r\n\r\nhello"
|
||||
@valid_no_body = "GET / HTTP/1.1\r\nHost: test.com\r\nX-Status: 204\r\nContent-Type: text/plain\r\n\r\n"
|
||||
|
||||
@headers = { "X-Header" => "Works" }
|
||||
@body = ["Hello"]
|
||||
|
@ -17,7 +18,8 @@ class TestPersistent < Test::Unit::TestCase
|
|||
|
||||
@simple = lambda do |env|
|
||||
@inputs << env['rack.input']
|
||||
[200, @headers, @body]
|
||||
status = Integer(env['HTTP_X_STATUS'] || 200)
|
||||
[status, @headers, @body]
|
||||
end
|
||||
|
||||
@server = Puma::Server.new @simple
|
||||
|
@ -76,6 +78,17 @@ class TestPersistent < Test::Unit::TestCase
|
|||
assert_equal "Hello", @client.read(5)
|
||||
end
|
||||
|
||||
def test_no_body_then_get
|
||||
@client << @valid_no_body
|
||||
assert_equal "HTTP/1.1 204 No Content\r\nX-Header: Works\r\n\r\n", lines(3)
|
||||
|
||||
@client << @valid_request
|
||||
sz = @body[0].size.to_s
|
||||
|
||||
assert_equal "HTTP/1.1 200 OK\r\nX-Header: Works\r\nContent-Length: #{sz}\r\n\r\n", lines(4)
|
||||
assert_equal "Hello", @client.read(5)
|
||||
end
|
||||
|
||||
def test_chunked
|
||||
@body << "Chunked"
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue