//===----------------------------------------------------------------------===// // // This source file is part of the SwiftNIO open source project // // Copyright (c) 2017-2021 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 // //===----------------------------------------------------------------------===// import NIOCore /// An enum that represents websocket error codes. /// /// This enum provides names to all non-reserved code numbers, /// to avoid users needing to remember the specific numerical values /// of those codes. public enum WebSocketErrorCode: Sendable { /// Indicates a normal closure, meaning that the purpose for /// which the connection was established has been fulfilled. /// Corresponds to code 1000. case normalClosure /// Indicates that an endpoint is "going away", such as a server /// going down or a browser having navigated away from a page. /// Corresponds to code 1001. case goingAway /// Indicates that an endpoint is terminating the connection due /// to a protocol error. /// Corresponds to code 1002. case protocolError /// Indicates that an endpoint is terminating the connection /// because it has received a type of data it cannot accept (e.g. an /// endpoint that understands only text data may send this if it /// receives a binary message). /// Corresponds to code 1003. case unacceptableData /// Indicates that an endpoint is terminating the connection /// because it has received data within a message that was not /// consistent with the type of the message (e.g. non-UTF-8 /// data within a text message). /// Corresponds to code 1007. case dataInconsistentWithMessage /// Indicates that an endpoint is terminating the connection /// because it has received a message that violates its policy. This /// is a generic status code that can be returned when there is no /// other more suitable status code (e.g. 1003 or 1009) or if there /// is a need to hide specific details about the policy. /// Corresponds to code 1008. case policyViolation /// Indicates that an endpoint is terminating the connection /// because it has received a message that is too big for it to /// process. /// Corresponds to code 1009. case messageTooLarge /// Indicates that an endpoint (client) is terminating the /// connection because it has expected the server to negotiate one or /// more extension, but the server didn't return them in the response /// message of the WebSocket handshake. The list of extensions that /// are needed should appear in the `reason` part of the Close frame. /// Note that this status code is not used by the server, because it /// can fail the WebSocket handshake instead. /// Corresponds to code 1010. case missingExtension /// Indicates that a server is terminating the connection because /// it encountered an unexpected condition that prevented it from /// fulfilling the request. /// Corresponds to code 1011. case unexpectedServerError /// We don't have a better name for this error code. case unknown(UInt16) /// Create an error code from a raw 16-bit integer as sent on the /// network. /// /// - Parameters: /// integer: The integer form of the status code. internal init(networkInteger integer: UInt16) { switch integer { case 1000: self = .normalClosure case 1001: self = .goingAway case 1002: self = .protocolError case 1003: self = .unacceptableData case 1007: self = .dataInconsistentWithMessage case 1008: self = .policyViolation case 1009: self = .messageTooLarge case 1010: self = .missingExtension case 1011: self = .unexpectedServerError default: self = .unknown(integer) } } /// Create an error code from an integer. /// /// Will trap if the error code is not in the valid range. /// /// - Parameters: /// - codeNumber: The integer form of the status code. public init(codeNumber: Int) { self.init(networkInteger: UInt16(codeNumber)) } } extension WebSocketErrorCode: Equatable {} extension ByteBuffer { /// Read a websocket error code from a byte buffer. /// /// This method increments the reader index. /// /// - Returns: The error code, or `nil` if there were not enough readable bytes. public mutating func readWebSocketErrorCode() -> WebSocketErrorCode? { self.readInteger(as: UInt16.self).map { WebSocketErrorCode(networkInteger: $0) } } /// Get a websocket error code from a byte buffer. /// /// This method does not increment the reader index, and may be used to read an error /// code from outside the readable range of bytes. /// /// - Parameters: /// - index: The index into the buffer to read the error code from. /// - Returns: The error code, or `nil` if there were not enough bytes at that index. public func getWebSocketErrorCode(at index: Int) -> WebSocketErrorCode? { self.getInteger(at: index, as: UInt16.self).map { WebSocketErrorCode(networkInteger: $0) } } /// Write the given error code to the buffer. /// /// - Parameters: /// - code: The code to write into the buffer. public mutating func write(webSocketErrorCode code: WebSocketErrorCode) { self.writeInteger(UInt16(webSocketErrorCode: code)) } /// Read a `WebSocketErrorCode` from 2 bytes at the current `readerIndex`. Does not move the reader index. /// /// This method is equivalent to calling `getWebSocketErrorCode(at: readerIndex)`. /// /// - Returns: The error code, or `nil` if there are not enough bytes to read the code. @inlinable public func peekWebSocketErrorCode() -> WebSocketErrorCode? { self.getWebSocketErrorCode(at: self.readerIndex) } } extension UInt16 { /// Create a UInt16 corresponding to a given `WebSocketErrorCode`. /// /// - Parameters: /// - code: The `WebSocketErrorCode`. public init(webSocketErrorCode code: WebSocketErrorCode) { switch code { case .normalClosure: self = 1000 case .goingAway: self = 1001 case .protocolError: self = 1002 case .unacceptableData: self = 1003 case .dataInconsistentWithMessage: self = 1007 case .policyViolation: self = 1008 case .messageTooLarge: self = 1009 case .missingExtension: self = 1010 case .unexpectedServerError: self = 1011 case .unknown(let i): self = i } } }