NIO: Implement BSD sockets APIs for Windows (#1471)

Motivation:

Split out the internal implied BSD socket API code to allow alternative implementations of these interfaces on other platforms.

Modifications:

- Split the code apart.
- Provide POSIX and Windows implementations of the BSD socket API.

Result:

Should be easier to run NIO unmodified on Windows.
This commit is contained in:
Saleem Abdulrasool 2020-07-03 02:14:36 -07:00 committed by GitHub
parent 8aae654645
commit 5de1e41310
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1013 additions and 175 deletions

View File

@ -40,6 +40,7 @@ let package = Package(
EOF
cp "$here/../../Tests/NIOTests/SystemCallWrapperHelpers.swift" \
"$here/../../Sources/NIO/BSDSocketAPI.swift" \
"$here/../../Sources/NIO/BSDSocketAPIPosix.swift" \
"$here/../../Sources/NIO/System.swift" \
"$here/../../Sources/NIO/IO.swift" \
"$tmpdir/syscallwrapper/Sources/syscallwrapper"

View File

@ -26,6 +26,39 @@ typedef struct {
unsigned int msg_len;
} NIO(mmsghdr);
static inline __attribute__((__always_inline__)) int
NIO(getsockopt)(SOCKET s, int level, int optname, void *optval, int *optlen) {
return getsockopt(s, level, optname, optval, optlen);
}
static inline __attribute__((__always_inline__)) int
NIO(recv)(SOCKET s, void *buf, int len, int flags) {
return recv(s, buf, len, flags);
}
static inline __attribute__((__always_inline__)) int
NIO(recvfrom)(SOCKET s, void *buf, int len, int flags, SOCKADDR *from,
int *fromlen) {
return recvfrom(s, buf, len, flags, from, fromlen);
}
static inline __attribute__((__always_inline__)) int
NIO(send)(SOCKET s, const void *buf, int len, int flags) {
return send(s, buf, len, flags);
}
static inline __attribute__((__always_inline__)) int
NIO(setsockopt)(SOCKET s, int level, int optname, const void *optval,
int optlen) {
return setsockopt(s, level, optname, optval, optlen);
}
static inline __attribute__((__always_inline__)) int
NIO(sendto)(SOCKET s, const void *buf, int len, int flags, const SOCKADDR *to,
int tolen) {
return sendto(s, buf, len, flags, to, tolen);
}
#undef NIO
#endif

View File

@ -2,7 +2,7 @@
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Copyright (c) 2020 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
@ -12,53 +12,22 @@
//
//===----------------------------------------------------------------------===//
#if os(Windows)
import let WinSDK.AF_INET
import let WinSDK.AF_INET6
import let WinSDK.AF_UNIX
protocol _SocketShutdownProtocol {
var cValue: CInt { get }
}
import let WinSDK.IPPROTO_IP
import let WinSDK.IPPROTO_IPV6
import let WinSDK.IPPROTO_TCP
import let WinSDK.IP_ADD_MEMBERSHIP
import let WinSDK.IP_DROP_MEMBERSHIP
import let WinSDK.IP_MULTICAST_IF
import let WinSDK.IP_MULTICAST_LOOP
import let WinSDK.IP_MULTICAST_TTL
import let WinSDK.IPV6_JOIN_GROUP
import let WinSDK.IPV6_LEAVE_GROUP
import let WinSDK.IPV6_MULTICAST_HOPS
import let WinSDK.IPV6_MULTICAST_IF
import let WinSDK.IPV6_MULTICAST_LOOP
import let WinSDK.IPV6_V6ONLY
import let WinSDK.PF_INET
import let WinSDK.PF_INET6
import let WinSDK.PF_UNIX
import let WinSDK.TCP_NODELAY
import let WinSDK.SO_ERROR
import let WinSDK.SO_KEEPALIVE
import let WinSDK.SO_LINGER
import let WinSDK.SO_RCVBUF
import let WinSDK.SO_RCVTIMEO
import let WinSDK.SO_REUSEADDR
import let WinSDK.SO_REUSE_UNICASTPORT
import let WinSDK.SOL_SOCKET
import let WinSDK.SOCK_DGRAM
import let WinSDK.SOCK_STREAM
#elseif os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
import Darwin
#else
import Glibc
#endif
internal enum Shutdown: _SocketShutdownProtocol {
case RD
case WR
case RDWR
}
public enum NIOBSDSocket {
#if os(Windows)
public typealias Handle = SOCKET
#else
public typealias Handle = CInt
#endif
}
extension NIOBSDSocket {
@ -367,3 +336,124 @@ extension NIOBSDSocket.Option {
NIOBSDSocket.Option(rawValue: SO_TIMESTAMP)
}
#endif
/// This protocol defines the methods that are expected to be found on `NIOBSDSocket`. While defined as a protocol
/// there is no expectation that any object other than `NIOBSDSocket` will implement this protocol: instead, this protocol
/// acts as a reference for what new supported operating systems must implement.
protocol _BSDSocketProtocol {
static func accept(socket s: NIOBSDSocket.Handle,
address addr: UnsafeMutablePointer<sockaddr>?,
address_len addrlen: UnsafeMutablePointer<socklen_t>?) throws -> NIOBSDSocket.Handle?
static func bind(socket s: NIOBSDSocket.Handle,
address addr: UnsafePointer<sockaddr>,
address_len namelen: socklen_t) throws
static func close(socket s: NIOBSDSocket.Handle) throws
static func connect(socket s: NIOBSDSocket.Handle,
address name: UnsafePointer<sockaddr>,
address_len namelen: socklen_t) throws -> Bool
static func getpeername(socket s: NIOBSDSocket.Handle,
address name: UnsafeMutablePointer<sockaddr>,
address_len namelen: UnsafeMutablePointer<socklen_t>) throws
static func getsockname(socket s: NIOBSDSocket.Handle,
address name: UnsafeMutablePointer<sockaddr>,
address_len namelen: UnsafeMutablePointer<socklen_t>) throws
static func getsockopt(socket: NIOBSDSocket.Handle,
level: NIOBSDSocket.OptionLevel,
option_name optname: NIOBSDSocket.Option,
option_value optval: UnsafeMutableRawPointer,
option_len optlen: UnsafeMutablePointer<socklen_t>) throws
static func listen(socket s: NIOBSDSocket.Handle, backlog: CInt) throws
static func recv(socket s: NIOBSDSocket.Handle,
buffer buf: UnsafeMutableRawPointer,
length len: size_t) throws -> IOResult<size_t>
static func recvfrom(socket s: NIOBSDSocket.Handle,
buffer buf: UnsafeMutableRawPointer,
length len: size_t,
address from: UnsafeMutablePointer<sockaddr>,
address_len fromlen: UnsafeMutablePointer<socklen_t>) throws -> IOResult<size_t>
static func send(socket s: NIOBSDSocket.Handle,
buffer buf: UnsafeRawPointer,
length len: size_t) throws -> IOResult<size_t>
static func setsockopt(socket: NIOBSDSocket.Handle,
level: NIOBSDSocket.OptionLevel,
option_name optname: NIOBSDSocket.Option,
option_value optval: UnsafeRawPointer,
option_len optlen: socklen_t) throws
// NOTE: this should return a `ssize_t`, however, that is not a standard
// type, and defining that type is difficult. Opt to return a `size_t`
// which is the same size, but is unsigned.
static func sendto(socket s: NIOBSDSocket.Handle,
buffer buf: UnsafeRawPointer,
length len: size_t,
dest_addr to: UnsafePointer<sockaddr>,
dest_len tolen: socklen_t) throws -> IOResult<size_t>
static func shutdown(socket: NIOBSDSocket.Handle, how: Shutdown) throws
static func socket(domain af: NIOBSDSocket.ProtocolFamily,
type: NIOBSDSocket.SocketType,
`protocol`: CInt) throws -> NIOBSDSocket.Handle
static func recvmmsg(socket: NIOBSDSocket.Handle,
msgvec: UnsafeMutablePointer<MMsgHdr>,
vlen: CUnsignedInt,
flags: CInt,
timeout: UnsafeMutablePointer<timespec>?) throws -> IOResult<Int>
static func sendmmsg(socket: NIOBSDSocket.Handle,
msgvec: UnsafeMutablePointer<MMsgHdr>,
vlen: CUnsignedInt,
flags: CInt) throws -> IOResult<Int>
// NOTE: this should return a `ssize_t`, however, that is not a standard
// type, and defining that type is difficult. Opt to return a `size_t`
// which is the same size, but is unsigned.
static func pread(socket: NIOBSDSocket.Handle,
pointer: UnsafeMutableRawPointer,
size: size_t,
offset: off_t) throws -> IOResult<size_t>
// NOTE: this should return a `ssize_t`, however, that is not a standard
// type, and defining that type is difficult. Opt to return a `size_t`
// which is the same size, but is unsigned.
static func pwrite(socket: NIOBSDSocket.Handle,
pointer: UnsafeRawPointer,
size: size_t,
offset: off_t) throws -> IOResult<size_t>
static func poll(fds: UnsafeMutablePointer<pollfd>,
nfds: nfds_t,
timeout: CInt) throws -> CInt
static func inet_ntop(af family: NIOBSDSocket.AddressFamily,
src addr: UnsafeRawPointer,
dst dstBuf: UnsafeMutablePointer<CChar>,
size dstSize: socklen_t) throws -> UnsafePointer<CChar>?
static func inet_pton(af family: NIOBSDSocket.AddressFamily,
src description: UnsafePointer<CChar>,
dst address: UnsafeMutableRawPointer) throws
static func sendfile(socket s: NIOBSDSocket.Handle,
fd: CInt,
offset: off_t,
len: off_t) throws -> IOResult<Int>
static func setNonBlocking(socket: NIOBSDSocket.Handle) throws
}
/// If this extension is hitting a compile error, your platform is missing one of the functions defined above!
extension NIOBSDSocket: _BSDSocketProtocol { }

View File

@ -0,0 +1,224 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#if os(Linux) || os(Android) || os(FreeBSD) || os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
extension Shutdown {
internal var cValue: CInt {
switch self {
case .RD:
return CInt(Posix.SHUT_RD)
case .WR:
return CInt(Posix.SHUT_WR)
case .RDWR:
return CInt(Posix.SHUT_RDWR)
}
}
}
// MARK: Implementation of _BSDSocketProtocol for POSIX systems
extension NIOBSDSocket {
static func accept(socket s: NIOBSDSocket.Handle,
address addr: UnsafeMutablePointer<sockaddr>?,
address_len addrlen: UnsafeMutablePointer<socklen_t>?) throws -> NIOBSDSocket.Handle? {
return try Posix.accept(descriptor: s, addr: addr, len: addrlen)
}
static func bind(socket s: NIOBSDSocket.Handle,
address addr: UnsafePointer<sockaddr>,
address_len namelen: socklen_t) throws {
return try Posix.bind(descriptor: s, ptr: addr, bytes: Int(namelen))
}
static func close(socket s: NIOBSDSocket.Handle) throws {
return try Posix.close(descriptor: s)
}
static func connect(socket s: NIOBSDSocket.Handle,
address name: UnsafePointer<sockaddr>,
address_len namelen: socklen_t) throws -> Bool {
return try Posix.connect(descriptor: s, addr: name, size: namelen)
}
static func getpeername(socket s: NIOBSDSocket.Handle,
address name: UnsafeMutablePointer<sockaddr>,
address_len namelen: UnsafeMutablePointer<socklen_t>) throws {
return try Posix.getpeername(socket: s, address: name, addressLength: namelen)
}
static func getsockname(socket s: NIOBSDSocket.Handle,
address name: UnsafeMutablePointer<sockaddr>,
address_len namelen: UnsafeMutablePointer<socklen_t>) throws {
return try Posix.getsockname(socket: s, address: name, addressLength: namelen)
}
static func getsockopt(socket: NIOBSDSocket.Handle,
level: NIOBSDSocket.OptionLevel,
option_name optname: NIOBSDSocket.Option,
option_value optval: UnsafeMutableRawPointer,
option_len optlen: UnsafeMutablePointer<socklen_t>) throws {
return try Posix.getsockopt(socket: socket,
level: level.rawValue,
optionName: optname.rawValue,
optionValue: optval,
optionLen: optlen)
}
static func listen(socket s: NIOBSDSocket.Handle, backlog: CInt) throws {
return try Posix.listen(descriptor: s, backlog: backlog)
}
static func recv(socket s: NIOBSDSocket.Handle,
buffer buf: UnsafeMutableRawPointer,
length len: size_t) throws -> IOResult<size_t> {
return try Posix.read(descriptor: s, pointer: buf, size: len)
}
static func recvfrom(socket s: NIOBSDSocket.Handle,
buffer buf: UnsafeMutableRawPointer,
length len: size_t,
address from: UnsafeMutablePointer<sockaddr>,
address_len fromlen: UnsafeMutablePointer<socklen_t>) throws -> IOResult<size_t> {
return try Posix.recvfrom(descriptor: s,
pointer: buf,
len: len,
addr: from,
addrlen: fromlen)
}
static func send(socket s: NIOBSDSocket.Handle,
buffer buf: UnsafeRawPointer,
length len: size_t) throws -> IOResult<size_t> {
return try Posix.write(descriptor: s, pointer: buf, size: len)
}
static func setsockopt(socket: NIOBSDSocket.Handle,
level: NIOBSDSocket.OptionLevel,
option_name optname: NIOBSDSocket.Option,
option_value optval: UnsafeRawPointer,
option_len optlen: socklen_t) throws {
return try Posix.setsockopt(socket: socket,
level: level.rawValue,
optionName: optname.rawValue,
optionValue: optval,
optionLen: optlen)
}
// NOTE: this should return a `ssize_t`, however, that is not a standard
// type, and defining that type is difficult. Opt to return a `size_t`
// which is the same size, but is unsigned.
static func sendto(socket s: NIOBSDSocket.Handle,
buffer buf: UnsafeRawPointer,
length len: size_t,
dest_addr to: UnsafePointer<sockaddr>,
dest_len tolen: socklen_t) throws -> IOResult<size_t> {
return try Posix.sendto(descriptor: s,
pointer: buf,
size: len,
destinationPtr: to,
destinationSize: tolen)
}
static func shutdown(socket: NIOBSDSocket.Handle, how: Shutdown) throws {
return try Posix.shutdown(descriptor: socket, how: how)
}
static func socket(domain af: NIOBSDSocket.ProtocolFamily,
type: NIOBSDSocket.SocketType,
`protocol`: CInt) throws -> NIOBSDSocket.Handle {
return try Posix.socket(domain: af, type: type, protocol: `protocol`)
}
static func recvmmsg(socket: NIOBSDSocket.Handle,
msgvec: UnsafeMutablePointer<MMsgHdr>,
vlen: CUnsignedInt,
flags: CInt,
timeout: UnsafeMutablePointer<timespec>?) throws -> IOResult<Int> {
return try Posix.recvmmsg(sockfd: socket,
msgvec: msgvec,
vlen: vlen,
flags: flags,
timeout: timeout)
}
static func sendmmsg(socket: NIOBSDSocket.Handle,
msgvec: UnsafeMutablePointer<MMsgHdr>,
vlen: CUnsignedInt,
flags: CInt) throws -> IOResult<Int> {
return try Posix.sendmmsg(sockfd: socket,
msgvec: msgvec,
vlen: vlen,
flags: flags)
}
// NOTE: this should return a `ssize_t`, however, that is not a standard
// type, and defining that type is difficult. Opt to return a `size_t`
// which is the same size, but is unsigned.
static func pread(socket: NIOBSDSocket.Handle,
pointer: UnsafeMutableRawPointer,
size: size_t,
offset: off_t) throws -> IOResult<size_t> {
return try Posix.pread(descriptor: socket,
pointer: pointer,
size: size,
offset: offset)
}
// NOTE: this should return a `ssize_t`, however, that is not a standard
// type, and defining that type is difficult. Opt to return a `size_t`
// which is the same size, but is unsigned.
static func pwrite(socket: NIOBSDSocket.Handle,
pointer: UnsafeRawPointer,
size: size_t,
offset: off_t) throws -> IOResult<size_t> {
return try Posix.pwrite(descriptor: socket, pointer: pointer, size: size, offset: offset)
}
static func poll(fds: UnsafeMutablePointer<pollfd>,
nfds: nfds_t,
timeout: CInt) throws -> CInt {
return try Posix.poll(fds: fds, nfds: nfds, timeout: timeout)
}
@discardableResult
static func inet_ntop(af family: NIOBSDSocket.AddressFamily,
src addr: UnsafeRawPointer,
dst dstBuf: UnsafeMutablePointer<CChar>,
size dstSize: socklen_t) throws -> UnsafePointer<CChar>? {
return try Posix.inet_ntop(addressFamily: sa_family_t(family.rawValue),
addressBytes: addr,
addressDescription: dstBuf,
addressDescriptionLength: dstSize)
}
static func inet_pton(af family: NIOBSDSocket.AddressFamily,
src description: UnsafePointer<CChar>,
dst address: UnsafeMutableRawPointer) throws {
return try Posix.inet_pton(addressFamily: sa_family_t(family.rawValue),
addressDescription: description,
address: address)
}
static func sendfile(socket s: NIOBSDSocket.Handle,
fd: CInt,
offset: off_t,
len: off_t) throws -> IOResult<Int> {
return try Posix.sendfile(descriptor: s, fd: fd, offset: offset, count: size_t(len))
}
static func setNonBlocking(socket: NIOBSDSocket.Handle) throws {
return try Posix.setNonBlocking(socket: socket)
}
}
#endif

View File

@ -0,0 +1,360 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#if os(Windows)
import ucrt
import let WinSDK.AF_INET
import let WinSDK.AF_INET6
import let WinSDK.AF_UNIX
import let WinSDK.IPPROTO_IP
import let WinSDK.IPPROTO_IPV6
import let WinSDK.IPPROTO_TCP
import let WinSDK.IP_ADD_MEMBERSHIP
import let WinSDK.IP_DROP_MEMBERSHIP
import let WinSDK.IP_MULTICAST_IF
import let WinSDK.IP_MULTICAST_LOOP
import let WinSDK.IP_MULTICAST_TTL
import let WinSDK.IPV6_JOIN_GROUP
import let WinSDK.IPV6_LEAVE_GROUP
import let WinSDK.IPV6_MULTICAST_HOPS
import let WinSDK.IPV6_MULTICAST_IF
import let WinSDK.IPV6_MULTICAST_LOOP
import let WinSDK.IPV6_V6ONLY
import let WinSDK.PF_INET
import let WinSDK.PF_INET6
import let WinSDK.PF_UNIX
import let WinSDK.TCP_NODELAY
import let WinSDK.SO_ERROR
import let WinSDK.SO_KEEPALIVE
import let WinSDK.SO_LINGER
import let WinSDK.SO_RCVBUF
import let WinSDK.SO_RCVTIMEO
import let WinSDK.SO_REUSEADDR
import let WinSDK.SO_REUSE_UNICASTPORT
import let WinSDK.SOL_SOCKET
import let WinSDK.SOCK_DGRAM
import let WinSDK.SOCK_STREAM
import let WinSDK.SOCKET_ERROR
import func WinSDK.WSAGetLastError
import struct WinSDK.socklen_t
import struct WinSDK.SOCKADDR
extension Shutdown {
internal var cValue: CInt {
switch self {
case .RD:
return WinSDK.SD_RECEIVE
case .WR:
return WinSDK.SD_SEND
case .RDWR:
return WinSDK.SD_BOTH
}
}
}
// MARK: _BSDSocketProtocol implementation
extension NIOBSDSocket {
@inline(never)
static func accept(socket s: NIOBSDSocket.Handle,
address addr: UnsafeMutablePointer<sockaddr>?,
address_len addrlen: UnsafeMutablePointer<socklen_t>?) throws -> NIOBSDSocket.Handle? {
let socket: NIOBSDSocket.Handle = WinSDK.accept(s, addr, addrlen)
if socket == WinSDK.INVALID_SOCKET {
throw IOError(winsock: WSAGetLastError(), reason: "accept")
}
return socket
}
@inline(never)
static func bind(socket s: NIOBSDSocket.Handle,
address addr: UnsafePointer<sockaddr>,
address_len namelen: socklen_t) throws {
if WinSDK.bind(s, addr, namelen) == SOCKET_ERROR {
throw IOError(winsock: WSAGetLastError(), reason: "bind")
}
}
@inline(never)
static func close(socket s: NIOBSDSocket.Handle) throws {
try Posix.close(descriptor: s)
}
@inline(never)
static func connect(socket s: NIOBSDSocket.Handle,
address name: UnsafePointer<sockaddr>,
address_len namelen: socklen_t) throws -> Bool {
if WinSDK.connect(s, name, namelen) == SOCKET_ERROR {
throw IOError(winsock: WSAGetLastError(), reason: "connect")
}
return true
}
@inline(never)
static func getpeername(socket s: NIOBSDSocket.Handle,
address name: UnsafeMutablePointer<sockaddr>,
address_len namelen: UnsafeMutablePointer<socklen_t>) throws {
if WinSDK.getpeername(s, name, namelen) == SOCKET_ERROR {
throw IOError(winsock: WSAGetLastError(), reason: "getpeername")
}
}
@inline(never)
static func getsockname(socket s: NIOBSDSocket.Handle,
address name: UnsafeMutablePointer<sockaddr>,
address_len namelen: UnsafeMutablePointer<socklen_t>) throws {
if WinSDK.getsockname(s, name, namelen) == SOCKET_ERROR {
throw IOError(winsock: WSAGetLastError(), reason: "getsockname")
}
}
@inline(never)
static func getsockopt(socket: NIOBSDSocket.Handle,
level: NIOBSDSocket.OptionLevel,
option_name optname: NIOBSDSocket.Option,
option_value optval: UnsafeMutableRawPointer,
option_len optlen: UnsafeMutablePointer<socklen_t>) throws {
if CNIOWindows_getsockopt(socket, level.rawValue, optname.rawValue,
optval, optlen) == SOCKET_ERROR {
throw IOError(winsock: WSAGetLastError(), reason: "getsockopt")
}
}
@inline(never)
static func listen(socket s: NIOBSDSocket.Handle, backlog: CInt) throws {
if WinSDK.listen(s, backlog) == SOCKET_ERROR {
throw IOError(winsock: WSAGetLastError(), reason: "listen")
}
}
@inline(never)
static func recv(socket s: NIOBSDSocket.Handle,
buffer buf: UnsafeMutableRawPointer,
length len: size_t) throws -> IOResult<size_t> {
let iResult: CInt = CNIOWindows_recv(s, buf, CInt(len), 0)
if iResult == SOCKET_ERROR {
throw IOError(winsock: WSAGetLastError(), reason: "recv")
}
return .processed(size_t(iResult))
}
@inline(never)
static func recvfrom(socket s: NIOBSDSocket.Handle,
buffer buf: UnsafeMutableRawPointer,
length len: size_t,
address from: UnsafeMutablePointer<sockaddr>,
address_len fromlen: UnsafeMutablePointer<socklen_t>) throws -> IOResult<size_t> {
let iResult: CInt = CNIOWindows_recvfrom(s, buf, CInt(len), 0, from, fromlen)
if iResult == SOCKET_ERROR {
throw IOError(winsock: WSAGetLastError(), reason: "recvfrom")
}
return .processed(size_t(iResult))
}
@inline(never)
static func send(socket s: NIOBSDSocket.Handle,
buffer buf: UnsafeRawPointer,
length len: size_t) throws -> IOResult<size_t> {
let iResult: CInt = CNIOWindows_send(s, buf, CInt(len), 0)
if iResult == SOCKET_ERROR {
throw IOError(winsock: WSAGetLastError(), reason: "send")
}
return .processed(size_t(iResult))
}
@inline(never)
static func setsockopt(socket: NIOBSDSocket.Handle,
level: NIOBSDSocket.OptionLevel,
option_name optname: NIOBSDSocket.Option,
option_value optval: UnsafeRawPointer,
option_len optlen: socklen_t) throws {
if CNIOWindows_setsockopt(socket, level.rawValue, optname.rawValue,
optval, optlen) == SOCKET_ERROR {
throw IOError(winsock: WSAGetLastError(), reason: "setsockopt")
}
}
// NOTE: this should return a `ssize_t`, however, that is not a standard
// type, and defining that type is difficult. Opt to return a `size_t`
// which is the same size, but is unsigned.
@inline(never)
static func sendto(socket s: NIOBSDSocket.Handle,
buffer buf: UnsafeRawPointer,
length len: size_t,
dest_addr to: UnsafePointer<sockaddr>,
dest_len tolen: socklen_t) throws -> IOResult<size_t> {
let iResult: CInt = CNIOWindows_sendto(s, buf, CInt(len), 0, to, tolen)
if iResult == SOCKET_ERROR {
throw IOError(winsock: WSAGetLastError(), reason: "sendto")
}
return .processed(size_t(iResult))
}
@inline(never)
static func shutdown(socket: NIOBSDSocket.Handle, how: Shutdown) throws {
if WinSDK.shutdown(socket, how.cValue) == SOCKET_ERROR {
throw IOError(winsock: WSAGetLastError(), reason: "shutdown")
}
}
@inline(never)
static func socket(domain af: NIOBSDSocket.ProtocolFamily,
type: NIOBSDSocket.SocketType,
`protocol`: CInt) throws -> NIOBSDSocket.Handle {
let socket: NIOBSDSocket.Handle = WinSDK.socket(af.rawValue, type.rawValue, `protocol`)
if socket == WinSDK.INVALID_SOCKET {
throw IOError(winsock: WSAGetLastError(), reason: "socket")
}
return socket
}
@inline(never)
static func recvmmsg(socket: NIOBSDSocket.Handle,
msgvec: UnsafeMutablePointer<MMsgHdr>,
vlen: CUnsignedInt,
flags: CInt,
timeout: UnsafeMutablePointer<timespec>?) throws -> IOResult<Int> {
return try Posix.recvmmsg(sockfd: socket,
msgvec: msgvec,
vlen: vlen,
flags: flags,
timeout: timeout)
}
@inline(never)
static func sendmmsg(socket: NIOBSDSocket.Handle,
msgvec: UnsafeMutablePointer<MMsgHdr>,
vlen: CUnsignedInt,
flags: CInt) throws -> IOResult<Int> {
return try Posix.sendmmsg(sockfd: socket,
msgvec: msgvec,
vlen: vlen,
flags: flags)
}
// NOTE: this should return a `ssize_t`, however, that is not a standard
// type, and defining that type is difficult. Opt to return a `size_t`
// which is the same size, but is unsigned.
@inline(never)
static func pread(socket: NIOBSDSocket.Handle,
pointer: UnsafeMutableRawPointer,
size: size_t,
offset: off_t) throws -> IOResult<size_t> {
var ovlOverlapped: OVERLAPPED = OVERLAPPED()
ovlOverlapped.OffsetHigh = DWORD(UInt32(offset >> 32) & 0xffffffff)
ovlOverlapped.Offset = DWORD(UInt32(offset >> 0) & 0xffffffff)
var nNumberOfBytesRead: DWORD = 0
if !ReadFile(HANDLE(bitPattern: UInt(socket)), pointer, DWORD(size),
&nNumberOfBytesRead, &ovlOverlapped) {
throw IOError(windows: GetLastError(), reason: "ReadFile")
}
return .processed(CInt(nNumberOfBytesRead))
}
// NOTE: this should return a `ssize_t`, however, that is not a standard
// type, and defining that type is difficult. Opt to return a `size_t`
// which is the same size, but is unsigned.
@inline(never)
static func pwrite(socket: NIOBSDSocket.Handle,
pointer: UnsafeRawPointer,
size: size_t,
offset: off_t) throws -> IOResult<size_t> {
var ovlOverlapped: OVERLAPPED = OVERLAPPED()
ovlOverlapped.OffsetHigh = DWORD(UInt32(offset >> 32) & 0xffffffff)
ovlOverlapped.Offset = DWORD(UInt32(offset >> 0) & 0xffffffff)
var nNumberOfBytesWritten: DWORD = 0
if !WriteFile(HANDLE(bitPattern: UInt(socket)), pointer, DWORD(size),
&nNumberOfBytesWritten, &ovlOverlapped) {
throw IOError(windows: GetLastError(), reason: "WriteFile")
}
return .processed(CInt(nNumberOfBytesWritten))
}
@inline(never)
static func poll(fds: UnsafeMutablePointer<pollfd>,
nfds: nfds_t,
timeout: CInt) throws -> CInt {
fatalError("Poll unsupported on Windows")
}
@discardableResult
@inline(never)
static func inet_ntop(af family: NIOBSDSocket.AddressFamily,
src addr: UnsafeRawPointer,
dst dstBuf: UnsafeMutablePointer<CChar>,
size dstSize: socklen_t) throws -> UnsafePointer<CChar>? {
// TODO(compnerd) use `InetNtopW` to ensure that we handle unicode properly
guard let result = WinSDK.inet_ntop(family.rawValue, addr, dstBuf,
Int(dstSize)) else {
throw IOError(windows: GetLastError(), reason: "inet_ntop")
}
return result
}
@discardableResult
@inline(never)
static func inet_pton(af family: NIOBSDSocket.AddressFamily,
src description: UnsafePointer<CChar>,
dst address: UnsafeMutableRawPointer) throws {
// TODO(compnerd) use `InetPtonW` to ensure that we handle unicode properly
switch WinSDK.inet_pton(family.rawValue, description, address) {
case 0: throw IOError(errnoCode: EINVAL, reason: "inet_pton")
case 1: return
default: throw IOError(winsock: WSAGetLastError(), reason: "inet_pton")
}
}
@inline(never)
static func sendfile(socket s: NIOBSDSocket.Handle,
fd: CInt,
offset: off_t,
len: off_t) throws -> IOResult<Int> {
let hFile: HANDLE = HANDLE(bitPattern: ucrt._get_osfhandle(fd))!
if hFile == INVALID_HANDLE_VALUE {
throw IOError(errnoCode: EBADF, reason: "_get_osfhandle")
}
var ovlOverlapped: OVERLAPPED = OVERLAPPED()
ovlOverlapped.Offset = DWORD(UInt32(offset >> 0) & 0xffffffff)
ovlOverlapped.OffsetHigh = DWORD(UInt32(offset >> 32) & 0xffffffff)
if !TransmitFile(s, hFile, DWORD(nNumberOfBytesToWrite), 0,
&ovlOverlapped, nil, DWORD(TF_USE_KERNEL_APC)) {
throw IOError(winsock: WSAGetLastError(), reason: "TransmitFile")
}
return .processed(Int(nNumberOfBytesToWrite))
}
@inline(never)
static func setNonBlocking(socket: NIOBSDSocket.Handle) throws {
var ulMode: u_long = 1
if WinSDK.ioctlsocket(socket, FIONBIO, &ulMode) == SOCKET_ERROR {
throw IOError(winsock: WSAGetLastError(), reason: "ioctlsocket")
}
}
}
#endif

View File

@ -14,6 +14,10 @@
import NIOConcurrencyHelpers
#if os(Windows)
import let WinSDK.INVALID_SOCKET
#endif
/// A Registration on a `Selector`, which is interested in an `SelectorEventSet`.
protocol Registration {
/// The `SelectorEventSet` in which the `Registration` is interested.
@ -29,10 +33,9 @@ protocol SockAddrProtocol {
internal func descriptionForAddress(family: NIOBSDSocket.AddressFamily, bytes: UnsafeRawPointer, length byteCount: Int) throws -> String {
var addressBytes: [Int8] = Array(repeating: 0, count: byteCount)
return try addressBytes.withUnsafeMutableBufferPointer { (addressBytesPtr: inout UnsafeMutableBufferPointer<Int8>) -> String in
try Posix.inet_ntop(addressFamily: sa_family_t(family.rawValue),
addressBytes: bytes,
addressDescription: addressBytesPtr.baseAddress!,
addressDescriptionLength: socklen_t(byteCount))
try NIOBSDSocket.inet_ntop(af: family, src: bytes,
dst: addressBytesPtr.baseAddress!,
size: socklen_t(byteCount))
return addressBytesPtr.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: byteCount) { addressBytesPtr -> String in
String(cString: addressBytesPtr)
}
@ -209,9 +212,13 @@ extension sockaddr_storage {
class BaseSocket: BaseSocketProtocol {
typealias SelectableType = BaseSocket
private var descriptor: CInt
private var descriptor: NIOBSDSocket.Handle
public var isOpen: Bool {
return descriptor >= 0
#if os(Windows)
return descriptor != WinSDK.INVALID_SOCKET
#else
return descriptor >= 0
#endif
}
/// Returns the local bound `SocketAddress` of the socket.
@ -219,7 +226,9 @@ class BaseSocket: BaseSocketProtocol {
/// - returns: The local bound address.
/// - throws: An `IOError` if the retrieval of the address failed.
func localAddress() throws -> SocketAddress {
return try get_addr { try Posix.getsockname(socket: $0, address: $1, addressLength: $2) }
return try get_addr {
try NIOBSDSocket.getsockname(socket: $0, address: $1, address_len: $2)
}
}
/// Returns the connected `SocketAddress` of the socket.
@ -227,11 +236,13 @@ class BaseSocket: BaseSocketProtocol {
/// - returns: The connected address.
/// - throws: An `IOError` if the retrieval of the address failed.
func remoteAddress() throws -> SocketAddress {
return try get_addr { try Posix.getpeername(socket: $0, address: $1, addressLength: $2) }
return try get_addr {
try NIOBSDSocket.getpeername(socket: $0, address: $1, address_len: $2)
}
}
/// Internal helper function for retrieval of a `SocketAddress`.
private func get_addr(_ body: (Int32, UnsafeMutablePointer<sockaddr>, UnsafeMutablePointer<socklen_t>) throws -> Void) throws -> SocketAddress {
private func get_addr(_ body: (NIOBSDSocket.Handle, UnsafeMutablePointer<sockaddr>, UnsafeMutablePointer<socklen_t>) throws -> Void) throws -> SocketAddress {
var addr = sockaddr_storage()
try addr.withMutableSockAddr { addressPtr, size in
@ -252,23 +263,23 @@ class BaseSocket: BaseSocketProtocol {
/// - setNonBlocking: Set non-blocking mode on the socket.
/// - returns: the file descriptor of the socket that was created.
/// - throws: An `IOError` if creation of the socket failed.
static func makeSocket(protocolFamily: NIOBSDSocket.ProtocolFamily, type: NIOBSDSocket.SocketType, setNonBlocking: Bool = false) throws -> CInt {
static func makeSocket(protocolFamily: NIOBSDSocket.ProtocolFamily, type: NIOBSDSocket.SocketType, setNonBlocking: Bool = false) throws -> NIOBSDSocket.Handle {
var sockType: CInt = type.rawValue
#if os(Linux)
if setNonBlocking {
sockType = type.rawValue | Linux.SOCK_NONBLOCK
}
#endif
let sock = try Posix.socket(domain: protocolFamily,
type: NIOBSDSocket.SocketType(rawValue: sockType),
protocol: 0)
let sock = try NIOBSDSocket.socket(domain: protocolFamily,
type: NIOBSDSocket.SocketType(rawValue: sockType),
protocol: 0)
#if !os(Linux)
if setNonBlocking {
do {
try Posix.setNonBlocking(socket: sock)
try NIOBSDSocket.setNonBlocking(socket: sock)
} catch {
// best effort close
try? Posix.close(descriptor: sock)
try? NIOBSDSocket.close(socket: sock)
throw error
}
}
@ -276,11 +287,11 @@ class BaseSocket: BaseSocketProtocol {
if protocolFamily == .inet6 {
var zero: Int32 = 0
do {
try Posix.setsockopt(socket: sock, level: NIOBSDSocket.OptionLevel.ipv6.rawValue, optionName: NIOBSDSocket.Option.ipv6_v6only.rawValue, optionValue: &zero, optionLen: socklen_t(MemoryLayout.size(ofValue: zero)))
try NIOBSDSocket.setsockopt(socket: sock, level: .ipv6, option_name: .ipv6_v6only, option_value: &zero, option_len: socklen_t(MemoryLayout.size(ofValue: zero)))
} catch let e as IOError {
if e.errnoCode != EAFNOSUPPORT {
// Ignore error that may be thrown by close.
_ = try? Posix.close(descriptor: sock)
_ = try? NIOBSDSocket.close(socket: sock)
throw e
}
/* we couldn't enable dual IP4/6 support, that's okay too. */
@ -298,8 +309,12 @@ class BaseSocket: BaseSocketProtocol {
///
/// - parameters:
/// - descriptor: The file descriptor to wrap.
init(descriptor: CInt) throws {
precondition(descriptor >= 0, "invalid file descriptor")
init(socket descriptor: NIOBSDSocket.Handle) throws {
#if os(Windows)
precondition(descriptor != WinSDK.INVALID_SOCKET, "invalid socket")
#else
precondition(descriptor >= 0, "invalid socket")
#endif
self.descriptor = descriptor
do {
try self.ignoreSIGPIPE()
@ -324,7 +339,7 @@ class BaseSocket: BaseSocketProtocol {
/// throws: An `IOError` if the operation failed.
final func setNonBlocking() throws {
return try self.withUnsafeHandle {
try Posix.setNonBlocking(socket: $0)
try NIOBSDSocket.setNonBlocking(socket: $0)
}
}
@ -346,12 +361,12 @@ class BaseSocket: BaseSocketProtocol {
return try self.withUnsafeHandle {
var val = value
try Posix.setsockopt(
try NIOBSDSocket.setsockopt(
socket: $0,
level: level.rawValue,
optionName: name.rawValue,
optionValue: &val,
optionLen: socklen_t(MemoryLayout.size(ofValue: val)))
level: level,
option_name: name,
option_value: &val,
option_len: socklen_t(MemoryLayout.size(ofValue: val)))
}
}
@ -377,7 +392,7 @@ class BaseSocket: BaseSocketProtocol {
storage.deallocate()
}
try Posix.getsockopt(socket: fd, level: level.rawValue, optionName: name.rawValue, optionValue: val, optionLen: &length)
try NIOBSDSocket.getsockopt(socket: fd, level: level, option_name: name, option_value: val, option_len: &length)
return val.pointee
}
}
@ -390,7 +405,7 @@ class BaseSocket: BaseSocketProtocol {
func bind(to address: SocketAddress) throws {
try self.withUnsafeHandle { fd in
func doBind(ptr: UnsafePointer<sockaddr>, bytes: Int) throws {
try Posix.bind(descriptor: fd, ptr: ptr, bytes: bytes)
try NIOBSDSocket.bind(socket: fd, address: ptr, address_len: socklen_t(bytes))
}
switch address {
@ -413,7 +428,7 @@ class BaseSocket: BaseSocketProtocol {
///
/// - throws: An `IOError` if the operation failed.
func close() throws {
try Posix.close(descriptor: try self.takeDescriptorOwnership())
try NIOBSDSocket.close(socket: try self.takeDescriptorOwnership())
}
/// Takes the file descriptor's ownership.
@ -422,7 +437,7 @@ class BaseSocket: BaseSocketProtocol {
/// the underlying file descriptor.
///
/// - throws: An `IOError` if the operation failed.
final func takeDescriptorOwnership() throws -> CInt {
final func takeDescriptorOwnership() throws -> NIOBSDSocket.Handle {
return try self.withUnsafeHandle {
self.descriptor = -1
return $0
@ -431,7 +446,7 @@ class BaseSocket: BaseSocketProtocol {
}
extension BaseSocket: Selectable {
func withUnsafeHandle<T>(_ body: (CInt) throws -> T) throws -> T {
func withUnsafeHandle<T>(_ body: (NIOBSDSocket.Handle) throws -> T) throws -> T {
guard self.isOpen else {
throw IOError(errnoCode: EBADF, reason: "file descriptor already closed!")
}

View File

@ -225,13 +225,24 @@ public final class ServerBootstrap {
}
}
#if !os(Windows)
/// Use the existing bound socket file descriptor.
///
/// - parameters:
/// - descriptor: The _Unix file descriptor_ representing the bound stream socket.
@available(*, deprecated, renamed: "ServerBootstrap.withBoundSocket(_:)")
public func withBoundSocket(descriptor: CInt) -> EventLoopFuture<Channel> {
return withBoundSocket(descriptor)
}
#endif
/// Use the existing bound socket file descriptor.
///
/// - parameters:
/// - descriptor: The _Unix file descriptor_ representing the bound stream socket.
public func withBoundSocket(descriptor: CInt) -> EventLoopFuture<Channel> {
public func withBoundSocket(_ socket: NIOBSDSocket.Handle) -> EventLoopFuture<Channel> {
func makeChannel(_ eventLoop: SelectableEventLoop, _ childEventLoopGroup: EventLoopGroup) throws -> ServerSocketChannel {
return try ServerSocketChannel(descriptor: descriptor, eventLoop: eventLoop, group: childEventLoopGroup)
return try ServerSocketChannel(socket: socket, eventLoop: eventLoop, group: childEventLoopGroup)
}
return bind0(makeServerChannel: makeChannel) { (eventLoop, serverChannel) in
let promise = eventLoop.makePromise(of: Void.self)
@ -613,17 +624,29 @@ public final class ClientBootstrap: NIOClientTCPBootstrapProtocol {
}
}
#if !os(Windows)
/// Use the existing connected socket file descriptor.
///
/// - parameters:
/// - descriptor: The _Unix file descriptor_ representing the connected stream socket.
/// - returns: an `EventLoopFuture<Channel>` to deliver the `Channel`.
@available(*, deprecated, renamed: "ClientBoostrap.withConnectedSocket(_:)")
public func withConnectedSocket(descriptor: CInt) -> EventLoopFuture<Channel> {
return self.withConnectedSocket(descriptor)
}
#endif
/// Use the existing connected socket file descriptor.
///
/// - parameters:
/// - descriptor: The _Unix file descriptor_ representing the connected stream socket.
/// - returns: an `EventLoopFuture<Channel>` to deliver the `Channel`.
public func withConnectedSocket(descriptor: CInt) -> EventLoopFuture<Channel> {
public func withConnectedSocket(_ socket: NIOBSDSocket.Handle) -> EventLoopFuture<Channel> {
let eventLoop = group.next()
let channelInitializer = self.channelInitializer
let channel: SocketChannel
do {
channel = try SocketChannel(eventLoop: eventLoop as! SelectableEventLoop, descriptor: descriptor)
channel = try SocketChannel(eventLoop: eventLoop as! SelectableEventLoop, socket: socket)
} catch {
return eventLoop.makeFailedFuture(error)
}
@ -783,13 +806,24 @@ public final class DatagramBootstrap {
return self
}
#if !os(Windows)
/// Use the existing bound socket file descriptor.
///
/// - parameters:
/// - descriptor: The _Unix file descriptor_ representing the bound datagram socket.
@available(*, deprecated, renamed: "DatagramBootstrap.withBoundSocket(_:)")
public func withBoundSocket(descriptor: CInt) -> EventLoopFuture<Channel> {
return self.withBoundSocket(descriptor)
}
#endif
/// Use the existing bound socket file descriptor.
///
/// - parameters:
/// - descriptor: The _Unix file descriptor_ representing the bound datagram socket.
public func withBoundSocket(descriptor: CInt) -> EventLoopFuture<Channel> {
public func withBoundSocket(_ socket: NIOBSDSocket.Handle) -> EventLoopFuture<Channel> {
func makeChannel(_ eventLoop: SelectableEventLoop) throws -> DatagramChannel {
return try DatagramChannel(eventLoop: eventLoop, descriptor: descriptor)
return try DatagramChannel(eventLoop: eventLoop, socket: socket)
}
return bind0(makeChannel: makeChannel) { (eventLoop, channel) in
let promise = eventLoop.makePromise(of: Void.self)

View File

@ -19,5 +19,5 @@
/// `Selectable`s are not thread-safe, only to be used on the appropriate
/// `EventLoop`.
protocol Selectable {
func withUnsafeHandle<T>(_: (CInt) throws -> T) throws -> T
func withUnsafeHandle<T>(_: (NIOBSDSocket.Handle) throws -> T) throws -> T
}

View File

@ -31,7 +31,7 @@
/// - throws: An `IOError` if creation of the socket failed.
init(protocolFamily: NIOBSDSocket.ProtocolFamily, setNonBlocking: Bool = false) throws {
let sock = try BaseSocket.makeSocket(protocolFamily: protocolFamily, type: .stream, setNonBlocking: setNonBlocking)
try super.init(descriptor: sock)
try super.init(socket: sock)
}
/// Create a new instance.
@ -40,8 +40,21 @@
/// - descriptor: The _Unix file descriptor_ representing the bound socket.
/// - setNonBlocking: Set non-blocking mode on the socket.
/// - throws: An `IOError` if socket is invalid.
init(descriptor: CInt, setNonBlocking: Bool = false) throws {
try super.init(descriptor: descriptor)
#if !os(Windows)
@available(*, deprecated, renamed: "init(socket:setNonBlocking:)")
convenience init(descriptor: CInt, setNonBlocking: Bool = false) throws {
try self.init(socket: descriptor, setNonBlocking: setNonBlocking)
}
#endif
/// Create a new instance.
///
/// - parameters:
/// - descriptor: The _Unix file descriptor_ representing the bound socket.
/// - setNonBlocking: Set non-blocking mode on the socket.
/// - throws: An `IOError` if socket is invalid.
init(socket: NIOBSDSocket.Handle, setNonBlocking: Bool = false) throws {
try super.init(socket: socket)
if setNonBlocking {
try self.setNonBlocking()
}
@ -54,7 +67,7 @@
/// - throws: An `IOError` if creation of the socket failed.
func listen(backlog: Int32 = 128) throws {
try withUnsafeHandle {
_ = try Posix.listen(descriptor: $0, backlog: backlog)
_ = try NIOBSDSocket.listen(socket: $0, backlog: backlog)
}
}
@ -75,13 +88,13 @@
}
let result = try Linux.accept4(descriptor: fd, addr: nil, len: nil, flags: flags)
#else
let result = try Posix.accept(descriptor: fd, addr: nil, len: nil)
let result = try NIOBSDSocket.accept(socket: fd, address: nil, address_len: nil)
#endif
guard let fd = result else {
return nil
}
let sock = try Socket(descriptor: fd)
let sock = try Socket(socket: fd)
#if !os(Linux)
if setNonBlocking {
do {

View File

@ -34,7 +34,7 @@ typealias IOVector = iovec
/// - throws: An `IOError` if creation of the socket failed.
init(protocolFamily: NIOBSDSocket.ProtocolFamily, type: NIOBSDSocket.SocketType, setNonBlocking: Bool = false) throws {
let sock = try BaseSocket.makeSocket(protocolFamily: protocolFamily, type: type, setNonBlocking: setNonBlocking)
try super.init(descriptor: sock)
try super.init(socket: sock)
}
/// Create a new instance out of an already established socket.
@ -43,8 +43,21 @@ typealias IOVector = iovec
/// - descriptor: The existing socket descriptor.
/// - setNonBlocking: Set non-blocking mode on the socket.
/// - throws: An `IOError` if could not change the socket into non-blocking
init(descriptor: CInt, setNonBlocking: Bool) throws {
try super.init(descriptor: descriptor)
#if !os(Windows)
@available(*, deprecated, renamed: "init(socket:setNonBlocking:)")
convenience init(descriptor: CInt, setNonBlocking: Bool) throws {
try self.init(socket: descriptor, setNonBlocking: setNonBlocking)
}
#endif
/// Create a new instance out of an already established socket.
///
/// - parameters:
/// - descriptor: The existing socket descriptor.
/// - setNonBlocking: Set non-blocking mode on the socket.
/// - throws: An `IOError` if could not change the socket into non-blocking
init(socket: NIOBSDSocket.Handle, setNonBlocking: Bool) throws {
try super.init(socket: socket)
if setNonBlocking {
try self.setNonBlocking()
}
@ -57,8 +70,22 @@ typealias IOVector = iovec
///
/// - parameters:
/// - descriptor: The file descriptor to wrap.
override init(descriptor: CInt) throws {
try super.init(descriptor: descriptor)
#if !os(Windows)
@available(*, deprecated, renamed: "init(socket:)")
convenience init(descriptor: CInt) throws {
try self.init(socket: descriptor)
}
#endif
/// Create a new instance.
///
/// The ownership of the passed in descriptor is transferred to this class. A user must call `close` to close the underlying
/// file descriptor once it's not needed / used anymore.
///
/// - parameters:
/// - descriptor: The file descriptor to wrap.
override init(socket: NIOBSDSocket.Handle) throws {
try super.init(socket: socket)
}
/// Connect to the `SocketAddress`.
@ -84,7 +111,8 @@ typealias IOVector = iovec
var addr = addr
return try withUnsafePointer(to: &addr) { ptr in
try ptr.withMemoryRebound(to: sockaddr.self, capacity: 1) { ptr in
try Posix.connect(descriptor: fd, addr: ptr, size: socklen_t(MemoryLayout<T>.size))
try NIOBSDSocket.connect(socket: fd, address: ptr,
address_len: socklen_t(MemoryLayout<T>.size))
}
}
}
@ -108,7 +136,8 @@ typealias IOVector = iovec
/// - throws: An `IOError` if the operation failed.
func write(pointer: UnsafeRawBufferPointer) throws -> IOResult<Int> {
return try withUnsafeHandle {
try Posix.write(descriptor: $0, pointer: pointer.baseAddress!, size: pointer.count)
try NIOBSDSocket.send(socket: $0, buffer: pointer.baseAddress!,
length: pointer.count)
}
}
@ -133,9 +162,11 @@ typealias IOVector = iovec
/// - throws: An `IOError` if the operation failed.
func sendto(pointer: UnsafeRawBufferPointer, destinationPtr: UnsafePointer<sockaddr>, destinationSize: socklen_t) throws -> IOResult<Int> {
return try withUnsafeHandle {
try Posix.sendto(descriptor: $0, pointer: UnsafeMutableRawPointer(mutating: pointer.baseAddress!),
size: pointer.count, destinationPtr: destinationPtr,
destinationSize: destinationSize)
try NIOBSDSocket.sendto(socket: $0,
buffer: UnsafeMutableRawPointer(mutating: pointer.baseAddress!),
length: pointer.count,
dest_addr: destinationPtr,
dest_len: destinationSize)
}
}
@ -162,10 +193,10 @@ typealias IOVector = iovec
func recvfrom(pointer: UnsafeMutableRawBufferPointer, storage: inout sockaddr_storage, storageLen: inout socklen_t) throws -> IOResult<(Int)> {
return try withUnsafeHandle { fd in
try storage.withMutableSockAddr { (storagePtr, _) in
try Posix.recvfrom(descriptor: fd, pointer: pointer.baseAddress!,
len: pointer.count,
addr: storagePtr,
addrlen: &storageLen)
try NIOBSDSocket.recvfrom(socket: fd, buffer: pointer.baseAddress!,
length: pointer.count,
address: storagePtr,
address_len: &storageLen)
}
}
}
@ -180,7 +211,8 @@ typealias IOVector = iovec
/// - throws: An `IOError` if the operation failed.
func sendFile(fd: Int32, offset: Int, count: Int) throws -> IOResult<Int> {
return try withUnsafeHandle {
try Posix.sendfile(descriptor: $0, fd: fd, offset: off_t(offset), count: count)
try NIOBSDSocket.sendfile(socket: $0, fd: fd, offset: off_t(offset),
len: off_t(count))
}
}
@ -192,7 +224,9 @@ typealias IOVector = iovec
/// - throws: An `IOError` if the operation failed.
func recvmmsg(msgs: UnsafeMutableBufferPointer<MMsgHdr>) throws -> IOResult<Int> {
return try withUnsafeHandle {
try Posix.recvmmsg(sockfd: $0, msgvec: msgs.baseAddress!, vlen: CUnsignedInt(msgs.count), flags: 0, timeout: nil)
try NIOBSDSocket.recvmmsg(socket: $0, msgvec: msgs.baseAddress!,
vlen: CUnsignedInt(msgs.count), flags: 0,
timeout: nil)
}
}
@ -204,7 +238,8 @@ typealias IOVector = iovec
/// - throws: An `IOError` if the operation failed.
func sendmmsg(msgs: UnsafeMutableBufferPointer<MMsgHdr>) throws -> IOResult<Int> {
return try withUnsafeHandle {
try Posix.sendmmsg(sockfd: $0, msgvec: msgs.baseAddress!, vlen: CUnsignedInt(msgs.count), flags: 0)
try NIOBSDSocket.sendmmsg(socket: $0, msgvec: msgs.baseAddress!,
vlen: CUnsignedInt(msgs.count), flags: 0)
}
}
@ -215,7 +250,7 @@ typealias IOVector = iovec
/// - throws: An `IOError` if the operation failed.
func shutdown(how: Shutdown) throws {
return try withUnsafeHandle {
try Posix.shutdown(descriptor: $0, how: how)
try NIOBSDSocket.shutdown(socket: $0, how: how)
}
}
}

View File

@ -264,17 +264,26 @@ public enum SocketAddress: CustomStringConvertible {
/// - returns: the `SocketAddress` corresponding to this string and port combination.
/// - throws: may throw `SocketAddressError.failedToParseIPString` if the IP address cannot be parsed.
public init(ipAddress: String, port: Int) throws {
var ipv4Addr = in_addr()
var ipv6Addr = in6_addr()
self = try ipAddress.withCString {
if inet_pton(NIOBSDSocket.AddressFamily.inet.rawValue, $0, &ipv4Addr) == 1 {
do {
var ipv4Addr = in_addr()
try NIOBSDSocket.inet_pton(af: .inet, src: $0, dst: &ipv4Addr)
var addr = sockaddr_in()
addr.sin_family = sa_family_t(NIOBSDSocket.AddressFamily.inet.rawValue)
addr.sin_port = in_port_t(port).bigEndian
addr.sin_addr = ipv4Addr
return .v4(.init(address: addr, host: ""))
} else if inet_pton(NIOBSDSocket.AddressFamily.inet6.rawValue, $0, &ipv6Addr) == 1 {
} catch {
// If `inet_pton` fails as an IPv4 address, we will try as an
// IPv6 address.
}
do {
var ipv6Addr = in6_addr()
try NIOBSDSocket.inet_pton(af: .inet6, src: $0, dst: &ipv6Addr)
var addr = sockaddr_in6()
addr.sin6_family = sa_family_t(NIOBSDSocket.AddressFamily.inet6.rawValue)
addr.sin6_port = in_port_t(port).bigEndian
@ -282,9 +291,12 @@ public enum SocketAddress: CustomStringConvertible {
addr.sin6_addr = ipv6Addr
addr.sin6_scope_id = 0
return .v6(.init(address: addr, host: ""))
} else {
throw SocketAddressError.failedToParseIPString(ipAddress)
} catch {
// If `inet_pton` fails as an IPv6 address (and has failed as an
// IPv4 address above), we will throw an error below.
}
throw SocketAddressError.failedToParseIPString(ipAddress)
}
}

View File

@ -40,9 +40,9 @@ final class SocketChannel: BaseStreamSocketChannel<Socket> {
try super.init(socket: socket, parent: nil, eventLoop: eventLoop, recvAllocator: AdaptiveRecvByteBufferAllocator())
}
init(eventLoop: SelectableEventLoop, descriptor: CInt) throws {
let socket = try Socket(descriptor: descriptor, setNonBlocking: true)
try super.init(socket: socket, parent: nil, eventLoop: eventLoop, recvAllocator: AdaptiveRecvByteBufferAllocator())
init(eventLoop: SelectableEventLoop, socket: NIOBSDSocket.Handle) throws {
let sock = try Socket(socket: socket, setNonBlocking: true)
try super.init(socket: sock, parent: nil, eventLoop: eventLoop, recvAllocator: AdaptiveRecvByteBufferAllocator())
}
init(socket: Socket, parent: Channel? = nil, eventLoop: SelectableEventLoop) throws {
@ -147,9 +147,9 @@ final class ServerSocketChannel: BaseSocketChannel<ServerSocket> {
recvAllocator: AdaptiveRecvByteBufferAllocator())
}
convenience init(descriptor: CInt, eventLoop: SelectableEventLoop, group: EventLoopGroup) throws {
let socket = try ServerSocket(descriptor: descriptor, setNonBlocking: true)
try self.init(serverSocket: socket, eventLoop: eventLoop, group: group)
convenience init(socket: NIOBSDSocket.Handle, eventLoop: SelectableEventLoop, group: EventLoopGroup) throws {
let sock = try ServerSocket(socket: socket, setNonBlocking: true)
try self.init(serverSocket: sock, eventLoop: eventLoop, group: group)
try self.socket.listen(backlog: backlog)
}
@ -342,8 +342,8 @@ final class DatagramChannel: BaseSocketChannel<Socket> {
return super.isOpen
}
convenience init(eventLoop: SelectableEventLoop, descriptor: CInt) throws {
let socket = try Socket(descriptor: descriptor)
convenience init(eventLoop: SelectableEventLoop, socket: NIOBSDSocket.Handle) throws {
let socket = try Socket(socket: socket)
do {
try self.init(socket: socket, eventLoop: eventLoop)

View File

@ -26,6 +26,10 @@ internal typealias MMsgHdr = CNIODarwin_mmsghdr
@_exported import Glibc
import CNIOLinux
internal typealias MMsgHdr = CNIOLinux_mmsghdr
#elseif os(Windows)
import CNIOWindows
internal typealias sockaddr = WinSDK.SOCKADDR
internal typealias MMsgHdr = CNIOWindows_mmsghdr
#else
let badOS = { fatalError("unsupported OS") }()
#endif
@ -85,17 +89,21 @@ private let sysGetifaddrs: @convention(c) (UnsafeMutablePointer<UnsafeMutablePoi
private let sysFreeifaddrs: @convention(c) (UnsafeMutablePointer<ifaddrs>?) -> Void = freeifaddrs
private let sysIfNameToIndex: @convention(c) (UnsafePointer<CChar>?) -> CUnsignedInt = if_nametoindex
private let sysInet_ntop: @convention(c) (CInt, UnsafeRawPointer?, UnsafeMutablePointer<CChar>?, socklen_t) -> UnsafePointer<CChar>? = inet_ntop
private let sysInet_pton: @convention(c) (CInt, UnsafePointer<CChar>?, UnsafeMutableRawPointer?) -> CInt = inet_pton
private let sysSocketpair: @convention(c) (CInt, CInt, CInt, UnsafeMutablePointer<CInt>?) -> CInt = socketpair
#if os(Linux)
private let sysFstat: @convention(c) (CInt, UnsafeMutablePointer<stat>) -> CInt = fstat
private let sysSendMmsg: @convention(c) (CInt, UnsafeMutablePointer<CNIOLinux_mmsghdr>?, CUnsignedInt, CInt) -> CInt = CNIOLinux_sendmmsg
private let sysRecvMmsg: @convention(c) (CInt, UnsafeMutablePointer<CNIOLinux_mmsghdr>?, CUnsignedInt, CInt, UnsafeMutablePointer<timespec>?) -> CInt = CNIOLinux_recvmmsg
#else
#elseif os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
private let sysFstat: @convention(c) (CInt, UnsafeMutablePointer<stat>?) -> CInt = fstat
private let sysKevent = kevent
private let sysSendMmsg: @convention(c) (CInt, UnsafeMutablePointer<CNIODarwin_mmsghdr>?, CUnsignedInt, CInt) -> CInt = CNIODarwin_sendmmsg
private let sysRecvMmsg: @convention(c) (CInt, UnsafeMutablePointer<CNIODarwin_mmsghdr>?, CUnsignedInt, CInt, UnsafeMutablePointer<timespec>?) -> CInt = CNIODarwin_recvmmsg
#elseif os(Windows)
private let sysSendMmsg: @convention(c) (NIOBSDSocket.Handle, UnsafeMutablePointer<CNIOWindows_mmsghdr>?, CUnsignedInt, CInt) -> CInt = CNIOWindows_sendmmsg
private let sysRecvMmsg: @convention(c) (NIOBSDSocket.Handle, UnsafeMutablePointer<CNIOWindows_mmsghdr>?, CUnsignedInt, CInt, UnsafeMutablePointer<timespec>?) -> CInt = CNIOWindows_recvmmsg
#endif
private func isUnacceptableErrno(_ code: Int32) -> Bool {
@ -157,23 +165,6 @@ internal func wrapErrorIsNullReturnCall<T>(where function: String = #function, _
}
}
enum Shutdown {
case RD
case WR
case RDWR
fileprivate var cValue: CInt {
switch self {
case .RD:
return CInt(Posix.SHUT_RD)
case .WR:
return CInt(Posix.SHUT_WR)
case .RDWR:
return CInt(Posix.SHUT_RDWR)
}
}
}
internal enum Posix {
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
static let UIO_MAXIOV: Int = 1024
@ -190,6 +181,15 @@ internal enum Posix {
static var UIO_MAXIOV: Int {
fatalError("unsupported OS")
}
static var SHUT_RD: Int {
fatalError("unsupported OS")
}
static var SHUT_WR: Int {
fatalError("unsupported OS")
}
static var SHUT_RDWR: Int {
fatalError("unsupported OS")
}
#endif
@inline(never)
@ -403,6 +403,15 @@ internal enum Posix {
}
}
@inline(never)
public static func inet_pton(addressFamily: sa_family_t, addressDescription: UnsafePointer<CChar>, address: UnsafeMutableRawPointer) throws {
switch sysInet_pton(CInt(addressFamily), addressDescription, address) {
case 0: throw IOError(errnoCode: EINVAL, reason: #function)
case 1: return
default: throw IOError(errnoCode: errno, reason: #function)
}
}
// It's not really posix but exists on Linux and MacOS / BSD so just put it here for now to keep it simple
@inline(never)
public static func sendfile(descriptor: CInt, fd: CInt, offset: off_t, count: size_t) throws -> IOResult<Int> {

View File

@ -155,7 +155,7 @@ class BootstrapTest: XCTestCase {
socketVector: &socketFDs))
defer {
// 0 is closed together with the Channel below.
XCTAssertNoThrow(try Posix.close(descriptor: socketFDs[1]))
XCTAssertNoThrow(try NIOBSDSocket.close(socket: socketFDs[1]))
}
XCTAssertNoThrow(try ClientBootstrap(group: self.freshEventLoop())
@ -163,19 +163,20 @@ class BootstrapTest: XCTestCase {
XCTAssert(channel.eventLoop.inEventLoop)
return self.freshEventLoop().makeSucceededFuture(())
}
.withConnectedSocket(descriptor: socketFDs[0])
.withConnectedSocket(socketFDs[0])
.wait()
.close()
.wait())
}
func testPreConnectedServerSocketToleratesFuturesFromDifferentEventLoopsReturnedInInitializers() throws {
let socket = try Posix.socket(domain: .inet, type: .stream, protocol: 0)
let socket =
try NIOBSDSocket.socket(domain: .inet, type: .stream, protocol: 0)
let serverAddress = try assertNoThrowWithValue(SocketAddress.makeAddressResolvingHost("127.0.0.1", port: 0))
try serverAddress.withSockAddr { serverAddressPtr, size in
try Posix.bind(descriptor: socket, ptr: serverAddressPtr,
bytes: size)
try serverAddress.withSockAddr { address, len in
try NIOBSDSocket.bind(socket: socket, address: address,
address_len: socklen_t(len))
}
let childChannelDone = self.freshEventLoop().next().makePromise(of: Void.self)
@ -196,7 +197,7 @@ class BootstrapTest: XCTestCase {
}
return self.freshEventLoop().makeSucceededFuture(())
}
.withBoundSocket(descriptor: socket)
.withBoundSocket(socket)
.wait())
let client = try assertNoThrowWithValue(ClientBootstrap(group: self.freshEventLoop())
.channelInitializer { channel in
@ -330,7 +331,7 @@ class BootstrapTest: XCTestCase {
}
return channel.pipeline.addHandler(MakeSureAutoReadIsOffInChannelInitializer())
}
.withConnectedSocket(descriptor: fd)
.withConnectedSocket(fd)
.wait())
XCTAssertNotNil(channel)
XCTAssertNoThrow(try channel?.close().wait())

View File

@ -2471,7 +2471,8 @@ public final class ChannelTests: XCTestCase {
}
try channel.socket.withUnsafeHandle { fd in
var pollFd: pollfd = .init(fd: fd, events: Int16(POLLIN), revents: 0)
let nfds = try Posix.poll(fds: &pollFd, nfds: 1, timeout: -1)
let nfds =
try NIOBSDSocket.poll(fds: &pollFd, nfds: 1, timeout: -1)
XCTAssertEqual(1, nfds)
}
}

View File

@ -126,25 +126,30 @@ private extension Channel {
private extension SocketAddress {
init(host: String, ipAddress: String, port: Int) {
var v4addr = in_addr()
var v6addr = in6_addr()
do {
var v4addr = in_addr()
try NIOBSDSocket.inet_pton(af: .inet, src: ipAddress, dst: &v4addr)
if inet_pton(NIOBSDSocket.AddressFamily.inet.rawValue, ipAddress, &v4addr) == 1 {
var sockaddr = sockaddr_in()
sockaddr.sin_family = sa_family_t(NIOBSDSocket.AddressFamily.inet.rawValue)
sockaddr.sin_port = in_port_t(port).bigEndian
sockaddr.sin_addr = v4addr
self = .init(sockaddr, host: host)
} else if inet_pton(NIOBSDSocket.AddressFamily.inet6.rawValue, ipAddress, &v6addr) == 1 {
var sockaddr = sockaddr_in6()
sockaddr.sin6_family = sa_family_t(NIOBSDSocket.AddressFamily.inet6.rawValue)
sockaddr.sin6_port = in_port_t(port).bigEndian
sockaddr.sin6_flowinfo = 0
sockaddr.sin6_scope_id = 0
sockaddr.sin6_addr = v6addr
self = .init(sockaddr, host: host)
} else {
fatalError("Unable to convert to IP")
} catch {
do {
var v6addr = in6_addr()
try NIOBSDSocket.inet_pton(af: .inet6, src: ipAddress, dst: &v6addr)
var sockaddr = sockaddr_in6()
sockaddr.sin6_family = sa_family_t(NIOBSDSocket.AddressFamily.inet6.rawValue)
sockaddr.sin6_port = in_port_t(port).bigEndian
sockaddr.sin6_flowinfo = 0
sockaddr.sin6_scope_id = 0
sockaddr.sin6_addr = v6addr
self = .init(sockaddr, host: host)
} catch {
fatalError("Unable to convert to IP")
}
}
}
@ -153,10 +158,10 @@ private extension SocketAddress {
switch self {
case .v4(let address):
var baseAddress = address.address
precondition(inet_ntop(NIOBSDSocket.AddressFamily.inet.rawValue, &baseAddress.sin_addr, ptr, 256) != nil)
precondition(try! NIOBSDSocket.inet_ntop(af: .inet, src: &baseAddress.sin_addr, dst: ptr, size: 256) != nil)
case .v6(let address):
var baseAddress = address.address
precondition(inet_ntop(NIOBSDSocket.AddressFamily.inet6.rawValue, &baseAddress.sin6_addr, ptr, 256) != nil)
precondition(try! NIOBSDSocket.inet_ntop(af: .inet6, src: &baseAddress.sin6_addr, dst: ptr, size: 256) != nil)
case .unixDomainSocket:
fatalError("No UDS support in happy eyeballs.")
}

View File

@ -191,7 +191,8 @@ class NonBlockingFileIOTest: XCTestCase {
func testFailedIO() throws {
enum DummyError: Error { case dummy }
let unconnectedSockFH = NIOFileHandle(descriptor: try! Posix.socket(domain: .unix, type: .stream, protocol: 0))
let unconnectedSockFH =
NIOFileHandle(descriptor: try! NIOBSDSocket.socket(domain: .unix, type: .stream, protocol: 0))
defer {
XCTAssertNoThrow(try unconnectedSockFH.close())
}

View File

@ -376,9 +376,9 @@ class SelectorTest: XCTestCase {
}
class FakeSocket: Socket {
private let hasBeenClosedPromise: EventLoopPromise<Void>
init(hasBeenClosedPromise: EventLoopPromise<Void>, descriptor: CInt) throws {
init(hasBeenClosedPromise: EventLoopPromise<Void>, socket: NIOBSDSocket.Handle) throws {
self.hasBeenClosedPromise = hasBeenClosedPromise
try super.init(descriptor: descriptor)
try super.init(socket: socket)
}
override func close() throws {
self.hasBeenClosedPromise.succeed(())
@ -395,7 +395,7 @@ class SelectorTest: XCTestCase {
let el = group.next() as! SelectableEventLoop
let channelHasBeenClosedPromise = el.makePromise(of: Void.self)
let channel = try SocketChannel(socket: FakeSocket(hasBeenClosedPromise: channelHasBeenClosedPromise,
descriptor: socketFDs[0]), eventLoop: el)
socket: socketFDs[0]), eventLoop: el)
let sched = el.scheduleRepeatedTask(initialDelay: .microseconds(delayToUseInMicroSeconds),
delay: .microseconds(delayToUseInMicroSeconds)) { (_: RepeatedTask) in
_ = numberFires.add(1)

View File

@ -330,7 +330,7 @@ public final class SocketChannelTest : XCTestCase {
let serverSock = try Socket(protocolFamily: .inet, type: .stream)
try serverSock.bind(to: SocketAddress(ipAddress: "127.0.0.1", port: 0))
let serverChannelFuture = try serverSock.withUnsafeHandle {
ServerBootstrap(group: group).withBoundSocket(descriptor: dup($0))
ServerBootstrap(group: group).withBoundSocket(dup($0))
}
try serverSock.close()
let serverChannel = try serverChannelFuture.wait()
@ -339,7 +339,7 @@ public final class SocketChannelTest : XCTestCase {
let connected = try clientSock.connect(to: serverChannel.localAddress!)
XCTAssertEqual(connected, true)
let clientChannelFuture = try clientSock.withUnsafeHandle {
ClientBootstrap(group: group).withConnectedSocket(descriptor: dup($0))
ClientBootstrap(group: group).withConnectedSocket(dup($0))
}
try clientSock.close()
let clientChannel = try clientChannelFuture.wait()
@ -357,7 +357,7 @@ public final class SocketChannelTest : XCTestCase {
let serverSock = try Socket(protocolFamily: .inet, type: .datagram)
try serverSock.bind(to: SocketAddress(ipAddress: "127.0.0.1", port: 0))
let serverChannelFuture = try serverSock.withUnsafeHandle {
DatagramBootstrap(group: group).withBoundSocket(descriptor: dup($0))
DatagramBootstrap(group: group).withBoundSocket(dup($0))
}
try serverSock.close()
let serverChannel = try serverChannelFuture.wait()

View File

@ -253,10 +253,10 @@ class HookedSocket: Socket, UserKernelInterface {
fileprivate let userToKernel: LockedBox<UserToKernel>
fileprivate let kernelToUser: LockedBox<KernelToUser>
init(userToKernel: LockedBox<UserToKernel>, kernelToUser: LockedBox<KernelToUser>, descriptor: CInt) throws {
init(userToKernel: LockedBox<UserToKernel>, kernelToUser: LockedBox<KernelToUser>, socket: NIOBSDSocket.Handle) throws {
self.userToKernel = userToKernel
self.kernelToUser = kernelToUser
try super.init(descriptor: descriptor)
try super.init(socket: socket)
}
override func ignoreSIGPIPE() throws {
@ -505,7 +505,7 @@ extension SALTest {
}) {
try SocketChannel(socket: HookedSocket(userToKernel: self.userToKernelBox,
kernelToUser: self.kernelToUserBox,
descriptor: .max),
socket: .max),
eventLoop: eventLoop)
}
try self.assertParkedRightNow()
@ -527,7 +527,7 @@ extension SALTest {
}) {
try SocketChannel(socket: HookedSocket(userToKernel: self.userToKernelBox,
kernelToUser: self.kernelToUserBox,
descriptor: .max),
socket: .max),
eventLoop: self.loop)
}
try self.assertParkedRightNow()

View File

@ -27,7 +27,11 @@ class SystemTest: XCTestCase {
do {
_ = try withUnsafePointer(to: &randomBytes) { ptr in
try readFD.withUnsafeFileDescriptor { readFD in
try Posix.setsockopt(socket: readFD, level: -1, optionName: -1, optionValue: ptr, optionLen: 0)
try NIOBSDSocket.setsockopt(socket: readFD,
level: NIOBSDSocket.OptionLevel(rawValue: -1),
option_name: NIOBSDSocket.Option(rawValue: -1),
option_value: ptr,
option_len: 0)
}
}
XCTFail("success even though the call was invalid")