blob: 9880f8b8577e3cbe6049685d84f4a29e7f08f2df [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
/// An NWParameters object contains the parameters necessary to create
/// a network connection or listener. NWParameters include any preferences for
/// network paths (such as required, prohibited, and preferred networks, and local
/// endpoint requirements); preferences for data transfer and quality of service;
/// and the protocols to be used for a connection along with any protocol-specific
/// options.
@available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *)
public final class NWParameters : CustomDebugStringConvertible {
public var debugDescription: String {
return String("\(self.nw)")
}
internal let nw : nw_parameters_t
/// Creates a parameters object that is configured for TLS and TCP. The caller can use
/// the default configuration for TLS and TCP, or set specific options for each protocol,
/// or disable TLS.
///
/// - Parameter tls: TLS options or nil for no TLS
/// - Parameter tcp: TCP options. Defaults to NWProtocolTCP.Options() with no options overridden.
/// - Returns: NWParameters object that can be used for creating a connection or listener
public convenience init(tls: NWProtocolTLS.Options?, tcp: NWProtocolTCP.Options = NWProtocolTCP.Options()) {
self.init()
let protocolStack = self.defaultProtocolStack
protocolStack.transportProtocol = tcp
if let tls = tls {
protocolStack.applicationProtocols = [tls]
} else {
protocolStack.applicationProtocols = []
}
}
/// Creates a parameters object that is configured for DTLS and UDP. The caller can use
/// the default configuration for DTLS and UDP, or set specific options for each protocol,
/// or disable TLS.
///
/// - Parameter dtls: DTLS options or nil for no DTLS
/// - Parameter udp: UDP options. Defaults to NWProtocolUDP.Options() with no options overridden.
/// - Returns: NWParameters object that can be used for create a connection or listener
public convenience init(dtls: NWProtocolTLS.Options?, udp: NWProtocolUDP.Options = NWProtocolUDP.Options()) {
self.init()
let protocolStack = self.defaultProtocolStack
protocolStack.transportProtocol = udp
if let dtls = dtls {
protocolStack.applicationProtocols = [dtls]
} else {
protocolStack.applicationProtocols = []
}
}
/// Creates a generic NWParameters object. Note that in order to use parameters
/// with a NWConnection or a NetworkListener, the parameters must have protocols
/// added into the defaultProtocolStack. Clients using standard protocol
/// configurations should use init(tls:tcp:) or init(dtls:udp:).
public init() {
self.nw = nw_parameters_create()
}
private init(nw: nw_parameters_t) {
self.nw = nw
}
/// Default set of parameters for TLS over TCP
/// This is equivalent to calling init(tls:NWProtocolTLS.Options(), tcp:NWProtocolTCP.Options())
public class var tls: NWParameters {
return NWParameters(tls:NWProtocolTLS.Options())
}
/// Default set of parameters for DTLS over UDP
/// This is equivalent to calling init(dtls:NWProtocolTLS.Options(), udp:NWProtocolUDP.Options())
public class var dtls: NWParameters {
return NWParameters(dtls:NWProtocolTLS.Options())
}
/// Default set of parameters for TCP
/// This is equivalent to calling init(tls:nil, tcp:NWProtocolTCP.Options())
public class var tcp: NWParameters {
return NWParameters(tls:nil)
}
/// Default set of parameters for UDP
/// This is equivalent to calling init(dtls:nil, udp:NWProtocolUDP.Options())
public class var udp: NWParameters {
return NWParameters(dtls:nil)
}
/// Require a connection to use a specific interface, or fail if not available
public var requiredInterface: NWInterface? {
set {
if let interface = newValue {
nw_parameters_require_interface(self.nw, interface.nw)
} else {
nw_parameters_require_interface(self.nw, nil)
}
}
get {
if let interface = nw_parameters_copy_required_interface(self.nw) {
return NWInterface(interface)
} else {
return nil
}
}
}
/// Require a connection to use a specific interface type, or fail if not available
public var requiredInterfaceType: NWInterface.InterfaceType {
set {
nw_parameters_set_required_interface_type(self.nw, newValue.nw)
}
get {
return NWInterface.InterfaceType(nw_parameters_get_required_interface_type(self.nw))
}
}
/// Define one or more interfaces that a connection will not be allowed to use
public var prohibitedInterfaces: [NWInterface]? {
set {
nw_parameters_clear_prohibited_interfaces(self.nw)
if let prohibitedInterfaces = newValue {
for prohibitedInterface in prohibitedInterfaces {
nw_parameters_prohibit_interface(self.nw, prohibitedInterface.nw)
}
}
}
get {
var interfaces = [NWInterface]()
nw_parameters_iterate_prohibited_interfaces(self.nw) { (interface) in
interfaces.append(NWInterface(interface))
return true
}
return interfaces
}
}
/// Define one or more interface types that a connection will not be allowed to use
public var prohibitedInterfaceTypes: [NWInterface.InterfaceType]? {
set {
nw_parameters_clear_prohibited_interface_types(self.nw)
if let prohibitedInterfaceTypes = newValue {
for prohibitedInterfaceType in prohibitedInterfaceTypes {
nw_parameters_prohibit_interface_type(self.nw, prohibitedInterfaceType.nw)
}
}
}
get {
var interfaceTypes = [NWInterface.InterfaceType]()
nw_parameters_iterate_prohibited_interface_types(self.nw) { (interfaceType) in
interfaceTypes.append(NWInterface.InterfaceType(interfaceType))
return true
}
return interfaceTypes
}
}
/// Disallow connection from using interfaces considered expensive
public var prohibitExpensivePaths: Bool {
set {
nw_parameters_set_prohibit_expensive(self.nw, newValue)
}
get {
return nw_parameters_get_prohibit_expensive(self.nw)
}
}
/// If true, a direct connection will be attempted first even if proxies are configured. If the direct connection
/// fails, connecting through the proxies will still be attempted.
public var preferNoProxies: Bool {
set {
nw_parameters_set_prefer_no_proxy(self.nw, newValue)
}
get {
return nw_parameters_get_prefer_no_proxy(self.nw)
}
}
/// Force a specific local address to be used. This value is nil by
/// default, in which case the system selects the most appropriate
/// local address and selects a local port.
public var requiredLocalEndpoint: NWEndpoint? {
set {
if let endpoint = newValue {
nw_parameters_set_local_endpoint(self.nw, endpoint.nw)
} else {
nw_parameters_set_local_endpoint(self.nw, nil)
}
}
get {
if let endpoint = nw_parameters_copy_local_endpoint(self.nw) {
return NWEndpoint(endpoint)
} else {
return nil
}
}
}
/// Allow multiple connections to use the same local address and port
/// (SO_REUSEADDR and SO_REUSEPORT).
public var allowLocalEndpointReuse: Bool {
set {
nw_parameters_set_reuse_local_address(self.nw, newValue)
}
get {
return nw_parameters_get_reuse_local_address(self.nw)
}
}
/// Cause an NWListener to only advertise services on the local link,
/// and only accept connections from the local link.
public var acceptLocalOnly: Bool {
set {
nw_parameters_set_local_only(self.nw, newValue)
}
get {
return nw_parameters_get_local_only(self.nw)
}
}
/// Allow the inclusion of peer-to-peer interfaces when
/// listening or establishing outbound connections. This parameter
/// will not take effect if a specific interface is required.
/// This parameter is applicable when advertising a Bonjour service
/// on a listener, or connecting to a Bonjour service.
public var includePeerToPeer: Bool {
set {
nw_parameters_set_include_peer_to_peer(self.nw, newValue)
}
get {
return nw_parameters_get_include_peer_to_peer(self.nw)
}
}
/// The ServiceClass represents the network queuing priority to use
/// for traffic generated by a NWConnection.
public enum ServiceClass {
/// Default priority traffic
case bestEffort
/// Bulk traffic, or traffic that can be de-prioritized behind foreground traffic
case background
/// Interactive video traffic
case interactiveVideo
/// Interactive voice traffic
case interactiveVoice
/// Responsive data
case responsiveData
/// Signaling
case signaling
internal var nw: nw_service_class_t {
switch self {
case .bestEffort:
return Network.nw_service_class_best_effort
case .background:
return Network.nw_service_class_background
case .interactiveVideo:
return Network.nw_service_class_interactive_video
case .interactiveVoice:
return Network.nw_service_class_interactive_voice
case .responsiveData:
return Network.nw_service_class_responsive_data
case .signaling:
return Network.nw_service_class_signaling
}
}
internal init(_ nw: nw_service_class_t) {
switch nw {
case Network.nw_service_class_best_effort:
self = .bestEffort
case Network.nw_service_class_background:
self = .background
case Network.nw_service_class_interactive_video:
self = .interactiveVideo
case Network.nw_service_class_interactive_voice:
self = .interactiveVoice
case Network.nw_service_class_responsive_data:
self = .responsiveData
case Network.nw_service_class_signaling:
self = .signaling
default:
self = .bestEffort
}
}
}
public var serviceClass: NWParameters.ServiceClass {
set {
nw_parameters_set_service_class(self.nw, newValue.nw)
}
get {
return NWParameters.ServiceClass(nw_parameters_get_service_class(self.nw))
}
}
/// Multipath services represent the modes of multipath usage that are
/// allowed for connections.
public enum MultipathServiceType {
/// No multipath transport will be attempted
case disabled
/// Only use the expensive interface when the when the primary one is not available
case handover
/// Use the expensive interface more aggressively to reduce latency
case interactive
/// Use all available interfaces to provide the highest throughput and lowest latency
case aggregate
internal var nw: nw_multipath_service_t {
switch self {
case .disabled:
return Network.nw_multipath_service_disabled
case .handover:
return Network.nw_multipath_service_handover
case .interactive:
return Network.nw_multipath_service_interactive
case .aggregate:
return Network.nw_multipath_service_aggregate
}
}
internal init(_ nw: nw_multipath_service_t) {
switch nw {
case Network.nw_multipath_service_disabled:
self = .disabled
case Network.nw_multipath_service_handover:
self = .handover
case Network.nw_multipath_service_interactive:
self = .interactive
case Network.nw_multipath_service_aggregate:
self = .aggregate
default:
self = .disabled
}
}
}
public var multipathServiceType: NWParameters.MultipathServiceType {
set {
nw_parameters_set_multipath_service(self.nw, newValue.nw)
}
get {
return NWParameters.MultipathServiceType(nw_parameters_get_multipath_service(self.nw))
}
}
/// Use fast open for an outbound NWConnection, which may be done at any
/// protocol level. Use of fast open requires that the caller send
/// idempotent data on the connection before the connection may move
/// into ready state. As a side effect, this may implicitly enable
/// fast open for protocols in the stack, even if they did not have
/// fast open explicitly enabled on them (such as the option to enable
/// TCP Fast Open).
public var allowFastOpen: Bool {
set {
nw_parameters_set_fast_open_enabled(self.nw, newValue)
}
get {
return nw_parameters_get_fast_open_enabled(self.nw)
}
}
/// Allow or prohibit the use of expired DNS answers during connection establishment.
/// If allowed, a DNS answer that was previously returned may be re-used for new
/// connections even after the answers are considered expired. A query for fresh answers
/// will be sent in parallel, and the fresh answers will be used as alternate addresses
/// in case the expired answers do not result in successful connections.
/// By default, this value is .systemDefault, which allows the system to determine
/// if it is appropriate to use expired answers.
public enum ExpiredDNSBehavior {
/// Let the system determine whether or not to allow expired DNS answers
case systemDefault
/// Explicitly allow the use of expired DNS answers
case allow
/// Explicitly prohibit the use of expired DNS answers
case prohibit
internal var nw: nw_parameters_expired_dns_behavior_t {
switch self {
case .systemDefault:
return Network.nw_parameters_expired_dns_behavior_default
case .allow:
return Network.nw_parameters_expired_dns_behavior_allow
case .prohibit:
return Network.nw_parameters_expired_dns_behavior_prohibit
}
}
internal init(_ nw: nw_parameters_expired_dns_behavior_t) {
switch nw {
case Network.nw_parameters_expired_dns_behavior_default:
self = .systemDefault
case Network.nw_parameters_expired_dns_behavior_allow:
self = .allow
case Network.nw_parameters_expired_dns_behavior_prohibit:
self = .prohibit
default:
self = .systemDefault
}
}
}
public var expiredDNSBehavior: NWParameters.ExpiredDNSBehavior {
set {
nw_parameters_set_expired_dns_behavior(self.nw, newValue.nw)
}
get {
return NWParameters.ExpiredDNSBehavior(nw_parameters_get_expired_dns_behavior(self.nw))
}
}
/// A ProtocolStack contains a list of protocols to use for a connection.
/// The members of the protocol stack are NWProtocolOptions objects, each
/// defining which protocol to use within the stack along with any protocol-specific
/// options. Each stack includes an array of application-level protocols, a single
/// transport-level protocol, and an optional internet-level protocol. If the internet-
/// level protocol is not specified, any available and applicable IP address family
/// may be used.
public class ProtocolStack {
public var applicationProtocols: [NWProtocolOptions] {
set {
nw_protocol_stack_clear_application_protocols(self.nw)
for applicationProtocol in newValue.reversed() {
nw_protocol_stack_prepend_application_protocol(self.nw, applicationProtocol.nw)
}
}
get {
var applicationProtocols = [NWProtocolOptions]()
nw_protocol_stack_iterate_application_protocols(self.nw) { (protocolOptions) in
let applicationDefinition = nw_protocol_options_copy_definition(protocolOptions)
if (nw_protocol_definition_is_equal(applicationDefinition, NWProtocolTLS.definition.nw)) {
applicationProtocols.append(NWProtocolTLS.Options(protocolOptions))
}
}
return applicationProtocols
}
}
public var transportProtocol: NWProtocolOptions? {
set {
if let transport = newValue {
nw_protocol_stack_set_transport_protocol(self.nw, transport.nw)
}
}
get {
if let transport = nw_protocol_stack_copy_transport_protocol(nw) {
let transportDefinition = nw_protocol_options_copy_definition(transport)
if (nw_protocol_definition_is_equal(transportDefinition, NWProtocolTCP.definition.nw)) {
return NWProtocolTCP.Options(transport)
} else if (nw_protocol_definition_is_equal(transportDefinition, NWProtocolUDP.definition.nw)) {
return NWProtocolUDP.Options(transport)
}
}
return nil
}
}
public var internetProtocol: NWProtocolOptions? {
set {
// Not currently allowed
return
}
get {
if let ip = nw_protocol_stack_copy_internet_protocol(nw) {
if (nw_protocol_definition_is_equal(nw_protocol_options_copy_definition(ip), NWProtocolIP.definition.nw)) {
return NWProtocolIP.Options(ip)
}
}
return nil
}
}
internal let nw: nw_protocol_stack_t
internal init(_ nw: nw_protocol_stack_t) {
self.nw = nw
}
}
/// Every NWParameters has a default protocol stack, although it may start out empty.
public var defaultProtocolStack: NWParameters.ProtocolStack {
get {
return NWParameters.ProtocolStack(nw_parameters_copy_default_protocol_stack(self.nw))
}
}
/// Perform a deep copy of parameters
public func copy() -> NWParameters {
return NWParameters(nw: nw_parameters_copy(self.nw))
}
}