/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ext/node/polyfills/internal_binding/http_parser.ts
319 строк
9 KB
Kenta Moriuchi
chore: make prefer-primordials lint an internal plugin (#35978)
23 июл 2026, 11:53
Не верифицирован
23 июл 2026, 11:53
5b55d08
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. // deno-lint-ignore-file no-explicit-any deno-internal/prefer-primordials (function () { const { core } = __bootstrap; const { HTTPParser: NativeHTTPParser } = core.ops; const { Buffer } = core.loadExtScript("ext:deno_node/internal/buffer.mjs"); const { AsyncResource } = core.loadExtScript("ext:deno_node/async_hooks.ts"); // Method names indexed by llhttp method enum values. // Order must match llhttp_method_t in llhttp.h. const methods = [ "DELETE", "GET", "HEAD", "POST", "PUT", "CONNECT", "OPTIONS", "TRACE", "COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "SEARCH", "UNLOCK", "BIND", "REBIND", "UNBIND", "ACL", "REPORT", "MKACTIVITY", "CHECKOUT", "MERGE", "M-SEARCH", "NOTIFY", "SUBSCRIBE", "UNSUBSCRIBE", "PATCH", "PURGE", "MKCALENDAR", "LINK", "UNLINK", "SOURCE", "QUERY", ]; const allMethods = [ "DELETE", "GET", "HEAD", "POST", "PUT", "CONNECT", "OPTIONS", "TRACE", "COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "SEARCH", "UNLOCK", "BIND", "REBIND", "UNBIND", "ACL", "REPORT", "MKACTIVITY", "CHECKOUT", "MERGE", "M-SEARCH", "NOTIFY", "SUBSCRIBE", "UNSUBSCRIBE", "PATCH", "PURGE", "MKCALENDAR", "LINK", "UNLINK", "SOURCE", "PRI", "DESCRIBE", "ANNOUNCE", "SETUP", "PLAY", "PAUSE", "TEARDOWN", "GET_PARAMETER", "SET_PARAMETER", "REDIRECT", "RECORD", "FLUSH", "QUERY", ]; // Callback indices - used as indexed properties on the parser instance. const kOnMessageBegin = 0; const kOnHeaders = 1; const kOnHeadersComplete = 2; const kOnBody = 3; const kOnMessageComplete = 4; const kOnExecute = 5; const kOnTimeout = 6; /** * JS wrapper around the native llhttp-based HTTPParser cppgc object. * * Node.js's `_http_common.js` sets callbacks as indexed properties: * parser[kOnHeaders] = function(headers, url) { ... } * parser[kOnHeadersComplete] = function(major, minor, headers, ...) { ... } * * The native parser reads these during execute() and calls them * synchronously from the C callbacks. */ function HTTPParser(this: any, type?: number) { // Create the native cppgc parser this._native = new NativeHTTPParser(); // If type is provided in constructor, initialize immediately if (type !== undefined) { this._native.initialize(type, 0, 0); } } HTTPParser.prototype.initialize = function ( this: any, type: number, asyncResource?: any, maxHeaderSize?: number, lenientFlags?: number, ) { // Default to the global maxHeaderSize (16384) when not specified, // matching Node.js behavior where 0 means "use the default". const effectiveMaxHeaderSize = maxHeaderSize || 16384; // Store the async resource so execute() can run callbacks in the // correct async context (preserves AsyncLocalStorage through the // native parser, emulating Node's MakeCallback behavior). if (asyncResource) { this._asyncResource = new AsyncResource( asyncResource.type || "HTTPPARSER", ); } this._native.initialize( type, effectiveMaxHeaderSize, lenientFlags ?? 0, ); }; HTTPParser.prototype.execute = function ( this: any, buffer: Uint8Array, offset?: number, length?: number, ) { // Node.js calls execute(buffer) or execute(buffer, offset, length) let data: Uint8Array; if ( offset !== undefined && length !== undefined && (offset !== 0 || length !== buffer.length) ) { data = buffer.subarray(offset, offset + length); } else { data = buffer; } // Wrap the kOnBody callback to convert Uint8Array to Buffer. // Node.js passes Buffer objects to body callbacks. // deno-lint-ignore no-this-alias const parser = this; const origOnBody = this[kOnBody]; if (origOnBody) { this[kOnBody] = function (buf: Uint8Array) { return origOnBody.call( parser, Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength), ); }; } // Pass `this` (the JS wrapper) directly so callbacks set during // parsing (e.g. trailer headers set in kOnHeadersComplete) are // visible to subsequent callbacks. // Run within the async resource scope to preserve AsyncLocalStorage // context through native parser callbacks (emulates Node's MakeCallback). const doExecute = () => this._native.execute( this, new Uint8Array(data.buffer, data.byteOffset, data.byteLength), ); const result = this._asyncResource ? this._asyncResource.runInAsyncScope(doExecute) : doExecute(); // Restore original callback this[kOnBody] = origOnBody; // Re-throw any JS exception that was captured during parsing. // The #[op2(reentrant)] framework swallows pending v8 exceptions, // so we capture them in a TryCatch in the Rust callbacks and // store them on the JS parser object as __lastException. if (this.__lastException !== undefined) { const err = this.__lastException; this.__lastException = undefined; throw err; } // On parser error, create an Error object with code/reason/bytesParsed // matching Node.js behavior (node_http_parser.cc returns an Error object). if (result < 0) { const err: any = new Error("Parse Error"); if (this._native.hasHeaderOverflow()) { err.code = "HPE_HEADER_OVERFLOW"; err.reason = "Header overflow"; err.message = "Parse Error: Header overflow"; } else { const code = this._native.getLastErrorCode(); const reason = this._native.getLastErrorReason(); err.code = code && code !== "HPE_OK" ? code : "HPE_ERROR"; err.reason = reason || "Parse Error"; if (reason) { err.message = `Parse Error: ${reason}`; } } err.bytesParsed = this._native.getLastBytesParsed(); return err; } return result; }; HTTPParser.prototype.finish = function (this: any) { return this._native.finish(this); }; HTTPParser.prototype.pause = function (this: any) { this._native.pause(); }; HTTPParser.prototype.resume = function (this: any) { this._native.resume(); }; HTTPParser.prototype.close = function (this: any) { this._native.close(); }; HTTPParser.prototype.free = function (this: any) { this._native.free(); }; HTTPParser.prototype.remove = function (this: any) { this._native.remove(); }; HTTPParser.prototype.getCurrentBuffer = function (this: any) { return this._native.getCurrentBuffer(); }; // consume/unconsume - server optimization: data flows directly from the // TCP handle to the parser, bypassing the JS readable stream layer. HTTPParser.prototype.consume = function (this: any, handle: any) { this._native.consume(this, handle); }; HTTPParser.prototype.unconsume = function (this: any) { this._native.unconsume(); }; // Static constants HTTPParser.REQUEST = 1; HTTPParser.RESPONSE = 2; HTTPParser.kOnMessageBegin = kOnMessageBegin; HTTPParser.kOnHeaders = kOnHeaders; HTTPParser.kOnHeadersComplete = kOnHeadersComplete; HTTPParser.kOnBody = kOnBody; HTTPParser.kOnMessageComplete = kOnMessageComplete; HTTPParser.kOnExecute = kOnExecute; HTTPParser.kOnTimeout = kOnTimeout; HTTPParser.kLenientNone = 0; HTTPParser.kLenientHeaders = 1; HTTPParser.kLenientChunkedLength = 2; HTTPParser.kLenientKeepAlive = 4; HTTPParser.kLenientTransferEncoding = 8; HTTPParser.kLenientVersion = 16; HTTPParser.kLenientDataAfterClose = 32; HTTPParser.kLenientOptionalLFAfterCR = 64; HTTPParser.kLenientOptionalCRLFAfterChunk = 128; HTTPParser.kLenientOptionalCRBeforeLF = 256; HTTPParser.kLenientSpacesAfterChunkSize = 512; HTTPParser.kLenientAll = 1023; return { methods, allMethods, HTTPParser }; })();