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 backlog parameter to be set with ssl_bind DSL #2780

Merged
merged 6 commits into from Jan 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/architecture.md
Expand Up @@ -31,10 +31,10 @@ _workers_, and we sometimes call the threads created by Puma's
![https://bit.ly/2zwzhEK](images/puma-connection-flow.png)

* Upon startup, Puma listens on a TCP or UNIX socket.
* The backlog of this socket is configured (with a default of 1024). The
backlog determines the size of the queue for unaccepted connections.
Generally, you'll never hit the backlog cap in production. If the backlog is
full, the operating system refuses new connections.
* The backlog of this socket is configured with a default of 1024, but the
actual backlog value is capped by the `net.core.somaxconn` sysctl value.
The backlog determines the size of the queue for unaccepted connections. If
the backlog is full, the operating system is not accepting new connections.
* This socket backlog is distinct from the `backlog` of work as reported by
`Puma.stats` or the control server. The backlog that `Puma.stats` refers to
represents the number of connections in the process' `todo` set waiting for
Expand Down
7 changes: 4 additions & 3 deletions lib/puma/binder.rb
Expand Up @@ -168,9 +168,9 @@ def parse(binds, logger, log_msg = 'Listening')
params = Util.parse_query uri.query

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

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

@ios[ios_len..-1].each do |i|
addr = loc_addr_str i
Expand Down Expand Up @@ -255,7 +255,8 @@ def parse(binds, logger, log_msg = 'Listening')
logger.log "* Activated #{str}"
else
ios_len = @ios.length
io = add_ssl_listener uri.host, uri.port, ctx
backlog = params.fetch('backlog', 1024).to_i
io = add_ssl_listener uri.host, uri.port, ctx, optimize_for_latency = true, backlog

@ios[ios_len..-1].each do |i|
addr = loc_addr_str i
Expand Down
6 changes: 4 additions & 2 deletions lib/puma/dsl.rb
Expand Up @@ -48,14 +48,16 @@ def self.ssl_bind_str(host, port, opts)

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

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

if defined?(JRUBY_VERSION)
ssl_cipher_list = opts[:ssl_cipher_list] ?
"&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil

keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"

"ssl://#{host}:#{port}?#{keystore_additions}#{ssl_cipher_list}" \
"&verify_mode=#{verify}#{tls_str}#{ca_additions}"
"&verify_mode=#{verify}#{tls_str}#{ca_additions}#{backlog_str}"
else
ssl_cipher_filter = opts[:ssl_cipher_filter] ?
"&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
Expand All @@ -64,7 +66,7 @@ def self.ssl_bind_str(host, port, opts)
"&verification_flags=#{Array(ary).join ','}" : nil

"ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}" \
"#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}"
"#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}"
end
end

Expand Down
18 changes: 18 additions & 0 deletions test/test_binder.rb
Expand Up @@ -275,6 +275,24 @@ def test_env_contains_stderr
assert_equal @events.stderr, env_hash["rack.errors"]
end

def test_ssl_binder_sets_backlog
skip_unless :ssl

host = '127.0.0.1'
port = UniquePort.call
tcp_server = TCPServer.new(host, port)
tcp_server.define_singleton_method(:listen) do |backlog|
Thread.current[:backlog] = backlog
super(backlog)
end

TCPServer.stub(:new, tcp_server) do
@binder.parse ["ssl://#{host}:#{port}?#{ssl_query}&backlog=2048"], @events
end

assert_equal 2048, Thread.current[:backlog]
end

def test_close_calls_close_on_ios
@mocked_ios = [Minitest::Mock.new, Minitest::Mock.new]
@mocked_ios.each { |m| m.expect(:close, true) }
Expand Down
15 changes: 15 additions & 0 deletions test/test_config.rb
Expand Up @@ -98,6 +98,21 @@ def test_ssl_bind_with_cert_and_key_pem
assert_equal [ssl_binding], conf.options[:binds]
end

def test_ssl_bind_with_backlog
skip_unless :ssl

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

conf.load

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

def test_ssl_bind_jruby
skip_unless :jruby
skip_unless :ssl
Expand Down
2 changes: 1 addition & 1 deletion test/test_thread_pool.rb
Expand Up @@ -5,7 +5,7 @@
class TestThreadPool < Minitest::Test

def teardown
@pool.shutdown(1) if @pool
@pool.shutdown(1) if defined?(@pool)
end

def new_pool(min, max, &block)
Expand Down