IO/copy stream (#2923)
* Proof of Concept: Use `IO.copy_stream` to serve files Ref: https://puma/puma/issues/2697 ``` $ benchmarks/wrk/big_response.sh Puma starting in single mode... * Puma version: 5.5.0 (ruby 3.0.2-p107) ("Zawgyi") * Min threads: 4 * Max threads: 4 * Environment: development * PID: 17879 * Listening on http://0.0.0.0:9292 Use Ctrl-C to stop Running 1m test @ http://localhost:9292 2 threads and 4 connections Thread Stats Avg Stdev Max +/- Stdev Latency 3.37ms 5.89ms 48.28ms 94.46% Req/Sec 0.88k 148.97 1.07k 82.08% Latency Distribution 50% 2.21ms 75% 2.78ms 90% 4.09ms 99% 35.75ms 105651 requests in 1.00m, 108.24GB read Requests/sec: 1758.39 Transfer/sec: 1.80GB - Gracefully stopping, waiting for requests to finish ``` ``` $ benchmarks/wrk/big_file.sh Puma starting in single mode... * Puma version: 5.5.0 (ruby 3.0.2-p107) ("Zawgyi") * Min threads: 4 * Max threads: 4 * Environment: development * PID: 18034 * Listening on http://0.0.0.0:9292 Use Ctrl-C to stop Running 1m test @ http://localhost:9292 2 threads and 4 connections Thread Stats Avg Stdev Max +/- Stdev Latency 1.06ms 1.09ms 20.98ms 97.94% Req/Sec 1.85k 150.69 2.03k 89.92% Latency Distribution 50% 0.94ms 75% 1.03ms 90% 1.21ms 99% 4.91ms 221380 requests in 1.00m, 226.81GB read Requests/sec: 3689.18 Transfer/sec: 3.78GB - Gracefully stopping, waiting for requests to finish ``` * Ruby 2.2 compat * test_puma_server.rb - fixup test_file_body Co-authored-by: Jean Boussier <jean.boussier@gmail.com> Co-authored-by: MSP-Greg <Greg.mpls@gmail.com>
This commit is contained in:
parent
e2ef83b4ee
commit
5f3f489ee8
|
@ -0,0 +1,6 @@
|
|||
bundle exec bin/puma -t 4 test/rackup/big_file.ru &
|
||||
PID1=$!
|
||||
sleep 5
|
||||
wrk -c 4 -d 60 --latency http://localhost:9292
|
||||
|
||||
kill $PID1
|
|
@ -150,21 +150,26 @@ module Puma
|
|||
end
|
||||
|
||||
begin
|
||||
res_body.each do |part|
|
||||
next if part.bytesize.zero?
|
||||
if chunked
|
||||
fast_write io, (part.bytesize.to_s(16) << line_ending)
|
||||
fast_write io, part # part may have different encoding
|
||||
fast_write io, line_ending
|
||||
else
|
||||
fast_write io, part
|
||||
if !chunked && content_length && res_body.is_a?(::File)
|
||||
IO.copy_stream(res_body, io)
|
||||
io.flush
|
||||
else
|
||||
res_body.each do |part|
|
||||
next if part.bytesize.zero?
|
||||
if chunked
|
||||
fast_write io, (part.bytesize.to_s(16) << line_ending)
|
||||
fast_write io, part # part may have different encoding
|
||||
fast_write io, line_ending
|
||||
else
|
||||
fast_write io, part
|
||||
end
|
||||
io.flush
|
||||
end
|
||||
io.flush
|
||||
end
|
||||
|
||||
if chunked
|
||||
fast_write io, CLOSE_CHUNKED
|
||||
io.flush
|
||||
if chunked
|
||||
fast_write io, CLOSE_CHUNKED
|
||||
io.flush
|
||||
end
|
||||
end
|
||||
rescue SystemCallError, IOError
|
||||
raise ConnectionError, "Connection error detected during write"
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
static_file_path = File.join(Dir.tmpdir, "puma-static.txt")
|
||||
File.write(static_file_path, "Hello World" * 100_000)
|
||||
|
||||
run lambda { |env|
|
||||
f = File.open(static_file_path)
|
||||
[200, {"Content-Type" => "text/plain", "Content-Length" => f.size.to_s}, f]
|
||||
}
|
|
@ -132,6 +132,19 @@ class TestPumaServer < Minitest::Test
|
|||
assert_equal "Hello World", data.split("\n").last
|
||||
end
|
||||
|
||||
def test_file_body
|
||||
random_bytes = SecureRandom.random_bytes(4096 * 32)
|
||||
path = Tempfile.open { |f| f.path }
|
||||
File.binwrite path, random_bytes
|
||||
|
||||
server_run { |env| [200, {}, File.open(path, 'rb')] }
|
||||
|
||||
data = send_http_and_read "GET / HTTP/1.0\r\nHost: [::ffff:127.0.0.1]:9292\r\n\r\n"
|
||||
assert_equal random_bytes, data.split("\r\n", 3).last
|
||||
ensure
|
||||
File.delete(path) if File.exist?(path)
|
||||
end
|
||||
|
||||
def test_proper_stringio_body
|
||||
data = nil
|
||||
|
||||
|
|
Loading…
Reference in New Issue