mirror of
https://github.com/apple/swift-nio-extras.git
synced 2025-05-15 01:18:58 +08:00
135 lines
5.5 KiB
Swift
135 lines
5.5 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the SwiftNIO open source project
|
|
//
|
|
// Copyright (c) 2023 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 HTTPTypes
|
|
import NIOCore
|
|
import NIOHTTP1
|
|
import NIOHTTPTypes
|
|
|
|
/// A simple channel handler that translates shared HTTP types into HTTP/1 messages,
|
|
/// and vice versa, for use on the client side.
|
|
///
|
|
/// This is intended for compatibility purposes where a channel handler working with
|
|
/// HTTP/1 messages needs to work on top of the new version-independent HTTP types
|
|
/// abstraction.
|
|
public final class HTTPToHTTP1ClientCodec: ChannelDuplexHandler, RemovableChannelHandler, Sendable {
|
|
public typealias InboundIn = HTTPResponsePart
|
|
public typealias InboundOut = HTTPClientResponsePart
|
|
|
|
public typealias OutboundIn = HTTPClientRequestPart
|
|
public typealias OutboundOut = HTTPRequestPart
|
|
|
|
private let secure: Bool
|
|
private let splitCookie: Bool
|
|
|
|
/// Initializes a `HTTPToHTTP1ClientCodec`.
|
|
/// - Parameters:
|
|
/// - secure: Whether "https" or "http" is used.
|
|
/// - splitCookie: Whether the cookies sent by the client should be split
|
|
/// into multiple header fields. Splitting the `Cookie`
|
|
/// header field improves the performance of HTTP/2 and
|
|
/// HTTP/3 clients by allowing individual cookies to be
|
|
/// indexed separately in the dynamic table. It has no
|
|
/// effects in HTTP/1. Defaults to true.
|
|
public init(secure: Bool, splitCookie: Bool = true) {
|
|
self.secure = secure
|
|
self.splitCookie = splitCookie
|
|
}
|
|
|
|
public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
|
|
switch self.unwrapInboundIn(data) {
|
|
case .head(let head):
|
|
let oldResponse = HTTPResponseHead(head)
|
|
context.fireChannelRead(self.wrapInboundOut(.head(oldResponse)))
|
|
case .body(let body):
|
|
context.fireChannelRead(self.wrapInboundOut(.body(body)))
|
|
case .end(let trailers):
|
|
context.fireChannelRead(self.wrapInboundOut(.end(trailers.map(HTTPHeaders.init))))
|
|
}
|
|
}
|
|
|
|
public func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
|
|
switch self.unwrapOutboundIn(data) {
|
|
case .head(let request):
|
|
do {
|
|
let newRequest = try HTTPRequest(request, secure: self.secure, splitCookie: self.splitCookie)
|
|
context.write(self.wrapOutboundOut(.head(newRequest)), promise: promise)
|
|
} catch {
|
|
context.fireErrorCaught(error)
|
|
promise?.fail(error)
|
|
}
|
|
case .body(.byteBuffer(let body)):
|
|
context.write(self.wrapOutboundOut(.body(body)), promise: promise)
|
|
case .body:
|
|
fatalError("File region not supported")
|
|
case .end(let trailers):
|
|
let newTrailers = trailers.map { HTTPFields($0, splitCookie: false) }
|
|
context.write(self.wrapOutboundOut(.end(newTrailers)), promise: promise)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A simple channel handler that translates shared HTTP types into HTTP/1 messages,
|
|
/// and vice versa, for use on the server side.
|
|
///
|
|
/// This is intended for compatibility purposes where a channel handler working with
|
|
/// HTTP/1 messages needs to work on top of the new version-independent HTTP types
|
|
/// abstraction.
|
|
public final class HTTPToHTTP1ServerCodec: ChannelDuplexHandler, RemovableChannelHandler, Sendable {
|
|
public typealias InboundIn = HTTPRequestPart
|
|
public typealias InboundOut = HTTPServerRequestPart
|
|
|
|
public typealias OutboundIn = HTTPServerResponsePart
|
|
public typealias OutboundOut = HTTPResponsePart
|
|
|
|
/// Initializes a `HTTPToHTTP1ServerCodec`.
|
|
public init() {}
|
|
|
|
public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
|
|
switch self.unwrapInboundIn(data) {
|
|
case .head(let head):
|
|
do {
|
|
let oldRequest = try HTTPRequestHead(head)
|
|
context.fireChannelRead(self.wrapInboundOut(.head(oldRequest)))
|
|
} catch {
|
|
context.fireErrorCaught(error)
|
|
}
|
|
case .body(let body):
|
|
context.fireChannelRead(self.wrapInboundOut(.body(body)))
|
|
case .end(let trailers):
|
|
context.fireChannelRead(self.wrapInboundOut(.end(trailers.map(HTTPHeaders.init))))
|
|
}
|
|
}
|
|
|
|
public func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
|
|
switch self.unwrapOutboundIn(data) {
|
|
case .head(let response):
|
|
do {
|
|
let newResponse = try HTTPResponse(response)
|
|
context.write(self.wrapOutboundOut(.head(newResponse)), promise: promise)
|
|
} catch {
|
|
context.fireErrorCaught(error)
|
|
promise?.fail(error)
|
|
}
|
|
case .body(.byteBuffer(let body)):
|
|
context.write(self.wrapOutboundOut(.body(body)), promise: promise)
|
|
case .body:
|
|
fatalError("File region not supported")
|
|
case .end(let trailers):
|
|
let newTrailers = trailers.map { HTTPFields($0, splitCookie: false) }
|
|
context.write(self.wrapOutboundOut(.end(newTrailers)), promise: promise)
|
|
}
|
|
}
|
|
}
|