1
0
Fork 0
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:
Konstantin Haase 2011-12-13 14:45:12 +01:00 committed by Evan Phoenix
parent bcef6b82c5
commit e4a4eb964e
3 changed files with 26 additions and 1 deletions

View file

@ -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.

View file

@ -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

View file

@ -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"