here is a small reproducer program:
import NIOCore
import NIOPosix
import NIOHTTP1
import NIOHPACK
import NIOHTTP2
import NIOSSL
final
class OutboundShimHandler
{
init()
{
}
}
extension OutboundShimHandler:ChannelOutboundHandler
{
typealias OutboundIn = HTTPPart<HTTPResponseHead, ByteBuffer>
typealias OutboundOut = HTTPPart<HTTPResponseHead, IOData>
func write(context:ChannelHandlerContext, data:NIOAny, promise:EventLoopPromise<Void>?)
{
let part:OutboundOut = switch self.unwrapOutboundIn(data)
{
case .head(let head): .head(head)
case .body(let body): .body(.byteBuffer(body))
case .end(let tail): .end(tail)
}
context.write(self.wrapOutboundOut(part), promise: promise)
}
}
func main() async throws
{
let directory:String = "Local/Server/Certificates"
let threads:MultiThreadedEventLoopGroup = .init(numberOfThreads: 2)
let certificates:[NIOSSLCertificate] =
try NIOSSLCertificate.fromPEMFile("\(directory)/fullchain.pem")
let privateKey:NIOSSLPrivateKey =
try .init(file: "\(directory)/privkey.pem", format: .pem)
var configuration:TLSConfiguration = .makeServerConfiguration(
certificateChain: certificates.map(NIOSSLCertificateSource.certificate(_:)),
privateKey: .privateKey(privateKey))
configuration.applicationProtocols = ["h2"]
let niossl:NIOSSLContext = try .init(configuration: configuration)
let bootstrap:ServerBootstrap = .init(group: threads)
.serverChannelOption(ChannelOptions.backlog, value: 256)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.childChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.childChannelOption(ChannelOptions.maxMessagesPerRead, value: 1)
let listener:
NIOAsyncChannel<
EventLoopFuture<NIONegotiatedHTTPVersion<
NIOAsyncChannel<
HTTPPart<HTTPRequestHead, ByteBuffer>,
HTTPPart<HTTPResponseHead, ByteBuffer>>,
(
NIOAsyncChannel<HTTP2Frame, HTTP2Frame>,
NIOHTTP2Handler.AsyncStreamMultiplexer<NIOAsyncChannel<
HTTP2Frame.FramePayload,
HTTP2Frame.FramePayload>>
)>>,
Never> = try await bootstrap.bind(
host: "::",
port: 8443)
{
(channel:any Channel) in
channel.pipeline.addHandler(NIOSSLServerHandler.init(context: niossl))
.flatMap
{
channel.configureAsyncHTTPServerPipeline
{
(connection:any Channel) in
connection.eventLoop.makeCompletedFuture
{
try connection.pipeline.syncOperations.addHandler(
OutboundShimHandler.init())
return try NIOAsyncChannel<
HTTPPart<HTTPRequestHead, ByteBuffer>,
HTTPPart<HTTPResponseHead, ByteBuffer>>.init(
wrappingChannelSynchronously: connection,
configuration: .init())
}
}
http2ConnectionInitializer:
{
(connection:any Channel) in
connection.eventLoop.makeCompletedFuture
{
try NIOAsyncChannel<HTTP2Frame, HTTP2Frame>.init(
wrappingChannelSynchronously: connection,
configuration: .init())
}
}
http2StreamInitializer:
{
(stream:any Channel) in
stream.eventLoop.makeCompletedFuture
{
try NIOAsyncChannel<
HTTP2Frame.FramePayload,
HTTP2Frame.FramePayload>.init(
wrappingChannelSynchronously: stream,
configuration: .init())
}
}
}
}
try await listener.executeThenClose
{
for try await connection:EventLoopFuture<NIONegotiatedHTTPVersion<
NIOAsyncChannel<
HTTPPart<HTTPRequestHead, ByteBuffer>,
HTTPPart<HTTPResponseHead, ByteBuffer>>,
(
NIOAsyncChannel<HTTP2Frame, HTTP2Frame>,
NIOHTTP2Handler.AsyncStreamMultiplexer<NIOAsyncChannel<
HTTP2Frame.FramePayload,
HTTP2Frame.FramePayload>>
)>> in $0
{
switch try await connection.get()
{
case .http1_1(let connection):
try await connection.channel.close()
case .http2((let connection, let streams)):
do
{
for try await stream:NIOAsyncChannel<
HTTP2Frame.FramePayload,
HTTP2Frame.FramePayload> in streams.inbound
{
try await stream.executeThenClose
{
(_, _) in
}
}
}
catch let error
{
print(error)
}
try await connection.channel.close()
}
}
}
}
try await main()
the easiest way to run it is probably pasting it into an SPM snippet.
you’ll need TLS certificates to get this running, you can generate your own in the directory referenced from the snippet (Local/Server/Certificates
), or you can use these dummy localhost certificates:
fullchain.pem
-----BEGIN CERTIFICATE-----
MIIEGzCCAoOgAwIBAgIQL62HjbQWmPjVsRAzHNeu3TANBgkqhkiG9w0BAQsFADBp
MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExHzAdBgNVBAsMFnVidW50
dUBrbG9zc3kgKGRpYW5uYSkxJjAkBgNVBAMMHW1rY2VydCB1YnVudHVAa2xvc3N5
IChkaWFubmEpMB4XDTIzMDkyMTIxNTQwNVoXDTI1MTIyMTIyNTQwNVowSjEnMCUG
A1UEChMebWtjZXJ0IGRldmVsb3BtZW50IGNlcnRpZmljYXRlMR8wHQYDVQQLDBZ1
YnVudHVAa2xvc3N5IChkaWFubmEpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEA1M4+fL98qtJHpVVJr5JudOnDY+qBHL8nzSgqR7ZwPZY6uHHtZQ8xwSdn
YL7t6XOHIrjlmA7fXrUAMCJ4ElRutRbT/+culGo4LL7jYdtAKkuiV1S86Y/1CM5C
heq2Mdgem7cI05xZQ4Mx0wZiao0SgXvXRfBmkCTVPcCrmPLqLkLIJJf+0G0qcBA5
/oqK99izxjZibF0/W9ehPKdvmTMef1845BnQDgoYR0iyLRhPCC2IZ2ze19zxxJ2R
OVurvITNcxEFMar+LdXloG0MLi2t8a1A9WCMbgGNrfOMAXiPLMI/XO7XoGsCQgb/
A7lDtE+Wag0QLghOXDOhQDdisqkqywIDAQABo14wXDAOBgNVHQ8BAf8EBAMCBaAw
EwYDVR0lBAwwCgYIKwYBBQUHAwEwHwYDVR0jBBgwFoAUQXvcrc8QC11X6Xh2VMSI
mkyRG8wwFAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBCwUAA4IBgQA6
suTTsFBRaYZITcJDS12/R5ZL/MPwTOZQzQbANtXRVO9t2gQlEqmJdCy80n89JAB4
WO6lhd73EFSe1GX4hKwNrky+cR759Har5pCu9LPtbWGQEfVQp9bjSlHHjPww1eap
KYCJ94aD8HyReRqhvZiYkqUWuGJvU7caIFEXKcsp+yPCbE6Fqdbf3dwWEkmsHStl
1rJBkRk2U/CwNDhDLb6Yna75Ffqgin7SazaJqZ3GrHmlX9GSN4OoWI5VrTzUpVqC
dHML8G3XbVe8FSaD9bcRcRv4R5ev6chcDO9S3mOfORy4kHPW73PlFYOw7fyXPpMW
hCuy6FR2nOYFGzPimwu26GIx6EV7kbf5JGBTr+sWQHeq5gpNhSelUIAr9tmrtd5y
1+SWMv9CCRO5yRD4+oPsFLO7hRQKmsnz+bz4o2F/YfDVdeXbgroWRtQUEgxeNKbQ
w3RfzWsE0BVz8esIs0KOUgokoMIxV610bz0t2zxDj9c40ISgMcjSBBHSjuDCIS8=
-----END CERTIFICATE-----
privkey.pem
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDUzj58v3yq0kel
VUmvkm506cNj6oEcvyfNKCpHtnA9ljq4ce1lDzHBJ2dgvu3pc4ciuOWYDt9etQAw
IngSVG61FtP/5y6UajgsvuNh20AqS6JXVLzpj/UIzkKF6rYx2B6btwjTnFlDgzHT
BmJqjRKBe9dF8GaQJNU9wKuY8uouQsgkl/7QbSpwEDn+ior32LPGNmJsXT9b16E8
p2+ZMx5/XzjkGdAOChhHSLItGE8ILYhnbN7X3PHEnZE5W6u8hM1zEQUxqv4t1eWg
bQwuLa3xrUD1YIxuAY2t84wBeI8swj9c7tegawJCBv8DuUO0T5ZqDRAuCE5cM6FA
N2KyqSrLAgMBAAECggEACTcdMZ3BMkyE2b4FwNqgeeOdmHgRO0Nz38h7fDuERMZ6
qH4Wf6fWybyBF4ltGAzuryw+lQUf2yQPbAYyGOkbGjBw4cYLGFY5NIbXpecusiYw
U4PR4nNfcxArhU0SsrnfKXMVqMQ+gVPvFmpSXLbbNEw+mEK+zkMqENCFHcx3I6Wg
b0WYjWeQIE9jomWU9muu799H+Ybpeds81oAz5AvQ/JPBsGTrRSW30E0wPZPW+qPv
oCmPRKCtWkrYXqoOy9lk4vhjiVQg2XYeiPxl9WrbMSq2iW82t4Sljh5R03FZWQpj
wQgfMlxDOWtr6uvuSFe1RdZUnmaTOmnNsFY4hK52EQKBgQD5ALIIU8gYiuA5l+Z/
sUknGbQ8E9HxTg1/txSOf2CRdm6ziPR9bpBEWF+8WEGd2Mb6SoMKigJfu5w8EKg2
rZvfkK0LTEqgLe0csLfh6DQRLlc5fuK/IOqPznaqvpOlwifKCosMl97yuWmQkxHz
GRBbDf0AgsdJxhjNdhGDAtnxPwKBgQDaySZidiPCWLFcEWDPMwSxVkDbCuNvLgSz
fvwz4tL/vUf+weyGNr/rkKVwQoUY4Hq8pkcm6he7FLbDnQasoe0DI8OMDXUZHtos
D7x6B9dHm6eZwtOpsdC3Q6A+vAXa8tXr5w7My0HFDbLHh/Ch8jRxKZWFxU44oqPt
wX8ZVg7XdQKBgBE0KxjIMRsA/Vz9Ub+g0B0TeZBtDiRN8EDStWjjBBkIxb1BySKh
cPZH5NVug5oUUCsa2tLvlhpnK/Q6cmTUueBIbqxJKR7IDYnd69Z/5JkLSpt+WMw7
yfkFms1RPYJGV9ltwQ2tsIm0pcaHYsYZBThFTyWp43sFZNFNRwh2OfihAoGBANQd
nyRo68R53wKnKpfYG92fBWQYy2Y4VIB+RiA78lvV9J4u/5UkMbA+XddX9timkvih
sWwuG3Ha5FMEw7rNhw+7NdRsG7KOMfH0E8SwI20eoUC3HiVw6y0y2ILaIkcjlnmP
W8775TkaTdGbn5YzT9rC+V9naq4IKSzSo9o5kEwdAoGAQRMjcCKxzI3DTvYeyqCO
U52A2Ejw9XgjRlo/7KT+PHNace+XskuULPvMkWTJilUkbCsWxqBWhVMu5/WH1V+P
+5igKwyRy8tmCtGKK7MHJGPFuh410QqAfyBJFKqpxOuAEH0/eUvTvoUcnD0v8NhV
AkaHhmNosnEH28wUaxViRLk=
-----END PRIVATE KEY-----
to cause the crash, run the snippet and navigate to https://localhost:8443
.
$ swift run NIOCrash
Building for debugging...
[6/6] Linking NIOCrash
Build complete! (2.90s)
StreamClosed(streamID: HTTP2StreamID(15), errorCode: HTTP2ErrorCode<0x8 Cancel>, location: "NIOHTTP2/HTTP2StreamChannel.swift:865")
NIOCore/NIOAsyncWriter.swift:176: Fatal error: Deinited NIOAsyncWriter without calling finish()
Current stack trace:
0 libswiftCore.so 0x00007f9b4291e1c0 _swift_stdlib_reportFatalErrorInFile + 109
1 libswiftCore.so 0x00007f9b425e3d05 <unavailable> + 1461509
2 libswiftCore.so 0x00007f9b425e3b27 <unavailable> + 1461031
3 libswiftCore.so 0x00007f9b425e2a90 _assertionFailure(_:_:file:line:flags:) + 342
4 NIOCrash 0x000055a105b25b28 <unavailable> + 10144552
5 NIOCrash 0x000055a105b2613d <unavailable> + 10146109
6 libswiftCore.so 0x00007f9b428878e0 <unavailable> + 4229344
7 libswiftCore.so 0x00007f9b4288829b <unavailable> + 4231835
8 NIOCrash 0x000055a105b122a5 <unavailable> + 10064549
9 libswiftCore.so 0x00007f9b428aa7f0 <unavailable> + 4372464
10 NIOCrash 0x000055a105e87d4a <unavailable> + 13692234
11 NIOCrash 0x000055a105c3a76a <unavailable> + 11278186
12 NIOCrash 0x000055a105c34164 <unavailable> + 11252068
13 NIOCrash 0x000055a105c347f5 <unavailable> + 11253749
14 libswiftCore.so 0x00007f9b428878e0 <unavailable> + 4229344
15 libswiftCore.so 0x00007f9b4288829b <unavailable> + 4231835
16 NIOCrash 0x000055a105c8d8e0 <unavailable> + 11618528
17 libswift_Concurrency.so 0x00007f9b42cb244d <unavailable> + 300109
18 libswift_Concurrency.so 0x00007f9b42cb2c20 swift_job_run + 92
19 libdispatch.so 0x00007f9b41f2df89 <unavailable> + 176009
20 libdispatch.so 0x00007f9b41f2dd9f <unavailable> + 175519
21 libdispatch.so 0x00007f9b41f38dc6 <unavailable> + 220614
22 libpthread.so.0 0x00007f9b4111644b <unavailable> + 29771
23 libc.so.6 0x00007f9b404b24f0 clone + 63
💣 Program crashed: Illegal instruction at 0x00007f9b425e2bf2
Thread 1 crashed:
0 0x00007f9b425e2bf2 _assertionFailure(_:_:file:line:flags:) + 354 in libswiftCore.so
1 NIOAsyncWriter.InternalClass.deinit + 247 in NIOCrash at /swift/swift-unidoc/.build/checkouts/swift-nio/Sources/NIOCore/AsyncSequences/NIOAsyncWriter.swift:176:17
174│ deinit {
175│ if !self._finishOnDeinit && !self._storage.isWriterFinished {
176│ preconditionFailure("Deinited NIOAsyncWriter without calling finish()")
│ ▲
177│ } else {
178│ // We need to call finish here to resume any suspended continuation.
2 0x00007f9b428878e0 _swift_release_dealloc + 15 in libswiftCore.so
3 0x00007f9b4288829b bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1> >::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 122 in libswiftCore.so
4 0x00007f9b428aa7f0 void tuple_destroy<false, false>(swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*) + 47 in libswiftCore.so
5 EventLoopFuture.deinit + 163 in NIOCrash at /swift/swift-unidoc/.build/checkouts/swift-nio/Sources/NIOCore/EventLoopFuture.swift:431:5
429│ }
430│ }
431│ }
│ ▲
432│ }
433│
6 0x00007f9b428878e0 _swift_release_dealloc + 15 in libswiftCore.so
7 0x00007f9b4288829b bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1> >::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 122 in libswiftCore.so
8 closure #2 in main() + 159 in NIOCrash at /swift/swift-unidoc/Snippets/NIOCrash.swift:159:9
157│ try await connection.channel.close()
158│ }
159│ }
│ ▲
160│ }
161│ }
9 closure #1 in NIOAsyncChannel.executeThenClose<A>(_:) in NIOCrash at /swift/swift-unidoc/.build/checkouts/swift-nio/Sources/NIOCore/AsyncChannel/AsyncChannel.swift:303
301│ ) async throws -> Result where Outbound == Never {
302│ try await self.executeThenClose { inbound, _ in
303│ try await body(inbound)
│ ▲
304│ }
305│ }
10 NIOAsyncChannel.executeThenClose<A>(_:) in NIOCrash at /swift/swift-unidoc/.build/checkouts/swift-nio/Sources/NIOCore/AsyncChannel/AsyncChannel.swift:271
269│ let result: Result
270│ do {
271│ result = try await body(self._inbound, self._outbound)
│ ▲
272│ } catch let bodyError {
273│ do {
11 NIOAsyncChannel.executeThenClose<A>(_:) in NIOCrash at /swift/swift-unidoc/.build/checkouts/swift-nio/Sources/NIOCore/AsyncChannel/AsyncChannel.swift:302
300│ _ body: (_ inbound: NIOAsyncChannelInboundStream<Inbound>) async throws -> Result
301│ ) async throws -> Result where Outbound == Never {
302│ try await self.executeThenClose { inbound, _ in
│ ▲
303│ try await body(inbound)
304│ }
12 main() in NIOCrash at /swift/swift-unidoc/Snippets/NIOCrash.swift:121
119│ }
120│
121│ try await listener.executeThenClose
│ ▲
122│ {
123│ for try await connection:EventLoopFuture<NIONegotiatedHTTPVersion<
Illegal instruction (core dumped)