Finish streaming armoring
This commit is contained in:
parent
0af4742a14
commit
70f0e1d2f5
|
@ -124,23 +124,6 @@ function getCheckSum(data) {
|
||||||
return base64.encode(crc);
|
return base64.encode(crc);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCheckSumString(data) {
|
|
||||||
return util.Uint8Array_to_str(getCheckSum(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the checksum over the given data and compares it with the
|
|
||||||
* given base64 encoded checksum
|
|
||||||
* @param {String} data Data to create a CRC-24 checksum for
|
|
||||||
* @param {String} checksum Base64 encoded checksum
|
|
||||||
* @returns {Boolean} True if the given checksum is correct; otherwise false
|
|
||||||
*/
|
|
||||||
function verifyCheckSum(data, checksum) {
|
|
||||||
const c = getCheckSumString(data);
|
|
||||||
const d = checksum;
|
|
||||||
return c[0] === d[0] && c[1] === d[1] && c[2] === d[2] && c[3] === d[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal function to calculate a CRC-24 checksum over a given string (data)
|
* Internal function to calculate a CRC-24 checksum over a given string (data)
|
||||||
* @param {String} data Data to create a CRC-24 checksum for
|
* @param {String} data Data to create a CRC-24 checksum for
|
||||||
|
@ -195,34 +178,6 @@ function createcrc24(input) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Splits a message into two parts, the headers and the body. This is an internal function
|
|
||||||
* @param {String} text OpenPGP armored message part
|
|
||||||
* @returns {Object} An object with attribute "headers" containing the headers
|
|
||||||
* and an attribute "body" containing the body.
|
|
||||||
*/
|
|
||||||
function splitHeaders(text) {
|
|
||||||
// empty line with whitespace characters
|
|
||||||
const reEmptyLine = /^[ \f\r\t\u00a0\u2000-\u200a\u202f\u205f\u3000]*\n/m;
|
|
||||||
let headers = '';
|
|
||||||
let body = text;
|
|
||||||
|
|
||||||
const matchResult = reEmptyLine.exec(text);
|
|
||||||
|
|
||||||
if (matchResult !== null) {
|
|
||||||
headers = text.slice(0, matchResult.index);
|
|
||||||
body = text.slice(matchResult.index + matchResult[0].length);
|
|
||||||
} else {
|
|
||||||
throw new Error('Mandatory blank line missing between armor headers and armor data');
|
|
||||||
}
|
|
||||||
|
|
||||||
headers = headers.split('\n');
|
|
||||||
// remove empty entry
|
|
||||||
headers.pop();
|
|
||||||
|
|
||||||
return { headers: headers, body: body };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify armored headers. RFC4880, section 6.3: "OpenPGP should consider improperly formatted
|
* Verify armored headers. RFC4880, section 6.3: "OpenPGP should consider improperly formatted
|
||||||
* Armor Headers to be corruption of the ASCII Armor."
|
* Armor Headers to be corruption of the ASCII Armor."
|
||||||
|
@ -240,27 +195,6 @@ function verifyHeaders(headers) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Splits a message into two parts, the body and the checksum. This is an internal function
|
|
||||||
* @param {String} text OpenPGP armored message part
|
|
||||||
* @returns {Object} An object with attribute "body" containing the body
|
|
||||||
* and an attribute "checksum" containing the checksum.
|
|
||||||
*/
|
|
||||||
function splitChecksum(text) {
|
|
||||||
text = text.trim();
|
|
||||||
let body = text;
|
|
||||||
let checksum = "";
|
|
||||||
|
|
||||||
const lastEquals = text.lastIndexOf("=");
|
|
||||||
|
|
||||||
if (lastEquals >= 0 && lastEquals !== text.length - 1) { // '=' as the last char means no checksum
|
|
||||||
body = text.slice(0, lastEquals);
|
|
||||||
checksum = text.slice(lastEquals + 1).substr(0, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { body: body, checksum: checksum };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DeArmor an OpenPGP armored message; verify the checksum and return
|
* DeArmor an OpenPGP armored message; verify the checksum and return
|
||||||
* the encoded bytes
|
* the encoded bytes
|
||||||
|
@ -269,151 +203,94 @@ function splitChecksum(text) {
|
||||||
* an attribute "data" containing the bytes and "type" for the ASCII armor type
|
* an attribute "data" containing the bytes and "type" for the ASCII armor type
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
function dearmor(text) {
|
function dearmor(input) {
|
||||||
if (util.isString(text)) {
|
if (util.isString(input)) {
|
||||||
text = util.str_to_Uint8Array(text);
|
input = util.str_to_Uint8Array(util.encode_utf8(input));
|
||||||
}
|
}
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
const reSplit = /^-----[^-]+-----$/;
|
try {
|
||||||
const reEmptyLine = /^[ \f\r\t\u00a0\u2000-\u200a\u202f\u205f\u3000]*$/;
|
const reSplit = /^-----[^-]+-----$/;
|
||||||
|
const reEmptyLine = /^[ \f\r\t\u00a0\u2000-\u200a\u202f\u205f\u3000]*$/;
|
||||||
|
|
||||||
const reader = text.getReader();
|
const reader = stream.getReader(input);
|
||||||
let lineIndex = 0;
|
let lineIndex = 0;
|
||||||
let type;
|
let type;
|
||||||
const headers = {};
|
const headers = [];
|
||||||
let headersDone;
|
let lastHeaders = headers;
|
||||||
let controller;
|
let headersDone;
|
||||||
let [data, dataClone] = base64.decode(new ReadableStream({
|
let text = [];
|
||||||
async start(_controller) {
|
let textDone;
|
||||||
controller = _controller;
|
let controller;
|
||||||
}
|
let [data, dataClone] = stream.tee(base64.decode(new ReadableStream({
|
||||||
})).tee();
|
async start(_controller) {
|
||||||
let checksum;
|
controller = _controller;
|
||||||
const checksumVerified = getCheckSum(dataClone);
|
|
||||||
data = data.transform(async (done, value) => {
|
|
||||||
if (!done) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
const checksumVerifiedString = util.Uint8Array_to_str(await checksumVerified.readToEnd());
|
|
||||||
if (checksum !== checksumVerifiedString && (checksum || config.checksum_required)) {
|
|
||||||
throw new Error("Ascii armor integrity check on message failed: '" + checksum + "' should be '" +
|
|
||||||
checksumVerifiedString + "'");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
while (true) {
|
|
||||||
const value = await reader.readLine();
|
|
||||||
if (!value) break;
|
|
||||||
let text = util.Uint8Array_to_str(value);
|
|
||||||
if (lineIndex++ === 0) {
|
|
||||||
// trim string
|
|
||||||
text = text.trim();
|
|
||||||
}
|
|
||||||
// remove trailing whitespace at end of lines
|
|
||||||
text = text.replace(/[\t\r\n ]+$/g, '');
|
|
||||||
if (!type) {
|
|
||||||
if (reSplit.test(text)) {
|
|
||||||
type = getType(text);
|
|
||||||
}
|
}
|
||||||
} else if(!headersDone) {
|
})));
|
||||||
if (reSplit.test(text)) {
|
let checksum;
|
||||||
reject(new Error('Mandatory blank line missing between armor headers and armor data'));
|
const checksumVerified = getCheckSum(dataClone);
|
||||||
|
data = stream.getReader(data).substream(); // Convert to Stream
|
||||||
|
data = stream.transform(data, value => value, async () => {
|
||||||
|
const checksumVerifiedString = util.Uint8Array_to_str(await stream.readToEnd(checksumVerified));
|
||||||
|
if (checksum !== checksumVerifiedString && (checksum || config.checksum_required)) {
|
||||||
|
throw new Error("Ascii armor integrity check on message failed: '" + checksum + "' should be '" +
|
||||||
|
checksumVerifiedString + "'");
|
||||||
}
|
}
|
||||||
if (!reEmptyLine.test(text)) {
|
});
|
||||||
// Parse header
|
while (true) {
|
||||||
} else {
|
let line = await reader.readLine();
|
||||||
headersDone = true;
|
if (!line) break;
|
||||||
resolve({
|
line = util.decode_utf8(util.Uint8Array_to_str(line));
|
||||||
type,
|
if (lineIndex++ === 0) {
|
||||||
data
|
// trim string
|
||||||
});
|
line = line.trim();
|
||||||
}
|
}
|
||||||
} else {
|
// remove trailing whitespace at end of lines
|
||||||
if (!reSplit.test(text)) {
|
line = line.replace(/[\t\r\n ]+$/g, '');
|
||||||
if (text[0] !== '=') {
|
if (!type) {
|
||||||
controller.enqueue(util.str_to_Uint8Array(text));
|
if (reSplit.test(line)) {
|
||||||
|
type = getType(line);
|
||||||
|
}
|
||||||
|
} else if (!headersDone) {
|
||||||
|
if (reSplit.test(line)) {
|
||||||
|
reject(new Error('Mandatory blank line missing between armor headers and armor data'));
|
||||||
|
}
|
||||||
|
if (!reEmptyLine.test(line)) {
|
||||||
|
lastHeaders.push(line);
|
||||||
} else {
|
} else {
|
||||||
checksum = text.substr(1);
|
verifyHeaders(lastHeaders);
|
||||||
|
headersDone = true;
|
||||||
|
if (textDone || type !== 2) resolve({ text, data, headers, type });
|
||||||
|
}
|
||||||
|
} else if (!textDone && type === 2) {
|
||||||
|
if (!reSplit.test(line)) {
|
||||||
|
// Reverse dash-escaping for msg
|
||||||
|
text.push(line.replace(/^- /mg, ''));
|
||||||
|
} else {
|
||||||
|
text = text.join('\r\n');
|
||||||
|
textDone = true;
|
||||||
|
verifyHeaders(lastHeaders);
|
||||||
|
lastHeaders = [];
|
||||||
|
headersDone = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
controller.close();
|
if (!reSplit.test(line)) {
|
||||||
break;
|
if (line[0] !== '=') {
|
||||||
|
controller.enqueue(util.str_to_Uint8Array(line));
|
||||||
|
} else {
|
||||||
|
checksum = line.substr(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
controller.close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch(e) {
|
||||||
|
reject(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* DeArmor an OpenPGP armored message; verify the checksum and return
|
|
||||||
* the encoded bytes
|
|
||||||
* @param {String} text OpenPGP armored message
|
|
||||||
* @returns {Object} An object with attribute "text" containing the message text,
|
|
||||||
* an attribute "data" containing the bytes and "type" for the ASCII armor type
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
/*function dearmor(text) {
|
|
||||||
const reSplit = /^-----[^-]+-----$\n/m;
|
|
||||||
|
|
||||||
// trim string and remove trailing whitespace at end of lines
|
|
||||||
text = text.trim().replace(/[\t\r ]+\n/g, '\n');
|
|
||||||
|
|
||||||
const type = getType(text);
|
|
||||||
|
|
||||||
text = text + "\n";
|
|
||||||
const splittext = text.split(reSplit);
|
|
||||||
|
|
||||||
// IE has a bug in split with a re. If the pattern matches the beginning of the
|
|
||||||
// string it doesn't create an empty array element 0. So we need to detect this
|
|
||||||
// so we know the index of the data we are interested in.
|
|
||||||
let indexBase = 1;
|
|
||||||
|
|
||||||
let result;
|
|
||||||
let checksum;
|
|
||||||
let msg;
|
|
||||||
|
|
||||||
if (text.search(reSplit) !== splittext[0].length) {
|
|
||||||
indexBase = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type !== 2) {
|
|
||||||
msg = splitHeaders(splittext[indexBase]);
|
|
||||||
const msg_sum = splitChecksum(msg.body);
|
|
||||||
|
|
||||||
result = {
|
|
||||||
data: base64.decode(util.str_to_Uint8Array(msg_sum.body)),
|
|
||||||
headers: msg.headers,
|
|
||||||
type: type
|
|
||||||
};
|
|
||||||
|
|
||||||
checksum = msg_sum.checksum;
|
|
||||||
} else {
|
|
||||||
// Reverse dash-escaping for msg
|
|
||||||
msg = splitHeaders(splittext[indexBase].replace(/^- /mg, ''));
|
|
||||||
const sig = splitHeaders(splittext[indexBase + 1].replace(/^- /mg, ''));
|
|
||||||
verifyHeaders(sig.headers);
|
|
||||||
const sig_sum = splitChecksum(sig.body);
|
|
||||||
|
|
||||||
result = {
|
|
||||||
text: msg.body.replace(/\n$/, '').replace(/\n/g, "\r\n"),
|
|
||||||
data: base64.decode(util.str_to_Uint8Array(sig_sum.body)),
|
|
||||||
headers: msg.headers,
|
|
||||||
type: type
|
|
||||||
};
|
|
||||||
|
|
||||||
checksum = sig_sum.checksum;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!verifyCheckSum(result.data, checksum) && (checksum || config.checksum_required)) {
|
|
||||||
// will NOT throw error if checksum is empty AND checksum is not required (GPG compatibility)
|
|
||||||
throw new Error("Ascii armor integrity check on message failed: '" + checksum + "' should be '" +
|
|
||||||
getCheckSumString(result.data) + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
verifyHeaders(result.headers);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Armor an OpenPGP binary packet block
|
* Armor an OpenPGP binary packet block
|
||||||
|
@ -427,8 +304,10 @@ function dearmor(text) {
|
||||||
*/
|
*/
|
||||||
function armor(messagetype, body, partindex, parttotal, customComment) {
|
function armor(messagetype, body, partindex, parttotal, customComment) {
|
||||||
let text;
|
let text;
|
||||||
|
let hash;
|
||||||
if (messagetype === enums.armor.signed) {
|
if (messagetype === enums.armor.signed) {
|
||||||
text = body.text;
|
text = body.text;
|
||||||
|
hash = body.hash;
|
||||||
body = body.data;
|
body = body.data;
|
||||||
}
|
}
|
||||||
let bodyClone;
|
let bodyClone;
|
||||||
|
@ -451,7 +330,7 @@ function armor(messagetype, body, partindex, parttotal, customComment) {
|
||||||
break;
|
break;
|
||||||
case enums.armor.signed:
|
case enums.armor.signed:
|
||||||
result.push("\r\n-----BEGIN PGP SIGNED MESSAGE-----\r\n");
|
result.push("\r\n-----BEGIN PGP SIGNED MESSAGE-----\r\n");
|
||||||
result.push("Hash: " + body.hash + "\r\n\r\n");
|
result.push("Hash: " + hash + "\r\n\r\n");
|
||||||
result.push(text.replace(/^-/mg, "- -"));
|
result.push(text.replace(/^-/mg, "- -"));
|
||||||
result.push("\r\n-----BEGIN PGP SIGNATURE-----\r\n");
|
result.push("\r\n-----BEGIN PGP SIGNATURE-----\r\n");
|
||||||
result.push(addheader(customComment));
|
result.push(addheader(customComment));
|
||||||
|
|
|
@ -120,135 +120,139 @@ export default {
|
||||||
*/
|
*/
|
||||||
read: function(reader) {
|
read: function(reader) {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
const peekedBytes = await reader.peekBytes(2);
|
|
||||||
// some sanity checks
|
|
||||||
if (!peekedBytes || peekedBytes.length < 2 || (peekedBytes[0] & 0x80) === 0) {
|
|
||||||
reject(new Error("Error during parsing. This message / key probably does not conform to a valid OpenPGP format."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const headerByte = await reader.readByte();
|
|
||||||
let tag = -1;
|
|
||||||
let format = -1;
|
|
||||||
let packet_length;
|
|
||||||
|
|
||||||
format = 0; // 0 = old format; 1 = new format
|
|
||||||
if ((headerByte & 0x40) !== 0) {
|
|
||||||
format = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let packet_length_type;
|
|
||||||
if (format) {
|
|
||||||
// new format header
|
|
||||||
tag = headerByte & 0x3F; // bit 5-0
|
|
||||||
} else {
|
|
||||||
// old format header
|
|
||||||
tag = (headerByte & 0x3F) >> 2; // bit 5-2
|
|
||||||
packet_length_type = headerByte & 0x03; // bit 1-0
|
|
||||||
}
|
|
||||||
|
|
||||||
let controller;
|
let controller;
|
||||||
let bodydata = null;
|
try {
|
||||||
if (!format) {
|
const peekedBytes = await reader.peekBytes(2);
|
||||||
// 4.2.1. Old Format Packet Lengths
|
// some sanity checks
|
||||||
switch (packet_length_type) {
|
if (!peekedBytes || peekedBytes.length < 2 || (peekedBytes[0] & 0x80) === 0) {
|
||||||
case 0:
|
reject(new Error("Error during parsing. This message / key probably does not conform to a valid OpenPGP format."));
|
||||||
// The packet has a one-octet length. The header is 2 octets
|
return;
|
||||||
// long.
|
}
|
||||||
packet_length = await reader.readByte();
|
const headerByte = await reader.readByte();
|
||||||
break;
|
let tag = -1;
|
||||||
case 1:
|
let format = -1;
|
||||||
// The packet has a two-octet length. The header is 3 octets
|
let packet_length;
|
||||||
// long.
|
|
||||||
packet_length = (await reader.readByte() << 8) | await reader.readByte();
|
format = 0; // 0 = old format; 1 = new format
|
||||||
break;
|
if ((headerByte & 0x40) !== 0) {
|
||||||
case 2:
|
format = 1;
|
||||||
// The packet has a four-octet length. The header is 5
|
}
|
||||||
// octets long.
|
|
||||||
|
let packet_length_type;
|
||||||
|
if (format) {
|
||||||
|
// new format header
|
||||||
|
tag = headerByte & 0x3F; // bit 5-0
|
||||||
|
} else {
|
||||||
|
// old format header
|
||||||
|
tag = (headerByte & 0x3F) >> 2; // bit 5-2
|
||||||
|
packet_length_type = headerByte & 0x03; // bit 1-0
|
||||||
|
}
|
||||||
|
|
||||||
|
let bodydata = null;
|
||||||
|
if (!format) {
|
||||||
|
// 4.2.1. Old Format Packet Lengths
|
||||||
|
switch (packet_length_type) {
|
||||||
|
case 0:
|
||||||
|
// The packet has a one-octet length. The header is 2 octets
|
||||||
|
// long.
|
||||||
|
packet_length = await reader.readByte();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
// The packet has a two-octet length. The header is 3 octets
|
||||||
|
// long.
|
||||||
|
packet_length = (await reader.readByte() << 8) | await reader.readByte();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
// The packet has a four-octet length. The header is 5
|
||||||
|
// octets long.
|
||||||
|
packet_length = (await reader.readByte() << 24) | (await reader.readByte() << 16) | (await reader.readByte() <<
|
||||||
|
8) | await reader.readByte();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// 3 - The packet is of indeterminate length. The header is 1
|
||||||
|
// octet long, and the implementation must determine how long
|
||||||
|
// the packet is. If the packet is in a file, this means that
|
||||||
|
// the packet extends until the end of the file. In general,
|
||||||
|
// an implementation SHOULD NOT use indeterminate-length
|
||||||
|
// packets except where the end of the data will be clear
|
||||||
|
// from the context, and even then it is better to use a
|
||||||
|
// definite length, or a new format header. The new format
|
||||||
|
// headers described below have a mechanism for precisely
|
||||||
|
// encoding data of indeterminate length.
|
||||||
|
packet_length = Infinity;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else { // 4.2.2. New Format Packet Lengths
|
||||||
|
// 4.2.2.1. One-Octet Lengths
|
||||||
|
const lengthByte = await reader.readByte();
|
||||||
|
if (lengthByte < 192) {
|
||||||
|
packet_length = lengthByte;
|
||||||
|
// 4.2.2.2. Two-Octet Lengths
|
||||||
|
} else if (lengthByte >= 192 && lengthByte < 224) {
|
||||||
|
packet_length = ((lengthByte - 192) << 8) + (await reader.readByte()) + 192;
|
||||||
|
// 4.2.2.4. Partial Body Lengths
|
||||||
|
} else if (lengthByte > 223 && lengthByte < 255) {
|
||||||
|
packet_length = 1 << (lengthByte & 0x1F);
|
||||||
|
bodydata = new ReadableStream({
|
||||||
|
async start(_controller) {
|
||||||
|
controller = _controller;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resolve({
|
||||||
|
tag: tag,
|
||||||
|
packet: bodydata,
|
||||||
|
done: true
|
||||||
|
});
|
||||||
|
controller.enqueue(await reader.readBytes(packet_length));
|
||||||
|
let tmplen;
|
||||||
|
while (true) {
|
||||||
|
const tmplenByte = await reader.readByte();
|
||||||
|
if (tmplenByte < 192) {
|
||||||
|
tmplen = tmplenByte;
|
||||||
|
controller.enqueue(await reader.readBytes(tmplen));
|
||||||
|
break;
|
||||||
|
} else if (tmplenByte >= 192 && tmplenByte < 224) {
|
||||||
|
tmplen = ((tmplenByte - 192) << 8) + (await reader.readByte()) + 192;
|
||||||
|
controller.enqueue(await reader.readBytes(tmplen));
|
||||||
|
break;
|
||||||
|
} else if (tmplenByte > 223 && tmplenByte < 255) {
|
||||||
|
tmplen = 1 << (tmplenByte & 0x1F);
|
||||||
|
controller.enqueue(await reader.readBytes(tmplen));
|
||||||
|
} else {
|
||||||
|
tmplen = (await reader.readByte() << 24) | (await reader.readByte() << 16) | (await reader.readByte() << 8) | await reader.readByte();
|
||||||
|
controller.enqueue(await reader.readBytes(tmplen));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 4.2.2.3. Five-Octet Lengths
|
||||||
|
} else {
|
||||||
packet_length = (await reader.readByte() << 24) | (await reader.readByte() << 16) | (await reader.readByte() <<
|
packet_length = (await reader.readByte() << 24) | (await reader.readByte() << 16) | (await reader.readByte() <<
|
||||||
8) | await reader.readByte();
|
8) | await reader.readByte();
|
||||||
break;
|
}
|
||||||
default:
|
|
||||||
// 3 - The packet is of indeterminate length. The header is 1
|
|
||||||
// octet long, and the implementation must determine how long
|
|
||||||
// the packet is. If the packet is in a file, this means that
|
|
||||||
// the packet extends until the end of the file. In general,
|
|
||||||
// an implementation SHOULD NOT use indeterminate-length
|
|
||||||
// packets except where the end of the data will be clear
|
|
||||||
// from the context, and even then it is better to use a
|
|
||||||
// definite length, or a new format header. The new format
|
|
||||||
// headers described below have a mechanism for precisely
|
|
||||||
// encoding data of indeterminate length.
|
|
||||||
packet_length = Infinity;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else { // 4.2.2. New Format Packet Lengths
|
|
||||||
// 4.2.2.1. One-Octet Lengths
|
// if there wasn't a partial body length
|
||||||
const lengthByte = await reader.readByte();
|
if (bodydata === null) {
|
||||||
if (lengthByte < 192) {
|
bodydata = await reader.readBytes(packet_length);
|
||||||
packet_length = lengthByte;
|
|
||||||
// 4.2.2.2. Two-Octet Lengths
|
|
||||||
} else if (lengthByte >= 192 && lengthByte < 224) {
|
|
||||||
packet_length = ((lengthByte - 192) << 8) + (await reader.readByte()) + 192;
|
|
||||||
// 4.2.2.4. Partial Body Lengths
|
|
||||||
} else if (lengthByte > 223 && lengthByte < 255) {
|
|
||||||
packet_length = 1 << (lengthByte & 0x1F);
|
|
||||||
bodydata = new ReadableStream({
|
|
||||||
async start(_controller) {
|
|
||||||
controller = _controller;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
resolve({
|
resolve({
|
||||||
tag: tag,
|
tag: tag,
|
||||||
packet: bodydata,
|
packet: bodydata,
|
||||||
done: true
|
done: !await reader.peekBytes(1)
|
||||||
});
|
});
|
||||||
controller.enqueue(await reader.readBytes(packet_length));
|
|
||||||
let tmplen;
|
|
||||||
while (true) {
|
|
||||||
const tmplenByte = await reader.readByte();
|
|
||||||
if (tmplenByte < 192) {
|
|
||||||
tmplen = tmplenByte;
|
|
||||||
controller.enqueue(await reader.readBytes(tmplen));
|
|
||||||
break;
|
|
||||||
} else if (tmplenByte >= 192 && tmplenByte < 224) {
|
|
||||||
tmplen = ((tmplenByte - 192) << 8) + (await reader.readByte()) + 192;
|
|
||||||
controller.enqueue(await reader.readBytes(tmplen));
|
|
||||||
break;
|
|
||||||
} else if (tmplenByte > 223 && tmplenByte < 255) {
|
|
||||||
tmplen = 1 << (tmplenByte & 0x1F);
|
|
||||||
controller.enqueue(await reader.readBytes(tmplen));
|
|
||||||
} else {
|
|
||||||
tmplen = (await reader.readByte() << 24) | (await reader.readByte() << 16) | (await reader.readByte() << 8) | await reader.readByte();
|
|
||||||
controller.enqueue(await reader.readBytes(tmplen));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 4.2.2.3. Five-Octet Lengths
|
|
||||||
} else {
|
} else {
|
||||||
packet_length = (await reader.readByte() << 24) | (await reader.readByte() << 16) | (await reader.readByte() <<
|
|
||||||
8) | await reader.readByte();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there wasn't a partial body length
|
|
||||||
if (bodydata === null) {
|
|
||||||
bodydata = await reader.readBytes(packet_length);
|
|
||||||
|
|
||||||
resolve({
|
|
||||||
tag: tag,
|
|
||||||
packet: bodydata,
|
|
||||||
done: !await reader.peekBytes(1)
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
const { done } = await reader.read();
|
const { done } = await reader.read();
|
||||||
if (!done) {
|
if (!done) {
|
||||||
throw new Error('Packets after a packet with partial lengths are not supported');
|
throw new Error('Packets after a packet with partial lengths are not supported');
|
||||||
} else {
|
} else {
|
||||||
controller.close();
|
controller.close();
|
||||||
}
|
}
|
||||||
} catch(e) {
|
}
|
||||||
|
} catch(e) {
|
||||||
|
if (controller) {
|
||||||
controller.error(e);
|
controller.error(e);
|
||||||
|
} else {
|
||||||
|
reject(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user