From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.1 required=3.0 tests=ALL_TRUSTED,AWL,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 243731F55F for ; Tue, 5 Sep 2023 11:46:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=yhbt.net; s=selector1; t=1693914406; bh=OclZLhja4QsN7OwMc/IU3cpkiR4Qt6VkLe9FRjO9/3w=; h=From:To:Subject:Date:From; b=nNZtruKOKp/5U/iZXsyGb+UCjm42rIIkTJwdoZU9E0LlB33yCzua8Wl+mDSHfvMk0 MLPM45Hb/hCPXsGCn7nQIWFuJxhXyKdrcW73hx0yOYrC015RjTh3n/tcCpJIMKRC2a 6lydI2DmhAvaybutKmYk9NL9W1JYrB8OhHvc2qyQ= From: Eric Wong To: raindrops-public@yhbt.net Subject: [PATCH] aggregate/last_data_recv: support Socket#accept{,_nonblock} Date: Tue, 5 Sep 2023 11:46:45 +0000 Message-ID: <20230905114646.3832373-1-bofh@yhbt.net> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: Socket#accept and Socket#accept_nonblock return an Addrinfo object in addition to a client socket. This allows web servers to avoid having to make getpeername(2) syscalls to get the same information. --- lib/raindrops/aggregate/last_data_recv.rb | 23 ++++++++---- test/test_last_data_recv.rb | 43 +++++++++++++++++++++++ 2 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 test/test_last_data_recv.rb diff --git a/lib/raindrops/aggregate/last_data_recv.rb b/lib/raindrops/aggregate/last_data_recv.rb index 6919fbc..32908f2 100644 --- a/lib/raindrops/aggregate/last_data_recv.rb +++ b/lib/raindrops/aggregate/last_data_recv.rb @@ -10,6 +10,8 @@ # Methods wrapped include: # - TCPServer#accept # - TCPServer#accept_nonblock +# - Socket#accept +# - Socket#accept_nonblock # - Kgio::TCPServer#kgio_accept # - Kgio::TCPServer#kgio_tryaccept module Raindrops::Aggregate::LastDataRecv @@ -33,8 +35,10 @@ def self.default_aggregate=(agg) # automatically extends any TCPServer objects used by Unicorn def self.cornify! - Unicorn::HttpServer::LISTENERS.each do |sock| - sock.extend(self) if TCPServer === sock + Unicorn::HttpServer::LISTENERS.each do |s| + if TCPServer === s || (s.instance_of?(Socket) && s.local_address.ip?) + s.extend(self) + end end end @@ -60,8 +64,8 @@ def accept count! super end - def accept_nonblock - count! super + def accept_nonblock(exception: true) + count! super(exception: exception) end # :startdoc: @@ -72,12 +76,19 @@ def accept_nonblock # # We require TCP_DEFER_ACCEPT on the listen socket for # +last_data_recv+ to be accurate - def count!(io) + def count!(ret) + case ret + when :wait_readable + when Array # Socket#accept_nonblock + io = ret[0] + else # TCPSocket#accept_nonblock + io = ret + end if io x = Raindrops::TCP_Info.new(io) @raindrops_aggregate << x.last_data_recv end - io + ret end end diff --git a/test/test_last_data_recv.rb b/test/test_last_data_recv.rb new file mode 100644 index 0000000..ef84e05 --- /dev/null +++ b/test/test_last_data_recv.rb @@ -0,0 +1,43 @@ +require 'test/unit' +require 'raindrops' +require 'io/wait' + +class TestLastDataRecv < Test::Unit::TestCase + def test_accept_nonblock_agg + s = Socket.new(:INET, :STREAM, 0) + s.listen(128) + addr = s.connect_address + s.extend(Raindrops::Aggregate::LastDataRecv) + s.raindrops_aggregate = [] + c = Socket.new(:INET, :STREAM, 0) + c.connect(addr) + c.write '.' # for TCP_DEFER_ACCEPT + client, ai = s.accept_nonblock(exception: false) + assert client.kind_of?(Socket) + assert ai.kind_of?(Addrinfo) + assert_equal 1, s.raindrops_aggregate.size + assert s.raindrops_aggregate[0].instance_of?(Integer) + client, ai = s.accept_nonblock(exception: false) + assert_equal :wait_readable, client + assert_nil ai + assert_equal 1, s.raindrops_aggregate.size + assert_raise(IO::WaitReadable) { s.accept_nonblock } + end + + def test_accept_nonblock_one + s = TCPServer.new('127.0.0.1', 0) + s.extend(Raindrops::Aggregate::LastDataRecv) + s.raindrops_aggregate = [] + addr = s.addr + c = TCPSocket.new(addr[3], addr[1]) + c.write '.' # for TCP_DEFER_ACCEPT + client = s.accept_nonblock(exception: false) + assert client.kind_of?(TCPSocket) + assert_equal 1, s.raindrops_aggregate.size + assert s.raindrops_aggregate[0].instance_of?(Integer) + client = s.accept_nonblock(exception: false) + assert_equal :wait_readable, client + assert_equal 1, s.raindrops_aggregate.size + assert_raise(IO::WaitReadable) { s.accept_nonblock } + end +end if RUBY_PLATFORM =~ /linux/