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