"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); exports.AbstractMessageBuffer = void 0; const CR = 13; const LF = 10; const CRLF = '\r\n'; class AbstractMessageBuffer { constructor(encoding = 'utf-8') { this._encoding = encoding; this._chunks = []; this._totalLength = 0; } get encoding() { return this._encoding; } append(chunk) { const toAppend = typeof chunk === 'string' ? this.fromString(chunk, this._encoding) : chunk; this._chunks.push(toAppend); this._totalLength += toAppend.byteLength; } tryReadHeaders(lowerCaseKeys = false) { if (this._chunks.length === 0) { return undefined; } let state = 0; let chunkIndex = 0; let offset = 0; let chunkBytesRead = 0; row: while (chunkIndex < this._chunks.length) { const chunk = this._chunks[chunkIndex]; offset = 0; column: while (offset < chunk.length) { const value = chunk[offset]; switch (value) { case CR: switch (state) { case 0: state = 1; break; case 2: state = 3; break; default: state = 0; } break; case LF: switch (state) { case 1: state = 2; break; case 3: state = 4; offset++; break row; default: state = 0; } break; default: state = 0; } offset++; } chunkBytesRead += chunk.byteLength; chunkIndex++; } if (state !== 4) { return undefined; } // The buffer contains the two CRLF at the end. So we will // have two empty lines after the split at the end as well. const buffer = this._read(chunkBytesRead + offset); const result = new Map(); const headers = this.toString(buffer, 'ascii').split(CRLF); if (headers.length < 2) { return result; } for (let i = 0; i < headers.length - 2; i++) { const header = headers[i]; const index = header.indexOf(':'); if (index === -1) { throw new Error(`Message header must separate key and value using ':'\n${header}`); } const key = header.substr(0, index); const value = header.substr(index + 1).trim(); result.set(lowerCaseKeys ? key.toLowerCase() : key, value); } return result; } tryReadBody(length) { if (this._totalLength < length) { return undefined; } return this._read(length); } get numberOfBytes() { return this._totalLength; } _read(byteCount) { if (byteCount === 0) { return this.emptyBuffer(); } if (byteCount > this._totalLength) { throw new Error(`Cannot read so many bytes!`); } if (this._chunks[0].byteLength === byteCount) { // super fast path, precisely first chunk must be returned const chunk = this._chunks[0]; this._chunks.shift(); this._totalLength -= byteCount; return this.asNative(chunk); } if (this._chunks[0].byteLength > byteCount) { // fast path, the reading is entirely within the first chunk const chunk = this._chunks[0]; const result = this.asNative(chunk, byteCount); this._chunks[0] = chunk.slice(byteCount); this._totalLength -= byteCount; return result; } const result = this.allocNative(byteCount); let resultOffset = 0; let chunkIndex = 0; while (byteCount > 0) { const chunk = this._chunks[chunkIndex]; if (chunk.byteLength > byteCount) { // this chunk will survive const chunkPart = chunk.slice(0, byteCount); result.set(chunkPart, resultOffset); resultOffset += byteCount; this._chunks[chunkIndex] = chunk.slice(byteCount); this._totalLength -= byteCount; byteCount -= byteCount; } else { // this chunk will be entirely read result.set(chunk, resultOffset); resultOffset += chunk.byteLength; this._chunks.shift(); this._totalLength -= chunk.byteLength; byteCount -= chunk.byteLength; } } return result; } } exports.AbstractMessageBuffer = AbstractMessageBuffer;