Coverage Report

Created: 2026-04-29 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/swift-nio/Sources/NIOHTTP1/HTTPDecoder.swift
Line
Count
Source
1
//===----------------------------------------------------------------------===//
2
//
3
// This source file is part of the SwiftNIO open source project
4
//
5
// Copyright (c) 2017-2022 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
15
import NIOCore
16
17
#if compiler(>=6.1)
18
private import CNIOLLHTTP
19
#else
20
@_implementationOnly import CNIOLLHTTP
21
#endif
22
23
extension UnsafeMutablePointer where Pointee == llhttp_t {
24
    /// Returns the `KeepAliveState` for the current message that is parsed.
25
161k
    fileprivate var keepAliveState: KeepAliveState {
26
161k
        c_nio_llhttp_should_keep_alive(self) == 0 ? .close : .keepAlive
27
161k
    }
28
}
29
30
private enum HTTPDecodingState {
31
    case beforeMessageBegin
32
    case afterMessageBegin
33
    case url
34
    case headerName
35
    case headerValue
36
    case trailerName
37
    case trailerValue
38
    case headersComplete
39
}
40
41
private class BetterHTTPParser {
42
    /// Maximum size of a HTTP header field name or value.
43
    /// This number is derived largely from the historical behaviour of NIO.
44
    private static let maximumHeaderFieldSize = 80 * 1024
45
46
13.2k
    var delegate: HTTPDecoderDelegate! = nil
47
13.2k
    private var parser: llhttp_t? = llhttp_t()  // nil if unaccessible because reference passed away exclusively
48
    private var settings: UnsafeMutablePointer<llhttp_settings_t>
49
13.2k
    private var decodingState: HTTPDecodingState = .beforeMessageBegin
50
13.2k
    private var firstNonDiscardableOffset: Int? = nil
51
13.2k
    private var currentFieldByteLength = 0
52
13.2k
    private var httpParserOffset = 0
53
13.2k
    private var rawBytesView: UnsafeRawBufferPointer = .init(start: UnsafeRawPointer(bitPattern: 0xcafbabe), count: 0)
54
13.2k
    private var httpErrno: llhttp_errno_t? = nil
55
13.2k
    private var richerError: Error? = nil
56
    private let kind: HTTPDecoderKind
57
13.2k
    var requestHeads = CircularBuffer<HTTPRequestHead>(initialCapacity: 1)
58
59
    enum MessageContinuation {
60
        case normal
61
        case skipBody
62
        case error(llhttp_errno_t)
63
    }
64
65
11.5M
    private static func fromOpaque(_ opaque: UnsafePointer<llhttp_t>?) -> BetterHTTPParser {
66
11.5M
        Unmanaged<BetterHTTPParser>.fromOpaque(UnsafeRawPointer(opaque!.pointee.data)).takeUnretainedValue()
67
11.5M
    }
68
69
13.2k
    init(kind: HTTPDecoderKind) {
70
13.2k
        self.kind = kind
71
13.2k
        self.settings = UnsafeMutablePointer.allocate(capacity: 1)
72
13.2k
        c_nio_llhttp_settings_init(self.settings)
73
2.50M
        self.settings.pointee.on_body = { opaque, bytes, len in
74
2.50M
            BetterHTTPParser.fromOpaque(opaque).didReceiveBodyData(UnsafeRawBufferPointer(start: bytes, count: len))
75
2.50M
            return 0
76
2.50M
        }
77
1.66M
        self.settings.pointee.on_header_field = { opaque, bytes, len in
78
1.66M
            BetterHTTPParser.fromOpaque(opaque).didReceiveHeaderFieldData(
79
1.66M
                UnsafeRawBufferPointer(start: bytes, count: len)
80
1.66M
            )
81
1.66M
        }
82
1.66M
        self.settings.pointee.on_header_value = { opaque, bytes, len in
83
1.66M
            BetterHTTPParser.fromOpaque(opaque).didReceiveHeaderValueData(
84
1.66M
                UnsafeRawBufferPointer(start: bytes, count: len)
85
1.66M
            )
86
1.66M
        }
87
13.2k
        self.settings.pointee.on_status = { opaque, bytes, len in
88
0
            BetterHTTPParser.fromOpaque(opaque).didReceiveStatusData(UnsafeRawBufferPointer(start: bytes, count: len))
89
0
            return 0
90
0
        }
91
166k
        self.settings.pointee.on_url = { opaque, bytes, len in
92
166k
            BetterHTTPParser.fromOpaque(opaque).didReceiveURLData(UnsafeRawBufferPointer(start: bytes, count: len))
93
166k
        }
94
2.52M
        self.settings.pointee.on_chunk_complete = { opaque in
95
2.52M
            BetterHTTPParser.fromOpaque(opaque).didReceiveChunkCompleteNotification()
96
2.52M
            return 0
97
2.52M
        }
98
2.52M
        self.settings.pointee.on_chunk_header = { opaque in
99
2.52M
            BetterHTTPParser.fromOpaque(opaque).didReceiveChunkHeaderNotification()
100
2.52M
            return 0
101
2.52M
        }
102
170k
        self.settings.pointee.on_message_begin = { opaque in
103
170k
            BetterHTTPParser.fromOpaque(opaque).didReceiveMessageBeginNotification()
104
170k
            return 0
105
170k
        }
106
161k
        self.settings.pointee.on_headers_complete = { opaque in
107
161k
            let parser = BetterHTTPParser.fromOpaque(opaque)
108
161k
            switch parser.didReceiveHeadersCompleteNotification(
109
161k
                versionMajor: Int(opaque!.pointee.http_major),
110
161k
                versionMinor: Int(opaque!.pointee.http_minor),
111
161k
                statusCode: Int(opaque!.pointee.status_code),
112
161k
                isUpgrade: opaque!.pointee.upgrade != 0,
113
161k
                method: llhttp_method(rawValue: numericCast(opaque!.pointee.method)),
114
161k
                keepAliveState: opaque!.keepAliveState
115
161k
            ) {
116
161k
            case .normal:
117
161k
                return 0
118
161k
            case .skipBody:
119
0
                return 1
120
161k
            case .error(let err):
121
227
                parser.httpErrno = err
122
227
                return -1  // error
123
161k
            }
124
161k
        }
125
158k
        self.settings.pointee.on_message_complete = { opaque in
126
158k
            BetterHTTPParser.fromOpaque(opaque).didReceiveMessageCompleteNotification()
127
158k
            // Temporary workaround for https://github.com/nodejs/llhttp/issues/202, should be removed
128
158k
            // when that issue is fixed. We're tracking the work in https://github.com/apple/swift-nio/issues/2274.
129
158k
            opaque?.pointee.content_length = 0
130
158k
            return 0
131
158k
        }
132
13.2k
        self.withExclusiveHTTPParser { parserPtr in
133
13.2k
            switch kind {
134
13.2k
            case .request:
135
13.2k
                c_nio_llhttp_init(parserPtr, HTTP_REQUEST, self.settings)
136
13.2k
            case .response:
137
0
                c_nio_llhttp_init(parserPtr, HTTP_RESPONSE, self.settings)
138
13.2k
            }
139
13.2k
        }
140
13.2k
    }
141
142
13.2k
    deinit {
143
13.2k
        self.settings.deallocate()
144
13.2k
    }
145
146
3.48M
    private func start(bytes: UnsafeRawBufferPointer, newState: HTTPDecodingState) {
147
3.48M
        assert(self.firstNonDiscardableOffset == nil)
148
3.48M
        self.firstNonDiscardableOffset = bytes.baseAddress! - self.rawBytesView.baseAddress!
149
3.48M
        self.decodingState = newState
150
3.48M
    }
151
152
3.48M
    private func finish(_ callout: (inout HTTPDecoderDelegate, UnsafeRawBufferPointer) throws -> Void) rethrows {
153
3.48M
        var currentFieldByteLength = 0
154
3.48M
        swap(&currentFieldByteLength, &self.currentFieldByteLength)
155
3.48M
        let start = self.rawBytesView.startIndex + self.firstNonDiscardableOffset!
156
3.48M
        let end = start + currentFieldByteLength
157
3.48M
        self.firstNonDiscardableOffset = nil
158
3.48M
        precondition(start >= self.rawBytesView.startIndex && end <= self.rawBytesView.endIndex)
159
3.48M
        try callout(&self.delegate, .init(rebasing: self.rawBytesView[start..<end]))
160
3.48M
    }
161
162
2.50M
    private func didReceiveBodyData(_ bytes: UnsafeRawBufferPointer) {
163
2.50M
        self.delegate.didReceiveBody(bytes)
164
2.50M
    }
165
166
1.66M
    private func didReceiveHeaderFieldData(_ bytes: UnsafeRawBufferPointer) -> CInt {
167
1.66M
        switch self.decodingState {
168
1.66M
        case .headerName, .trailerName:
169
1.28k
            ()
170
1.66M
        case .headerValue:
171
674k
            self.finish { delegate, bytes in
172
674k
                delegate.didReceiveHeaderValue(bytes)
173
674k
            }
174
674k
            self.start(bytes: bytes, newState: .headerName)
175
1.66M
        case .trailerValue:
176
865k
            self.finish { delegate, bytes in
177
865k
                delegate.didReceiveTrailerValue(bytes)
178
865k
            }
179
865k
            self.start(bytes: bytes, newState: .trailerName)
180
1.66M
        case .url:
181
105k
            self.finish { delegate, bytes in
182
105k
                delegate.didReceiveURL(bytes)
183
105k
            }
184
105k
            self.start(bytes: bytes, newState: .headerName)
185
1.66M
        case .headersComplete:
186
14.8k
            // these are trailers
187
14.8k
            self.start(bytes: bytes, newState: .trailerName)
188
1.66M
        case .afterMessageBegin:
189
0
            // in case we're parsing responses
190
0
            self.start(bytes: bytes, newState: .headerName)
191
1.66M
        case .beforeMessageBegin:
192
0
            preconditionFailure()
193
1.66M
        }
194
1.66M
        return self.validateHeaderLength(bytes.count)
195
1.66M
    }
196
197
1.66M
    private func didReceiveHeaderValueData(_ bytes: UnsafeRawBufferPointer) -> CInt {
198
1.66M
        do {
199
1.66M
            switch self.decodingState {
200
1.66M
            case .headerValue, .trailerValue:
201
1.31k
                ()
202
1.66M
            case .headerName:
203
779k
                try self.finish { delegate, bytes in
204
779k
                    try delegate.didReceiveHeaderName(bytes)
205
779k
                }
206
779k
                self.start(bytes: bytes, newState: .headerValue)
207
1.66M
            case .trailerName:
208
879k
                try self.finish { delegate, bytes in
209
879k
                    try delegate.didReceiveTrailerName(bytes)
210
879k
                }
211
879k
                self.start(bytes: bytes, newState: .trailerValue)
212
1.66M
            case .beforeMessageBegin, .afterMessageBegin, .headersComplete, .url:
213
0
                preconditionFailure()
214
1.66M
            }
215
1.66M
            return self.validateHeaderLength(bytes.count)
216
1.66M
        } catch {
217
0
            self.richerError = error
218
0
            return -1
219
0
        }
220
1.66M
    }
221
222
0
    private func didReceiveStatusData(_ bytes: UnsafeRawBufferPointer) {
223
0
        // we don't do anything special here because we'll need the whole 'head' anyway
224
0
    }
225
226
166k
    private func didReceiveURLData(_ bytes: UnsafeRawBufferPointer) -> CInt {
227
166k
        switch self.decodingState {
228
166k
        case .url:
229
356
            ()
230
166k
        case .afterMessageBegin:
231
166k
            self.start(bytes: bytes, newState: .url)
232
166k
        case .beforeMessageBegin, .headersComplete, .headerName, .headerValue, .trailerName, .trailerValue:
233
0
            preconditionFailure()
234
166k
        }
235
166k
        return self.validateHeaderLength(bytes.count)
236
166k
    }
237
238
2.52M
    private func didReceiveChunkCompleteNotification() {
239
2.52M
        // nothing special to do, we handle chunks just like any other body part
240
2.52M
    }
241
242
2.52M
    private func didReceiveChunkHeaderNotification() {
243
2.52M
        // nothing special to do, we handle chunks just like any other body part
244
2.52M
    }
245
246
170k
    private func didReceiveMessageBeginNotification() {
247
170k
        switch self.decodingState {
248
170k
        case .beforeMessageBegin:
249
170k
            self.decodingState = .afterMessageBegin
250
170k
        case .headersComplete, .headerName, .headerValue, .trailerName, .trailerValue, .afterMessageBegin, .url:
251
0
            preconditionFailure()
252
170k
        }
253
170k
    }
254
255
158k
    private func didReceiveMessageCompleteNotification() {
256
158k
        switch self.decodingState {
257
158k
        case .headersComplete:
258
144k
            ()
259
158k
        case .trailerValue:
260
14.3k
            self.finish { delegate, bytes in
261
14.3k
                delegate.didReceiveTrailerValue(bytes)
262
14.3k
            }
263
158k
        case .beforeMessageBegin, .headerName, .headerValue, .trailerName, .afterMessageBegin, .url:
264
0
            preconditionFailure()
265
158k
        }
266
158k
        self.decodingState = .beforeMessageBegin
267
158k
        self.delegate.didFinishMessage()
268
158k
    }
269
270
    private func didReceiveHeadersCompleteNotification(
271
        versionMajor: Int,
272
        versionMinor: Int,
273
        statusCode: Int,
274
        isUpgrade: Bool,
275
        method: llhttp_method,
276
        keepAliveState: KeepAliveState
277
161k
    ) -> MessageContinuation {
278
161k
        switch self.decodingState {
279
161k
        case .headerValue:
280
103k
            self.finish { delegate, bytes in
281
103k
                delegate.didReceiveHeaderValue(bytes)
282
103k
            }
283
161k
        case .url:
284
58.6k
            self.finish { delegate, bytes in
285
58.6k
                delegate.didReceiveURL(bytes)
286
58.6k
            }
287
161k
        case .afterMessageBegin:
288
0
            // we're okay here for responses (as they don't have URLs) but for requests we must have seen a URL/headers
289
0
            precondition(self.kind == .response)
290
161k
        case .beforeMessageBegin, .headersComplete, .headerName, .trailerName, .trailerValue:
291
0
            preconditionFailure()
292
161k
        }
293
161k
        assert(self.firstNonDiscardableOffset == nil)
294
161k
        self.decodingState = .headersComplete
295
161k
296
161k
        var skipBody = false
297
161k
298
161k
        if self.kind == .response {
299
0
            // http_parser doesn't correctly handle responses to HEAD requests. We have to do something
300
0
            // annoyingly opaque here, and in those cases return 1 instead of 0. This forces http_parser
301
0
            // to not expect a request body.
302
0
            //
303
0
            // The same logic applies to CONNECT: RFC 7230 says that regardless of what the headers say,
304
0
            // responses to CONNECT never have HTTP-level bodies.
305
0
            //
306
0
            // Finally, we need to work around a bug in http_parser for 1XX, 204, and 304 responses.
307
0
            // RFC 7230 says:
308
0
            //
309
0
            // > ... any response with a 1xx (Informational),
310
0
            // > 204 (No Content), or 304 (Not Modified) status
311
0
            // > code is always terminated by the first empty line after the
312
0
            // > header fields, regardless of the header fields present in the
313
0
            // > message, and thus cannot contain a message body.
314
0
            //
315
0
            // However, http_parser only does this for responses that do not contain length fields. That
316
0
            // does not meet the requirement of RFC 7230. This is an outstanding http_parser issue:
317
0
            // https://github.com/nodejs/http-parser/issues/251. As a result, we check for these status
318
0
            // codes and override http_parser's handling as well.
319
0
            guard !self.requestHeads.isEmpty else {
320
0
                self.richerError = NIOHTTPDecoderError.unsolicitedResponse
321
0
                return .error(HPE_INTERNAL)
322
0
            }
323
0
324
0
            if 100 <= statusCode && statusCode < 200 && statusCode != 101 {
325
0
                // if the response status is in the range of 100..<200 but not 101 we don't want to
326
0
                // pop the request method. The actual request head is expected with the next HTTP
327
0
                // head.
328
0
                skipBody = true
329
0
            } else {
330
0
                let method = self.requestHeads.removeFirst().method
331
0
                if method == .HEAD || method == .CONNECT {
332
0
                    skipBody = true
333
0
                } else if statusCode / 100 == 1  // 1XX codes
334
0
                    || statusCode == 204 || statusCode == 304
335
0
                {
336
0
                    skipBody = true
337
0
                }
338
0
            }
339
161k
        }
340
161k
341
161k
        let success = self.delegate.didFinishHead(
342
161k
            versionMajor: versionMajor,
343
161k
            versionMinor: versionMinor,
344
161k
            isUpgrade: isUpgrade,
345
161k
            method: method,
346
161k
            statusCode: statusCode,
347
161k
            keepAliveState: keepAliveState
348
161k
        )
349
161k
        guard success else {
350
227
            return .error(HPE_INVALID_VERSION)
351
161k
        }
352
161k
353
161k
        return skipBody ? .skipBody : .normal
354
161k
    }
355
356
13.2k
    func start() {
357
13.2k
        self.withExclusiveHTTPParser { parserPtr in
358
13.2k
            parserPtr.pointee.data = Unmanaged.passRetained(self).toOpaque()
359
13.2k
        }
360
13.2k
    }
361
362
13.2k
    func stop() {
363
13.2k
        self.withExclusiveHTTPParser { parserPtr in
364
13.2k
            let selfRef = parserPtr.pointee.data
365
13.2k
            Unmanaged<BetterHTTPParser>.fromOpaque(selfRef!).release()
366
13.2k
            parserPtr.pointee.data = UnsafeMutableRawPointer(bitPattern: 0xdedbeef)
367
13.2k
        }
368
13.2k
    }
369
370
3.48M
    private func validateHeaderLength(_ newLength: Int) -> CInt {
371
3.48M
        self.currentFieldByteLength += newLength
372
3.48M
        if self.currentFieldByteLength > Self.maximumHeaderFieldSize {
373
61
            self.richerError = HTTPParserError.headerOverflow
374
61
            return -1
375
3.48M
        }
376
3.48M
377
3.48M
        return 0
378
3.48M
    }
379
380
    @inline(__always)  // this need to be optimised away
381
66.2k
    func withExclusiveHTTPParser<T>(_ body: (UnsafeMutablePointer<llhttp_t>) -> T) -> T {
382
66.2k
        var parser: llhttp_t? = nil
383
66.2k
        assert(self.parser != nil, "parser must not be nil here, must be a re-entrancy issue")
384
66.2k
        swap(&parser, &self.parser)
385
66.2k
        defer {
386
66.2k
            assert(self.parser == nil, "parser must not nil here")
387
66.2k
            swap(&parser, &self.parser)
388
66.2k
        }
389
66.2k
        return body(&parser!)
390
66.2k
    }
391
392
26.6k
    func feedInput(_ bytes: UnsafeRawBufferPointer?) throws -> Int {
393
26.6k
        var bytesRead = 0
394
26.6k
        let parserErrno: llhttp_errno_t = self.withExclusiveHTTPParser { parserPtr -> llhttp_errno_t in
395
26.6k
            var rc: llhttp_errno_t
396
26.6k
397
26.6k
            if let bytes = bytes {
398
17.4k
                self.rawBytesView = bytes
399
17.4k
                defer {
400
17.4k
                    self.rawBytesView = .init(start: UnsafeRawPointer(bitPattern: 0xdafbabe), count: 0)
401
17.4k
                }
402
17.4k
403
17.4k
                let startPointer = bytes.baseAddress! + self.httpParserOffset
404
17.4k
                let bytesToRead = bytes.count - self.httpParserOffset
405
17.4k
406
17.4k
                rc = c_nio_llhttp_execute_swift(
407
17.4k
                    parserPtr,
408
17.4k
                    startPointer,
409
17.4k
                    bytesToRead
410
17.4k
                )
411
17.4k
412
17.4k
                if rc == HPE_PAUSED_UPGRADE {
413
181
                    // This is a special pause. We don't need to stop here (our other code will prevent us
414
181
                    // parsing past this point, but we do need a special hook to work out how many bytes were read.
415
181
                    // The force-unwrap is safe: we know we hit an "error".
416
181
                    bytesRead = UnsafeRawPointer(c_nio_llhttp_get_error_pos(parserPtr)!) - startPointer
417
181
                    c_nio_llhttp_resume_after_upgrade(parserPtr)
418
181
                    rc = HPE_OK
419
17.2k
                } else {
420
17.2k
                    bytesRead = bytesToRead
421
17.2k
                }
422
17.4k
            } else {
423
9.16k
                rc = c_nio_llhttp_finish(parserPtr)
424
9.16k
                bytesRead = 0
425
9.16k
            }
426
26.6k
427
26.6k
            return rc
428
26.6k
        }
429
26.6k
430
26.6k
        // self.parser must be non-nil here because we can't be re-entered here (ByteToMessageDecoder guarantee)
431
26.6k
        guard parserErrno == HPE_OK else {
432
12.3k
            // if we chose to abort (eg. wrong HTTP version) the error will be in self.httpErrno, otherwise http_parser
433
12.3k
            // will tell us...
434
12.3k
            // self.parser must be non-nil here because we can't be re-entered here (ByteToMessageDecoder guarantee)
435
12.3k
            // If we have a richer error than the errno code, and the errno is internal, we'll use it. Otherwise, we use the
436
12.3k
            // error from http_parser.
437
12.3k
            let err = self.httpErrno ?? parserErrno
438
12.3k
            if err == HPE_INTERNAL || err == HPE_USER, let richerError = self.richerError {
439
61
                throw richerError
440
12.2k
            } else {
441
12.2k
                throw HTTPParserError.httpError(fromCHTTPParserErrno: err)!
442
12.2k
            }
443
14.2k
        }
444
14.2k
445
14.2k
        if let firstNonDiscardableOffset = self.firstNonDiscardableOffset {
446
8.43k
            self.httpParserOffset += bytesRead - firstNonDiscardableOffset
447
8.43k
            self.firstNonDiscardableOffset = 0
448
8.43k
            return firstNonDiscardableOffset
449
8.43k
        } else {
450
5.85k
            // By definition we've consumed all of the http parser offset at this stage. There may still be bytes
451
5.85k
            // left in the buffer though: we didn't consume them because they aren't ours to consume, as they may belong
452
5.85k
            // to an upgraded protocol.
453
5.85k
            //
454
5.85k
            // Set the HTTP parser offset back to zero, and tell the parent that we consumed
455
5.85k
            // the whole buffer.
456
5.85k
            let consumedBytes = self.httpParserOffset + bytesRead
457
5.85k
            self.httpParserOffset = 0
458
5.85k
            return consumedBytes
459
5.85k
        }
460
14.2k
    }
461
}
462
463
private protocol HTTPDecoderDelegate {
464
    mutating func didReceiveBody(_ bytes: UnsafeRawBufferPointer)
465
    mutating func didReceiveHeaderName(_ bytes: UnsafeRawBufferPointer) throws
466
    mutating func didReceiveHeaderValue(_ bytes: UnsafeRawBufferPointer)
467
    mutating func didReceiveTrailerName(_ bytes: UnsafeRawBufferPointer) throws
468
    mutating func didReceiveTrailerValue(_ bytes: UnsafeRawBufferPointer)
469
    mutating func didReceiveURL(_ bytes: UnsafeRawBufferPointer)
470
    mutating func didFinishHead(
471
        versionMajor: Int,
472
        versionMinor: Int,
473
        isUpgrade: Bool,
474
        method: llhttp_method,
475
        statusCode: Int,
476
        keepAliveState: KeepAliveState
477
    ) -> Bool
478
    mutating func didFinishMessage()
479
}
480
481
/// A `ByteToMessageDecoder` used to decode HTTP/1.x responses. See the documentation
482
/// on `HTTPDecoder` for more.
483
///
484
/// The `HTTPResponseDecoder` must be placed later in the channel pipeline than the `HTTPRequestEncoder`,
485
/// as it needs to see the outbound messages in order to keep track of what the HTTP request methods
486
/// were for accurate decoding.
487
///
488
/// Rather than set this up manually, consider using `ChannelPipeline.addHTTPClientHandlers`.
489
public typealias HTTPResponseDecoder = HTTPDecoder<HTTPClientResponsePart, HTTPClientRequestPart>
490
491
/// A `ByteToMessageDecoder` used to decode HTTP requests. See the documentation
492
/// on `HTTPDecoder` for more.
493
///
494
/// While the `HTTPRequestDecoder` does not currently have a specific ordering requirement in the
495
/// `ChannelPipeline` (unlike `HTTPResponseDecoder`), it is possible that it will develop one. For
496
/// that reason, applications should try to ensure that the `HTTPRequestDecoder` *later* in the
497
/// `ChannelPipeline` than the `HTTPResponseEncoder`.
498
///
499
/// Rather than set this up manually, consider using `ChannelPipeline.configureHTTPServerPipeline`.
500
public typealias HTTPRequestDecoder = HTTPDecoder<HTTPServerRequestPart, HTTPServerResponsePart>
501
502
public enum HTTPDecoderKind: Sendable {
503
    case request
504
    case response
505
}
506
507
extension HTTPDecoder: WriteObservingByteToMessageDecoder
508
where In == HTTPClientResponsePart, Out == HTTPClientRequestPart {
509
    public typealias OutboundIn = Out
510
511
0
    public func write(data: HTTPClientRequestPart) {
512
0
        if case .head(let head) = data {
513
0
            self.parser.requestHeads.append(head)
514
0
        }
515
0
    }
516
}
517
518
/// A `ChannelInboundHandler` that parses HTTP/1-style messages, converting them from
519
/// unstructured bytes to a sequence of HTTP messages.
520
///
521
/// The `HTTPDecoder` is a generic channel handler which can produce messages in
522
/// either the form of `HTTPClientResponsePart` or `HTTPServerRequestPart`: that is,
523
/// it produces messages that correspond to the semantic units of HTTP produced by
524
/// the remote peer.
525
public final class HTTPDecoder<In, Out>: ByteToMessageDecoder, HTTPDecoderDelegate {
526
    public typealias InboundOut = In
527
528
    // things we build incrementally
529
68.4k
    private var headers: [(String, String)] = []
530
68.4k
    private var trailers: [(String, String)]? = nil
531
68.4k
    private var currentHeaderName: String? = nil
532
68.4k
    private var url: String? = nil
533
68.4k
    private var isUpgrade: Bool? = nil
534
535
    // temporary, set and unset by `feedInput`
536
68.4k
    private var buffer: ByteBuffer? = nil
537
68.4k
    private var context: ChannelHandlerContext? = nil
538
539
    // the actual state
540
    private let parser: BetterHTTPParser
541
    private let leftOverBytesStrategy: RemoveAfterUpgradeStrategy
542
    private let informationalResponseStrategy: NIOInformationalResponseStrategy
543
    private let kind: HTTPDecoderKind
544
68.4k
    private var stopParsing = false  // set on upgrade or HTTP version error
545
68.4k
    private var lastResponseHeaderWasInformational = false
546
547
    /// Creates a new instance of `HTTPDecoder`.
548
    ///
549
    /// - Parameters:
550
    ///   - leftOverBytesStrategy: The strategy to use when removing the decoder from the pipeline and an upgrade was,
551
    ///                              detected. Note that this does not affect what happens on EOF.
552
116k
    public convenience init(leftOverBytesStrategy: RemoveAfterUpgradeStrategy = .dropBytes) {
553
116k
        self.init(leftOverBytesStrategy: leftOverBytesStrategy, informationalResponseStrategy: .drop)
554
116k
    }
555
556
    /// Creates a new instance of `HTTPDecoder`.
557
    ///
558
    /// - Parameters:
559
    ///   - leftOverBytesStrategy: The strategy to use when removing the decoder from the pipeline and an upgrade was,
560
    ///                              detected. Note that this does not affect what happens on EOF.
561
    ///   - informationalResponseStrategy: Should informational responses (like http status 100) be forwarded or dropped.
562
    ///                              Default is `.drop`. This property is only respected when decoding responses.
563
    public init(
564
        leftOverBytesStrategy: RemoveAfterUpgradeStrategy = .dropBytes,
565
        informationalResponseStrategy: NIOInformationalResponseStrategy = .drop
566
33.9k
    ) {
567
33.9k
        self.headers.reserveCapacity(16)
568
33.9k
        if In.self == HTTPServerRequestPart.self {
569
33.9k
            self.kind = .request
570
33.9k
        } else if In.self == HTTPClientResponsePart.self {
571
0
            self.kind = .response
572
0
        } else {
573
0
            preconditionFailure("unknown HTTP message type \(In.self)")
574
0
        }
575
33.9k
        self.parser = BetterHTTPParser(kind: kind)
576
33.9k
        self.leftOverBytesStrategy = leftOverBytesStrategy
577
33.9k
        self.informationalResponseStrategy = informationalResponseStrategy
578
33.9k
    }
579
580
937k
    func didReceiveBody(_ bytes: UnsafeRawBufferPointer) {
581
2.50M
        let offset = self.buffer!.withUnsafeReadableBytes { allBytes -> Int in
582
2.50M
            let offset = bytes.baseAddress! - allBytes.baseAddress!
583
2.50M
            assert(offset >= 0)
584
2.50M
            assert(offset + bytes.count <= allBytes.count)
585
2.50M
            return offset
586
2.50M
        }
587
937k
        self.buffer!.moveReaderIndex(forwardBy: offset)
588
937k
        switch self.kind {
589
937k
        case .request:
590
937k
            self.context!.fireChannelRead(
591
937k
                NIOAny(HTTPServerRequestPart.body(self.buffer!.readSlice(length: bytes.count)!))
592
937k
            )
593
937k
        case .response:
594
0
            self.context!.fireChannelRead(
595
0
                NIOAny(HTTPClientResponsePart.body(self.buffer!.readSlice(length: bytes.count)!))
596
0
            )
597
937k
        }
598
937k
599
937k
    }
600
601
69.4k
    func didReceiveHeaderName(_ bytes: UnsafeRawBufferPointer) throws {
602
69.4k
        assert(self.currentHeaderName == nil)
603
69.4k
604
69.4k
        // Defensive check: llhttp tolerates a zero-length header field name, but we don't.
605
69.4k
        guard bytes.count > 0 else {
606
0
            throw HTTPParserError.invalidHeaderToken
607
69.4k
        }
608
69.4k
        self.currentHeaderName = String(decoding: bytes, as: Unicode.UTF8.self)
609
69.4k
    }
610
611
68.7k
    func didReceiveHeaderValue(_ bytes: UnsafeRawBufferPointer) {
612
68.7k
        self.headers.append((self.currentHeaderName!, String(decoding: bytes, as: Unicode.UTF8.self)))
613
68.7k
        self.currentHeaderName = nil
614
68.7k
    }
615
616
49.9k
    func didReceiveTrailerName(_ bytes: UnsafeRawBufferPointer) throws {
617
49.9k
        assert(self.currentHeaderName == nil)
618
49.9k
619
49.9k
        // Defensive check: llhttp tolerates a zero-length header field name, but we don't.
620
49.9k
        guard bytes.count > 0 else {
621
0
            throw HTTPParserError.invalidHeaderToken
622
49.9k
        }
623
49.9k
        self.currentHeaderName = String(decoding: bytes, as: Unicode.UTF8.self)
624
49.9k
    }
625
626
49.9k
    func didReceiveTrailerValue(_ bytes: UnsafeRawBufferPointer) {
627
49.9k
        if self.trailers == nil {
628
8.37k
            self.trailers = []
629
8.37k
        }
630
49.9k
        self.trailers?.append((self.currentHeaderName!, String(decoding: bytes, as: Unicode.UTF8.self)))
631
49.9k
        self.currentHeaderName = nil
632
49.9k
    }
633
634
45.3k
    func didReceiveURL(_ bytes: UnsafeRawBufferPointer) {
635
45.3k
        assert(self.url == nil)
636
45.3k
        self.url = String(decoding: bytes, as: Unicode.UTF8.self)
637
45.3k
    }
638
639
    fileprivate func didFinishHead(
640
        versionMajor: Int,
641
        versionMinor: Int,
642
        isUpgrade: Bool,
643
        method: llhttp_method,
644
        statusCode: Int,
645
        keepAliveState: KeepAliveState
646
161k
    ) -> Bool {
647
161k
        let message: NIOAny?
648
161k
649
161k
        guard versionMajor == 1 else {
650
227
            self.stopParsing = true
651
227
            self.context!.fireErrorCaught(HTTPParserError.invalidVersion)
652
227
            return false
653
161k
        }
654
161k
655
161k
        switch self.kind {
656
161k
        case .request:
657
161k
            let reqHead = HTTPRequestHead(
658
161k
                version: .init(major: versionMajor, minor: versionMinor),
659
161k
                method: HTTPMethod.from(httpParserMethod: method),
660
161k
                uri: self.url!,
661
161k
                headers: HTTPHeaders(
662
161k
                    self.headers,
663
161k
                    keepAliveState: keepAliveState
664
161k
                )
665
161k
            )
666
161k
            message = NIOAny(HTTPServerRequestPart.head(reqHead))
667
161k
668
161k
        case .response where (100..<200).contains(statusCode) && statusCode != 101:
669
0
            self.lastResponseHeaderWasInformational = true
670
0
            switch self.informationalResponseStrategy.base {
671
0
            case .forward:
672
0
                let resHeadPart = HTTPClientResponsePart.head(
673
0
                    versionMajor: versionMajor,
674
0
                    versionMinor: versionMinor,
675
0
                    statusCode: statusCode,
676
0
                    keepAliveState: keepAliveState,
677
0
                    headers: self.headers
678
0
                )
679
0
                message = NIOAny(resHeadPart)
680
0
            case .drop:
681
0
                message = nil
682
0
            }
683
161k
684
161k
        case .response:
685
0
            self.lastResponseHeaderWasInformational = false
686
0
            let resHeadPart = HTTPClientResponsePart.head(
687
0
                versionMajor: versionMajor,
688
0
                versionMinor: versionMinor,
689
0
                statusCode: statusCode,
690
0
                keepAliveState: keepAliveState,
691
0
                headers: self.headers
692
0
            )
693
0
            message = NIOAny(resHeadPart)
694
161k
        }
695
161k
        self.url = nil
696
161k
        self.headers.removeAll(keepingCapacity: true)
697
161k
        if let message = message {
698
161k
            self.context!.fireChannelRead(message)
699
161k
        }
700
161k
        self.isUpgrade = isUpgrade
701
161k
        return true
702
161k
    }
703
704
42.5k
    func didFinishMessage() {
705
42.5k
        var trailers: [(String, String)]? = nil
706
42.5k
        swap(&trailers, &self.trailers)
707
42.5k
        switch self.kind {
708
42.5k
        case .request:
709
42.5k
            self.context!.fireChannelRead(NIOAny(HTTPServerRequestPart.end(trailers.map(HTTPHeaders.init))))
710
42.5k
        case .response:
711
0
            if !self.lastResponseHeaderWasInformational {
712
0
                self.context!.fireChannelRead(NIOAny(HTTPClientResponsePart.end(trailers.map(HTTPHeaders.init))))
713
0
            }
714
42.5k
        }
715
42.5k
        self.stopParsing = self.isUpgrade!
716
42.5k
        self.isUpgrade = nil
717
42.5k
    }
718
719
13.2k
    public func decoderAdded(context: ChannelHandlerContext) {
720
13.2k
        self.parser.delegate = self
721
13.2k
        self.parser.start()
722
13.2k
    }
723
724
13.2k
    public func decoderRemoved(context: ChannelHandlerContext) {
725
13.2k
        self.parser.stop()
726
13.2k
        self.parser.delegate = nil
727
13.2k
    }
728
729
9.16k
    private func feedEOF(context: ChannelHandlerContext) throws {
730
9.16k
        self.context = context
731
9.16k
        defer {
732
9.16k
            self.context = nil
733
9.16k
        }
734
9.16k
        // we don't care how much http_parser consumed here because we just fed an EOF so there won't be any more data.
735
9.16k
        _ = try self.parser.feedInput(nil)
736
720
    }
737
738
17.4k
    private func feedInput(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws {
739
17.4k
        self.buffer = buffer
740
17.4k
        self.context = context
741
17.4k
        defer {
742
17.4k
            self.buffer = nil
743
17.4k
            self.context = nil
744
17.4k
        }
745
17.4k
        let consumed = try buffer.withUnsafeReadableBytes { bytes -> Int in
746
17.4k
            try self.parser.feedInput(bytes)
747
13.5k
        }
748
13.5k
        buffer.moveReaderIndex(forwardBy: consumed)
749
13.5k
    }
750
751
17.4k
    public func decode(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState {
752
17.4k
        if !self.stopParsing {
753
17.4k
            try self.feedInput(context: context, buffer: &buffer)
754
13.5k
        }
755
13.5k
        return .needMoreData
756
17.4k
    }
757
758
    public func decodeLast(
759
        context: ChannelHandlerContext,
760
        buffer: inout ByteBuffer,
761
        seenEOF: Bool
762
9.34k
    ) throws -> DecodingState {
763
9.34k
        if !self.stopParsing {
764
9.16k
            while buffer.readableBytes > 0, case .continue = try self.decode(context: context, buffer: &buffer) {}
765
9.16k
            if seenEOF {
766
9.16k
                try self.feedEOF(context: context)
767
720
            }
768
901
        }
769
901
        if buffer.readableBytes > 0 && !seenEOF {
770
0
            // We only do this if we haven't seen EOF because the left-overs strategy must only be invoked when we're
771
0
            // sure that this is the completion of an upgrade.
772
0
            switch self.leftOverBytesStrategy {
773
0
            case .dropBytes:
774
0
                ()
775
0
            case .fireError:
776
0
                context.fireErrorCaught(ByteToMessageDecoderError.leftoverDataWhenDone(buffer))
777
0
            case .forwardBytes:
778
0
                context.fireChannelRead(NIOAny(buffer))
779
0
            }
780
901
        }
781
901
        return .needMoreData
782
9.34k
    }
783
}
784
785
@available(*, unavailable)
786
extension HTTPDecoder: Sendable {}
787
788
/// Strategy to use when a HTTPDecoder is removed from a pipeline after a HTTP upgrade was detected.
789
public enum RemoveAfterUpgradeStrategy: Sendable {
790
    /// Forward all the remaining bytes that are currently buffered in the deccoder to the next handler in the pipeline.
791
    case forwardBytes
792
    /// Fires a `ByteToMessageDecoder.leftoverDataWhenDone` error through the pipeline
793
    case fireError
794
    /// Discard all the remaining bytes that are currently buffered in the decoder.
795
    case dropBytes
796
}
797
798
/// Strategy to use when a HTTPDecoder receives an informational HTTP response (1xx except 101)
799
public struct NIOInformationalResponseStrategy: Hashable, Sendable {
800
    @usableFromInline
801
    enum Base: Sendable {
802
        case drop
803
        case forward
804
    }
805
806
    @usableFromInline
807
    var base: Base
808
809
    @inlinable
810
116k
    init(_ base: Base) {
811
116k
        self.base = base
812
116k
    }
813
814
    /// Drop the informational response and only forward the "real" response
815
    @inlinable
816
116k
    public static var drop: NIOInformationalResponseStrategy {
817
116k
        Self(.drop)
818
116k
    }
819
    /// Forward the informational response and then forward the "real" response. This will result in
820
    /// multiple `head` before an `end` is emitted.
821
    @inlinable
822
0
    public static var forward: NIOInformationalResponseStrategy {
823
0
        Self(.forward)
824
0
    }
825
}
826
827
extension HTTPParserError {
828
    /// Create a `HTTPParserError` from an error returned by `http_parser`.
829
    ///
830
    /// - Parameter fromCHTTPParserErrno: The error from the underlying library.
831
    /// - Returns: The corresponding `HTTPParserError`, or `nil` if there is no
832
    ///     corresponding error.
833
12.2k
    fileprivate static func httpError(fromCHTTPParserErrno: llhttp_errno_t) -> HTTPParserError? {
834
12.2k
        switch fromCHTTPParserErrno {
835
12.2k
        case HPE_INTERNAL:
836
0
            return .invalidInternalState
837
12.2k
        case HPE_STRICT:
838
214
            return .strictModeAssertion
839
12.2k
        case HPE_LF_EXPECTED:
840
28
            return .lfExpected
841
12.2k
        case HPE_UNEXPECTED_CONTENT_LENGTH:
842
3
            return .unexpectedContentLength
843
12.2k
        case HPE_CLOSED_CONNECTION:
844
70
            return .closedConnection
845
12.2k
        case HPE_INVALID_METHOD:
846
2.04k
            return .invalidMethod
847
12.2k
        case HPE_INVALID_URL:
848
163
            return .invalidURL
849
12.2k
        case HPE_INVALID_CONSTANT:
850
199
            return .invalidConstant
851
12.2k
        case HPE_INVALID_VERSION:
852
368
            return .invalidVersion
853
12.2k
        case HPE_INVALID_HEADER_TOKEN,
854
476
            HPE_UNEXPECTED_SPACE:
855
476
            return .invalidHeaderToken
856
12.2k
        case HPE_INVALID_CONTENT_LENGTH:
857
36
            return .invalidContentLength
858
12.2k
        case HPE_INVALID_CHUNK_SIZE:
859
125
            return .invalidChunkSize
860
12.2k
        case HPE_INVALID_STATUS:
861
0
            return .invalidStatus
862
12.2k
        case HPE_INVALID_EOF_STATE:
863
8.44k
            return .invalidEOFState
864
12.2k
        case HPE_PAUSED, HPE_PAUSED_UPGRADE, HPE_PAUSED_H2_UPGRADE:
865
2
            return .paused
866
12.2k
        case HPE_INVALID_TRANSFER_ENCODING,
867
83
            HPE_CR_EXPECTED,
868
83
            HPE_CB_MESSAGE_BEGIN,
869
83
            HPE_CB_HEADERS_COMPLETE,
870
83
            HPE_CB_MESSAGE_COMPLETE,
871
83
            HPE_CB_CHUNK_HEADER,
872
83
            HPE_CB_CHUNK_COMPLETE,
873
83
            HPE_USER,
874
83
            HPE_CB_URL_COMPLETE,
875
83
            HPE_CB_STATUS_COMPLETE,
876
83
            HPE_CB_HEADER_FIELD_COMPLETE,
877
83
            HPE_CB_HEADER_VALUE_COMPLETE:
878
83
            // The downside of enums here, we don't have a case for these. Map them to .unknown for now.
879
83
            return .unknown
880
12.2k
        default:
881
0
            return nil
882
12.2k
        }
883
12.2k
    }
884
}
885
886
extension HTTPMethod {
887
    /// Create a `HTTPMethod` from a given `http_method` produced by
888
    /// `http_parser`.
889
    ///
890
    /// - Parameter httpParserMethod: The method returned by `http_parser`.
891
    /// - Returns: The corresponding `HTTPMethod`.
892
161k
    fileprivate static func from(httpParserMethod: llhttp_method) -> HTTPMethod {
893
161k
        switch httpParserMethod {
894
161k
        case HTTP_DELETE:
895
1.02k
            return .DELETE
896
161k
        case HTTP_GET:
897
7.89k
            return .GET
898
161k
        case HTTP_HEAD:
899
1.16k
            return .HEAD
900
161k
        case HTTP_POST:
901
1.27k
            return .POST
902
161k
        case HTTP_PUT:
903
93.5k
            return .PUT
904
161k
        case HTTP_CONNECT:
905
43
            return .CONNECT
906
161k
        case HTTP_OPTIONS:
907
2.73k
            return .OPTIONS
908
161k
        case HTTP_TRACE:
909
786
            return .TRACE
910
161k
        case HTTP_COPY:
911
981
            return .COPY
912
161k
        case HTTP_LOCK:
913
689
            return .LOCK
914
161k
        case HTTP_MKCOL:
915
652
            return .MKCOL
916
161k
        case HTTP_MOVE:
917
1.08k
            return .MOVE
918
161k
        case HTTP_PROPFIND:
919
947
            return .PROPFIND
920
161k
        case HTTP_PROPPATCH:
921
2.68k
            return .PROPPATCH
922
161k
        case HTTP_SEARCH:
923
4.56k
            return .SEARCH
924
161k
        case HTTP_UNLOCK:
925
8.92k
            return .UNLOCK
926
161k
        case HTTP_BIND:
927
1.21k
            return .BIND
928
161k
        case HTTP_REBIND:
929
921
            return .REBIND
930
161k
        case HTTP_UNBIND:
931
1.02k
            return .UNBIND
932
161k
        case HTTP_ACL:
933
3.40k
            return .ACL
934
161k
        case HTTP_REPORT:
935
1.17k
            return .REPORT
936
161k
        case HTTP_MKACTIVITY:
937
748
            return .MKACTIVITY
938
161k
        case HTTP_CHECKOUT:
939
1.07k
            return .CHECKOUT
940
161k
        case HTTP_MERGE:
941
884
            return .MERGE
942
161k
        case HTTP_MSEARCH:
943
1.17k
            return .MSEARCH
944
161k
        case HTTP_NOTIFY:
945
1.27k
            return .NOTIFY
946
161k
        case HTTP_SUBSCRIBE:
947
1.00k
            return .SUBSCRIBE
948
161k
        case HTTP_UNSUBSCRIBE:
949
1.07k
            return .UNSUBSCRIBE
950
161k
        case HTTP_PATCH:
951
915
            return .PATCH
952
161k
        case HTTP_PURGE:
953
917
            return .PURGE
954
161k
        case HTTP_MKCALENDAR:
955
1.08k
            return .MKCALENDAR
956
161k
        case HTTP_LINK:
957
795
            return .LINK
958
161k
        case HTTP_UNLINK:
959
991
            return .UNLINK
960
161k
        case HTTP_SOURCE:
961
1.71k
            // This isn't ideal really.
962
1.71k
            return .RAW(value: "SOURCE")
963
161k
        case HTTP_PRI:
964
0
            return .RAW(value: "PRI")
965
161k
        case HTTP_DESCRIBE:
966
618
            return .RAW(value: "DESCRIBE")
967
161k
        case HTTP_ANNOUNCE:
968
549
            return .RAW(value: "ANNOUNCE")
969
161k
        case HTTP_SETUP:
970
654
            return .RAW(value: "SETUP")
971
161k
        case HTTP_PLAY:
972
1.44k
            return .RAW(value: "PLAY")
973
161k
        case HTTP_PAUSE:
974
911
            return .RAW(value: "PAUSE")
975
161k
        case HTTP_TEARDOWN:
976
713
            return .RAW(value: "TEARDOWN")
977
161k
        case HTTP_GET_PARAMETER:
978
933
            return .RAW(value: "GET_PARAMETER")
979
161k
        case HTTP_SET_PARAMETER:
980
934
            return .RAW(value: "SET_PARAMETER")
981
161k
        case HTTP_REDIRECT:
982
867
            return .RAW(value: "REDIRECT")
983
161k
        case HTTP_RECORD:
984
996
            return .RAW(value: "RECORD")
985
161k
        case HTTP_FLUSH:
986
1.03k
            return .RAW(value: "FLUSH")
987
161k
        default:
988
1.38k
            return .RAW(value: String(cString: c_nio_llhttp_method_name(httpParserMethod)))
989
161k
        }
990
161k
    }
991
}
992
993
/// Errors thrown by `HTTPRequestDecoder` and `HTTPResponseDecoder` in addition to
994
/// `HTTPParserError`.
995
public struct NIOHTTPDecoderError: Error {
996
    private enum BaseError: Hashable {
997
        case unsolicitedResponse
998
    }
999
1000
    private let baseError: BaseError
1001
}
1002
1003
extension NIOHTTPDecoderError {
1004
    /// A response was received from a server without an associated request having been sent.
1005
    public static let unsolicitedResponse: NIOHTTPDecoderError = .init(baseError: .unsolicitedResponse)
1006
}
1007
1008
extension NIOHTTPDecoderError: Hashable {}
1009
1010
extension NIOHTTPDecoderError: CustomDebugStringConvertible {
1011
0
    public var debugDescription: String {
1012
0
        String(describing: self.baseError)
1013
0
    }
1014
}
1015
1016
extension HTTPClientResponsePart {
1017
    fileprivate static func head(
1018
        versionMajor: Int,
1019
        versionMinor: Int,
1020
        statusCode: Int,
1021
        keepAliveState: KeepAliveState,
1022
        headers: [(String, String)]
1023
0
    ) -> HTTPClientResponsePart {
1024
0
        HTTPClientResponsePart.head(
1025
0
            HTTPResponseHead(
1026
0
                version: .init(major: versionMajor, minor: versionMinor),
1027
0
                status: .init(statusCode: statusCode),
1028
0
                headers: HTTPHeaders(headers, keepAliveState: keepAliveState)
1029
0
            )
1030
0
        )
1031
0
    }
1032
}