From da7c04777b2997543eea13cb531acde8021bce19 Mon Sep 17 00:00:00 2001 From: Peter Adams <63288215+PeterAdams-A@users.noreply.github.com> Date: Wed, 3 Aug 2022 09:34:45 +0100 Subject: [PATCH] Docnioextras (#169) * Improve documentation for NIOExtras Motivation: Docs will help users do things correctly. Modifications: Add missing comments, improve links. Result: Better docc documentation * Docc in NIOHTTPCompression * NIOSOCKS docc * Correct bad symbol * Minor typo Co-authored-by: Cory Benfield --- .../NIOExtras/DebugInboundEventsHandler.swift | 90 +++++++++++++++---- .../DebugOutboundEventsHandler.swift | 86 +++++++++++++++--- Sources/NIOExtras/Docs.docc/Article.md | 13 +++ .../NIOExtras/FixedLengthFrameDecoder.swift | 16 +++- .../JSONRPCFraming+ContentLengthHeader.swift | 41 ++++++--- Sources/NIOExtras/JSONRPCFraming.swift | 1 + .../LengthFieldBasedFrameDecoder.swift | 53 ++++++----- Sources/NIOExtras/LengthFieldPrepender.swift | 21 +++-- Sources/NIOExtras/LineBasedFrameDecoder.swift | 20 ++++- Sources/NIOExtras/NIOExtrasError.swift | 1 + .../NIOExtras/NIOLengthFieldBitLength.swift | 14 ++- Sources/NIOExtras/PCAPRingBuffer.swift | 2 +- Sources/NIOExtras/QuiescingHelper.swift | 7 +- .../NIOExtras/RequestResponseHandler.swift | 16 ++-- Sources/NIOExtras/WritePCAPHandler.swift | 23 ++--- .../NIOHTTPCompression/HTTPCompression.swift | 16 +++- .../HTTPDecompression.swift | 4 + .../HTTPRequestCompressor.swift | 12 ++- .../HTTPRequestDecompressor.swift | 7 ++ .../HTTPResponseCompressor.swift | 16 +++- .../HTTPResponseDecompressor.swift | 7 ++ .../Channel Handlers/SOCKSClientHandler.swift | 11 ++- .../SOCKSServerHandshakeHandler.swift | 15 +++- .../NIOSOCKS/Messages/ClientGreeting.swift | 2 +- Sources/NIOSOCKS/Messages/SOCKSRequest.swift | 11 +-- Sources/NIOSOCKS/Messages/SOCKSResponse.swift | 2 +- .../SelectedAuthenticationMethod.swift | 2 +- 27 files changed, 393 insertions(+), 116 deletions(-) create mode 100644 Sources/NIOExtras/Docs.docc/Article.md diff --git a/Sources/NIOExtras/DebugInboundEventsHandler.swift b/Sources/NIOExtras/DebugInboundEventsHandler.swift index eb53d24..10c2e5b 100644 --- a/Sources/NIOExtras/DebugInboundEventsHandler.swift +++ b/Sources/NIOExtras/DebugInboundEventsHandler.swift @@ -19,76 +19,135 @@ import Glibc import NIOCore -/// ChannelInboundHandler that prints all inbound events that pass through the pipeline by default, -/// overridable by providing your own closure for custom logging. See DebugOutboundEventsHandler for outbound events. +/// `ChannelInboundHandler` that prints all inbound events that pass through the pipeline by default, +/// overridable by providing your own closure for custom logging. See ``DebugOutboundEventsHandler`` for outbound events. public class DebugInboundEventsHandler: ChannelInboundHandler { - + /// The type of the inbound data which is wrapped in `NIOAny`. public typealias InboundIn = Any + /// The type of the inbound data which will be forwarded to the next `ChannelInboundHandler` in the `ChannelPipeline`. public typealias InboudOut = Any - + + /// Enumeration of possible `ChannelHandler` events which can occur. public enum Event { + /// Channel was registered. case registered + /// Channel was unregistered. case unregistered + /// Channel became active. case active + /// Channel became inactive. case inactive + /// Data was received. case read(data: NIOAny) + /// Current read loop finished. case readComplete + /// Writability state of the channel changed. case writabilityChanged(isWritable: Bool) + /// A user inbound event was received. case userInboundEventTriggered(event: Any) + /// An error was caught. case errorCaught(Error) } var logger: (Event, ChannelHandlerContext) -> () - + + /// Initialiser. + /// - Parameter logger: Method for logging events which occur. public init(logger: @escaping (Event, ChannelHandlerContext) -> () = DebugInboundEventsHandler.defaultPrint) { self.logger = logger } - + + /// Logs ``Event.registered`` to ``logger`` + /// Called when the `Channel` has successfully registered with its `EventLoop` to handle I/O. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func channelRegistered(context: ChannelHandlerContext) { logger(.registered, context) context.fireChannelRegistered() } - + + /// Logs ``Event.unregistered`` to ``logger`` + /// Called when the `Channel` has unregistered from its `EventLoop`, and so will no longer be receiving I/O events. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func channelUnregistered(context: ChannelHandlerContext) { logger(.unregistered, context) context.fireChannelUnregistered() } - + + /// Logs ``Event.active`` to ``logger`` + /// Called when the `Channel` has become active, and is able to send and receive data. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func channelActive(context: ChannelHandlerContext) { logger(.active, context) context.fireChannelActive() } - + + /// Logs ``Event.inactive`` to ``logger`` + /// Called when the `Channel` has become inactive and is no longer able to send and receive data`. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func channelInactive(context: ChannelHandlerContext) { logger(.inactive, context) context.fireChannelInactive() } - + + /// Logs ``Event.read`` to ``logger`` + /// Called when some data has been read from the remote peer. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - data: The data read from the remote peer, wrapped in a `NIOAny`. public func channelRead(context: ChannelHandlerContext, data: NIOAny) { logger(.read(data: data), context) context.fireChannelRead(data) } - + + /// Logs ``Event.readComplete`` to ``logger`` + /// Called when the `Channel` has completed its current read loop, either because no more data is available + /// to read from the transport at this time, or because the `Channel` needs to yield to the event loop to process + /// other I/O events for other `Channel`s. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func channelReadComplete(context: ChannelHandlerContext) { logger(.readComplete, context) context.fireChannelReadComplete() } - + + /// Logs ``Event.writabilityChanged`` to ``logger`` + /// The writability state of the `Channel` has changed, either because it has buffered more data than the writability + /// high water mark, or because the amount of buffered data has dropped below the writability low water mark. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func channelWritabilityChanged(context: ChannelHandlerContext) { logger(.writabilityChanged(isWritable: context.channel.isWritable), context) context.fireChannelWritabilityChanged() } - + + /// Logs ``Event.userInboundEventTriggered`` to ``logger`` + /// Called when a user inbound event has been triggered. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - event: The event. public func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) { logger(.userInboundEventTriggered(event: event), context) context.fireUserInboundEventTriggered(event) } - + + /// Logs ``Event.errorCaught`` to ``logger`` + /// An error was encountered earlier in the inbound `ChannelPipeline`. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - error: The `Error` that was encountered. public func errorCaught(context: ChannelHandlerContext, error: Error) { logger(.errorCaught(error), context) context.fireErrorCaught(error) } - + + /// Print and flush a textual description of an ``Event``. + /// - parameters: + /// - event: The ``Event`` to print. + /// - context: The context `event` was received in. public static func defaultPrint(event: Event, in context: ChannelHandlerContext) { let message: String switch event { @@ -114,5 +173,4 @@ public class DebugInboundEventsHandler: ChannelInboundHandler { print(message + " in \(context.name)") fflush(stdout) } - } diff --git a/Sources/NIOExtras/DebugOutboundEventsHandler.swift b/Sources/NIOExtras/DebugOutboundEventsHandler.swift index 43d83a8..eb0437a 100644 --- a/Sources/NIOExtras/DebugOutboundEventsHandler.swift +++ b/Sources/NIOExtras/DebugOutboundEventsHandler.swift @@ -21,69 +21,130 @@ import Glibc import NIOCore /// ChannelOutboundHandler that prints all outbound events that pass through the pipeline by default, -/// overridable by providing your own closure for custom logging. See DebugInboundEventsHandler for inbound events. +/// overridable by providing your own closure for custom logging. See ``DebugInboundEventsHandler`` for inbound events. public class DebugOutboundEventsHandler: ChannelOutboundHandler { - + /// The type of the outbound data which is wrapped in `NIOAny`. public typealias OutboundIn = Any + /// The type of the outbound data which will be forwarded to the next `ChannelOutboundHandler` in the `ChannelPipeline`. public typealias OutboundOut = Any - + + /// All possible outbound events which could occur. public enum Event { + /// `Channel` registered for I/O events. case register + /// Bound to a `SocketAddress` case bind(address: SocketAddress) + /// Connected to an address. case connect(address: SocketAddress) + /// Write operation. case write(data: NIOAny) + /// Pending writes flushed. case flush + /// Ready to read more data. case read + /// Close the channel. case close(mode: CloseMode) + /// User outbound event triggered. case triggerUserOutboundEvent(event: Any) } var logger: (Event, ChannelHandlerContext) -> () - + + /// Initialiser. + /// - parameters: + /// - logger: Method for logging events which happen. public init(logger: @escaping (Event, ChannelHandlerContext) -> () = DebugOutboundEventsHandler.defaultPrint) { self.logger = logger } - + + /// Logs ``Event.register`` to ``logger`` + /// Called to request that the `Channel` register itself for I/O events with its `EventLoop`. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place. public func register(context: ChannelHandlerContext, promise: EventLoopPromise?) { logger(.register, context) context.register(promise: promise) } - + + /// Logs ``Event.bind`` to ``logger`` + /// Called to request that the `Channel` bind to a specific `SocketAddress`. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - to: The `SocketAddress` to which this `Channel` should bind. + /// - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place. public func bind(context: ChannelHandlerContext, to address: SocketAddress, promise: EventLoopPromise?) { logger(.bind(address: address), context) context.bind(to: address, promise: promise) } - + + /// Logs ``Event.connect`` to ``logger`` + /// Called to request that the `Channel` connect to a given `SocketAddress`. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - to: The `SocketAddress` to which the the `Channel` should connect. + /// - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place. public func connect(context: ChannelHandlerContext, to address: SocketAddress, promise: EventLoopPromise?) { logger(.connect(address: address), context) context.connect(to: address, promise: promise) } - + + /// Logs ``Event.data`` to ``logger`` + /// Called to request a write operation. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - data: The data to write through the `Channel`, wrapped in a `NIOAny`. + /// - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place. public func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise?) { logger(.write(data: data), context) context.write(data, promise: promise) } - + + /// Logs ``Event.flush`` to ``logger`` + /// Called to request that the `Channel` flush all pending writes. The flush operation will try to flush out all previous written messages + /// that are pending. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func flush(context: ChannelHandlerContext) { logger(.flush, context) context.flush() } - + + /// Logs ``Event.read`` to ``logger`` + /// Called to request that the `Channel` perform a read when data is ready. The read operation will signal that we are ready to read more data. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func read(context: ChannelHandlerContext) { logger(.read, context) context.read() } - + + /// Logs ``Event.close`` to ``logger`` + /// Called to request that the `Channel` close itself down`. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - mode: The `CloseMode` to apply + /// - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place. public func close(context: ChannelHandlerContext, mode: CloseMode, promise: EventLoopPromise?) { logger(.close(mode: mode), context) context.close(mode: mode, promise: promise) } - + + /// Logs ``Event.triggerUserOutboundEvent`` to ``logger`` + /// Called when an user outbound event is triggered. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - event: The triggered event. + /// - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place. public func triggerUserOutboundEvent(context: ChannelHandlerContext, event: Any, promise: EventLoopPromise?) { logger(.triggerUserOutboundEvent(event: event), context) context.triggerUserOutboundEvent(event, promise: promise) } + /// Print textual event description to stdout. + /// - parameters: + /// - event: The ``Event`` to print. + /// - in: The context the event occured in. public static func defaultPrint(event: Event, in context: ChannelHandlerContext) { let message: String switch event { @@ -108,5 +169,4 @@ public class DebugOutboundEventsHandler: ChannelOutboundHandler { print(message + " in \(context.name)") fflush(stdout) } - } diff --git a/Sources/NIOExtras/Docs.docc/Article.md b/Sources/NIOExtras/Docs.docc/Article.md new file mode 100644 index 0000000..ed5c0a4 --- /dev/null +++ b/Sources/NIOExtras/Docs.docc/Article.md @@ -0,0 +1,13 @@ +# Article + +Summary + +## Overview + +Text + +## Topics + +### Group + +- ``Symbol`` diff --git a/Sources/NIOExtras/FixedLengthFrameDecoder.swift b/Sources/NIOExtras/FixedLengthFrameDecoder.swift index bf3249c..4f4a326 100644 --- a/Sources/NIOExtras/FixedLengthFrameDecoder.swift +++ b/Sources/NIOExtras/FixedLengthFrameDecoder.swift @@ -21,7 +21,7 @@ import NIOCore /// | A | BC | DEFG | HI | /// +---+----+------+----+ /// -/// A `FixedLengthFrameDecoder` will decode them into the +/// A ``FixedLengthFrameDecoder`` will decode them into the /// following three packets with the fixed length: /// /// +-----+-----+-----+ @@ -29,7 +29,9 @@ import NIOCore /// +-----+-----+-----+ /// public final class FixedLengthFrameDecoder: ByteToMessageDecoder { + /// Data type we receive. public typealias InboundIn = ByteBuffer + /// Data type we send to the next stage. public typealias InboundOut = ByteBuffer @available(*, deprecated, message: "No longer used") @@ -45,6 +47,11 @@ public final class FixedLengthFrameDecoder: ByteToMessageDecoder { self.frameLength = frameLength } + /// Get a frame of data and `fireChannelRead` if sufficient data exists in the buffer. + /// - Parameters: + /// - context: Calling context. + /// - buffer: Buffer containing data. + /// - Returns: Status detailing if more data is required or if a successful decode occurred. public func decode(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState { guard let slice = buffer.readSlice(length: frameLength) else { return .needMoreData @@ -54,6 +61,13 @@ public final class FixedLengthFrameDecoder: ByteToMessageDecoder { return .continue } + /// Repeatedly decode frames until there is not enough data to decode any more. + /// Reports an error through `fireErrorCaught` if this doesn't empty the buffer exactly. + /// - Parameters: + /// - context: Calling context + /// - buffer: Buffer containing data. + /// - seenEOF: If end of file has been seen. + /// - Returns: needMoreData always as all data is consumed. public func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState { while case .continue = try self.decode(context: context, buffer: &buffer) {} if buffer.readableBytes > 0 { diff --git a/Sources/NIOExtras/JSONRPCFraming+ContentLengthHeader.swift b/Sources/NIOExtras/JSONRPCFraming+ContentLengthHeader.swift index 7cc1700..099e93c 100644 --- a/Sources/NIOExtras/JSONRPCFraming+ContentLengthHeader.swift +++ b/Sources/NIOExtras/JSONRPCFraming+ContentLengthHeader.swift @@ -15,22 +15,31 @@ import NIOCore extension NIOJSONRPCFraming { - /// `ContentLengthHeaderFrameEncoder` is responsible for emitting JSON-RPC wire protocol with 'Content-Length' + /// ``ContentLengthHeaderFrameEncoder`` is responsible for emitting JSON-RPC wire protocol with 'Content-Length' /// HTTP-like headers as used by for example by LSP (Language Server Protocol). public final class ContentLengthHeaderFrameEncoder: ChannelOutboundHandler { - /// We'll get handed one message through the `Channel` and ... + /// We'll get handed one message through the `Channel` of this type and will encode into `OutboundOut` public typealias OutboundIn = ByteBuffer - /// ... will encode it into a `ByteBuffer`. + /// Outbound data will be encoded into a `ByteBuffer`. public typealias OutboundOut = ByteBuffer private var scratchBuffer: ByteBuffer! public init() {} + /// Called when this `ChannelHandler` is added to the `ChannelPipeline`. + /// + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func handlerAdded(context: ChannelHandlerContext) { self.scratchBuffer = context.channel.allocator.buffer(capacity: 512) } + /// Called to request a write operation. Writes write protocol header and then the message. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - data: The data to write through the `Channel`, wrapped in a `NIOAny`. + /// - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place. public func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise?) { let data = self.unwrapOutboundIn(data) // Step 1, clear the target buffer (note, we are re-using it so if we get lucky we don't need to @@ -52,7 +61,7 @@ extension NIOJSONRPCFraming { } } - /// `ContentLengthHeaderFrameDecoder` is responsible for parsing JSON-RPC wire protocol with 'Content-Length' + /// ``ContentLengthHeaderFrameDecoder`` is responsible for parsing JSON-RPC wire protocol with 'Content-Length' /// HTTP-like headers as used by for example by LSP (Language Server Protocol). public struct ContentLengthHeaderFrameDecoder: ByteToMessageDecoder { /// We're emitting one `ByteBuffer` corresponding exactly to one full payload, no headers etc. @@ -60,15 +69,15 @@ extension NIOJSONRPCFraming { /// `ContentLengthHeaderFrameDecoder` is a simple state machine. private enum State { - /// either we're waiting for the end of the header block or a new header field, ... + /// Waiting for the end of the header block or a new header field case waitingForHeaderNameOrHeaderBlockEnd - /// ... or for a header value, or ... + /// Waiting for a header value case waitingForHeaderValue(name: String) - /// ... or for the payload of a given size. + /// Waiting for the payload of a given size. case waitingForPayload(length: Int) } - /// A `DecodingError` is sent through the pipeline if anything went wrong. + /// A ``DecodingError`` is sent through the pipeline if anything went wrong. public enum DecodingError: Error, Equatable { /// Missing 'Content-Length' header. case missingContentLengthHeader @@ -106,7 +115,12 @@ extension NIOJSONRPCFraming { } } - // `decode` will be invoked whenever there is more data available (or if we return `.continue`). + /// Decode the data in the supplied `buffer`. + /// `decode` will be invoked whenever there is more data available (or if we return `.continue`). + /// - parameters: + /// - context: Calling context. + /// - buffer: The data to decode. + /// - returns: Status describing need for more data or otherwise. public mutating func decode(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState { switch self.state { case .waitingForHeaderNameOrHeaderBlockEnd: @@ -166,7 +180,14 @@ extension NIOJSONRPCFraming { } } - /// Invoked when the `Channel` is being brough down. + /// Decode all remaining data. + /// Invoked when the `Channel` is being brought down. + /// Reports error through `ByteToMessageDecoderError.leftoverDataWhenDone` if not all data is consumed. + /// - parameters: + /// - context: Calling context. + /// - buffer: Buffer of data to decode. + /// - seenEOF: If the end of file has been seen. + /// - returns: .needMoreData always as all data should be consumed. public mutating func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState { diff --git a/Sources/NIOExtras/JSONRPCFraming.swift b/Sources/NIOExtras/JSONRPCFraming.swift index 2ba0c11..37099dc 100644 --- a/Sources/NIOExtras/JSONRPCFraming.swift +++ b/Sources/NIOExtras/JSONRPCFraming.swift @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +/// Namespace to contain JSON framing implementation. public enum NIOJSONRPCFraming { // just a name-space } diff --git a/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift b/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift index f461bce..071591a 100644 --- a/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift +++ b/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift @@ -47,23 +47,25 @@ extension ByteBuffer { } public enum NIOLengthFieldBasedFrameDecoderError: Error { - /// This error can be thrown by `LengthFieldBasedFrameDecoder` if the length field value is larger than `Int.max` + /// This error can be thrown by ``LengthFieldBasedFrameDecoder`` if the length field value is larger than `Int.max` case lengthFieldValueTooLarge - /// This error can be thrown by `LengthFieldBasedFrameDecoder` if the length field value is larger than `LengthFieldBasedFrameDecoder.maxSupportedLengthFieldSize` + /// This error can be thrown by ``LengthFieldBasedFrameDecoder`` if the length field value is larger than `LengthFieldBasedFrameDecoder.maxSupportedLengthFieldSize` case lengthFieldValueLargerThanMaxSupportedSize } /// /// A decoder that splits the received `ByteBuffer` by the number of bytes specified in a fixed length header /// contained within the buffer. +/// /// For example, if you received the following four fragmented packets: +/// /// +---+----+------+----+ /// | A | BC | DEFG | HI | /// +---+----+------+----+ /// /// Given that the specified header length is 1 byte, /// where the first header specifies 3 bytes while the second header specifies 4 bytes, -/// a `LengthFieldBasedFrameDecoder` will decode them into the following packets: +/// a ``LengthFieldBasedFrameDecoder`` will decode them into the following packets: /// /// +-----+------+ /// | BCD | FGHI | @@ -72,15 +74,18 @@ public enum NIOLengthFieldBasedFrameDecoderError: Error { /// 'A' and 'E' will be the headers and will not be passed forward. /// public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { - /// Maximum supported length field size in bytes of `LengthFieldBasedFrameDecoder` and is currently `Int32.max` + /// Maximum supported length field size in bytes of ``LengthFieldBasedFrameDecoder`` and is currently `Int32.max` public static let maxSupportedLengthFieldSize: Int = Int(Int32.max) - /// + /// An enumeration to describe the length of a piece of data in bytes. - /// public enum ByteLength { + /// One byte case one + /// Two bytes case two + /// Four bytes case four + /// Eight bytes case eight fileprivate var bitLength: NIOLengthFieldBitLength { @@ -92,18 +97,20 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { } } } - - /// + /// The decoder has two distinct sections of data to read. /// Each must be fully present before it is considered as read. - /// During the time when it is not present the decoder must wait. `DecoderReadState` details that waiting state. - /// + /// During the time when it is not present the decoder must wait. ``DecoderReadState`` details that waiting state. private enum DecoderReadState { + // Expending a header next. case waitingForHeader + // Expecting the frame next. case waitingForFrame(length: Int) } + /// Incoming data is in `ByteBuffer` public typealias InboundIn = ByteBuffer + /// `ByteBuffer` is type passed to next stage. public typealias InboundOut = ByteBuffer @available(*, deprecated, message: "No longer used") @@ -118,7 +125,6 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { /// - parameters: /// - lengthFieldLength: The length of the field specifying the remaining length of the frame. /// - lengthFieldEndianness: The endianness of the field specifying the remaining length of the frame. - /// public convenience init(lengthFieldLength: ByteLength, lengthFieldEndianness: Endianness = .big) { self.init(lengthFieldBitLength: lengthFieldLength.bitLength, lengthFieldEndianness: lengthFieldEndianness) } @@ -128,12 +134,16 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { /// - parameters: /// - lengthFieldBitLength: The length of the field specifying the remaining length of the frame. /// - lengthFieldEndianness: The endianness of the field specifying the remaining length of the frame. - /// public init(lengthFieldBitLength: NIOLengthFieldBitLength, lengthFieldEndianness: Endianness = .big) { self.lengthFieldLength = lengthFieldBitLength self.lengthFieldEndianness = lengthFieldEndianness } - + + /// Decode supplied data. + /// - Parameters: + /// - context: Calling context. + /// - buffer: data to decode. + /// - Returns: `DecodingState` describing what's needed next. public func decode(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState { if case .waitingForHeader = self.readState { @@ -152,7 +162,14 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { return .continue } - + + /// Decode all data supplied. No more is expected after this. + /// If all data is not exactly consumed reports and error through `context.fireErrorCaught` + /// - Parameters: + /// - context: Calling context. + /// - buffer: The data to decode + /// - seenEOF: If End of File has been seen. + /// - Returns: .needMoreData always as all data has been consumed. public func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState { // we'll just try to decode as much as we can as usually while case .continue = try self.decode(context: context, buffer: &buffer) {} @@ -162,12 +179,10 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { return .needMoreData } - /// /// Attempts to read the header data. Updates the status is successful. /// /// - parameters: /// - buffer: The buffer containing the integer frame length. - /// private func readNextLengthFieldToState(buffer: inout ByteBuffer) throws { // Convert the length field to an integer specifying the length @@ -177,14 +192,12 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { self.readState = .waitingForFrame(length: lengthFieldValue) } - - /// + /// Attempts to read the body data for a given length. Updates the status is successful. /// /// - parameters: /// - buffer: The buffer containing the frame data. /// - frameLength: The length of the frame data to be read. - /// private func readNextFrame(buffer: inout ByteBuffer, frameLength: Int) throws -> ByteBuffer? { guard let contentsFieldSlice = buffer.readSlice(length: frameLength) else { @@ -196,13 +209,11 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { return contentsFieldSlice } - /// /// Decodes the specified region of the buffer into an unadjusted frame length. The default implementation is /// capable of decoding the specified region into an unsigned 8/16/24/32/64 bit integer. /// /// - parameters: /// - buffer: The buffer containing the integer frame length. - /// private func readFrameLength(for buffer: inout ByteBuffer) throws -> Int? { let frameLength: Int? switch self.lengthFieldLength.bitLength { diff --git a/Sources/NIOExtras/LengthFieldPrepender.swift b/Sources/NIOExtras/LengthFieldPrepender.swift index 6242366..37d797c 100644 --- a/Sources/NIOExtras/LengthFieldPrepender.swift +++ b/Sources/NIOExtras/LengthFieldPrepender.swift @@ -34,30 +34,35 @@ extension ByteBuffer { } +/// Error types from ``LengthFieldPrepender`` public enum LengthFieldPrependerError: Error { + /// More data was given than the maximum encodable length value. case messageDataTooLongForLengthField } -/// /// An encoder that takes a `ByteBuffer` message and prepends the number of bytes in the message. /// The length field is always the same fixed length specified on construction. /// These bytes contain a binary specification of the message size. /// /// For example, if you received a packet with the 3 byte length (BCD)... /// Given that the specified header length is 1 byte, there would be a single byte prepended which contains the number 3 +/// /// +---+-----+ /// | A | BCD | ('A' contains 0x03) /// +---+-----+ +/// /// This initial prepended byte is called the 'length field'. /// public final class LengthFieldPrepender: ChannelOutboundHandler { - /// /// An enumeration to describe the length of a piece of data in bytes. - /// public enum ByteLength { + /// One byte case one + /// Two bytes case two + /// Four bytes case four + /// Eight bytes case eight fileprivate var bitLength: NIOLengthFieldBitLength { @@ -70,7 +75,9 @@ public final class LengthFieldPrepender: ChannelOutboundHandler { } } + /// `ByteBuffer` is the expected type to be given for encoding. public typealias OutboundIn = ByteBuffer + /// Encoded output is passed in a `ByteBuffer` public typealias OutboundOut = ByteBuffer private let lengthFieldLength: NIOLengthFieldBitLength @@ -78,15 +85,19 @@ public final class LengthFieldPrepender: ChannelOutboundHandler { private var lengthBuffer: ByteBuffer? - /// Create `LengthFieldPrepender` with a given length field length. + /// Create ``LengthFieldPrepender`` with a given length field length. /// /// - parameters: /// - lengthFieldLength: The length of the field specifying the remaining length of the frame. /// - lengthFieldEndianness: The endianness of the field specifying the remaining length of the frame. - /// public convenience init(lengthFieldLength: ByteLength, lengthFieldEndianness: Endianness = .big) { self.init(lengthFieldBitLength: lengthFieldLength.bitLength, lengthFieldEndianness: lengthFieldEndianness) } + + /// Create ``LengthFieldPrepender`` with a given length field length. + /// - parameters: + /// - lengthFieldBitLength: The length of the field specifying the remaining length of the frame. + /// - lengthFieldEndianness: The endianness of the field specifying the remaining length of the frame. public init(lengthFieldBitLength: NIOLengthFieldBitLength, lengthFieldEndianness: Endianness = .big) { // The value contained in the length field must be able to be represented by an integer type on the platform. // ie. .eight == 64bit which would not fit into the Int type on a 32bit platform. diff --git a/Sources/NIOExtras/LineBasedFrameDecoder.swift b/Sources/NIOExtras/LineBasedFrameDecoder.swift index d0bb854..fd7a809 100644 --- a/Sources/NIOExtras/LineBasedFrameDecoder.swift +++ b/Sources/NIOExtras/LineBasedFrameDecoder.swift @@ -23,7 +23,7 @@ import NIOCore /// | AB | C\nDE | F\r\nGHI\n | /// +----+-------+------------+ /// -/// A instance of `LineBasedFrameDecoder` will split this buffer +/// A instance of ``LineBasedFrameDecoder`` will split this buffer /// as follows: /// /// +-----+-----+-----+ @@ -31,7 +31,9 @@ import NIOCore /// +-----+-----+-----+ /// public class LineBasedFrameDecoder: ByteToMessageDecoder { + /// `ByteBuffer` is the expected type passed in. public typealias InboundIn = ByteBuffer + /// `ByteBuffer`s will be passed to the next stage. public typealias InboundOut = ByteBuffer @available(*, deprecated, message: "No longer used") @@ -40,7 +42,12 @@ public class LineBasedFrameDecoder: ByteToMessageDecoder { private var lastScanOffset = 0 public init() { } - + + /// Decode data in the supplied buffer. + /// - Parameters: + /// - context: Calling cotext + /// - buffer: Buffer containing data to decode. + /// - Returns: State describing if more data is required. public func decode(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState { if let frame = try self.findNextFrame(buffer: &buffer) { context.fireChannelRead(wrapInboundOut(frame)) @@ -49,7 +56,14 @@ public class LineBasedFrameDecoder: ByteToMessageDecoder { return .needMoreData } } - + + /// Decode all remaining data. + /// If it is not possible to consume all the data then ``NIOExtrasErrors/LeftOverBytesError`` is reported via `context.fireErrorCaught` + /// - Parameters: + /// - context: Calling context. + /// - buffer: Buffer containing the data to decode. + /// - seenEOF: Has end of file been seen. + /// - Returns: Always .needMoreData as all data will be consumed. public func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState { while try self.decode(context: context, buffer: &buffer) == .continue {} if buffer.readableBytes > 0 { diff --git a/Sources/NIOExtras/NIOExtrasError.swift b/Sources/NIOExtras/NIOExtrasError.swift index 8c88b1b..9750f56 100644 --- a/Sources/NIOExtras/NIOExtrasError.swift +++ b/Sources/NIOExtras/NIOExtrasError.swift @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// import NIOCore +/// Base type for errors from NIOExtras public protocol NIOExtrasError: Equatable, Error { } /// Errors that are raised in NIOExtras. diff --git a/Sources/NIOExtras/NIOLengthFieldBitLength.swift b/Sources/NIOExtras/NIOLengthFieldBitLength.swift index 38978b7..a41ab11 100644 --- a/Sources/NIOExtras/NIOLengthFieldBitLength.swift +++ b/Sources/NIOExtras/NIOLengthFieldBitLength.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -/// An struct to describe the length of a piece of data in bits +/// A struct to describe the length of a piece of data in bits public struct NIOLengthFieldBitLength { internal enum Backing { case bits8 @@ -23,16 +23,26 @@ public struct NIOLengthFieldBitLength { } internal let bitLength: Backing + /// One byte - the same as ``eightBits`` public static let oneByte = NIOLengthFieldBitLength(bitLength: .bits8) + /// Two bytes - the same as ``sixteenBits`` public static let twoBytes = NIOLengthFieldBitLength(bitLength: .bits16) + /// Three bytes - the same as ``twentyFourBits`` public static let threeBytes = NIOLengthFieldBitLength(bitLength: .bits24) + /// Four bytes - the same as ``thirtyTwoBits`` public static let fourBytes = NIOLengthFieldBitLength(bitLength: .bits32) + /// Eight bytes - the same as ``sixtyFourBits`` public static let eightBytes = NIOLengthFieldBitLength(bitLength: .bits64) - + + /// Eight bits - the same as ``oneByte`` public static let eightBits = NIOLengthFieldBitLength(bitLength: .bits8) + /// Sixteen bits - the same as ``twoBytes`` public static let sixteenBits = NIOLengthFieldBitLength(bitLength: .bits16) + /// Twenty-four bits - the same as ``threeBytes`` public static let twentyFourBits = NIOLengthFieldBitLength(bitLength: .bits24) + /// Thirty-two bits - the same as ``fourBytes`` public static let thirtyTwoBits = NIOLengthFieldBitLength(bitLength: .bits32) + /// Sixty-four bits - the same as ``eightBytes`` public static let sixtyFourBits = NIOLengthFieldBitLength(bitLength: .bits64) internal var length: Int { diff --git a/Sources/NIOExtras/PCAPRingBuffer.swift b/Sources/NIOExtras/PCAPRingBuffer.swift index 3100f2c..4273d28 100644 --- a/Sources/NIOExtras/PCAPRingBuffer.swift +++ b/Sources/NIOExtras/PCAPRingBuffer.swift @@ -16,7 +16,7 @@ import NIOCore // MARK: NIOPCAPRingBuffer /// Storage for the most recent set of packets captured subject to constraints. -/// Use `addFragment` as the sink to a `NIOWritePCAPHandler` and call `emitPCAP` +/// Use ``addFragment(_:)`` as the sink to a ``NIOWritePCAPHandler`` and call ``emitPCAP()`` /// when you wish to get the recorded data. /// - Warning: This class is not thread safe so should only be called from one thread. public class NIOPCAPRingBuffer { diff --git a/Sources/NIOExtras/QuiescingHelper.swift b/Sources/NIOExtras/QuiescingHelper.swift index 193d394..ea1ffec 100644 --- a/Sources/NIOExtras/QuiescingHelper.swift +++ b/Sources/NIOExtras/QuiescingHelper.swift @@ -191,8 +191,8 @@ private final class CollectAcceptedChannelsHandler: ChannelInboundHandler { /// Helper that can be used to orchestrate the quiescing of a server `Channel` and all the child `Channel`s that are /// open at a given point in time. /// -/// `ServerQuiescingHelper` makes it easy to collect all child `Channel`s that a given server `Channel` accepts. When -/// the quiescing period starts (that is when `ServerQuiescingHelper.initiateShutdown` is invoked), it will perform the +/// ``ServerQuiescingHelper`` makes it easy to collect all child `Channel`s that a given server `Channel` accepts. When +/// the quiescing period starts (that is when ``initiateShutdown(promise:)`` is invoked), it will perform the /// following actions: /// /// 1. close the server `Channel` so no further connections get accepted @@ -240,8 +240,9 @@ public final class ServerQuiescingHelper { return CollectAcceptedChannelsHandler(channelCollector: collector) } - /// Initiate the shutdown. The following actions will be performed: + /// Initiate the shutdown. /// + /// The following actions will be performed: /// 1. close the server `Channel` so no further connections get accepted /// 2. send a `ChannelShouldQuiesceEvent` user event to all currently still open child `Channel`s /// 3. after all previously open child `Channel`s have closed, notify `promise` diff --git a/Sources/NIOExtras/RequestResponseHandler.swift b/Sources/NIOExtras/RequestResponseHandler.swift index 011e58e..907ce2e 100644 --- a/Sources/NIOExtras/RequestResponseHandler.swift +++ b/Sources/NIOExtras/RequestResponseHandler.swift @@ -14,21 +14,25 @@ import NIOCore -/// `RequestResponseHandler` receives a `Request` alongside an `EventLoopPromise` from the `Channel`'s +/// ``RequestResponseHandler`` receives a `Request` alongside an `EventLoopPromise` from the `Channel`'s /// outbound side. It will fulfill the promise with the `Response` once it's received from the `Channel`'s inbound /// side. /// -/// `RequestResponseHandler` does support pipelining `Request`s and it will send them pipelined further down the -/// `Channel`. Should `RequestResponseHandler` receive an error from the `Channel`, it will fail all promises meant for +/// ``RequestResponseHandler`` does support pipelining `Request`s and it will send them pipelined further down the +/// `Channel`. Should ``RequestResponseHandler`` receive an error from the `Channel`, it will fail all promises meant for /// the outstanding `Reponse`s and close the `Channel`. All requests enqueued after an error occured will be immediately /// failed with the first error the channel received. /// -/// `RequestResponseHandler` requires that the `Response`s arrive on `Channel` in the same order as the `Request`s +/// ``RequestResponseHandler`` requires that the `Response`s arrive on `Channel` in the same order as the `Request`s /// were submitted. public final class RequestResponseHandler: ChannelDuplexHandler { + /// `Response` is the type this class expects to receive inbound. public typealias InboundIn = Response + /// Don't expect to pass anything on in-bound. public typealias InboundOut = Never + /// Type this class expect to receive in an outbound direction. public typealias OutboundIn = (Request, EventLoopPromise) + /// Type this class passes out. public typealias OutboundOut = Request private enum State { @@ -49,10 +53,10 @@ public final class RequestResponseHandler: ChannelDuplexHandl private var promiseBuffer: CircularBuffer> - /// Create a new `RequestResponseHandler`. + /// Create a new ``RequestResponseHandler``. /// /// - parameters: - /// - initialBufferCapacity: `RequestResponseHandler` saves the promises for all outstanding responses in a + /// - initialBufferCapacity: ``RequestResponseHandler`` saves the promises for all outstanding responses in a /// buffer. `initialBufferCapacity` is the initial capacity for this buffer. You usually do not need to set /// this parameter unless you intend to pipeline very deeply and don't want the buffer to resize. public init(initialBufferCapacity: Int = 4) { diff --git a/Sources/NIOExtras/WritePCAPHandler.swift b/Sources/NIOExtras/WritePCAPHandler.swift index b457a23..8ace0bb 100644 --- a/Sources/NIOExtras/WritePCAPHandler.swift +++ b/Sources/NIOExtras/WritePCAPHandler.swift @@ -119,7 +119,7 @@ struct PCAPRecordHeader { /// example when your real network traffic is TLS protected (so `tcpdump`/Wireshark can't read it directly), or if you /// don't have enough privileges on the running host to dump the network traffic. /// -/// `NIOWritePCAPHandler` will also work with Unix Domain Sockets in which case it will still synthesize a TCP packet +/// ``NIOWritePCAPHandler`` will also work with Unix Domain Sockets in which case it will still synthesize a TCP packet /// capture with local address `111.111.111.111` (port `1111`) and remote address `222.222.222.222` (port `2222`). public class NIOWritePCAPHandler: RemovableChannelHandler { public enum Mode { @@ -127,21 +127,21 @@ public class NIOWritePCAPHandler: RemovableChannelHandler { case server } - /// Settings for `NIOWritePCAPHandler`. + /// Settings for ``NIOWritePCAPHandler``. public struct Settings { /// When to issue data into the `.pcap` file. public enum EmitPCAP { - /// Write the data immediately when `NIOWritePCAPHandler` saw the event on the `ChannelPipeline`. + /// Write the data immediately when ``NIOWritePCAPHandler`` saw the event on the `ChannelPipeline`. /// /// For writes this means when the `write` event is triggered. Please note that this will write potentially /// unflushed data into the `.pcap` file. /// - /// If in doubt, prefer `.whenCompleted`. + /// If in doubt, prefer ``whenCompleted``. case whenIssued /// Write the data when the event completed. /// - /// For writes this means when the `write` promise is succeeded. The `whenCompleted` mode mirrors most + /// For writes this means when the `write` promise is succeeded. The ``whenCompleted`` mode mirrors most /// closely what's actually sent over the wire. case whenCompleted } @@ -149,7 +149,7 @@ public class NIOWritePCAPHandler: RemovableChannelHandler { /// When to emit the data from the `write` event into the `.pcap` file. public var emitPCAPWrites: EmitPCAP - /// Default settings for the `NIOWritePCAPHandler`. + /// Default settings for the ``NIOWritePCAPHandler``. public init() { self = .init(emitPCAPWrites: .whenCompleted) } @@ -157,7 +157,7 @@ public class NIOWritePCAPHandler: RemovableChannelHandler { /// Settings with customization. /// /// - parameters: - /// - emitPCAPWrites: When to issue the writes into the `.pcap` file, see `EmitPCAP`. + /// - emitPCAPWrites: When to issue the writes into the `.pcap` file, see ``EmitPCAP``. public init(emitPCAPWrites: EmitPCAP) { self.emitPCAPWrites = emitPCAPWrites } @@ -184,20 +184,21 @@ public class NIOWritePCAPHandler: RemovableChannelHandler { private var localAddress: SocketAddress? private var remoteAddress: SocketAddress? + /// Reusable header for `. pcap` file. public static var pcapFileHeader: ByteBuffer { var buffer = ByteBufferAllocator().buffer(capacity: 24) buffer.writePCAPHeader() return buffer } - /// Initialize a `NIOWritePCAPHandler`. + /// Initialize a ``NIOWritePCAPHandler``. /// /// - parameters: /// - fakeLocalAddress: Allows you to optionally override the local address to be different from the real one. /// - fakeRemoteAddress: Allows you to optionally override the remote address to be different from the real one. - /// - settings: The settings for the `NIOWritePCAPHandler`. + /// - settings: The settings for the ``NIOWritePCAPHandler``. /// - fileSink: The `fileSink` closure is called every time a new chunk of the `.pcap` file is ready to be - /// written to disk or elsewhere. See `SynchronizedFileSink` for a convenient way to write to + /// written to disk or elsewhere. See ``SynchronizedFileSink`` for a convenient way to write to /// disk. public init(mode: Mode, fakeLocalAddress: SocketAddress? = nil, @@ -215,7 +216,7 @@ public class NIOWritePCAPHandler: RemovableChannelHandler { } } - /// Initialize a `NIOWritePCAPHandler` with default settings. + /// Initialize a ``NIOWritePCAPHandler`` with default settings. /// /// - parameters: /// - fakeLocalAddress: Allows you to optionally override the local address to be different from the real one. diff --git a/Sources/NIOHTTPCompression/HTTPCompression.swift b/Sources/NIOHTTPCompression/HTTPCompression.swift index 9a30f94..8eb9b86 100644 --- a/Sources/NIOHTTPCompression/HTTPCompression.swift +++ b/Sources/NIOHTTPCompression/HTTPCompression.swift @@ -15,8 +15,10 @@ import CNIOExtrasZlib import NIOCore +/// Namespace for compression code. public enum NIOCompression { + /// Which algorithm should be used for compression. public struct Algorithm: CustomStringConvertible, Equatable { fileprivate enum AlgorithmEnum: String { case gzip @@ -26,11 +28,14 @@ public enum NIOCompression { /// return as String public var description: String { return algorithm.rawValue } - + + /// `gzip` method public static let gzip = Algorithm(algorithm: .gzip) + /// `deflate` method public static let deflate = Algorithm(algorithm: .deflate) } - + + /// Error types for ``NIOCompression`` public struct Error: Swift.Error, CustomStringConvertible, Equatable { fileprivate enum ErrorEnum: String { case uncompressedWritesPending @@ -40,11 +45,14 @@ public enum NIOCompression { /// return as String public var description: String { return error.rawValue } - + + /// There were writes pending which were not processed before shutdown. public static let uncompressedWritesPending = Error(error: .uncompressedWritesPending) + /// Currently never used. public static let noDataToWrite = Error(error: .noDataToWrite) } - + + /// Data compression utility. struct Compressor { private var stream = z_stream() var isActive = false diff --git a/Sources/NIOHTTPCompression/HTTPDecompression.swift b/Sources/NIOHTTPCompression/HTTPDecompression.swift index c030fa1..9bf6b51 100644 --- a/Sources/NIOHTTPCompression/HTTPDecompression.swift +++ b/Sources/NIOHTTPCompression/HTTPDecompression.swift @@ -46,9 +46,13 @@ public enum NIOHTTPDecompression { } } + /// Error types for ``NIOHTTPCompression`` public enum DecompressionError: Error, Equatable { + /// The set ``NIOHTTPDecompression/DecompressionLimit`` has been exceeded case limit + /// An error occured when inflating. Error code is included to aid diagnosis. case inflationError(Int) + /// Decoder could not be initialised. Error code is included to aid diagnosis. case initializationError(Int) } diff --git a/Sources/NIOHTTPCompression/HTTPRequestCompressor.swift b/Sources/NIOHTTPCompression/HTTPRequestCompressor.swift index 555afe7..52d262d 100644 --- a/Sources/NIOHTTPCompression/HTTPRequestCompressor.swift +++ b/Sources/NIOHTTPCompression/HTTPRequestCompressor.swift @@ -16,7 +16,7 @@ import CNIOExtrasZlib import NIOCore import NIOHTTP1 -/// NIOHTTPResponseCompressor is an outbound channel handler that handles automatic streaming compression of +/// ``NIOHTTPRequestCompressor`` is an outbound channel handler that handles automatic streaming compression of /// HTTP requests. /// /// This compressor supports gzip and deflate. It works best if many writes are made between flushes. @@ -27,7 +27,9 @@ import NIOHTTP1 /// benefit. This channel handler should be present in the pipeline only for dynamically-generated and /// highly-compressible content, which will see the biggest benefits from streaming compression. public final class NIOHTTPRequestCompressor: ChannelOutboundHandler, RemovableChannelHandler { + /// Class takes `HTTPClientRequestPart` as the type to send. public typealias OutboundIn = HTTPClientRequestPart + /// Passes `HTTPClientRequestPart` to the next stage in the pipeline when sending. public typealias OutboundOut = HTTPClientRequestPart /// Handler state @@ -53,18 +55,22 @@ public final class NIOHTTPRequestCompressor: ChannelOutboundHandler, RemovableCh /// pending write promise var pendingWritePromise: EventLoopPromise! - /// Initialize a NIOHTTPRequestCompressor + /// Initialize a ``NIOHTTPRequestCompressor`` /// - Parameter encoding: Compression algorithm to use public init(encoding: NIOCompression.Algorithm) { self.encoding = encoding self.state = .idle self.compressor = NIOCompression.Compressor() } - + + /// Add handler to the pipeline. + /// - Parameter context: Calling context. public func handlerAdded(context: ChannelHandlerContext) { pendingWritePromise = context.eventLoop.makePromise() } + /// Remove handler from pipeline. + /// - Parameter context: Calling context. public func handlerRemoved(context: ChannelHandlerContext) { pendingWritePromise.fail(NIOCompression.Error.uncompressedWritesPending) compressor.shutdownIfActive() diff --git a/Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift b/Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift index 368b711..cf8d672 100644 --- a/Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift +++ b/Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift @@ -16,10 +16,15 @@ import CNIOExtrasZlib import NIOHTTP1 import NIOCore +/// Channel hander to decompress incoming HTTP data. public final class NIOHTTPRequestDecompressor: ChannelDuplexHandler, RemovableChannelHandler { + /// Expect to receive `HTTPServerRequestPart` from the network public typealias InboundIn = HTTPServerRequestPart + /// Pass `HTTPServerRequestPart` to the next pipeline state in an inbound direction. public typealias InboundOut = HTTPServerRequestPart + /// Pass through `HTTPServerResponsePart` outbound. public typealias OutboundIn = HTTPServerResponsePart + /// Pass through `HTTPServerResponsePart` outbound. public typealias OutboundOut = HTTPServerResponsePart private struct Compression { @@ -30,6 +35,8 @@ public final class NIOHTTPRequestDecompressor: ChannelDuplexHandler, RemovableCh private var decompressor: NIOHTTPDecompression.Decompressor private var compression: Compression? + /// Initialise with limits. + /// - Parameter limit: Limit to how much inflation can occur to protect against bad cases. public init(limit: NIOHTTPDecompression.DecompressionLimit) { self.decompressor = NIOHTTPDecompression.Decompressor(limit: limit) self.compression = nil diff --git a/Sources/NIOHTTPCompression/HTTPResponseCompressor.swift b/Sources/NIOHTTPCompression/HTTPResponseCompressor.swift index dcfb7ac..614b1af 100644 --- a/Sources/NIOHTTPCompression/HTTPResponseCompressor.swift +++ b/Sources/NIOHTTPCompression/HTTPResponseCompressor.swift @@ -46,7 +46,7 @@ private func qValueFromHeader(_ text: S) -> Float { return qValue } -/// A HTTPResponseCompressor is a duplex channel handler that handles automatic streaming compression of +/// A ``HTTPResponseCompressor`` is a duplex channel handler that handles automatic streaming compression of /// HTTP responses. It respects the client's Accept-Encoding preferences, including q-values if present, /// and ensures that clients are served the compression algorithm that works best for them. /// @@ -58,14 +58,20 @@ private func qValueFromHeader(_ text: S) -> Float { /// benefit. This channel handler should be present in the pipeline only for dynamically-generated and /// highly-compressible content, which will see the biggest benefits from streaming compression. public final class HTTPResponseCompressor: ChannelDuplexHandler, RemovableChannelHandler { + /// This class accepts `HTTPServerRequestPart` inbound public typealias InboundIn = HTTPServerRequestPart + /// This class emits `HTTPServerRequestPart` inbound. public typealias InboundOut = HTTPServerRequestPart + /// This class accepts `HTTPServerResponsePart` outbound, public typealias OutboundIn = HTTPServerResponsePart + /// This class emits `HTTPServerResponsePart` outbound. public typealias OutboundOut = HTTPServerResponsePart - + /// Errors which can occur when compressing public enum CompressionError: Error { + // Writes were still pending when shutdown. case uncompressedWritesPending + /// Data was somehow lost without being written. case noDataToWrite } @@ -79,16 +85,22 @@ public final class HTTPResponseCompressor: ChannelDuplexHandler, RemovableChanne private let initialByteBufferCapacity: Int + /// Initialise a ``HTTPResponseCompressor`` + /// - Parameter initialByteBufferCapacity: Initial size of buffer to allocate when hander is first added. public init(initialByteBufferCapacity: Int = 1024) { self.initialByteBufferCapacity = initialByteBufferCapacity self.compressor = NIOCompression.Compressor() } + /// Setup and add to the pipeline. + /// - Parameter context: Calling context. public func handlerAdded(context: ChannelHandlerContext) { pendingResponse = PartialHTTPResponse(bodyBuffer: context.channel.allocator.buffer(capacity: initialByteBufferCapacity)) pendingWritePromise = context.eventLoop.makePromise() } + /// Remove channel handler from the pipeline. + /// - Parameter context: Calling context public func handlerRemoved(context: ChannelHandlerContext) { pendingWritePromise?.fail(CompressionError.uncompressedWritesPending) compressor.shutdownIfActive() diff --git a/Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift b/Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift index 639215f..4c90a8d 100644 --- a/Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift +++ b/Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift @@ -15,10 +15,15 @@ import NIOCore import NIOHTTP1 +/// Duplex channel handler which will accept deflate and gzip encoded responses and decompress them. public final class NIOHTTPResponseDecompressor: ChannelDuplexHandler, RemovableChannelHandler { + /// Expect `HTTPClientResponsePart` inbound. public typealias InboundIn = HTTPClientResponsePart + /// Sends `HTTPClientResponsePart` to the next pipeline stage inbound. public typealias InboundOut = HTTPClientResponsePart + /// Expect `HTTPClientRequestPart` outbound. public typealias OutboundIn = HTTPClientRequestPart + /// Send `HTTPClientRequestPart` to the next stage outbound. public typealias OutboundOut = HTTPClientRequestPart /// this struct encapsulates the state of a single http response decompression @@ -34,6 +39,8 @@ public final class NIOHTTPResponseDecompressor: ChannelDuplexHandler, RemovableC private var compression: Compression? = nil private var decompressor: NIOHTTPDecompression.Decompressor + /// Initialise + /// - Parameter limit: Limit on the amount of decompression allowed. public init(limit: NIOHTTPDecompression.DecompressionLimit) { self.decompressor = NIOHTTPDecompression.Decompressor(limit: limit) } diff --git a/Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift b/Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift index 05e6a9c..ad3994d 100644 --- a/Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift +++ b/Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift @@ -19,10 +19,13 @@ import NIOCore /// channel's pipeline. Note that SOCKS only supports fully-qualified /// domain names and IPv4 or IPv6 sockets, and not UNIX sockets. public final class SOCKSClientHandler: ChannelDuplexHandler { - + /// Accepts `ByteBuffer` as input where receiving. public typealias InboundIn = ByteBuffer + /// Sends `ByteBuffer` to the next pipeline stage when receiving. public typealias InboundOut = ByteBuffer + /// Accepts `ByteBuffer` as the type to send. public typealias OutboundIn = ByteBuffer + /// Sends `ByteBuffer` to the next outbound stage. public typealias OutboundOut = ByteBuffer private let targetAddress: SOCKSAddress @@ -33,7 +36,7 @@ public final class SOCKSClientHandler: ChannelDuplexHandler { private var bufferedWrites: MarkedCircularBuffer<(NIOAny, EventLoopPromise?)> = .init(initialCapacity: 8) - /// Creates a new `SOCKSClientHandler` that connects to a server + /// Creates a new ``SOCKSClientHandler`` that connects to a server /// and instructs the server to connect to `targetAddress`. /// - parameter targetAddress: The desired end point - note that only IPv4, IPv6, and FQDNs are supported. public init(targetAddress: SOCKSAddress) { @@ -52,7 +55,9 @@ public final class SOCKSClientHandler: ChannelDuplexHandler { public func channelActive(context: ChannelHandlerContext) { self.beginHandshake(context: context) } - + + /// Add handler to pipeline and start handshake. + /// - Parameter context: Calling context. public func handlerAdded(context: ChannelHandlerContext) { self.beginHandshake(context: context) } diff --git a/Sources/NIOSOCKS/Channel Handlers/SOCKSServerHandshakeHandler.swift b/Sources/NIOSOCKS/Channel Handlers/SOCKSServerHandshakeHandler.swift index b5b455a..537c615 100644 --- a/Sources/NIOSOCKS/Channel Handlers/SOCKSServerHandshakeHandler.swift +++ b/Sources/NIOSOCKS/Channel Handlers/SOCKSServerHandshakeHandler.swift @@ -17,13 +17,16 @@ import NIOCore /// Add this handshake handler to the front of your channel, closest to the network. /// The handler will receive bytes from the network and run them through a state machine /// and parser to enforce SOCKSv5 protocol correctness. Inbound bytes will by parsed into -/// `ClientMessage` for downstream consumption. Send `ServerMessage` to this +/// ``ClientMessage`` for downstream consumption. Send ``ServerMessage`` to this /// handler. public final class SOCKSServerHandshakeHandler: ChannelDuplexHandler, RemovableChannelHandler { - + /// Accepts `ByteBuffer` when receiving data. public typealias InboundIn = ByteBuffer + /// Passes `ClientMessage` to the next stage of the pipeline when receiving data. public typealias InboundOut = ClientMessage + /// Accepts `ServerMessage` when sending data. public typealias OutboundIn = ServerMessage + /// Passes `ByteBuffer` to the next pipeline stage when sending data. public typealias OutboundOut = ByteBuffer var inboundBuffer: ByteBuffer? @@ -52,7 +55,9 @@ public final class SOCKSServerHandshakeHandler: ChannelDuplexHandler, RemovableC context.fireErrorCaught(error) } } - + + /// Add hander to pipeline and enter state ready for connection establishment. + /// - Parameter context: Calling context public func handlerAdded(context: ChannelHandlerContext) { do { try self.stateMachine.connectionEstablished() @@ -60,7 +65,9 @@ public final class SOCKSServerHandshakeHandler: ChannelDuplexHandler, RemovableC context.fireErrorCaught(error) } } - + + /// Remove handler from channel pipeline. Causes any inbound buffer to be surfaced. + /// - Parameter context: Calling context. public func handlerRemoved(context: ChannelHandlerContext) { guard let buffer = self.inboundBuffer else { return diff --git a/Sources/NIOSOCKS/Messages/ClientGreeting.swift b/Sources/NIOSOCKS/Messages/ClientGreeting.swift index 7f62173..5186213 100644 --- a/Sources/NIOSOCKS/Messages/ClientGreeting.swift +++ b/Sources/NIOSOCKS/Messages/ClientGreeting.swift @@ -26,7 +26,7 @@ public struct ClientGreeting: Hashable { /// The SOCKS server will select one to use. public var methods: [AuthenticationMethod] - /// Creates a new `ClientGreeting` + /// Creates a new ``ClientGreeting`` /// - parameter methods: The client-supported authentication methods. public init(methods: [AuthenticationMethod]) { self.methods = methods diff --git a/Sources/NIOSOCKS/Messages/SOCKSRequest.swift b/Sources/NIOSOCKS/Messages/SOCKSRequest.swift index 4e62232..37e55e3 100644 --- a/Sources/NIOSOCKS/Messages/SOCKSRequest.swift +++ b/Sources/NIOSOCKS/Messages/SOCKSRequest.swift @@ -35,7 +35,7 @@ public struct SOCKSRequest: Hashable { /// The target host address. public var addressType: SOCKSAddress - /// Creates a new `SOCKSRequest`. + /// Creates a new ``SOCKSRequest``. /// - parameter command: How to connect to the host. /// - parameter addressType: The target host address. public init(command: SOCKSCommand, addressType: SOCKSAddress) { @@ -87,9 +87,10 @@ public struct SOCKSCommand: Hashable { /// Used to establish an association within the UDP relay process to /// handle UDP datagrams. public static let udpAssociate = SOCKSCommand(value: 0x03) - + + /// Command value as defined in RFC public var value: UInt8 - + public init(value: UInt8) { self.value = value } @@ -99,9 +100,9 @@ public struct SOCKSCommand: Hashable { /// The address used to connect to the target host. public enum SOCKSAddress: Hashable { - + /// Socket Adress case address(SocketAddress) - + /// Host and port case domain(String, port: Int) static let ipv4IdentifierByte: UInt8 = 0x01 diff --git a/Sources/NIOSOCKS/Messages/SOCKSResponse.swift b/Sources/NIOSOCKS/Messages/SOCKSResponse.swift index 9a23b54..d8b244b 100644 --- a/Sources/NIOSOCKS/Messages/SOCKSResponse.swift +++ b/Sources/NIOSOCKS/Messages/SOCKSResponse.swift @@ -30,7 +30,7 @@ public struct SOCKSResponse: Hashable { /// The host address. public var boundAddress: SOCKSAddress - /// Creates a new `SOCKSResponse`. + /// Creates a new ``SOCKSResponse``. /// - parameter reply: The status of the connection - used to check if the request /// succeeded or failed. /// - parameter boundAddress: The host address. diff --git a/Sources/NIOSOCKS/Messages/SelectedAuthenticationMethod.swift b/Sources/NIOSOCKS/Messages/SelectedAuthenticationMethod.swift index 0f3729e..1d94d0f 100644 --- a/Sources/NIOSOCKS/Messages/SelectedAuthenticationMethod.swift +++ b/Sources/NIOSOCKS/Messages/SelectedAuthenticationMethod.swift @@ -25,7 +25,7 @@ public struct SelectedAuthenticationMethod: Hashable { /// The server's selected authentication method. public var method: AuthenticationMethod - /// Creates a new `MethodSelection` wrapping an `AuthenticationMethod`. + /// Creates a new `MethodSelection` wrapping an ``AuthenticationMethod``. /// - parameter method: The selected `AuthenticationMethod`. public init(method: AuthenticationMethod) { self.method = method