mirror of
https://github.com/apple/swift-nio-extras.git
synced 2025-06-01 18:53:35 +08:00
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 <lukasa@apple.com>
This commit is contained in:
parent
ca22c12528
commit
da7c04777b
Sources
NIOExtras
DebugInboundEventsHandler.swiftDebugOutboundEventsHandler.swift
Docs.docc
FixedLengthFrameDecoder.swiftJSONRPCFraming+ContentLengthHeader.swiftJSONRPCFraming.swiftLengthFieldBasedFrameDecoder.swiftLengthFieldPrepender.swiftLineBasedFrameDecoder.swiftNIOExtrasError.swiftNIOLengthFieldBitLength.swiftPCAPRingBuffer.swiftQuiescingHelper.swiftRequestResponseHandler.swiftWritePCAPHandler.swiftNIOHTTPCompression
HTTPCompression.swiftHTTPDecompression.swiftHTTPRequestCompressor.swiftHTTPRequestDecompressor.swiftHTTPResponseCompressor.swiftHTTPResponseDecompressor.swift
NIOSOCKS
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Void>?) {
|
||||
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<Void>?) {
|
||||
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<Void>?) {
|
||||
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<Void>?) {
|
||||
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<Void>?) {
|
||||
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<Void>?) {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
13
Sources/NIOExtras/Docs.docc/Article.md
Normal file
13
Sources/NIOExtras/Docs.docc/Article.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Article
|
||||
|
||||
<!--@START_MENU_TOKEN@-->Summary<!--@END_MENU_TOKEN@-->
|
||||
|
||||
## Overview
|
||||
|
||||
<!--@START_MENU_TOKEN@-->Text<!--@END_MENU_TOKEN@-->
|
||||
|
||||
## Topics
|
||||
|
||||
### <!--@START_MENU_TOKEN@-->Group<!--@END_MENU_TOKEN@-->
|
||||
|
||||
- <!--@START_MENU_TOKEN@-->``Symbol``<!--@END_MENU_TOKEN@-->
|
@ -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 {
|
||||
|
@ -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<Void>?) {
|
||||
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 {
|
||||
|
@ -12,6 +12,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Namespace to contain JSON framing implementation.
|
||||
public enum NIOJSONRPCFraming {
|
||||
// just a name-space
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -13,6 +13,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
import NIOCore
|
||||
|
||||
/// Base type for errors from NIOExtras
|
||||
public protocol NIOExtrasError: Equatable, Error { }
|
||||
|
||||
/// Errors that are raised in NIOExtras.
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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`
|
||||
|
@ -14,21 +14,25 @@
|
||||
|
||||
import NIOCore
|
||||
|
||||
/// `RequestResponseHandler` receives a `Request` alongside an `EventLoopPromise<Response>` from the `Channel`'s
|
||||
/// ``RequestResponseHandler`` receives a `Request` alongside an `EventLoopPromise<Response>` 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<Request, Response>: 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<Response>)
|
||||
/// Type this class passes out.
|
||||
public typealias OutboundOut = Request
|
||||
|
||||
private enum State {
|
||||
@ -49,10 +53,10 @@ public final class RequestResponseHandler<Request, Response>: ChannelDuplexHandl
|
||||
private var promiseBuffer: CircularBuffer<EventLoopPromise<Response>>
|
||||
|
||||
|
||||
/// 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) {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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<Void>!
|
||||
|
||||
/// 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()
|
||||
|
@ -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
|
||||
|
@ -46,7 +46,7 @@ private func qValueFromHeader<S: StringProtocol>(_ 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<S: StringProtocol>(_ 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()
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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<Void>?)> = .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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user