Files
swift-nio/Sources/NIOHTTP1/HTTPHeaderValidator.swift
aryan-25 a1605a3303 Use explicit class name in all Self.[un]wrap{In}{Out}bound{In}{Out} calls (#3463)
### 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.
2025-12-15 17:36:11 +00:00

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 {}