mirror of
https://github.com/apple/swift-nio.git
synced 2026-05-20 20:30:36 +00:00
a1605a3303
### Motivation:
Swift does not currently generic specialize static methods on final
classes that have no parent classes when the method is implemented in a
protocol. This means that calling such methods through `Self` will not
involve a generic specialization, whereas using the explicit type name
will.
This pattern manifests in the `[un]wrap{In}{Out}bound{In}{Out}` static
methods defined in the
[`ChannelInboundHandler`](https://github.com/apple/swift-nio/blob/27146d484478b1bb0f150e848758f3a34ed9cbd0/Sources/NIOCore/TypeAssistedChannelHandler.swift#L60)
and
[`ChannelOutboundHandler`](https://github.com/apple/swift-nio/blob/27146d484478b1bb0f150e848758f3a34ed9cbd0/Sources/NIOCore/TypeAssistedChannelHandler.swift#L95)
protocols and their use from all channel handler classes. As such, we
should replace the `Self` part in all
`Self.[un]wrap{In}{Out}bound{In}{Out}` calls with the explicit class
name.
### Modifications:
Replaced all `Self.[un]wrap{In}{Out}bound{In}{Out}` calls to use the
explicit class name.
### Result:
Eliminates unnecessary overhead.
114 lines
4.5 KiB
Swift
114 lines
4.5 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the SwiftNIO open source project
|
|
//
|
|
// Copyright (c) 2022 Apple Inc. and the SwiftNIO project authors
|
|
// Licensed under Apache License v2.0
|
|
//
|
|
// See LICENSE.txt for license information
|
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
import NIOCore
|
|
|
|
/// A ChannelHandler to validate that outbound request headers are spec-compliant.
|
|
///
|
|
/// The HTTP RFCs constrain the bytes that are validly present within a HTTP/1.1 header block.
|
|
/// ``NIOHTTPRequestHeadersValidator`` polices this constraint and ensures that only valid header blocks
|
|
/// are emitted on the network. If a header block is invalid, then ``NIOHTTPRequestHeadersValidator``
|
|
/// will send a ``HTTPParserError/invalidHeaderToken``.
|
|
///
|
|
/// ``NIOHTTPRequestHeadersValidator`` will also valid that the HTTP trailers are within specification,
|
|
/// if they are present.
|
|
public final class NIOHTTPRequestHeadersValidator: ChannelOutboundHandler, RemovableChannelHandler {
|
|
public typealias OutboundIn = HTTPClientRequestPart
|
|
public typealias OutboundOut = HTTPClientRequestPart
|
|
|
|
public init() {}
|
|
|
|
public func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
|
|
switch NIOHTTPRequestHeadersValidator.unwrapOutboundIn(data) {
|
|
case .head(let head):
|
|
guard head.headers.areValidToSend else {
|
|
promise?.fail(HTTPParserError.invalidHeaderToken)
|
|
context.fireErrorCaught(HTTPParserError.invalidHeaderToken)
|
|
return
|
|
}
|
|
case .body, .end(.none):
|
|
()
|
|
case .end(.some(let trailers)):
|
|
guard trailers.areValidToSend else {
|
|
promise?.fail(HTTPParserError.invalidHeaderToken)
|
|
context.fireErrorCaught(HTTPParserError.invalidHeaderToken)
|
|
return
|
|
}
|
|
}
|
|
|
|
context.write(data, promise: promise)
|
|
}
|
|
}
|
|
|
|
/// A ChannelHandler to validate that outbound response headers are spec-compliant.
|
|
///
|
|
/// The HTTP RFCs constrain the bytes that are validly present within a HTTP/1.1 header block.
|
|
/// ``NIOHTTPResponseHeadersValidator`` polices this constraint and ensures that only valid header blocks
|
|
/// are emitted on the network. If a header block is invalid, then ``NIOHTTPResponseHeadersValidator``
|
|
/// will send a ``HTTPParserError/invalidHeaderToken``.
|
|
///
|
|
/// ``NIOHTTPResponseHeadersValidator`` will also valid that the HTTP trailers are within specification,
|
|
/// if they are present.
|
|
public final class NIOHTTPResponseHeadersValidator: ChannelOutboundHandler, RemovableChannelHandler {
|
|
public typealias OutboundIn = HTTPServerResponsePart
|
|
public typealias OutboundOut = HTTPServerResponsePart
|
|
|
|
private enum State {
|
|
/// Validating response parts.
|
|
case validating
|
|
/// Dropping all response parts. This is a terminal state.
|
|
case dropping
|
|
}
|
|
|
|
private var state: State
|
|
|
|
public init() {
|
|
self.state = .validating
|
|
}
|
|
|
|
public func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
|
|
switch (NIOHTTPResponseHeadersValidator.unwrapOutboundIn(data), self.state) {
|
|
case (.head(let head), .validating):
|
|
if head.headers.areValidToSend {
|
|
context.write(data, promise: promise)
|
|
} else {
|
|
self.state = .dropping
|
|
promise?.fail(HTTPParserError.invalidHeaderToken)
|
|
context.fireErrorCaught(HTTPParserError.invalidHeaderToken)
|
|
}
|
|
|
|
case (.body, .validating):
|
|
context.write(data, promise: promise)
|
|
|
|
case (.end(let trailers), .validating):
|
|
// No trailers are always valid trailers.
|
|
if trailers?.areValidToSend ?? true {
|
|
context.write(data, promise: promise)
|
|
} else {
|
|
self.state = .dropping
|
|
promise?.fail(HTTPParserError.invalidHeaderToken)
|
|
context.fireErrorCaught(HTTPParserError.invalidHeaderToken)
|
|
}
|
|
|
|
case (.head, .dropping), (.body, .dropping), (.end, .dropping):
|
|
promise?.fail(HTTPParserError.invalidHeaderToken)
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
extension NIOHTTPRequestHeadersValidator: Sendable {}
|
|
|
|
@available(*, unavailable)
|
|
extension NIOHTTPResponseHeadersValidator: Sendable {}
|