1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2020 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// MARK: Universal Client Bootstrap
extension NIOClientTCPBootstrapProtocol {
/// Apply any understood convenience options to the bootstrap, removing them from the set of options if they are consumed.
/// - parameters:
/// - options: The options to try applying - the options applied should be consumed from here.
/// - returns: The updated bootstrap with and options applied.
public func _applyChannelConvenienceOptions(_ options: inout ChannelOptions.TCPConvenienceOptions) -> Self {
// Default is to consume no options and not update self.
return self
}
}
extension NIOClientTCPBootstrap {
/// Specifies some `TCPConvenienceOption`s to be applied to the channel.
/// These are preferred over regular channel options as they are easier to use and restrict
/// options to those which a normal user would consider.
/// - Parameter options: Set of convenience options to apply.
/// - Returns: The updated bootstrap (`self` being mutated)
public func channelConvenienceOptions(_ options: ChannelOptions.TCPConvenienceOptions) -> NIOClientTCPBootstrap {
var optionsRemaining = options
// First give the underlying a chance to consume options.
let withUnderlyingOverrides =
NIOClientTCPBootstrap(self,
updating: underlyingBootstrap._applyChannelConvenienceOptions(&optionsRemaining))
// Default apply any remaining options.
return optionsRemaining.applyFallbackMapping(withUnderlyingOverrides)
}
}
// MARK: Utility
extension ChannelOptions.Types {
/// Has an option been set?
/// Option has a value of generic type ValueType.
public enum ConvenienceOptionValue<ValueType> {
/// The option was not set.
case notSet
/// The option was set with a value of type ValueType.
case set(ValueType)
}
}
extension ChannelOptions.Types.ConvenienceOptionValue where ValueType == () {
/// Convenience method working with bool options as bool values for set.
public var isSet: Bool {
get {
switch self {
case .notSet:
return false
case .set(()):
return true
}
}
}
}
extension ChannelOptions.Types.ConvenienceOptionValue where ValueType == () {
fileprivate init(flag: Bool) {
if flag {
self = .set(())
} else {
self = .notSet
}
}
}
// MARK: TCP - data
extension ChannelOptions {
/// A TCP channel option which can be applied to a bootstrap using convenience notation.
public struct TCPConvenienceOption: Hashable {
fileprivate var data: ConvenienceOption
private init(_ data: ConvenienceOption) {
self.data = data
}
fileprivate enum ConvenienceOption: Hashable {
case allowLocalEndpointReuse
case disableAutoRead
case allowRemoteHalfClosure
}
}
}
/// Approved convenience options.
extension ChannelOptions.TCPConvenienceOption {
/// Allow immediately reusing a local address.
public static let allowLocalEndpointReuse = ChannelOptions.TCPConvenienceOption(.allowLocalEndpointReuse)
/// The user will manually call `Channel.read` once all the data is read from the transport.
public static let disableAutoRead = ChannelOptions.TCPConvenienceOption(.disableAutoRead)
/// Allows users to configure whether the `Channel` will close itself when its remote
/// peer shuts down its send stream, or whether it will remain open. If set to `false` (the default), the `Channel`
/// will be closed automatically if the remote peer shuts down its send stream. If set to true, the `Channel` will
/// not be closed: instead, a `ChannelEvent.inboundClosed` user event will be sent on the `ChannelPipeline`,
/// and no more data will be received.
public static let allowRemoteHalfClosure =
ChannelOptions.TCPConvenienceOption(.allowRemoteHalfClosure)
}
extension ChannelOptions {
/// A set of `TCPConvenienceOption`s
public struct TCPConvenienceOptions: ExpressibleByArrayLiteral, Hashable {
var allowLocalEndpointReuse = false
var disableAutoRead = false
var allowRemoteHalfClosure = false
/// Construct from an array literal.
@inlinable
public init(arrayLiteral elements: TCPConvenienceOption...) {
for element in elements {
self.add(element)
}
}
@usableFromInline
mutating func add(_ element: TCPConvenienceOption) {
switch element.data {
case .allowLocalEndpointReuse:
self.allowLocalEndpointReuse = true
case .allowRemoteHalfClosure:
self.allowRemoteHalfClosure = true
case .disableAutoRead:
self.disableAutoRead = true
}
}
/// Caller is consuming the knowledge that `allowLocalEndpointReuse` was set or not.
/// The setting will nolonger be set after this call.
/// - Returns: If `allowLocalEndpointReuse` was set.
public mutating func consumeAllowLocalEndpointReuse() -> Types.ConvenienceOptionValue<Void> {
defer {
self.allowLocalEndpointReuse = false
}
return Types.ConvenienceOptionValue<Void>(flag: self.allowLocalEndpointReuse)
}
/// Caller is consuming the knowledge that disableAutoRead was set or not.
/// The setting will nolonger be set after this call.
/// - Returns: If disableAutoRead was set.
public mutating func consumeDisableAutoRead() -> Types.ConvenienceOptionValue<Void> {
defer {
self.disableAutoRead = false
}
return Types.ConvenienceOptionValue<Void>(flag: self.disableAutoRead)
}
/// Caller is consuming the knowledge that allowRemoteHalfClosure was set or not.
/// The setting will nolonger be set after this call.
/// - Returns: If allowRemoteHalfClosure was set.
public mutating func consumeAllowRemoteHalfClosure() -> Types.ConvenienceOptionValue<Void> {
defer {
self.allowRemoteHalfClosure = false
}
return Types.ConvenienceOptionValue<Void>(flag: self.allowRemoteHalfClosure)
}
mutating func applyFallbackMapping(_ universalBootstrap: NIOClientTCPBootstrap) -> NIOClientTCPBootstrap {
var result = universalBootstrap
if self.consumeAllowLocalEndpointReuse().isSet {
result = result.channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
}
if self.consumeAllowRemoteHalfClosure().isSet {
result = result.channelOption(ChannelOptions.allowRemoteHalfClosure, value: true)
}
if self.consumeDisableAutoRead().isSet {
result = result.channelOption(ChannelOptions.autoRead, value: false)
}
return result
}
}
}
|