152 lines
5.5 KiB
JavaScript
152 lines
5.5 KiB
JavaScript
"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 :');
|
|
}
|
|
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;
|