Skip to content

Commit

Permalink
ChannelOption: Allow types to be accessed with leading dot syntax (#2816
Browse files Browse the repository at this point in the history
)

Motivation:

Since Swift 5.5 and [SE-0299](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0299-extend-generic-static-member-lookup.md) it is possible to add static members to protocols which are discoverable through the shorthand dot syntax.
This change reduces type repetition and improves call-site legibility.

Modifications:

Added extensions for ChannelOption with static members where Self is bound to a concrete type.

Result:

ChannelOption types can be used with the leading dot syntax. For eg:

```
//before
.channelOption(ChannelOptions.explicitCongestionNotification, value: true)
.channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)

//after
.channelOption(.explicitCongestionNotification, value: true)
.channelOption(.socketOption(.so_reuseaddr), value: 1)
```
  • Loading branch information
ayush1794 authored Jul 30, 2024
1 parent 0d2f721 commit 7840b6b
Show file tree
Hide file tree
Showing 52 changed files with 407 additions and 312 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ private final class SimpleHTTPServer: ChannelInboundHandler {

func doRequests(group: EventLoopGroup, number numberOfRequests: Int) throws -> Int {
let serverChannel = try ServerBootstrap(group: group)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.serverChannelOption(.socketOption(.so_reuseaddr), value: 1)
.childChannelInitializer { channel in
channel.pipeline.configureHTTPServerPipeline(
withPipeliningAssistance: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ func run(identifier: String) {
let iterations = 1000

for _ in 0..<iterations {
let autoReadOption = try! server.getOption(ChannelOptions.autoRead).wait()
try! server.setOption(ChannelOptions.autoRead, value: !autoReadOption).wait()
let autoReadOption = try! server.getOption(.autoRead).wait()
try! server.setOption(.autoRead, value: !autoReadOption).wait()
}

return iterations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ func run(identifier: String) {
let syncOptions = server.syncOptions!

for _ in 0..<iterations {
let autoReadOption = try! syncOptions.getOption(ChannelOptions.autoRead)
try! syncOptions.setOption(ChannelOptions.autoRead, value: !autoReadOption)
let autoReadOption = try! syncOptions.getOption(.autoRead)
try! syncOptions.setOption(.autoRead, value: !autoReadOption)
}

return iterations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func run(identifier: String) {
}

let serverChannel = try! ServerBootstrap(group: group)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.serverChannelOption(.socketOption(.so_reuseaddr), value: 1)
.childChannelInitializer { channel in
channel.pipeline.addHandler(ReceiveAndCloseHandler())
}.bind(host: "127.0.0.1", port: 0).wait()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ private final class ClientHandler: ChannelInboundHandler {

func run(identifier: String) {
let serverChannel = try! DatagramBootstrap(group: group)
.channelOption(ChannelOptions.explicitCongestionNotification, value: true)
.channelOption(.explicitCongestionNotification, value: true)
// Set the handlers that are applied to the bound channel
.channelInitializer { channel in
channel.pipeline.addHandler(ServerEchoHandler())
Expand All @@ -106,7 +106,7 @@ func run(identifier: String) {
let clientHandler = ClientHandler(remoteAddress: remoteAddress)

let clientChannel = try! DatagramBootstrap(group: group)
.channelOption(ChannelOptions.explicitCongestionNotification, value: true)
.channelOption(.explicitCongestionNotification, value: true)
.channelInitializer { channel in
channel.pipeline.addHandler(clientHandler)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ private final class PongHandler: ChannelInboundHandler {

func doPingPongRequests(group: EventLoopGroup, number numberOfRequests: Int) throws -> Int {
let serverChannel = try ServerBootstrap(group: group)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.childChannelOption(ChannelOptions.recvAllocator, value: FixedSizeRecvByteBufferAllocator(capacity: 4))
.serverChannelOption(.socketOption(.so_reuseaddr), value: 1)
.childChannelOption(.recvAllocator, value: FixedSizeRecvByteBufferAllocator(capacity: 4))
.childChannelInitializer { channel in
channel.pipeline.addHandler(ByteToMessageHandler(PongDecoder())).flatMap {
channel.pipeline.addHandler(PongHandler())
Expand All @@ -137,7 +137,7 @@ func doPingPongRequests(group: EventLoopGroup, number numberOfRequests: Int) thr
.channelInitializer { channel in
channel.pipeline.addHandler(pingHandler)
}
.channelOption(ChannelOptions.recvAllocator, value: FixedSizeRecvByteBufferAllocator(capacity: 4))
.channelOption(.recvAllocator, value: FixedSizeRecvByteBufferAllocator(capacity: 4))
.connect(to: serverChannel.localAddress!)
.wait()

Expand Down
2 changes: 1 addition & 1 deletion Sources/NIOChatClient/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ private final class ChatHandler: ChannelInboundHandler {
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let bootstrap = ClientBootstrap(group: group)
// Enable SO_REUSEADDR.
.channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.channelOption(.socketOption(.so_reuseaddr), value: 1)
.channelInitializer { channel in
channel.pipeline.addHandler(ChatHandler())
}
Expand Down
10 changes: 5 additions & 5 deletions Sources/NIOChatServer/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ let chatHandler = ChatHandler()
let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
let bootstrap = ServerBootstrap(group: group)
// Specify backlog and enable SO_REUSEADDR for the server itself
.serverChannelOption(ChannelOptions.backlog, value: 256)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.serverChannelOption(.backlog, value: 256)
.serverChannelOption(.socketOption(.so_reuseaddr), value: 1)

// Set the handlers that are applied to the accepted Channels
.childChannelInitializer { channel in
Expand All @@ -143,9 +143,9 @@ let bootstrap = ServerBootstrap(group: group)
}

// Enable SO_REUSEADDR for the accepted Channels
.childChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.childChannelOption(ChannelOptions.maxMessagesPerRead, value: 16)
.childChannelOption(ChannelOptions.recvAllocator, value: AdaptiveRecvByteBufferAllocator())
.childChannelOption(.socketOption(.so_reuseaddr), value: 1)
.childChannelOption(.maxMessagesPerRead, value: 16)
.childChannelOption(.recvAllocator, value: AdaptiveRecvByteBufferAllocator())
defer {
try! group.syncShutdownGracefully()
}
Expand Down
91 changes: 91 additions & 0 deletions Sources/NIOCore/ChannelOption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,97 @@ public struct ChannelOptions: Sendable {
public static let receivePacketInfo = Types.ReceivePacketInfo()
}

/// - seealso: `SocketOption`.
extension ChannelOption where Self == ChannelOptions.Types.SocketOption {
#if !(os(Windows))
public static func socket(_ level: SocketOptionLevel, _ name: SocketOptionName) -> Self {
.init(level: NIOBSDSocket.OptionLevel(rawValue: CInt(level)), name: NIOBSDSocket.Option(rawValue: CInt(name)))
}
#endif

public static func socketOption(_ name: NIOBSDSocket.Option) -> Self {
.init(level: .socket, name: name)
}

public static func ipOption(_ name: NIOBSDSocket.Option) -> Self {
.init(level: .ip, name: name)
}

public static func tcpOption(_ name: NIOBSDSocket.Option) -> Self {
.init(level: .tcp, name: name)
}
}

/// - seealso: `AllocatorOption`.
extension ChannelOption where Self == ChannelOptions.Types.AllocatorOption {
public static var allocator: Self { .init() }
}

/// - seealso: `RecvAllocatorOption`.
extension ChannelOption where Self == ChannelOptions.Types.RecvAllocatorOption {
public static var recvAllocator: Self { .init() }
}

/// - seealso: `AutoReadOption`.
extension ChannelOption where Self == ChannelOptions.Types.AutoReadOption {
public static var autoRead: Self { .init() }
}

/// - seealso: `MaxMessagesPerReadOption`.
extension ChannelOption where Self == ChannelOptions.Types.MaxMessagesPerReadOption {
public static var maxMessagesPerRead: Self { .init() }
}

/// - seealso: `BacklogOption`.
extension ChannelOption where Self == ChannelOptions.Types.BacklogOption {
public static var backlog: Self { .init() }
}

/// - seealso: `WriteSpinOption`.
extension ChannelOption where Self == ChannelOptions.Types.WriteSpinOption {
public static var writeSpin: Self { .init() }
}

/// - seealso: `WriteBufferWaterMarkOption`.
extension ChannelOption where Self == ChannelOptions.Types.WriteBufferWaterMarkOption {
public static var writeBufferWaterMark: Self { .init() }
}

/// - seealso: `ConnectTimeoutOption`.
extension ChannelOption where Self == ChannelOptions.Types.ConnectTimeoutOption {
public static var connectTimeout: Self { .init() }
}

/// - seealso: `AllowRemoteHalfClosureOption`.
extension ChannelOption where Self == ChannelOptions.Types.AllowRemoteHalfClosureOption {
public static var allowRemoteHalfClosure: Self { .init() }
}

/// - seealso: `DatagramVectorReadMessageCountOption`.
extension ChannelOption where Self == ChannelOptions.Types.DatagramVectorReadMessageCountOption {
public static var datagramVectorReadMessageCount: Self { .init() }
}

/// - seealso: `DatagramSegmentSize`.
extension ChannelOption where Self == ChannelOptions.Types.DatagramSegmentSize {
public static var datagramSegmentSize: Self { .init() }
}

/// - seealso: `DatagramReceiveOffload`.
extension ChannelOption where Self == ChannelOptions.Types.DatagramReceiveOffload {
public static var datagramReceiveOffload: Self { .init() }
}

/// - seealso: `ExplicitCongestionNotificationsOption`.
extension ChannelOption where Self == ChannelOptions.Types.ExplicitCongestionNotificationsOption {
public static var explicitCongestionNotification: Self { .init() }
}

/// - seealso: `ReceivePacketInfo`.
extension ChannelOption where Self == ChannelOptions.Types.ReceivePacketInfo {
public static var receivePacketInfo: Self { .init() }
}

extension ChannelOptions {
/// A type-safe storage facility for `ChannelOption`s. You will only ever need this if you implement your own
/// `Channel` that needs to store `ChannelOption`s.
Expand Down
6 changes: 3 additions & 3 deletions Sources/NIOCore/ConvenienceOptionSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,13 @@ extension ChannelOptions {
mutating func applyFallbackMapping(_ universalBootstrap: NIOClientTCPBootstrap) -> NIOClientTCPBootstrap {
var result = universalBootstrap
if self.consumeAllowLocalEndpointReuse().isSet {
result = result.channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
result = result.channelOption(.socketOption(.so_reuseaddr), value: 1)
}
if self.consumeAllowRemoteHalfClosure().isSet {
result = result.channelOption(ChannelOptions.allowRemoteHalfClosure, value: true)
result = result.channelOption(.allowRemoteHalfClosure, value: true)
}
if self.consumeDisableAutoRead().isSet {
result = result.channelOption(ChannelOptions.autoRead, value: false)
result = result.channelOption(.autoRead, value: false)
}
return result
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/NIOCrashTester/OutputGrepper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ internal struct OutputGrepper {

// We gotta `dup` everything because Pipe is bad and closes file descriptors on `deinit` :(
let channelFuture = NIOPipeBootstrap(group: group)
.channelOption(ChannelOptions.allowRemoteHalfClosure, value: true)
.channelOption(.allowRemoteHalfClosure, value: true)
.channelInitializer { channel in
channel.eventLoop.makeCompletedFuture {
try channel.pipeline.syncOperations.addHandlers([
Expand Down
2 changes: 1 addition & 1 deletion Sources/NIOEchoClient/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ private final class EchoHandler: ChannelInboundHandler {
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let bootstrap = ClientBootstrap(group: group)
// Enable SO_REUSEADDR.
.channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.channelOption(.socketOption(.so_reuseaddr), value: 1)
.channelInitializer { channel in
channel.pipeline.addHandler(EchoHandler())
}
Expand Down
10 changes: 5 additions & 5 deletions Sources/NIOEchoServer/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ private final class EchoHandler: ChannelInboundHandler {
let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
let bootstrap = ServerBootstrap(group: group)
// Specify backlog and enable SO_REUSEADDR for the server itself
.serverChannelOption(ChannelOptions.backlog, value: 256)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.serverChannelOption(.backlog, value: 256)
.serverChannelOption(.socketOption(.so_reuseaddr), value: 1)

// Set the handlers that are appled to the accepted Channels
.childChannelInitializer { channel in
Expand All @@ -53,9 +53,9 @@ let bootstrap = ServerBootstrap(group: group)
}

// Enable SO_REUSEADDR for the accepted Channels
.childChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.childChannelOption(ChannelOptions.maxMessagesPerRead, value: 16)
.childChannelOption(ChannelOptions.recvAllocator, value: AdaptiveRecvByteBufferAllocator())
.childChannelOption(.socketOption(.so_reuseaddr), value: 1)
.childChannelOption(.maxMessagesPerRead, value: 16)
.childChannelOption(.recvAllocator, value: AdaptiveRecvByteBufferAllocator())
defer {
try! group.syncShutdownGracefully()
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/NIOHTTP1Client/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ private final class HTTPEchoHandler: ChannelInboundHandler {
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let bootstrap = ClientBootstrap(group: group)
// Enable SO_REUSEADDR.
.channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.channelOption(.socketOption(.so_reuseaddr), value: 1)
.channelInitializer { channel in
channel.pipeline.addHTTPClientHandlers(
position: .first,
Expand Down
14 changes: 7 additions & 7 deletions Sources/NIOHTTP1Server/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -604,22 +604,22 @@ func childChannelInitializer(channel: Channel) -> EventLoopFuture<Void> {
let fileIO = NonBlockingFileIO(threadPool: .singleton)
let socketBootstrap = ServerBootstrap(group: MultiThreadedEventLoopGroup.singleton)
// Specify backlog and enable SO_REUSEADDR for the server itself
.serverChannelOption(ChannelOptions.backlog, value: 256)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.serverChannelOption(.backlog, value: 256)
.serverChannelOption(.socketOption(.so_reuseaddr), value: 1)

// Set the handlers that are applied to the accepted Channels
.childChannelInitializer(childChannelInitializer(channel:))

// Enable SO_REUSEADDR for the accepted Channels
.childChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.childChannelOption(ChannelOptions.maxMessagesPerRead, value: 1)
.childChannelOption(ChannelOptions.allowRemoteHalfClosure, value: allowHalfClosure)
.childChannelOption(.socketOption(.so_reuseaddr), value: 1)
.childChannelOption(.maxMessagesPerRead, value: 1)
.childChannelOption(.allowRemoteHalfClosure, value: allowHalfClosure)
let pipeBootstrap = NIOPipeBootstrap(group: MultiThreadedEventLoopGroup.singleton)
// Set the handlers that are applied to the accepted Channels
.channelInitializer(childChannelInitializer(channel:))

.channelOption(ChannelOptions.maxMessagesPerRead, value: 1)
.channelOption(ChannelOptions.allowRemoteHalfClosure, value: allowHalfClosure)
.channelOption(.maxMessagesPerRead, value: 1)
.channelOption(.allowRemoteHalfClosure, value: allowHalfClosure)
print("htdocs = \(htdocs)")

let channel = try { () -> Channel in
Expand Down
2 changes: 1 addition & 1 deletion Sources/NIOMulticastChat/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)

// Begin by setting up the basics of the bootstrap.
var datagramBootstrap = DatagramBootstrap(group: group)
.channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.channelOption(.socketOption(.so_reuseaddr), value: 1)
.channelInitializer { channel in
channel.pipeline.addHandler(ChatMessageEncoder()).flatMap {
channel.pipeline.addHandler(ChatMessageDecoder())
Expand Down
4 changes: 2 additions & 2 deletions Sources/NIOPerformanceTester/UDPBenchmark.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ extension UDPBenchmark: Benchmark {
let address = try SocketAddress.makeAddressResolvingHost("127.0.0.1", port: 0)
self.server = try DatagramBootstrap(group: group)
// zero is the same as not applying the option.
.channelOption(ChannelOptions.datagramVectorReadMessageCount, value: self.vectorReads)
.channelOption(.datagramVectorReadMessageCount, value: self.vectorReads)
.channelInitializer { channel in
channel.pipeline.addHandler(EchoHandler())
}
Expand All @@ -56,7 +56,7 @@ extension UDPBenchmark: Benchmark {

self.client = try DatagramBootstrap(group: group)
// zero is the same as not applying the option.
.channelOption(ChannelOptions.datagramVectorReadMessageCount, value: self.vectorReads)
.channelOption(.datagramVectorReadMessageCount, value: self.vectorReads)
.channelInitializer { channel in
let handler = EchoHandlerClient(
eventLoop: channel.eventLoop,
Expand Down
2 changes: 1 addition & 1 deletion Sources/NIOPerformanceTester/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ defer {
}

let serverChannel = try ServerBootstrap(group: group)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.serverChannelOption(.socketOption(.so_reuseaddr), value: 1)
.childChannelInitializer { channel in
channel.pipeline.configureHTTPServerPipeline(withPipeliningAssistance: true).flatMap {
channel.pipeline.addHandler(SimpleHTTPServer())
Expand Down
2 changes: 1 addition & 1 deletion Sources/NIOPosix/BaseSocketChannel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1140,7 +1140,7 @@ class BaseSocketChannel<SocketType: BaseSocketProtocol>: SelectableChannel, Chan
//
// getOption0 can only fail if the channel is not active anymore but we assert further up that it is. If
// that's not the case this is a precondition failure and we would like to know.
let allowRemoteHalfClosure = try! self.getOption0(ChannelOptions.allowRemoteHalfClosure)
let allowRemoteHalfClosure = try! self.getOption0(.allowRemoteHalfClosure)

// For EOF, we always fire read complete.
self.pipeline.syncOperations.fireChannelReadComplete()
Expand Down
Loading

0 comments on commit 7840b6b

Please sign in to comment.