/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(¤tFieldByteLength, &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 | | } |