Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow setting TCP low_latency with SSL listener, minor fixes #3065

Merged
merged 7 commits into from
Feb 11, 2023
7 changes: 4 additions & 3 deletions lib/puma/binder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,10 @@ def parse(binds, log_writer = nil, log_msg = 'Listening')
ios_len = @ios.length
params = Util.parse_query uri.query

opt = params.key?('low_latency') && params['low_latency'] != 'false'
low_latency = params.key?('low_latency') && params['low_latency'] != 'false'
backlog = params.fetch('backlog', 1024).to_i

io = add_tcp_listener uri.host, uri.port, opt, backlog
io = add_tcp_listener uri.host, uri.port, low_latency, backlog

@ios[ios_len..-1].each do |i|
addr = loc_addr_str i
Expand Down Expand Up @@ -251,7 +251,8 @@ def parse(binds, log_writer = nil, log_msg = 'Listening')
else
ios_len = @ios.length
backlog = params.fetch('backlog', 1024).to_i
io = add_ssl_listener uri.host, uri.port, ctx, optimize_for_latency = true, backlog
low_latency = params['low_latency'] != 'false'
io = add_ssl_listener uri.host, uri.port, ctx, low_latency, backlog

@ios[ios_len..-1].each do |i|
addr = loc_addr_str i
Expand Down
3 changes: 2 additions & 1 deletion lib/puma/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def self.ssl_bind_str(host, port, opts)

ca_additions = "&ca=#{Puma::Util.escape(opts[:ca])}" if ['peer', 'force_peer'].include?(verify)

low_latency_str = opts.key?(:low_latency) ? "&low_latency=#{opts[:low_latency]}" : ''
backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : ''

if defined?(JRUBY_VERSION)
Expand Down Expand Up @@ -114,7 +115,7 @@ def self.ssl_bind_str(host, port, opts)
end

"ssl://#{host}:#{port}?#{cert_flags}#{key_flags}#{ssl_cipher_filter}" \
"#{reuse_flag}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}"
"#{reuse_flag}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}#{low_latency_str}"
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/puma/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,8 @@ def fast_write_str(socket, str)
def fast_write_response(socket, body, io_buffer, chunked, content_length)
if body.is_a?(::File) && body.respond_to?(:read)
if chunked # would this ever happen?
while part = body.read(BODY_LEN_MAX)
io_buffer.append part.bytesize.to_s(16), LINE_END, part, LINE_END
while chunk = body.read(BODY_LEN_MAX)
io_buffer.append chunk.bytesize.to_s(16), LINE_END, chunk, LINE_END
end
fast_write_str socket, CLOSE_CHUNKED
else
Expand Down
1 change: 1 addition & 0 deletions test/rackup/hello-bind_rack3.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
run lambda { |env| [200, {"Content-Type" => "text/plain"}, ["Hello World"]] }
33 changes: 31 additions & 2 deletions test/test_binder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,16 @@ def test_pre_existing_unix
end
end

def test_binder_parses_nil_low_latency
def test_binder_tcp_defaults_to_low_latency_off
skip_if :jruby
@binder.parse ["tcp://0.0.0.0:0"], @log_writer

socket = @binder.listeners.first.last

refute socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY).bool
end

def test_binder_tcp_parses_nil_low_latency
skip_if :jruby
@binder.parse ["tcp://0.0.0.0:0?low_latency"], @log_writer

Expand All @@ -201,7 +210,7 @@ def test_binder_parses_nil_low_latency
assert socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY).bool
end

def test_binder_parses_true_low_latency
def test_binder_tcp_parses_true_low_latency
skip_if :jruby
@binder.parse ["tcp://0.0.0.0:0?low_latency=true"], @log_writer

Expand All @@ -219,6 +228,26 @@ def test_binder_parses_false_low_latency
refute socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY).bool
end

def test_binder_ssl_defaults_to_true_low_latency
skip_unless :ssl
skip_if :jruby
@binder.parse ["ssl://0.0.0.0:0?#{ssl_query}"], @log_writer

socket = @binder.listeners.first.last

assert socket.to_io.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY).bool
end

def test_binder_ssl_parses_false_low_latency
skip_unless :ssl
skip_if :jruby
@binder.parse ["ssl://0.0.0.0:0?#{ssl_query}&low_latency=false"], @log_writer

socket = @binder.listeners.first.last

refute socket.to_io.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY).bool
end

def test_binder_parses_tlsv1_disabled
skip_unless :ssl
@binder.parse ["ssl://0.0.0.0:0?#{ssl_query}&no_tlsv1=true"], @log_writer
Expand Down
47 changes: 44 additions & 3 deletions test/test_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,16 @@ def test_default_max_threads
end

def test_app_from_rackup
skip_if :rack3
if Rack::RELEASE >= '3'
fn = "test/rackup/hello-bind_rack3.ru"
bind = "tcp://0.0.0.0:9292"
else
fn = "test/rackup/hello-bind.ru"
bind = "tcp://127.0.0.1:9292"
end

conf = Puma::Configuration.new do |c|
c.rackup "test/rackup/hello-bind.ru"
c.rackup fn
end
conf.load

Expand All @@ -29,7 +36,9 @@ def test_app_from_rackup
conf.app
end

assert_equal ["tcp://127.0.0.1:9292"], conf.options[:binds]
assert_equal [200, {"Content-Type"=>"text/plain"}, ["Hello World"]], conf.app.call({})

assert_equal [bind], conf.options[:binds]
end

def test_app_from_app_DSL
Expand Down Expand Up @@ -147,6 +156,38 @@ def test_ssl_bind_with_backlog
assert ssl_binding.include?('&backlog=2048')
end

def test_ssl_bind_with_low_latency_true
skip_unless :ssl
skip_if :jruby

conf = Puma::Configuration.new do |c|
c.ssl_bind "0.0.0.0", "9292", {
low_latency: true
}
end

conf.load

ssl_binding = conf.options[:binds].first
assert ssl_binding.include?('&low_latency=true')
end

def test_ssl_bind_with_low_latency_false
skip_unless :ssl
skip_if :jruby

conf = Puma::Configuration.new do |c|
c.ssl_bind "0.0.0.0", "9292", {
low_latency: false
}
end

conf.load

ssl_binding = conf.options[:binds].first
assert ssl_binding.include?('&low_latency=false')
end

def test_ssl_bind_jruby
skip_unless :jruby
skip_unless :ssl
Expand Down