diff --git a/IntegrationTests/tests_04_performance/test_01_resources/shared.swift b/IntegrationTests/tests_04_performance/test_01_resources/shared.swift index 8d759327bf..1bda8323a2 100644 --- a/IntegrationTests/tests_04_performance/test_01_resources/shared.swift +++ b/IntegrationTests/tests_04_performance/test_01_resources/shared.swift @@ -99,7 +99,6 @@ private final class SimpleHTTPServer: ChannelInboundHandler { func doRequests(group: EventLoopGroup, number numberOfRequests: Int) throws -> Int { let serverChannel = try ServerBootstrap(group: group) .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) - .childChannelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) .childChannelInitializer { channel in channel.pipeline.configureHTTPServerPipeline(withPipeliningAssistance: true, withErrorHandling: false).flatMap { @@ -120,7 +119,6 @@ func doRequests(group: EventLoopGroup, number numberOfRequests: Int) throws -> I channel.pipeline.addHandler(repeatedRequestsHandler) } } - .channelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) .connect(to: serverChannel.localAddress!) .wait() diff --git a/IntegrationTests/tests_04_performance/test_01_resources/test_ping_pong_1000_reqs_1_conn.swift b/IntegrationTests/tests_04_performance/test_01_resources/test_ping_pong_1000_reqs_1_conn.swift index f829a5f0bf..f52d07651c 100644 --- a/IntegrationTests/tests_04_performance/test_01_resources/test_ping_pong_1000_reqs_1_conn.swift +++ b/IntegrationTests/tests_04_performance/test_01_resources/test_ping_pong_1000_reqs_1_conn.swift @@ -112,7 +112,6 @@ private final class PongHandler: ChannelInboundHandler { func doPingPongRequests(group: EventLoopGroup, number numberOfRequests: Int) throws -> Int { let serverChannel = try ServerBootstrap(group: group) .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) - .childChannelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) .childChannelOption(ChannelOptions.recvAllocator, value: FixedSizeRecvByteBufferAllocator(capacity: 4)) .childChannelInitializer { channel in channel.pipeline.addHandler(ByteToMessageHandler(PongDecoder())).flatMap { @@ -129,7 +128,6 @@ func doPingPongRequests(group: EventLoopGroup, number numberOfRequests: Int) thr .channelInitializer { channel in channel.pipeline.addHandler(pingHandler) } - .channelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) .channelOption(ChannelOptions.recvAllocator, value: FixedSizeRecvByteBufferAllocator(capacity: 4)) .connect(to: serverChannel.localAddress!) .wait() diff --git a/Sources/NIO/Bootstrap.swift b/Sources/NIO/Bootstrap.swift index 46fd6aa0d8..f4fc315b1a 100644 --- a/Sources/NIO/Bootstrap.swift +++ b/Sources/NIO/Bootstrap.swift @@ -36,8 +36,7 @@ /// } /// } /// -/// // Enable TCP_NODELAY and SO_REUSEADDR for the accepted Channels -/// .childChannelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) +/// // Enable SO_REUSEADDR for the accepted Channels /// .childChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) /// .childChannelOption(ChannelOptions.maxMessagesPerRead, value: 16) /// .childChannelOption(ChannelOptions.recvAllocator, value: AdaptiveRecvByteBufferAllocator()) @@ -80,6 +79,7 @@ public final class ServerBootstrap { public init(group: EventLoopGroup, childGroup: EventLoopGroup) { self.group = group self.childGroup = childGroup + self._serverChannelOptions.append(key: ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) } /// Initialize the `ServerSocketChannel` with `initializer`. The most common task in initializer is to add @@ -363,6 +363,7 @@ public final class ClientBootstrap { /// - group: The `EventLoopGroup` to use. public init(group: EventLoopGroup) { self.group = group + self._channelOptions.append(key: ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) } /// Initialize the connected `SocketChannel` with `initializer`. The most common task in initializer is to add diff --git a/Sources/NIO/ChannelOption.swift b/Sources/NIO/ChannelOption.swift index 8496a22d44..9008a47cc3 100644 --- a/Sources/NIO/ChannelOption.swift +++ b/Sources/NIO/ChannelOption.swift @@ -227,7 +227,9 @@ extension ChannelOptions { @usableFromInline internal var _storage: [(Any, (Any, (Channel) -> (Any, Any) -> EventLoopFuture))] = [] - public init() { } + public init() { + self._storage.reserveCapacity(2) + } /// Add `Options`, a `ChannelOption` to the `ChannelOptions.Storage`. /// diff --git a/Sources/NIOChatServer/main.swift b/Sources/NIOChatServer/main.swift index 96225be406..9924723fa4 100644 --- a/Sources/NIOChatServer/main.swift +++ b/Sources/NIOChatServer/main.swift @@ -132,8 +132,7 @@ let bootstrap = ServerBootstrap(group: group) } } - // Enable TCP_NODELAY and SO_REUSEADDR for the accepted Channels - .childChannelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) + // Enable SO_REUSEADDR for the accepted Channels .childChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) .childChannelOption(ChannelOptions.maxMessagesPerRead, value: 16) .childChannelOption(ChannelOptions.recvAllocator, value: AdaptiveRecvByteBufferAllocator()) diff --git a/Sources/NIOEchoServer/main.swift b/Sources/NIOEchoServer/main.swift index 8b6397996f..5cd328f101 100644 --- a/Sources/NIOEchoServer/main.swift +++ b/Sources/NIOEchoServer/main.swift @@ -50,8 +50,7 @@ let bootstrap = ServerBootstrap(group: group) } } - // Enable TCP_NODELAY and SO_REUSEADDR for the accepted Channels - .childChannelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) + // Enable SO_REUSEADDR for the accepted Channels .childChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) .childChannelOption(ChannelOptions.maxMessagesPerRead, value: 16) .childChannelOption(ChannelOptions.recvAllocator, value: AdaptiveRecvByteBufferAllocator()) diff --git a/Sources/NIOHTTP1/HTTPPipelineSetup.swift b/Sources/NIOHTTP1/HTTPPipelineSetup.swift index a2bf5598c3..b006eca2dc 100644 --- a/Sources/NIOHTTP1/HTTPPipelineSetup.swift +++ b/Sources/NIOHTTP1/HTTPPipelineSetup.swift @@ -122,4 +122,3 @@ extension ChannelPipeline { return self.addHandlers(handlers, position: position) } } - diff --git a/Sources/NIOHTTP1Server/main.swift b/Sources/NIOHTTP1Server/main.swift index 3a577b504c..1929592738 100644 --- a/Sources/NIOHTTP1Server/main.swift +++ b/Sources/NIOHTTP1Server/main.swift @@ -525,8 +525,7 @@ let bootstrap = ServerBootstrap(group: group) } } - // Enable TCP_NODELAY and SO_REUSEADDR for the accepted Channels - .childChannelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) + // Enable SO_REUSEADDR for the accepted Channels .childChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) .childChannelOption(ChannelOptions.maxMessagesPerRead, value: 1) .childChannelOption(ChannelOptions.allowRemoteHalfClosure, value: allowHalfClosure) diff --git a/Sources/NIOPerformanceTester/main.swift b/Sources/NIOPerformanceTester/main.swift index 67f3426599..4d14abd837 100644 --- a/Sources/NIOPerformanceTester/main.swift +++ b/Sources/NIOPerformanceTester/main.swift @@ -124,7 +124,6 @@ defer { let serverChannel = try ServerBootstrap(group: group) .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) - .childChannelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) .childChannelInitializer { channel in channel.pipeline.configureHTTPServerPipeline(withPipeliningAssistance: true).flatMap { channel.pipeline.addHandler(SimpleHTTPServer()) @@ -578,7 +577,6 @@ measureAndPrint(desc: "http1_10k_reqs_1_conn") { let repeatedRequestsHandler = RepeatedRequests(numberOfRequests: 10_000, eventLoop: group.next()) let clientChannel = try! ClientBootstrap(group: group) - .channelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) .channelInitializer { channel in channel.pipeline.addHTTPClientHandlers().flatMap { channel.pipeline.addHandler(repeatedRequestsHandler) diff --git a/Sources/NIOWebSocketServer/main.swift b/Sources/NIOWebSocketServer/main.swift index 599a7ade82..b1ac277f03 100644 --- a/Sources/NIOWebSocketServer/main.swift +++ b/Sources/NIOWebSocketServer/main.swift @@ -226,8 +226,7 @@ let bootstrap = ServerBootstrap(group: group) } } - // Enable TCP_NODELAY and SO_REUSEADDR for the accepted Channels - .childChannelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) + // Enable SO_REUSEADDR for the accepted Channels .childChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) defer { diff --git a/Tests/NIOTests/ChannelTests+XCTest.swift b/Tests/NIOTests/ChannelTests+XCTest.swift index c784de4469..3fa3a9bfa5 100644 --- a/Tests/NIOTests/ChannelTests+XCTest.swift +++ b/Tests/NIOTests/ChannelTests+XCTest.swift @@ -78,6 +78,7 @@ extension ChannelTests { ("testApplyingTwoDistinctSocketOptionsOfSameTypeWorks", testApplyingTwoDistinctSocketOptionsOfSameTypeWorks), ("testUnprocessedOutboundUserEventFailsOnServerSocketChannel", testUnprocessedOutboundUserEventFailsOnServerSocketChannel), ("testAcceptHandlerDoesNotSwallowCloseErrorsWhenQuiescing", testAcceptHandlerDoesNotSwallowCloseErrorsWhenQuiescing), + ("testTCP_NODELAYisOnByDefault", testTCP_NODELAYisOnByDefault), ] } } diff --git a/Tests/NIOTests/ChannelTests.swift b/Tests/NIOTests/ChannelTests.swift index b2105a020e..8a4c99cd97 100644 --- a/Tests/NIOTests/ChannelTests.swift +++ b/Tests/NIOTests/ChannelTests.swift @@ -2592,7 +2592,7 @@ public final class ChannelTests: XCTestCase { .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_TIMESTAMP), value: 1) .childChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_KEEPALIVE), value: 1) - .childChannelOption(ChannelOptions.socket(SocketOptionLevel(IPPROTO_TCP), TCP_NODELAY), value: 1) + .childChannelOption(ChannelOptions.socket(SocketOptionLevel(IPPROTO_TCP), TCP_NODELAY), value: 0) .childChannelInitializer { channel in acceptedChannels[numberOfAcceptedChannel].succeed(channel) numberOfAcceptedChannel += 1 @@ -2608,7 +2608,7 @@ public final class ChannelTests: XCTestCase { let client1 = try assertNoThrowWithValue(ClientBootstrap(group: singleThreadedELG) .channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) - .channelOption(ChannelOptions.socket(SocketOptionLevel(IPPROTO_TCP), TCP_NODELAY), value: 1) + .channelOption(ChannelOptions.socket(SocketOptionLevel(IPPROTO_TCP), TCP_NODELAY), value: 0) .connect(to: server.localAddress!) .wait()) let accepted1 = try assertNoThrowWithValue(acceptedChannels[0].futureResult.wait()) @@ -2633,27 +2633,27 @@ public final class ChannelTests: XCTestCase { XCTAssertTrue(try getBoolSocketOption(channel: client1, level: SOL_SOCKET, name: SO_REUSEADDR)) - XCTAssertTrue(try getBoolSocketOption(channel: client1, level: IPPROTO_TCP, name: TCP_NODELAY)) + XCTAssertFalse(try getBoolSocketOption(channel: client1, level: IPPROTO_TCP, name: TCP_NODELAY)) XCTAssertTrue(try getBoolSocketOption(channel: accepted1, level: SOL_SOCKET, name: SO_KEEPALIVE)) - XCTAssertTrue(try getBoolSocketOption(channel: accepted1, level: IPPROTO_TCP, name: TCP_NODELAY)) + XCTAssertFalse(try getBoolSocketOption(channel: accepted1, level: IPPROTO_TCP, name: TCP_NODELAY)) XCTAssertTrue(try getBoolSocketOption(channel: client2, level: SOL_SOCKET, name: SO_REUSEADDR)) - XCTAssertFalse(try getBoolSocketOption(channel: client2, level: IPPROTO_TCP, name: TCP_NODELAY)) + XCTAssertTrue(try getBoolSocketOption(channel: client2, level: IPPROTO_TCP, name: TCP_NODELAY)) XCTAssertTrue(try getBoolSocketOption(channel: accepted2, level: SOL_SOCKET, name: SO_KEEPALIVE)) - XCTAssertTrue(try getBoolSocketOption(channel: accepted2, level: IPPROTO_TCP, name: TCP_NODELAY)) + XCTAssertFalse(try getBoolSocketOption(channel: accepted2, level: IPPROTO_TCP, name: TCP_NODELAY)) XCTAssertFalse(try getBoolSocketOption(channel: client3, level: SOL_SOCKET, name: SO_REUSEADDR)) - XCTAssertFalse(try getBoolSocketOption(channel: client3, level: IPPROTO_TCP, name: TCP_NODELAY)) + XCTAssertTrue(try getBoolSocketOption(channel: client3, level: IPPROTO_TCP, name: TCP_NODELAY)) XCTAssertTrue(try getBoolSocketOption(channel: accepted3, level: SOL_SOCKET, name: SO_KEEPALIVE)) - XCTAssertTrue(try getBoolSocketOption(channel: accepted3, level: IPPROTO_TCP, name: TCP_NODELAY)) + XCTAssertFalse(try getBoolSocketOption(channel: accepted3, level: IPPROTO_TCP, name: TCP_NODELAY)) } func testUnprocessedOutboundUserEventFailsOnServerSocketChannel() throws { @@ -2714,6 +2714,42 @@ public final class ChannelTests: XCTestCase { XCTAssertEqual(["userInboundEventTriggered", "close", "errorCaught"], counter.allTriggeredEvents()) XCTAssertEqual(1, counter.errorCaughtCalls) } + + func testTCP_NODELAYisOnByDefault() throws { + let singleThreadedELG = MultiThreadedEventLoopGroup(numberOfThreads: 1) + defer { + XCTAssertNoThrow(try singleThreadedELG.syncShutdownGracefully()) + } + var numberOfAcceptedChannel = 0 + var acceptedChannel = singleThreadedELG.next().makePromise(of: Channel.self) + let server = try assertNoThrowWithValue(ServerBootstrap(group: singleThreadedELG) + .childChannelInitializer { channel in + acceptedChannel.succeed(channel) + return channel.eventLoop.makeSucceededFuture(()) + } + .bind(host: "127.0.0.1", port: 0) + .wait()) + defer { + XCTAssertNoThrow(try server.close().wait()) + } + XCTAssertNoThrow(XCTAssertTrue(try getBoolSocketOption(channel: server, + level: IPPROTO_TCP, + name: TCP_NODELAY))) + + let client = try assertNoThrowWithValue(ClientBootstrap(group: singleThreadedELG) + .connect(to: server.localAddress!) + .wait()) + let accepted = try assertNoThrowWithValue(acceptedChannel.futureResult.wait()) + defer { + XCTAssertNoThrow(try client.close().wait()) + } + XCTAssertNoThrow(XCTAssertTrue(try getBoolSocketOption(channel: accepted, + level: IPPROTO_TCP, + name: TCP_NODELAY))) + XCTAssertNoThrow(XCTAssertTrue(try getBoolSocketOption(channel: client, + level: IPPROTO_TCP, + name: TCP_NODELAY))) + } } fileprivate final class FailRegistrationAndDelayCloseHandler: ChannelOutboundHandler { diff --git a/Tests/NIOTests/EchoServerClientTest.swift b/Tests/NIOTests/EchoServerClientTest.swift index 3e7b0484a6..57bf9216af 100644 --- a/Tests/NIOTests/EchoServerClientTest.swift +++ b/Tests/NIOTests/EchoServerClientTest.swift @@ -636,7 +636,6 @@ class EchoServerClientTest : XCTestCase { } let serverChannel = try assertNoThrowWithValue(ServerBootstrap(group: group) .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) - .childChannelOption(ChannelOptions.socket(SocketOptionLevel(IPPROTO_TCP), TCP_NODELAY), value: 1) .childChannelInitializer { channel in channel.pipeline.addHandler(WriteWhenActiveHandler(str, dpGroup)) }.bind(host: "127.0.0.1", port: 0).wait()) diff --git a/docker/docker-compose.1604.51.yaml b/docker/docker-compose.1604.51.yaml index f15005728b..a531cce556 100644 --- a/docker/docker-compose.1604.51.yaml +++ b/docker/docker-compose.1604.51.yaml @@ -22,7 +22,7 @@ services: image: swift-nio:16.04-5.1 environment: - MAX_ALLOCS_ALLOWED_1000_reqs_1_conn=30600 - - MAX_ALLOCS_ALLOWED_1_reqs_1000_conn=584100 + - MAX_ALLOCS_ALLOWED_1_reqs_1000_conn=592100 - MAX_ALLOCS_ALLOWED_ping_pong_1000_reqs_1_conn=4600 - MAX_ALLOCS_ALLOWED_bytebuffer_lots_of_rw=2100 - MAX_ALLOCS_ALLOWED_future_lots_of_callbacks=99100 diff --git a/docker/docker-compose.1804.50.yaml b/docker/docker-compose.1804.50.yaml index 717953d66b..f067290dad 100644 --- a/docker/docker-compose.1804.50.yaml +++ b/docker/docker-compose.1804.50.yaml @@ -20,7 +20,7 @@ services: image: swift-nio:18.04-5.0 environment: - MAX_ALLOCS_ALLOWED_1000_reqs_1_conn=31200 - - MAX_ALLOCS_ALLOWED_1_reqs_1000_conn=1054050 + - MAX_ALLOCS_ALLOWED_1_reqs_1000_conn=1062050 - MAX_ALLOCS_ALLOWED_ping_pong_1000_reqs_1_conn=4600 - MAX_ALLOCS_ALLOWED_bytebuffer_lots_of_rw=2100 - MAX_ALLOCS_ALLOWED_future_lots_of_callbacks=99100