Coverage Report

Created: 2026-10-13 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/swift-nio/Sources/NIOHTTP1/HTTPServerPipelineHandler.swift
Line
Count
Source
1
//===----------------------------------------------------------------------===//
2
//
3
// This source file is part of the SwiftNIO open source project
4
//
5
// Copyright (c) 2017-2024 Apple Inc. and the SwiftNIO project authors
6
// Licensed under Apache License v2.0
7
//
8
// See LICENSE.txt for license information
9
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
10
//
11
// SPDX-License-Identifier: Apache-2.0
12
//
13
//===----------------------------------------------------------------------===//
14
import NIOCore
15
16
/// A `ChannelHandler` that handles HTTP pipelining by buffering inbound data until a
17
/// response has been sent.
18
///
19
/// This handler ensures that HTTP server pipelines only process one request at a time.
20
/// This is the safest way for pipelining-unaware code to operate, as it ensures that
21
/// mutation of any shared server state is not parallelised, and that responses are always
22
/// sent for each request in turn. In almost all cases this is the behaviour that a
23
/// pipeline will want. This is achieved without doing too much buffering by preventing
24
/// the `Channel` from reading from the socket until a complete response is processed,
25
/// ensuring that a malicious client is not capable of overwhelming a server by shoving
26
/// an enormous amount of data down the `Channel` while a server is processing a
27
/// slow response.
28
///
29
/// See [RFC 7320 Section 6.3.2](https://tools.ietf.org/html/rfc7230#section-6.3.2) for
30
/// more details on safely handling HTTP pipelining.
31
///
32
/// In addition to handling the request buffering, this `ChannelHandler` is aware of
33
/// TCP half-close. While there are very few HTTP clients that are capable of TCP
34
/// half-close, clients that are not HTTP specific (e.g. `netcat`) may trigger a TCP
35
/// half-close. Having this `ChannelHandler` be aware of TCP half-close makes it easier
36
/// to build HTTP servers that are resilient to this kind of behaviour.
37
///
38
/// The TCP half-close handling is done by buffering the half-close notification along
39
/// with the HTTP request parts. The half-close notification will be delivered in order
40
/// with the rest of the reads. If the half-close occurs either before a request is received
41
/// or during a request body upload, it will be delivered immediately. If a half-close is
42
/// received immediately after `HTTPServerRequestPart.end`, it will also be passed along
43
/// immediately, allowing this signal to be seen by the HTTP server as early as possible.
44
public final class HTTPServerPipelineHandler: ChannelDuplexHandler, RemovableChannelHandler {
45
    public typealias InboundIn = HTTPServerRequestPart
46
    public typealias InboundOut = HTTPServerRequestPart
47
    public typealias OutboundIn = HTTPServerResponsePart
48
    public typealias OutboundOut = HTTPServerResponsePart
49
50
    // If this is true AND we're in a debug build, crash the program when an invariant in violated
51
    // Otherwise, we will try to handle the situation as cleanly as possible
52
0
    internal var failOnPreconditions: Bool = true
53
54
0
    public init() {
55
0
        self.nextExpectedInboundMessage = nil
56
0
        self.nextExpectedOutboundMessage = nil
57
0
58
0
        debugOnly {
59
0
            self.nextExpectedInboundMessage = .head
60
0
            self.nextExpectedOutboundMessage = .head
61
0
        }
62
0
    }
63
64
    private enum ConnectionStateAction {
65
        /// A precondition has been violated. Should send an error down the pipeline
66
        case warnPreconditionViolated(message: String)
67
68
        /// A further state change was attempted when a precondition has already been violated.
69
        /// Should force close this connection
70
        case forceCloseConnection
71
72
        /// Nothing to do
73
        case none
74
    }
75
76
    public struct ConnectionStateError: Error, CustomStringConvertible, Hashable {
77
        enum Base: Hashable, CustomStringConvertible {
78
            /// A precondition was violated
79
            case preconditionViolated(message: String)
80
81
0
            var description: String {
82
0
                switch self {
83
0
                case .preconditionViolated(let message):
84
0
                    return "Precondition violated \(message)"
85
0
                }
86
0
            }
87
        }
88
89
        private var base: Base
90
        private var file: String
91
        private var line: Int
92
93
0
        private init(base: Base, file: String, line: Int) {
94
0
            self.base = base
95
0
            self.file = file
96
0
            self.line = line
97
0
        }
98
99
0
        public static func == (lhs: ConnectionStateError, rhs: ConnectionStateError) -> Bool {
100
0
            lhs.base == rhs.base
101
0
        }
102
103
0
        public func hash(into hasher: inout Hasher) {
104
0
            hasher.combine(self.base)
105
0
        }
106
107
        /// A precondition was violated
108
0
        public static func preconditionViolated(message: String, file: String = #fileID, line: Int = #line) -> Self {
109
0
            .init(base: .preconditionViolated(message: message), file: file, line: line)
110
0
        }
111
112
0
        public var description: String {
113
0
            "\(self.base) file \(self.file) line \(self.line)"
114
0
        }
115
    }
116
117
    /// The state of the HTTP connection.
118
    private enum ConnectionState {
119
        /// We are waiting for a HTTP response to complete before we
120
        /// let the next request in.
121
        case responseEndPending
122
123
        /// We are in the middle of both a request and a response and waiting for both `.end`s.
124
        case requestAndResponseEndPending
125
126
        /// Nothing is active on this connection, the next message we expect would be a request `.head`.
127
        case idle
128
129
        /// The server has responded early, before the request has completed. We need
130
        /// to wait for the request to complete, but won't block anything.
131
        case requestEndPending
132
133
        /// The server has closed the output partway through a request. The server will never
134
        /// act again, but this may not be in error, so we'll forward the rest of this request to the server.
135
        case sentCloseOutputRequestEndPending
136
137
        /// The server has closed the output, and a complete request has been delivered.
138
        /// It's never going to act again. Generally we expect this to be closely followed
139
        /// by read EOF, but we need to keep reading to make that possible, so we
140
        /// never suppress reads again.
141
        case sentCloseOutput
142
143
        /// The user has violated an invariant. We should refuse further IO now
144
        case preconditionFailed
145
146
0
        mutating func requestHeadReceived() -> ConnectionStateAction {
147
0
            switch self {
148
0
            case .preconditionFailed:
149
0
                return .forceCloseConnection
150
0
            case .idle:
151
0
                self = .requestAndResponseEndPending
152
0
                return .none
153
0
            case .requestAndResponseEndPending, .responseEndPending, .requestEndPending,
154
0
                .sentCloseOutputRequestEndPending, .sentCloseOutput:
155
0
                let message = "received request head in state \(self)"
156
0
                self = .preconditionFailed
157
0
                return .warnPreconditionViolated(message: message)
158
0
            }
159
0
        }
160
161
0
        mutating func responseEndReceived() -> ConnectionStateAction {
162
0
            switch self {
163
0
            case .preconditionFailed:
164
0
                return .forceCloseConnection
165
0
            case .responseEndPending:
166
0
                // Got the response we were waiting for.
167
0
                self = .idle
168
0
                return .none
169
0
            case .requestAndResponseEndPending:
170
0
                // We got a response while still receiving a request, which we have to
171
0
                // wait for.
172
0
                self = .requestEndPending
173
0
                return .none
174
0
            case .sentCloseOutput, .sentCloseOutputRequestEndPending:
175
0
                // This is a user error: they have sent close(mode: .output), but are continuing to write.
176
0
                // The write will fail, so we can allow it to pass.
177
0
                return .none
178
0
            case .requestEndPending, .idle:
179
0
                let message = "Unexpectedly received a response in state \(self)"
180
0
                self = .preconditionFailed
181
0
                return .warnPreconditionViolated(message: message)
182
0
            }
183
0
        }
184
185
0
        mutating func requestEndReceived() -> ConnectionStateAction {
186
0
            switch self {
187
0
            case .preconditionFailed:
188
0
                return .forceCloseConnection
189
0
            case .requestEndPending:
190
0
                // Got the request end we were waiting for.
191
0
                self = .idle
192
0
                return .none
193
0
            case .requestAndResponseEndPending:
194
0
                // We got a request and the response isn't done, wait for the
195
0
                // response.
196
0
                self = .responseEndPending
197
0
                return .none
198
0
            case .sentCloseOutputRequestEndPending:
199
0
                // Got the request end we were waiting for.
200
0
                self = .sentCloseOutput
201
0
                return .none
202
0
            case .responseEndPending, .idle, .sentCloseOutput:
203
0
                let message = "Received second request"
204
0
                self = .preconditionFailed
205
0
                return .warnPreconditionViolated(message: message)
206
0
            }
207
0
        }
208
209
0
        mutating func closeOutputSent() {
210
0
            switch self {
211
0
            case .preconditionFailed:
212
0
                break
213
0
            case .idle, .responseEndPending:
214
0
                self = .sentCloseOutput
215
0
            case .requestEndPending, .requestAndResponseEndPending:
216
0
                self = .sentCloseOutputRequestEndPending
217
0
            case .sentCloseOutput, .sentCloseOutputRequestEndPending:
218
0
                // Weird to duplicate fail, but we tolerate it in both cases.
219
0
                ()
220
0
            }
221
0
        }
222
    }
223
224
    /// The events that this handler buffers while waiting for the server to
225
    /// generate a response.
226
    private enum BufferedEvent {
227
        /// A channelRead event.
228
        case channelRead(NIOAny)
229
230
        case error(HTTPParserError)
231
232
        /// A TCP half-close. This is buffered to ensure that subsequent channel
233
        /// handlers that are aware of TCP half-close are informed about it in
234
        /// the appropriate order.
235
        case halfClose
236
    }
237
238
    /// The connection state
239
0
    private var state = ConnectionState.idle
240
241
    /// While we're waiting to send the response we don't read from the socket.
242
    /// This keeps track of whether we need to call read() when we've send our response.
243
0
    private var readPending = false
244
245
    /// The buffered HTTP requests that are not going to be addressed yet. In general clients
246
    /// don't pipeline, so this initially allocates no space for data at all. Clients that
247
    /// do pipeline will cause dynamic resizing of the buffer, which is generally acceptable.
248
0
    private var eventBuffer = CircularBuffer<BufferedEvent>(initialCapacity: 0)
249
250
    enum NextExpectedMessageType {
251
        case head
252
        case bodyOrEnd
253
    }
254
255
    enum LifecycleState {
256
        /// Operating normally, accepting all events.
257
        case acceptingEvents
258
259
        /// Quiescing but we're still waiting for the request's `.end` which means we still need to process input.
260
        case quiescingWaitingForRequestEnd
261
262
        /// Quiescing and the last request's `.end` has been seen which means we no longer accept any input.
263
        case quiescingLastRequestEndReceived
264
265
        /// Quiescing and we have issued a channel close. Further I/O here is not expected, and won't be managed.
266
        case quiescingCompleted
267
    }
268
269
0
    private var lifecycleState: LifecycleState = .acceptingEvents
270
271
    // always `nil` in release builds, never `nil` in debug builds
272
    private var nextExpectedInboundMessage: Optional<NextExpectedMessageType>
273
    // always `nil` in release builds, never `nil` in debug builds
274
    private var nextExpectedOutboundMessage: Optional<NextExpectedMessageType>
275
276
0
    public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
277
0
        switch self.lifecycleState {
278
0
        case .quiescingLastRequestEndReceived, .quiescingCompleted:
279
0
            // We're done, no more data for you.
280
0
            return
281
0
        case .acceptingEvents, .quiescingWaitingForRequestEnd:
282
0
            // Still accepting I/O
283
0
            ()
284
0
        }
285
0
286
0
        if self.state == .sentCloseOutput {
287
0
            // Drop all events in this state.
288
0
            return
289
0
        }
290
0
291
0
        if self.eventBuffer.count != 0 || self.state == .responseEndPending {
292
0
            self.eventBuffer.append(.channelRead(data))
293
0
            return
294
0
        } else {
295
0
            let connectionStateAction = self.deliverOneMessage(context: context, data: data)
296
0
            _ = self.handleConnectionStateAction(context: context, action: connectionStateAction, promise: nil)
297
0
        }
298
0
    }
299
300
0
    private func deliverOneMessage(context: ChannelHandlerContext, data: NIOAny) -> ConnectionStateAction {
301
0
        self.checkAssertion(
302
0
            self.lifecycleState != .quiescingLastRequestEndReceived && self.lifecycleState != .quiescingCompleted,
303
0
            "deliverOneMessage called in lifecycle illegal state \(self.lifecycleState)"
304
0
        )
305
0
        let msg = self.unwrapInboundIn(data)
306
0
307
0
        debugOnly {
308
0
            switch msg {
309
0
            case .head:
310
0
                self.checkAssertion(self.nextExpectedInboundMessage == .head)
311
0
                self.nextExpectedInboundMessage = .bodyOrEnd
312
0
            case .body:
313
0
                self.checkAssertion(self.nextExpectedInboundMessage == .bodyOrEnd)
314
0
            case .end:
315
0
                self.checkAssertion(self.nextExpectedInboundMessage == .bodyOrEnd)
316
0
                self.nextExpectedInboundMessage = .head
317
0
            }
318
0
        }
319
0
320
0
        let action: ConnectionStateAction
321
0
        switch msg {
322
0
        case .head:
323
0
            action = self.state.requestHeadReceived()
324
0
        case .end:
325
0
            // New request is complete. We don't want any more data from now on.
326
0
            action = self.state.requestEndReceived()
327
0
328
0
            if self.lifecycleState == .quiescingWaitingForRequestEnd {
329
0
                self.lifecycleState = .quiescingLastRequestEndReceived
330
0
                self.eventBuffer.removeAll()
331
0
            }
332
0
            if self.lifecycleState == .quiescingLastRequestEndReceived && self.state == .idle {
333
0
                self.lifecycleState = .quiescingCompleted
334
0
                context.close(promise: nil)
335
0
            }
336
0
        case .body:
337
0
            action = .none
338
0
        }
339
0
340
0
        context.fireChannelRead(data)
341
0
        return action
342
0
    }
343
344
0
    private func deliverOneError(context: ChannelHandlerContext, error: Error) {
345
0
        // there is one interesting case in this error sending logic: If we receive a `HTTPParserError` and we haven't
346
0
        // received a full request nor the beginning of a response we should treat this as a full request. The reason
347
0
        // is that what the user will probably do is send a `.badRequest` response and we should be in a state which
348
0
        // allows that.
349
0
        if (self.state == .idle || self.state == .requestEndPending) && error is HTTPParserError {
350
0
            self.state = .responseEndPending
351
0
        }
352
0
        context.fireErrorCaught(error)
353
0
    }
354
355
0
    public func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) {
356
0
        switch event {
357
0
        case is ChannelShouldQuiesceEvent:
358
0
            self.checkAssertion(
359
0
                self.lifecycleState == .acceptingEvents,
360
0
                "unexpected lifecycle state when receiving ChannelShouldQuiesceEvent: \(self.lifecycleState)"
361
0
            )
362
0
            switch self.state {
363
0
            case .responseEndPending:
364
0
                // we're not in the middle of a request, let's just shut the door
365
0
                self.lifecycleState = .quiescingLastRequestEndReceived
366
0
                self.eventBuffer.removeAll()
367
0
            case .preconditionFailed,
368
0
                // An invariant has been violated already, this time we close the connection
369
0
                .idle, .sentCloseOutput:
370
0
                // we're completely idle, let's just close
371
0
                self.lifecycleState = .quiescingCompleted
372
0
                self.eventBuffer.removeAll()
373
0
                context.close(promise: nil)
374
0
            case .requestEndPending, .requestAndResponseEndPending, .sentCloseOutputRequestEndPending:
375
0
                // we're in the middle of a request, we'll need to keep accepting events until we see the .end.
376
0
                // It's ok for us to forget we saw close output here, the lifecycle event will close for us.
377
0
                self.lifecycleState = .quiescingWaitingForRequestEnd
378
0
            }
379
0
        case ChannelEvent.inputClosed:
380
0
            // We only buffer half-close if there are request parts we're waiting to send.
381
0
            // Otherwise we deliver the half-close immediately. Note that we deliver this
382
0
            // even if the server has sent close output, as it's useful information.
383
0
            if case .responseEndPending = self.state, self.eventBuffer.count > 0 {
384
0
                self.eventBuffer.append(.halfClose)
385
0
            } else {
386
0
                context.fireUserInboundEventTriggered(event)
387
0
            }
388
0
        default:
389
0
            context.fireUserInboundEventTriggered(event)
390
0
        }
391
0
    }
392
393
0
    public func errorCaught(context: ChannelHandlerContext, error: Error) {
394
0
        guard let httpError = error as? HTTPParserError else {
395
0
            self.deliverOneError(context: context, error: error)
396
0
            return
397
0
        }
398
0
        if case .responseEndPending = self.state {
399
0
            self.eventBuffer.append(.error(httpError))
400
0
            return
401
0
        }
402
0
        self.deliverOneError(context: context, error: error)
403
0
    }
404
405
0
    public func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
406
0
        self.checkAssertion(
407
0
            self.state != .requestEndPending,
408
0
            "Received second response while waiting for first one to complete"
409
0
        )
410
0
        debugOnly {
411
0
            let res = HTTPServerPipelineHandler.unwrapOutboundIn(data)
412
0
            switch res {
413
0
            case .head(let head) where head.isInformational:
414
0
                self.checkAssertion(self.nextExpectedOutboundMessage == .head)
415
0
            case .head:
416
0
                self.checkAssertion(self.nextExpectedOutboundMessage == .head)
417
0
                self.nextExpectedOutboundMessage = .bodyOrEnd
418
0
            case .body:
419
0
                self.checkAssertion(self.nextExpectedOutboundMessage == .bodyOrEnd)
420
0
            case .end:
421
0
                self.checkAssertion(self.nextExpectedOutboundMessage == .bodyOrEnd)
422
0
                self.nextExpectedOutboundMessage = .head
423
0
            }
424
0
        }
425
0
426
0
        var startReadingAgain = false
427
0
428
0
        switch HTTPServerPipelineHandler.unwrapOutboundIn(data) {
429
0
        case .head(var head) where self.lifecycleState != .acceptingEvents:
430
0
            if head.isKeepAlive {
431
0
                head.headers.replaceOrAdd(name: "connection", value: "close")
432
0
            }
433
0
            context.write(HTTPServerPipelineHandler.wrapOutboundOut(.head(head)), promise: promise)
434
0
        case .end:
435
0
            startReadingAgain = true
436
0
437
0
            switch self.lifecycleState {
438
0
            case .quiescingWaitingForRequestEnd where self.state == .responseEndPending:
439
0
                // we just received the .end that we're missing so we can fall through to closing the connection
440
0
                fallthrough
441
0
            case .quiescingLastRequestEndReceived:
442
0
                let loopBoundContext = context.loopBound
443
0
                self.lifecycleState = .quiescingCompleted
444
0
                context.write(data).flatMap {
445
0
                    let context = loopBoundContext.value
446
0
                    return context.close()
447
0
                }.cascade(to: promise)
448
0
            case .acceptingEvents, .quiescingWaitingForRequestEnd:
449
0
                context.write(data, promise: promise)
450
0
            case .quiescingCompleted:
451
0
                // Uh, why are we writing more data here? We'll write it, but it should be guaranteed
452
0
                // to fail.
453
0
                self.assertionFailed("Wrote in quiescing completed state")
454
0
                context.write(data, promise: promise)
455
0
            }
456
0
        case .body, .head:
457
0
            context.write(data, promise: promise)
458
0
        }
459
0
460
0
        if startReadingAgain {
461
0
            let connectionStateAction = self.state.responseEndReceived()
462
0
            if self.handleConnectionStateAction(context: context, action: connectionStateAction, promise: promise) {
463
0
                return
464
0
            }
465
0
            self.deliverPendingRequests(context: context)
466
0
            self.startReading(context: context)
467
0
        }
468
0
    }
469
470
0
    public func read(context: ChannelHandlerContext) {
471
0
        switch self.lifecycleState {
472
0
        case .quiescingLastRequestEndReceived, .quiescingCompleted:
473
0
            // We swallow all reads now, as we're going to close the connection.
474
0
            ()
475
0
        case .acceptingEvents, .quiescingWaitingForRequestEnd:
476
0
            if case .responseEndPending = self.state {
477
0
                self.readPending = true
478
0
            } else {
479
0
                context.read()
480
0
            }
481
0
        }
482
0
    }
483
484
0
    public func handlerRemoved(context: ChannelHandlerContext) {
485
0
        // We're being removed from the pipeline. We need to do a few things:
486
0
        //
487
0
        // 1. If we have buffered events, deliver them. While we shouldn't be
488
0
        //     re-entrantly called, we want to ensure that so we take a local copy.
489
0
        // 2. If we are quiescing, we swallowed a quiescing event from the user: replay it,
490
0
        //     as the user has hopefully added a handler that will do something with this.
491
0
        // 3. Finally, if we have a read pending, we need to release it.
492
0
        //
493
0
        // The basic theory here is that if there is anything we were going to do when we received
494
0
        // either a request .end or a response .end, we do it now because there is no future for us.
495
0
        // We also need to ensure we do not drop any data on the floor.
496
0
        //
497
0
        // At this stage we are no longer in the pipeline, so all further content should be
498
0
        // blocked from reaching us. Thus we can avoid mutating our own internal state any
499
0
        // longer.
500
0
        let bufferedEvents = self.eventBuffer
501
0
        for event in bufferedEvents {
502
0
            switch event {
503
0
            case .channelRead(let read):
504
0
                context.fireChannelRead(read)
505
0
            case .halfClose:
506
0
                context.fireUserInboundEventTriggered(ChannelEvent.inputClosed)
507
0
            case .error(let error):
508
0
                context.fireErrorCaught(error)
509
0
            }
510
0
        }
511
0
512
0
        switch self.lifecycleState {
513
0
        case .quiescingLastRequestEndReceived, .quiescingWaitingForRequestEnd:
514
0
            context.fireUserInboundEventTriggered(ChannelShouldQuiesceEvent())
515
0
        case .acceptingEvents, .quiescingCompleted:
516
0
            // Either we haven't quiesced, or we succeeded in doing it.
517
0
            ()
518
0
        }
519
0
520
0
        if self.readPending {
521
0
            context.read()
522
0
        }
523
0
    }
524
525
0
    public func channelInactive(context: ChannelHandlerContext) {
526
0
        // Welp, this channel isn't going to work anymore. We may as well drop our pending events here, as we
527
0
        // cannot be expected to act on them any longer.
528
0
        //
529
0
        // Side note: it's important that we drop these. If we don't, handlerRemoved will deliver them all.
530
0
        // While it's fair to immediately pipeline a channel where the user chose to remove the HTTPPipelineHandler,
531
0
        // it's deeply unfair to do so to a user that didn't choose to do that, where it happened to them only because
532
0
        // the channel closed.
533
0
        //
534
0
        // We set keepingCapacity to avoid this reallocating a buffer, as we'll just free it shortly anyway.
535
0
        self.eventBuffer.removeAll(keepingCapacity: true)
536
0
        context.fireChannelInactive()
537
0
    }
538
539
0
    public func close(context: ChannelHandlerContext, mode: CloseMode, promise: EventLoopPromise<Void>?) {
540
0
        var shouldRead = false
541
0
542
0
        if mode == .output {
543
0
            // We need to do special handling here. If the server is closing output they don't intend to write anymore.
544
0
            // That means we want to drop anything up to the end of the in-flight request.
545
0
            self.dropAllButInFlightRequest()
546
0
            self.state.closeOutputSent()
547
0
548
0
            // If there's a read pending, we should deliver it after we forward the close on.
549
0
            shouldRead = self.readPending
550
0
        }
551
0
552
0
        context.close(mode: mode, promise: promise)
553
0
554
0
        // Double-check readPending here in case something weird happened.
555
0
        //
556
0
        // Note that because of the state transition in closeOutputSent() above we likely won't actually
557
0
        // forward any further reads to the user, unless they belong to a request currently streaming in.
558
0
        // Any reads past that point will be dropped in channelRead().
559
0
        if shouldRead && self.readPending {
560
0
            self.readPending = false
561
0
            context.read()
562
0
        }
563
0
    }
564
565
    /// - Returns: True if an error was sent, ie the caller should not continue
566
    private func handleConnectionStateAction(
567
        context: ChannelHandlerContext,
568
        action: ConnectionStateAction,
569
        promise: EventLoopPromise<Void>?
570
0
    ) -> Bool {
571
0
        switch action {
572
0
        case .warnPreconditionViolated(let message):
573
0
            self.assertionFailed(message)
574
0
            let error = ConnectionStateError.preconditionViolated(message: message)
575
0
            self.deliverOneError(context: context, error: error)
576
0
            promise?.fail(error)
577
0
            return true
578
0
        case .forceCloseConnection:
579
0
            let message =
580
0
                "The connection has been forcefully closed because further IO was attempted after a precondition was violated"
581
0
            let error = ConnectionStateError.preconditionViolated(message: message)
582
0
            promise?.fail(error)
583
0
            self.close(context: context, mode: .all, promise: nil)
584
0
            return true
585
0
        case .none:
586
0
            return false
587
0
        }
588
0
    }
589
590
    /// A response has been sent: we can now start passing reads through
591
    /// again if there are no further pending requests, and send any read()
592
    /// call we may have swallowed.
593
0
    private func startReading(context: ChannelHandlerContext) {
594
0
        if self.readPending && self.state != .responseEndPending {
595
0
            switch self.lifecycleState {
596
0
            case .quiescingLastRequestEndReceived, .quiescingCompleted:
597
0
                // No more reads in these states.
598
0
                ()
599
0
            case .acceptingEvents, .quiescingWaitingForRequestEnd:
600
0
                self.readPending = false
601
0
                context.read()
602
0
            }
603
0
        }
604
0
    }
605
606
    /// A response has been sent: deliver all pending requests and
607
    /// mark the channel ready to handle more requests.
608
0
    private func deliverPendingRequests(context: ChannelHandlerContext) {
609
0
        var deliveredRead = false
610
0
611
0
        while self.state != .responseEndPending, let event = self.eventBuffer.first {
612
0
            self.eventBuffer.removeFirst()
613
0
614
0
            switch event {
615
0
            case .channelRead(let read):
616
0
                let connectionStateAction = self.deliverOneMessage(context: context, data: read)
617
0
                if !self.handleConnectionStateAction(context: context, action: connectionStateAction, promise: nil) {
618
0
                    deliveredRead = true
619
0
                }
620
0
            case .error(let error):
621
0
                self.deliverOneError(context: context, error: error)
622
0
            case .halfClose:
623
0
                // When we fire the half-close, we want to forget all prior reads.
624
0
                // They will just trigger further half-close notifications we don't
625
0
                // need.
626
0
                self.readPending = false
627
0
                context.fireUserInboundEventTriggered(ChannelEvent.inputClosed)
628
0
            }
629
0
        }
630
0
631
0
        if deliveredRead {
632
0
            context.fireChannelReadComplete()
633
0
        }
634
0
635
0
        // We need to quickly check whether there is an EOF waiting here, because
636
0
        // if there is we should also unbuffer it and pass it along. There is no
637
0
        // advantage in sitting on it, and it may help the later channel handlers
638
0
        // be more sensible about keep-alive logic if they can see this early.
639
0
        // This is done after `fireChannelReadComplete` to keep the same observable
640
0
        // behaviour as `SocketChannel`, which fires these events in this order.
641
0
        if case .some(.halfClose) = self.eventBuffer.first {
642
0
            self.eventBuffer.removeFirst()
643
0
            self.readPending = false
644
0
            context.fireUserInboundEventTriggered(ChannelEvent.inputClosed)
645
0
        }
646
0
    }
647
648
0
    private func dropAllButInFlightRequest() {
649
0
        // We're going to walk the request buffer up to the next `.head` and drop from there.
650
0
        let maybeFirstHead = self.eventBuffer.firstIndex(where: { element in
651
0
            switch element {
652
0
            case .channelRead(let read):
653
0
                switch HTTPServerPipelineHandler.unwrapInboundIn(read) {
654
0
                case .head:
655
0
                    return true
656
0
                case .body, .end:
657
0
                    return false
658
0
                }
659
0
            case .error, .halfClose:
660
0
                // Leave these where they are, if they're before the next .head we still want to deliver them.
661
0
                // If they're after the next .head, we don't care.
662
0
                return false
663
0
            }
664
0
        })
665
0
666
0
        guard let firstHead = maybeFirstHead else {
667
0
            return
668
0
        }
669
0
670
0
        self.eventBuffer.removeSubrange(firstHead...)
671
0
    }
672
673
    /// A utility function that runs the body code only in debug builds, without
674
    /// emitting compiler warnings.
675
    ///
676
    /// This is currently the only way to do this in Swift: see
677
    /// https://forums.swift.org/t/support-debug-only-code/11037 for a discussion.
678
0
    private func debugOnly(_ body: () -> Void) {
679
0
        self.checkAssertion(
680
0
            {
681
0
                body()
682
0
                return true
683
0
            }()
Unexecuted instantiation: $s8NIOHTTP125HTTPServerPipelineHandlerC9debugOnly33_60DBF281C59552393B20BFC998B34533LLyyyyXEFSbyXEfu_
Unexecuted instantiation: $s8NIOHTTP125HTTPServerPipelineHandlerC9debugOnly33_60DBF281C59552393B20BFC998B34533LLyyyyXEFSbyXEfu_SbyXEfU_
684
0
        )
685
0
    }
686
687
    /// Calls assertionFailure if and only if `self.failOnPreconditions` is true. This allows us to avoid terminating the program in tests
688
0
    private func assertionFailed(_ message: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) {
689
0
        if self.failOnPreconditions {
690
0
            assertionFailure(message(), file: file, line: line)
691
0
        }
692
0
    }
693
694
    /// Calls assert if and only if `self.failOnPreconditions` is true. This allows us to avoid terminating the program in tests
695
    private func checkAssertion(
696
        _ closure: @autoclosure () -> Bool,
697
0
        _ message: @autoclosure () -> String = String(),
698
        file: StaticString = #file,
699
        line: UInt = #line
700
0
    ) {
701
0
        if self.failOnPreconditions {
702
0
            assert(closure(), message(), file: file, line: line)
703
0
        }
704
0
    }
705
}
706
707
@available(*, unavailable)
708
extension HTTPServerPipelineHandler: Sendable {}