Coverage Report

Created: 2026-06-01 06:32

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