explain some force tries/unwraps (#619)
Motivation: It's generally good style to explain why something needs to be force unwrapped/tried if not obvious. Modifications: Add explanations to a bunch of places. Result: Code easier to understand.
This commit is contained in:
parent
cb2367f391
commit
2e96e47016
|
@ -24,13 +24,13 @@ protocol SockAddrProtocol {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a description for the given address.
|
/// Returns a description for the given address.
|
||||||
internal func descriptionForAddress(family: CInt, bytes: UnsafeRawPointer, length byteCount: Int) -> String {
|
internal func descriptionForAddress(family: CInt, bytes: UnsafeRawPointer, length byteCount: Int) throws -> String {
|
||||||
var addressBytes: [Int8] = Array(repeating: 0, count: byteCount)
|
var addressBytes: [Int8] = Array(repeating: 0, count: byteCount)
|
||||||
return addressBytes.withUnsafeMutableBufferPointer { (addressBytesPtr: inout UnsafeMutableBufferPointer<Int8>) -> String in
|
return try addressBytes.withUnsafeMutableBufferPointer { (addressBytesPtr: inout UnsafeMutableBufferPointer<Int8>) -> String in
|
||||||
try! Posix.inet_ntop(addressFamily: family,
|
try Posix.inet_ntop(addressFamily: family,
|
||||||
addressBytes: bytes,
|
addressBytes: bytes,
|
||||||
addressDescription: addressBytesPtr.baseAddress!,
|
addressDescription: addressBytesPtr.baseAddress!,
|
||||||
addressDescriptionLength: socklen_t(byteCount))
|
addressDescriptionLength: socklen_t(byteCount))
|
||||||
return addressBytesPtr.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: byteCount) { addressBytesPtr -> String in
|
return addressBytesPtr.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: byteCount) { addressBytesPtr -> String in
|
||||||
String(cString: addressBytesPtr)
|
String(cString: addressBytesPtr)
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,8 @@ extension sockaddr_in: SockAddrProtocol {
|
||||||
/// Returns a description of the `sockaddr_in`.
|
/// Returns a description of the `sockaddr_in`.
|
||||||
mutating func addressDescription() -> String {
|
mutating func addressDescription() -> String {
|
||||||
return withUnsafePointer(to: &self.sin_addr) { addrPtr in
|
return withUnsafePointer(to: &self.sin_addr) { addrPtr in
|
||||||
descriptionForAddress(family: AF_INET, bytes: addrPtr, length: Int(INET_ADDRSTRLEN))
|
// this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC)
|
||||||
|
try! descriptionForAddress(family: AF_INET, bytes: addrPtr, length: Int(INET_ADDRSTRLEN))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +102,8 @@ extension sockaddr_in6: SockAddrProtocol {
|
||||||
/// Returns a description of the `sockaddr_in6`.
|
/// Returns a description of the `sockaddr_in6`.
|
||||||
mutating func addressDescription() -> String {
|
mutating func addressDescription() -> String {
|
||||||
return withUnsafePointer(to: &self.sin6_addr) { addrPtr in
|
return withUnsafePointer(to: &self.sin6_addr) { addrPtr in
|
||||||
descriptionForAddress(family: AF_INET6, bytes: addrPtr, length: Int(INET6_ADDRSTRLEN))
|
// this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC)
|
||||||
|
try! descriptionForAddress(family: AF_INET6, bytes: addrPtr, length: Int(INET6_ADDRSTRLEN))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -805,6 +805,9 @@ class BaseSocketChannel<T: BaseSocket>: SelectableChannel, ChannelCore {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.lifecycleManager.isPreRegistered {
|
if self.lifecycleManager.isPreRegistered {
|
||||||
|
// we expect kqueue/epoll registration to always succeed which is basically true, except for errors that
|
||||||
|
// should be fatal (EBADF, EFAULT, ESRCH, ENOMEM) and a two 'table full' (EMFILE, ENFILE) error kinds which
|
||||||
|
// we don't handle yet but might do in the future (#469).
|
||||||
try! becomeFullyRegistered0()
|
try! becomeFullyRegistered0()
|
||||||
if self.lifecycleManager.isRegisteredFully {
|
if self.lifecycleManager.isRegisteredFully {
|
||||||
self.becomeActive0(promise: promise)
|
self.becomeActive0(promise: promise)
|
||||||
|
@ -966,6 +969,9 @@ class BaseSocketChannel<T: BaseSocket>: SelectableChannel, ChannelCore {
|
||||||
if let channelErr = err as? ChannelError, channelErr == ChannelError.eof {
|
if let channelErr = err as? ChannelError, channelErr == ChannelError.eof {
|
||||||
readStreamState = .eof
|
readStreamState = .eof
|
||||||
// Directly call getOption0 as we are already on the EventLoop and so not need to create an extra future.
|
// Directly call getOption0 as we are already on the EventLoop and so not need to create an extra future.
|
||||||
|
|
||||||
|
// 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.
|
||||||
if self.lifecycleManager.isActive, try! getOption0(option: ChannelOptions.allowRemoteHalfClosure) {
|
if self.lifecycleManager.isActive, try! getOption0(option: ChannelOptions.allowRemoteHalfClosure) {
|
||||||
// If we want to allow half closure we will just mark the input side of the Channel
|
// If we want to allow half closure we will just mark the input side of the Channel
|
||||||
// as closed.
|
// as closed.
|
||||||
|
|
|
@ -277,7 +277,7 @@ extension ByteBuffer {
|
||||||
///
|
///
|
||||||
/// - returns: A `ByteBuffer` sharing storage containing the readable bytes only.
|
/// - returns: A `ByteBuffer` sharing storage containing the readable bytes only.
|
||||||
public func slice() -> ByteBuffer {
|
public func slice() -> ByteBuffer {
|
||||||
return getSlice(at: self.readerIndex, length: self.readableBytes)!
|
return getSlice(at: self.readerIndex, length: self.readableBytes)! // must work, bytes definitely in the buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Slice `length` bytes off this `ByteBuffer` and move the reader index forward by `length`.
|
/// Slice `length` bytes off this `ByteBuffer` and move the reader index forward by `length`.
|
||||||
|
|
|
@ -53,7 +53,7 @@ public struct ByteBufferView: ContiguousCollection, RandomAccessCollection {
|
||||||
guard position >= self.range.lowerBound && position < self.range.upperBound else {
|
guard position >= self.range.lowerBound && position < self.range.upperBound else {
|
||||||
preconditionFailure("index \(position) out of range")
|
preconditionFailure("index \(position) out of range")
|
||||||
}
|
}
|
||||||
return self.buffer.getInteger(at: position)!
|
return self.buffer.getInteger(at: position)! // range check above
|
||||||
}
|
}
|
||||||
|
|
||||||
public subscript(range: Range<Index>) -> ByteBufferView {
|
public subscript(range: Range<Index>) -> ByteBufferView {
|
||||||
|
|
|
@ -185,7 +185,7 @@ extension Heap: CustomDebugStringConvertible {
|
||||||
return "<empty heap>"
|
return "<empty heap>"
|
||||||
}
|
}
|
||||||
let descriptions = self.storage.map { String(describing: $0) }
|
let descriptions = self.storage.map { String(describing: $0) }
|
||||||
let maxLen: Int = descriptions.map { $0.count }.max()!
|
let maxLen: Int = descriptions.map { $0.count }.max()! // storage checked non-empty above
|
||||||
let paddedDescs = descriptions.map { (desc: String) -> String in
|
let paddedDescs = descriptions.map { (desc: String) -> String in
|
||||||
var desc = desc
|
var desc = desc
|
||||||
while desc.count < maxLen {
|
while desc.count < maxLen {
|
||||||
|
|
|
@ -331,6 +331,8 @@ final class Selector<R: Registration> {
|
||||||
is likely to cause performance problems. By abusing ARC, we get the guarantee that there won't be any future
|
is likely to cause performance problems. By abusing ARC, we get the guarantee that there won't be any future
|
||||||
wakeup calls as there are no references to this selector left. 💁
|
wakeup calls as there are no references to this selector left. 💁
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// we try! this because `close` only fails in cases that should never happen (EBADF).
|
||||||
#if os(Linux)
|
#if os(Linux)
|
||||||
try! Posix.close(descriptor: self.eventfd)
|
try! Posix.close(descriptor: self.eventfd)
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -84,14 +84,16 @@ public enum SocketAddress: CustomStringConvertible {
|
||||||
host = addr.host.isEmpty ? nil : addr.host
|
host = addr.host.isEmpty ? nil : addr.host
|
||||||
type = "IPv4"
|
type = "IPv4"
|
||||||
var mutAddr = addr.address.sin_addr
|
var mutAddr = addr.address.sin_addr
|
||||||
addressString = descriptionForAddress(family: AF_INET, bytes: &mutAddr, length: Int(INET_ADDRSTRLEN))
|
// this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC)
|
||||||
|
addressString = try! descriptionForAddress(family: AF_INET, bytes: &mutAddr, length: Int(INET_ADDRSTRLEN))
|
||||||
|
|
||||||
port = "\(self.port!)"
|
port = "\(self.port!)"
|
||||||
case .v6(let addr):
|
case .v6(let addr):
|
||||||
host = addr.host.isEmpty ? nil : addr.host
|
host = addr.host.isEmpty ? nil : addr.host
|
||||||
type = "IPv6"
|
type = "IPv6"
|
||||||
var mutAddr = addr.address.sin6_addr
|
var mutAddr = addr.address.sin6_addr
|
||||||
addressString = descriptionForAddress(family: AF_INET6, bytes: &mutAddr, length: Int(INET6_ADDRSTRLEN))
|
// this uses inet_ntop which is documented to only fail if family is not AF_INET or AF_INET6 (or ENOSPC)
|
||||||
|
addressString = try! descriptionForAddress(family: AF_INET6, bytes: &mutAddr, length: Int(INET6_ADDRSTRLEN))
|
||||||
|
|
||||||
port = "\(self.port!)"
|
port = "\(self.port!)"
|
||||||
case .unixDomainSocket(let addr):
|
case .unixDomainSocket(let addr):
|
||||||
|
|
|
@ -78,6 +78,7 @@ private func isBlacklistedErrno(_ code: Int32) -> Bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func assertIsNotBlacklistedErrno(err: CInt, where function: StaticString) -> Void {
|
private func assertIsNotBlacklistedErrno(err: CInt, where function: StaticString) -> Void {
|
||||||
|
// strerror is documented to return "Unknown error: ..." for illegal value so it won't ever fail
|
||||||
assert(!isBlacklistedErrno(err), "blacklisted errno \(err) \(String(cString: strerror(err)!)) in \(function))")
|
assert(!isBlacklistedErrno(err), "blacklisted errno \(err) \(String(cString: strerror(err)!)) in \(function))")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -246,7 +246,7 @@ private extension String {
|
||||||
case .string(let string):
|
case .string(let string):
|
||||||
self = string
|
self = string
|
||||||
case .byteBuffer(let buffer):
|
case .byteBuffer(let buffer):
|
||||||
self = buffer.getString(at: buffer.readerIndex, length: buffer.readableBytes)!
|
self = buffer.getString(at: buffer.readerIndex, length: buffer.readableBytes)! // bytes definitely in buffer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,7 +189,7 @@ extension Heap: CustomDebugStringConvertible {
|
||||||
return "<empty heap>"
|
return "<empty heap>"
|
||||||
}
|
}
|
||||||
let descriptions = self.storage.map { String(describing: $0) }
|
let descriptions = self.storage.map { String(describing: $0) }
|
||||||
let maxLen: Int = descriptions.map { $0.count }.max()!
|
let maxLen: Int = descriptions.map { $0.count }.max()! // storage is guarded to be non-empty
|
||||||
let paddedDescs = descriptions.map { (desc: String) -> String in
|
let paddedDescs = descriptions.map { (desc: String) -> String in
|
||||||
var desc = desc
|
var desc = desc
|
||||||
while desc.count < maxLen {
|
while desc.count < maxLen {
|
||||||
|
|
|
@ -171,7 +171,7 @@ public class SniHandler: ByteToMessageDecoder {
|
||||||
//
|
//
|
||||||
// From this point onwards if we don't have enough data to satisfy a read, this is an error and
|
// From this point onwards if we don't have enough data to satisfy a read, this is an error and
|
||||||
// we will fall back to let the upper layers handle it.
|
// we will fall back to let the upper layers handle it.
|
||||||
tempBuffer = tempBuffer.getSlice(at: tempBuffer.readerIndex, length: Int(contentLength))!
|
tempBuffer = tempBuffer.getSlice(at: tempBuffer.readerIndex, length: Int(contentLength))! // length check above
|
||||||
|
|
||||||
// Now parse the handshake header. If the length of the handshake message is not exactly the
|
// Now parse the handshake header. If the length of the handshake message is not exactly the
|
||||||
// length of this record, something has gone wrong and we should give up.
|
// length of this record, something has gone wrong and we should give up.
|
||||||
|
@ -204,7 +204,7 @@ public class SniHandler: ByteToMessageDecoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the content type.
|
// Check the content type.
|
||||||
let contentType: UInt8 = buffer.readInteger()!
|
let contentType: UInt8 = buffer.readInteger()! // length check above
|
||||||
guard contentType == tlsContentTypeHandshake else {
|
guard contentType == tlsContentTypeHandshake else {
|
||||||
// Whatever this is, it's not a handshake message, so something has gone
|
// Whatever this is, it's not a handshake message, so something has gone
|
||||||
// wrong. We're going to fall back to the default handler here and let
|
// wrong. We're going to fall back to the default handler here and let
|
||||||
|
@ -213,7 +213,7 @@ public class SniHandler: ByteToMessageDecoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, check the major version.
|
// Now, check the major version.
|
||||||
let majorVersion: UInt8 = buffer.readInteger()!
|
let majorVersion: UInt8 = buffer.readInteger()! // length check above
|
||||||
guard majorVersion == 3 else {
|
guard majorVersion == 3 else {
|
||||||
// A major version of 3 is the major version used for SSLv3 and all subsequent versions
|
// A major version of 3 is the major version used for SSLv3 and all subsequent versions
|
||||||
// of the protocol. If that's not what this is, we don't know what's happening here.
|
// of the protocol. If that's not what this is, we don't know what's happening here.
|
||||||
|
@ -223,7 +223,7 @@ public class SniHandler: ByteToMessageDecoder {
|
||||||
|
|
||||||
// Skip the minor version byte, then grab the content length.
|
// Skip the minor version byte, then grab the content length.
|
||||||
buffer.moveReaderIndex(forwardBy: 1)
|
buffer.moveReaderIndex(forwardBy: 1)
|
||||||
let contentLength: UInt16 = buffer.readInteger()!
|
let contentLength: UInt16 = buffer.readInteger()! // length check above
|
||||||
return Int(contentLength)
|
return Int(contentLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,8 @@ extension WebSocketMaskingKey: ExpressibleByArrayLiteral {
|
||||||
public typealias ArrayLiteralElement = UInt8
|
public typealias ArrayLiteralElement = UInt8
|
||||||
|
|
||||||
public init(arrayLiteral elements: UInt8...) {
|
public init(arrayLiteral elements: UInt8...) {
|
||||||
self.init(elements)!
|
precondition(elements.count == 4, "WebSocketMaskingKeys must be exactly 4 bytes long")
|
||||||
|
self.init(elements)! // length precondition above
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,9 +156,12 @@ public struct WebSocketFrame {
|
||||||
/// The opcode for this frame.
|
/// The opcode for this frame.
|
||||||
public var opcode: WebSocketOpcode {
|
public var opcode: WebSocketOpcode {
|
||||||
get {
|
get {
|
||||||
|
// this is a public initialiser which only fails if the opcode is invalid. But all opcodes in 0...0xF
|
||||||
|
// space are valid so this can never fail.
|
||||||
return WebSocketOpcode(encodedWebSocketOpcode: firstByte & 0x0F)!
|
return WebSocketOpcode(encodedWebSocketOpcode: firstByte & 0x0F)!
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
|
// this ! isn't actually safe (won't cause problems in reality though). Filed as #617 to be fixed in NIO 2.0
|
||||||
self.firstByte = (self.firstByte & 0xF0) + UInt8(webSocketOpcode: newValue)!
|
self.firstByte = (self.firstByte & 0xF0) + UInt8(webSocketOpcode: newValue)!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue