Don't mutate prototypes of Uint8Array, ReadableStream and ReadableStreamDefaultWriter

This commit is contained in:
Daniel Huigens 2018-05-15 18:56:25 +02:00
parent 70f0e1d2f5
commit 4ada3fa590
13 changed files with 145 additions and 121 deletions

View File

@ -7,6 +7,7 @@
* @requires asmcrypto.js * @requires asmcrypto.js
* @requires hash.js * @requires hash.js
* @requires crypto/hash/md5 * @requires crypto/hash/md5
* @requires stream
* @requires util * @requires util
* @module crypto/hash * @module crypto/hash
*/ */
@ -19,6 +20,7 @@ import sha384 from 'hash.js/lib/hash/sha/384';
import sha512 from 'hash.js/lib/hash/sha/512'; import sha512 from 'hash.js/lib/hash/sha/512';
import { ripemd160 } from 'hash.js/lib/hash/ripemd'; import { ripemd160 } from 'hash.js/lib/hash/ripemd';
import md5 from './md5'; import md5 from './md5';
import stream from '../../stream';
import util from '../../util'; import util from '../../util';
const rusha = new Rusha(); const rusha = new Rusha();
@ -36,7 +38,7 @@ function node_hash(type) {
function hashjs_hash(hash) { function hashjs_hash(hash) {
return function(data) { return function(data) {
const hashInstance = hash(); const hashInstance = hash();
return data.transform((done, value) => { return stream.transform(data, (done, value) => {
if (!done) { if (!done) {
hashInstance.update(value); hashInstance.update(value);
} else { } else {

View File

@ -4,6 +4,7 @@
* @requires crypto/public_key * @requires crypto/public_key
* @requires crypto/pkcs1 * @requires crypto/pkcs1
* @requires enums * @requires enums
* @requires stream
* @requires util * @requires util
* @module crypto/signature * @module crypto/signature
*/ */
@ -12,6 +13,7 @@ import BN from 'bn.js';
import publicKey from './public_key'; import publicKey from './public_key';
import pkcs1 from './pkcs1'; import pkcs1 from './pkcs1';
import enums from '../enums'; import enums from '../enums';
import stream from '../stream';
import util from '../util'; import util from '../util';
export default { export default {
@ -29,7 +31,7 @@ export default {
* @async * @async
*/ */
verify: async function(algo, hash_algo, msg_MPIs, pub_MPIs, data) { verify: async function(algo, hash_algo, msg_MPIs, pub_MPIs, data) {
data = await data.readToEnd(); data = await stream.readToEnd(data);
switch (algo) { switch (algo) {
case enums.publicKey.rsa_encrypt_sign: case enums.publicKey.rsa_encrypt_sign:
case enums.publicKey.rsa_encrypt: case enums.publicKey.rsa_encrypt:
@ -83,7 +85,7 @@ export default {
* @async * @async
*/ */
sign: async function(algo, hash_algo, key_params, data) { sign: async function(algo, hash_algo, key_params, data) {
data = await data.readToEnd(); data = await stream.readToEnd(data);
switch (algo) { switch (algo) {
case enums.publicKey.rsa_encrypt_sign: case enums.publicKey.rsa_encrypt_sign:
case enums.publicKey.rsa_encrypt: case enums.publicKey.rsa_encrypt:

View File

@ -19,6 +19,7 @@
* @requires encoding/base64 * @requires encoding/base64
* @requires enums * @requires enums
* @requires config * @requires config
* @requires stream
* @requires util * @requires util
* @module encoding/armor * @module encoding/armor
*/ */
@ -26,6 +27,7 @@
import base64 from './base64.js'; import base64 from './base64.js';
import enums from '../enums.js'; import enums from '../enums.js';
import config from '../config'; import config from '../config';
import stream from '../stream';
import util from '../util'; import util from '../util';
/** /**
@ -166,7 +168,7 @@ const crc_table = [
function createcrc24(input) { function createcrc24(input) {
let crc = 0xB704CE; let crc = 0xB704CE;
return input.transform((done, value) => { return stream.transform(input, (done, value) => {
if (!done) { if (!done) {
for (let index = 0; index < value.length; index++) { for (let index = 0; index < value.length; index++) {
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ value[index]) & 0xff]; crc = (crc << 8) ^ crc_table[((crc >> 16) ^ value[index]) & 0xff];
@ -311,7 +313,7 @@ function armor(messagetype, body, partindex, parttotal, customComment) {
body = body.data; body = body.data;
} }
let bodyClone; let bodyClone;
[body, bodyClone] = body.tee(); [body, bodyClone] = stream.tee(body);
const result = []; const result = [];
switch (messagetype) { switch (messagetype) {
case enums.armor.multipart_section: case enums.armor.multipart_section:

View File

@ -12,10 +12,12 @@
*/ */
/** /**
* @requires stream
* @requires util * @requires util
* @module encoding/base64 * @module encoding/base64
*/ */
import stream from '../stream';
import util from '../util'; import util from '../util';
const b64s = util.str_to_Uint8Array('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'); // Standard radix-64 const b64s = util.str_to_Uint8Array('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'); // Standard radix-64
@ -37,7 +39,7 @@ function s2r(t, u = false) {
let l = 0; let l = 0;
let s = 0; let s = 0;
return t.transform((done, value) => { return stream.transform(t, (done, value) => {
const r = []; const r = [];
if (!done) { if (!done) {
@ -106,7 +108,7 @@ function r2s(t, u) {
let s = 0; let s = 0;
let a = 0; let a = 0;
return t.transform((done, value) => { return stream.transform(t, (done, value) => {
if (!done) { if (!done) {
const r = []; const r = [];
const tl = value.length; const tl = value.length;

View File

@ -101,10 +101,10 @@ export { default as KDFParams } from './type/kdf_params';
export { default as OID } from './type/oid'; export { default as OID } from './type/oid';
/** /**
* @see module:type/oid * @see module:stream
* @name module:openpgp.OID * @name module:openpgp.stream
*/ */
export { default as Stream } from './type/stream'; export { default as stream } from './stream';
/** /**
* @see module:encoding/armor * @see module:encoding/armor

View File

@ -24,6 +24,7 @@
* @requires key * @requires key
* @requires config * @requires config
* @requires enums * @requires enums
* @requires stream
* @requires util * @requires util
* @requires polyfills * @requires polyfills
* @requires worker/async_proxy * @requires worker/async_proxy
@ -43,6 +44,7 @@ import { CleartextMessage } from './cleartext';
import { generate, reformat } from './key'; import { generate, reformat } from './key';
import config from './config/config'; import config from './config/config';
import enums from './enums'; import enums from './enums';
import stream from './stream';
import util from './util'; import util from './util';
import AsyncProxy from './worker/async_proxy'; import AsyncProxy from './worker/async_proxy';
@ -596,12 +598,12 @@ async function parseMessage(message, format, asStream) {
if (format === 'binary') { if (format === 'binary') {
data = message.getLiteralData(); data = message.getLiteralData();
if (!asStream && util.isStream(data)) { if (!asStream && util.isStream(data)) {
data = await data.readToEnd(); data = await stream.readToEnd(data);
} }
} else if (format === 'utf8') { } else if (format === 'utf8') {
data = message.getText(); data = message.getText();
if (!asStream && util.isStream(data)) { if (!asStream && util.isStream(data)) {
data = await data.readToEnd(chunks => chunks.join('')); data = await stream.readToEnd(data, chunks => chunks.join(''));
} }
} else { } else {
throw new Error('Invalid format'); throw new Error('Invalid format');

View File

@ -17,10 +17,12 @@
/** /**
* @requires enums * @requires enums
* @requires stream
* @requires util * @requires util
*/ */
import enums from '../enums'; import enums from '../enums';
import stream from '../stream';
import util from '../util'; import util from '../util';
/** /**
@ -67,7 +69,7 @@ Literal.prototype.getText = function() {
let text; let text;
if (this.text === null) { if (this.text === null) {
let lastChar = ''; let lastChar = '';
[this.data, this.text] = this.data.tee(); [this.data, this.text] = stream.tee(this.data);
this.text = stream.transform(this.text, value => { this.text = stream.transform(this.text, value => {
const text = lastChar + util.Uint8Array_to_str(value); const text = lastChar + util.Uint8Array_to_str(value);
// decode UTF8 and normalize EOL to \n // decode UTF8 and normalize EOL to \n
@ -82,7 +84,7 @@ Literal.prototype.getText = function() {
return normalized.slice(0, -1); return normalized.slice(0, -1);
}, () => lastChar); }, () => lastChar);
} }
[text, this.text] = this.text.tee(); [text, this.text] = stream.tee(this.text);
return text; return text;
}; };
@ -140,7 +142,7 @@ Literal.prototype.getFilename = function() {
* @returns {module:packet.Literal} object representation * @returns {module:packet.Literal} object representation
*/ */
Literal.prototype.read = async function(bytes) { Literal.prototype.read = async function(bytes) {
const reader = bytes.getReader(); const reader = stream.getReader(bytes);
// - A one-octet field that describes how the data is formatted. // - A one-octet field that describes how the data is formatted.
const format = enums.read(enums.literal, await reader.readByte()); const format = enums.read(enums.literal, await reader.readByte());

View File

@ -4,6 +4,7 @@
* @requires packet/packet * @requires packet/packet
* @requires config * @requires config
* @requires enums * @requires enums
* @requires stream
* @requires util * @requires util
*/ */
@ -11,6 +12,7 @@ import * as packets from './all_packets';
import packetParser from './packet'; import packetParser from './packet';
import config from '../config'; import config from '../config';
import enums from '../enums'; import enums from '../enums';
import stream from '../stream';
import util from '../util'; import util from '../util';
/** /**
@ -34,7 +36,7 @@ function List() {
* @param {Uint8Array} A Uint8Array of bytes. * @param {Uint8Array} A Uint8Array of bytes.
*/ */
List.prototype.read = async function (bytes) { List.prototype.read = async function (bytes) {
const reader = bytes.getReader(); const reader = stream.getReader(bytes);
while (true) { while (true) {
const parsed = await packetParser.read(reader); const parsed = await packetParser.read(reader);
@ -78,7 +80,7 @@ List.prototype.write = function () {
let bufferLength = 0; let bufferLength = 0;
const minLength = 512; const minLength = 512;
arr.push(packetParser.writeTag(this[i].tag)); arr.push(packetParser.writeTag(this[i].tag));
arr.push(packetbytes.transform((done, value) => { arr.push(stream.transform(packetbytes, (done, value) => {
if (!done) { if (!done) {
buffer.push(value); buffer.push(value);
bufferLength += value.length; bufferLength += value.length;

View File

@ -19,6 +19,7 @@
* @requires asmcrypto.js * @requires asmcrypto.js
* @requires crypto * @requires crypto
* @requires enums * @requires enums
* @requires stream
* @requires util * @requires util
*/ */
@ -26,6 +27,7 @@ import { AES_CFB_Decrypt, AES_CFB_Encrypt } from 'asmcrypto.js/src/aes/cfb/expor
import crypto from '../crypto'; import crypto from '../crypto';
import enums from '../enums'; import enums from '../enums';
import stream from '../stream';
import util from '../util'; import util from '../util';
const nodeCrypto = util.getNodeCrypto(); const nodeCrypto = util.getNodeCrypto();
@ -61,7 +63,7 @@ function SymEncryptedIntegrityProtected() {
} }
SymEncryptedIntegrityProtected.prototype.read = async function (bytes) { SymEncryptedIntegrityProtected.prototype.read = async function (bytes) {
const reader = bytes.getReader(); const reader = stream.getReader(bytes);
// - A one-octet version number. The only currently defined value is 1. // - A one-octet version number. The only currently defined value is 1.
if (await reader.readByte() !== VERSION) { if (await reader.readByte() !== VERSION) {
@ -92,7 +94,7 @@ SymEncryptedIntegrityProtected.prototype.encrypt = async function (sessionKeyAlg
const prefix = util.concatUint8Array([prefixrandom, repeat]); const prefix = util.concatUint8Array([prefixrandom, repeat]);
const mdc = new Uint8Array([0xD3, 0x14]); // modification detection code packet const mdc = new Uint8Array([0xD3, 0x14]); // modification detection code packet
let [tohash, tohashClone] = util.concatUint8Array([bytes, mdc]).tee(); let [tohash, tohashClone] = stream.tee(util.concatUint8Array([bytes, mdc]));
const hash = crypto.hash.sha1(util.concatUint8Array([prefix, tohashClone])); const hash = crypto.hash.sha1(util.concatUint8Array([prefix, tohashClone]));
tohash = util.concatUint8Array([tohash, hash]); tohash = util.concatUint8Array([tohash, hash]);
@ -100,7 +102,7 @@ SymEncryptedIntegrityProtected.prototype.encrypt = async function (sessionKeyAlg
this.encrypted = aesEncrypt(sessionKeyAlgorithm, util.concatUint8Array([prefix, tohash]), key); this.encrypted = aesEncrypt(sessionKeyAlgorithm, util.concatUint8Array([prefix, tohash]), key);
} else { } else {
this.encrypted = crypto.cfb.encrypt(prefixrandom, sessionKeyAlgorithm, tohash, key, false); this.encrypted = crypto.cfb.encrypt(prefixrandom, sessionKeyAlgorithm, tohash, key, false);
this.encrypted = this.encrypted.subarray(0, prefix.length + tohash.length); this.encrypted = stream.subarray(this.encrypted, 0, prefix.length + tohash.length);
} }
return true; return true;
}; };
@ -113,7 +115,7 @@ SymEncryptedIntegrityProtected.prototype.encrypt = async function (sessionKeyAlg
* @async * @async
*/ */
SymEncryptedIntegrityProtected.prototype.decrypt = async function (sessionKeyAlgorithm, key) { SymEncryptedIntegrityProtected.prototype.decrypt = async function (sessionKeyAlgorithm, key) {
const [encrypted, encryptedClone] = this.encrypted.tee(); const [encrypted, encryptedClone] = stream.tee(this.encrypted);
let decrypted; let decrypted;
if (sessionKeyAlgorithm.substr(0, 3) === 'aes') { // AES optimizations. Native code for node, asmCrypto for browser. if (sessionKeyAlgorithm.substr(0, 3) === 'aes') { // AES optimizations. Native code for node, asmCrypto for browser.
decrypted = aesDecrypt(sessionKeyAlgorithm, encrypted, key); decrypted = aesDecrypt(sessionKeyAlgorithm, encrypted, key);
@ -122,20 +124,20 @@ SymEncryptedIntegrityProtected.prototype.decrypt = async function (sessionKeyAlg
} }
let decryptedClone; let decryptedClone;
[decrypted, decryptedClone] = decrypted.tee(); [decrypted, decryptedClone] = stream.tee(decrypted);
// there must be a modification detection code packet as the // there must be a modification detection code packet as the
// last packet and everything gets hashed except the hash itself // last packet and everything gets hashed except the hash itself
const encryptedPrefix = await encryptedClone.subarray(0, crypto.cipher[sessionKeyAlgorithm].blockSize + 2).readToEnd(); const encryptedPrefix = await stream.readToEnd(stream.subarray(encryptedClone, 0, crypto.cipher[sessionKeyAlgorithm].blockSize + 2));
const prefix = crypto.cfb.mdc(sessionKeyAlgorithm, key, encryptedPrefix); const prefix = crypto.cfb.mdc(sessionKeyAlgorithm, key, encryptedPrefix);
let [bytes, bytesClone] = decrypted.subarray(0, -20).tee(); let [bytes, bytesClone] = stream.tee(stream.subarray(decrypted, 0, -20));
const tohash = util.concatUint8Array([prefix, bytes]); const tohash = util.concatUint8Array([prefix, bytes]);
this.hash = util.Uint8Array_to_str(await crypto.hash.sha1(tohash).readToEnd()); this.hash = util.Uint8Array_to_str(await stream.readToEnd(crypto.hash.sha1(tohash)));
const mdc = util.Uint8Array_to_str(await decryptedClone.subarray(-20).readToEnd()); const mdc = util.Uint8Array_to_str(await stream.readToEnd(stream.subarray(decryptedClone, -20)));
if (this.hash !== mdc) { if (this.hash !== mdc) {
throw new Error('Modification detected.'); throw new Error('Modification detected.');
} else { } else {
await this.packets.read(bytesClone.subarray(0, -2)); await this.packets.read(stream.subarray(bytesClone, 0, -2));
} }
return true; return true;
@ -156,7 +158,7 @@ function aesEncrypt(algo, pt, key) {
return nodeEncrypt(algo, pt, key); return nodeEncrypt(algo, pt, key);
} // asm.js fallback } // asm.js fallback
const cfb = new AES_CFB_Encrypt(key); const cfb = new AES_CFB_Encrypt(key);
return pt.transform((done, value) => { return stream.transform(pt, (done, value) => {
if (!done) { if (!done) {
return cfb.process(value).result; return cfb.process(value).result;
} }
@ -170,14 +172,14 @@ function aesDecrypt(algo, ct, key) {
pt = nodeDecrypt(algo, ct, key); pt = nodeDecrypt(algo, ct, key);
} else { // asm.js fallback } else { // asm.js fallback
const cfb = new AES_CFB_Decrypt(key); const cfb = new AES_CFB_Decrypt(key);
pt = ct.transform((done, value) => { pt = stream.transform(ct, (done, value) => {
if (!done) { if (!done) {
return cfb.process(value).result; return cfb.process(value).result;
} }
return cfb.finish().result; return cfb.finish().result;
}); });
} }
return pt.subarray(crypto.cipher[algo].blockSize + 2); // Remove random prefix return stream.subarray(pt, crypto.cipher[algo].blockSize + 2); // Remove random prefix
} }
function nodeEncrypt(algo, prefix, pt, key) { function nodeEncrypt(algo, prefix, pt, key) {

View File

@ -1,7 +1,7 @@
import util from '../util'; import util from './util';
function concat(arrays) { function concat(arrays) {
const readers = arrays.map(entry => entry.getReader()); const readers = arrays.map(getReader);
let current = 0; let current = 0;
return new ReadableStream({ return new ReadableStream({
async pull(controller) { async pull(controller) {
@ -17,7 +17,80 @@ function concat(arrays) {
}); });
} }
export default { concat }; function getReader(input) {
return new Reader(input);
}
function transform(input, fn) {
if (util.isStream(input)) {
const reader = getReader(input);
return new ReadableStream({
async pull(controller) {
try {
const { done, value } = await reader.read();
const result = await fn(done, value);
if (result) controller.enqueue(result);
else if (!done) await this.pull(controller); // ??? Chrome bug?
if (done) controller.close();
} catch(e) {
controller.error(e);
}
}
});
}
const result1 = fn(false, input);
const result2 = fn(true, undefined);
if (result1 && result2) return util.concatUint8Array([result1, result2]);
return result1 || result2;
}
function tee(input) {
if (util.isStream(input)) {
return input.tee();
}
return [input, input];
}
function subarray(input, begin=0, end=Infinity) {
if (util.isStream(input)) {
if (begin >= 0 && end >= 0) {
const reader = getReader(input);
let bytesRead = 0;
return new ReadableStream({
async pull (controller) {
const { done, value } = await reader.read();
if (!done && bytesRead < end) {
if (bytesRead + value.length >= begin) {
controller.enqueue(value.subarray(Math.max(begin - bytesRead, 0), end - bytesRead));
}
bytesRead += value.length;
await this.pull(controller); // Only necessary if the above call to enqueue() didn't happen
} else {
controller.close();
}
}
});
}
return new ReadableStream({
pull: async controller => {
// TODO: Don't read entire stream into memory here.
controller.enqueue((await readToEnd(input)).subarray(begin, end));
controller.close();
}
});
}
return input.subarray(begin, end);
}
async function readToEnd(input, join) {
if (util.isStream(input)) {
return getReader(input).readToEnd(join);
}
return input;
}
export default { concat, getReader, transform, tee, subarray, readToEnd };
/*const readerAcquiredMap = new Map(); /*const readerAcquiredMap = new Map();
@ -33,60 +106,23 @@ ReadableStream.prototype.getReader = function() {
};*/ };*/
ReadableStream.prototype.transform = function(fn) { function Reader(input) {
const reader = this.getReader(); if (util.isStream(input)) {
return new ReadableStream({ const reader = input.getReader();
async pull(controller) { this._read = reader.read.bind(reader);
try { return;
const { done, value } = await reader.read();
const result = await fn(done, value);
if (result) controller.enqueue(result);
else if (!done) await this.pull(controller); // ??? Chrome bug?
if (done) controller.close();
} catch(e) {
controller.error(e);
} }
}
});
};
ReadableStream.prototype.readToEnd = async function(join) {
return this.getReader().readToEnd(join);
};
Uint8Array.prototype.getReader = function() {
let doneReading = false; let doneReading = false;
const reader = Object.create(ReadableStreamDefaultReader.prototype); this._read = async () => {
reader._read = async () => {
if (doneReading) { if (doneReading) {
return { value: undefined, done: true }; return { value: undefined, done: true };
} }
doneReading = true; doneReading = true;
return { value: this, done: false }; return { value: input, done: false };
};
return reader;
}; };
}
Uint8Array.prototype.transform = function(fn) { Reader.prototype.read = async function() {
const result1 = fn(false, this);
const result2 = fn(true, undefined);
if (result1 && result2) return util.concatUint8Array([result1, result2]);
return result1 || result2;
};
Uint8Array.prototype.tee = function() {
return [this, this];
};
Uint8Array.prototype.readToEnd = async function() {
return this;
};
const ReadableStreamDefaultReader = new ReadableStream().getReader().constructor;
ReadableStreamDefaultReader.prototype._read = ReadableStreamDefaultReader.prototype.read;
ReadableStreamDefaultReader.prototype.read = async function() {
if (this.externalBuffer && this.externalBuffer.length) { if (this.externalBuffer && this.externalBuffer.length) {
const value = this.externalBuffer.shift(); const value = this.externalBuffer.shift();
return { done: false, value }; return { done: false, value };
@ -94,7 +130,7 @@ ReadableStreamDefaultReader.prototype.read = async function() {
return this._read(); return this._read();
}; };
ReadableStreamDefaultReader.prototype.readLine = async function() { Reader.prototype.readLine = async function() {
let buffer = []; let buffer = [];
let returnVal; let returnVal;
while (!returnVal) { while (!returnVal) {
@ -116,7 +152,7 @@ ReadableStreamDefaultReader.prototype.readLine = async function() {
return returnVal; return returnVal;
}; };
ReadableStreamDefaultReader.prototype.readByte = async function() { Reader.prototype.readByte = async function() {
const { done, value } = await this.read(); const { done, value } = await this.read();
if (done) return; if (done) return;
const byte = value[0]; const byte = value[0];
@ -124,7 +160,7 @@ ReadableStreamDefaultReader.prototype.readByte = async function() {
return byte; return byte;
}; };
ReadableStreamDefaultReader.prototype.readBytes = async function(length) { Reader.prototype.readBytes = async function(length) {
const buffer = []; const buffer = [];
let bufferLength = 0; let bufferLength = 0;
while (true) { while (true) {
@ -143,20 +179,20 @@ ReadableStreamDefaultReader.prototype.readBytes = async function(length) {
} }
}; };
ReadableStreamDefaultReader.prototype.peekBytes = async function(length) { Reader.prototype.peekBytes = async function(length) {
const bytes = await this.readBytes(length); const bytes = await this.readBytes(length);
this.unshift(bytes); this.unshift(bytes);
return bytes; return bytes;
}; };
ReadableStreamDefaultReader.prototype.unshift = function(...values) { Reader.prototype.unshift = function(...values) {
if (!this.externalBuffer) { if (!this.externalBuffer) {
this.externalBuffer = []; this.externalBuffer = [];
} }
this.externalBuffer.unshift(...values.filter(value => value && value.length)); this.externalBuffer.unshift(...values.filter(value => value && value.length));
}; };
ReadableStreamDefaultReader.prototype.substream = function() { Reader.prototype.substream = function() {
return new ReadableStream({ pull: pullFrom(this) }); return new ReadableStream({ pull: pullFrom(this) });
}; };
@ -171,35 +207,7 @@ function pullFrom(reader) {
}; };
} }
ReadableStream.prototype.subarray = function(begin=0, end=Infinity) { Reader.prototype.readToEnd = async function(join=util.concatUint8Array) {
if (begin >= 0 && end >= 0) {
const reader = this.getReader();
let bytesRead = 0;
return new ReadableStream({
async pull (controller) {
const { done, value } = await reader.read();
if (!done && bytesRead < end) {
if (bytesRead + value.length >= begin) {
controller.enqueue(value.subarray(Math.max(begin - bytesRead, 0), end - bytesRead));
}
bytesRead += value.length;
await this.pull(controller); // Only necessary if the above call to enqueue() didn't happen
} else {
controller.close();
}
}
});
}
return new ReadableStream({
pull: async controller => {
// TODO: Don't read entire stream into memory here.
controller.enqueue((await this.readToEnd()).subarray(begin, end));
controller.close();
}
});
};
ReadableStreamDefaultReader.prototype.readToEnd = async function(join=util.concatUint8Array) {
const result = []; const result = [];
while (true) { while (true) {
const { done, value } = await this.read(); const { done, value } = await this.read();

View File

@ -29,7 +29,7 @@ import rfc2822 from 'address-rfc2822';
import config from './config'; import config from './config';
import util from './util'; // re-import module to access util functions import util from './util'; // re-import module to access util functions
import b64 from './encoding/base64'; import b64 from './encoding/base64';
import Stream from './type/stream'; import Stream from './stream';
const isIE11 = typeof navigator !== 'undefined' && !!navigator.userAgent.match(/Trident\/7\.0.*rv:([0-9.]+).*\).*Gecko$/); const isIE11 = typeof navigator !== 'undefined' && !!navigator.userAgent.match(/Trident\/7\.0.*rv:([0-9.]+).*\).*Gecko$/);
@ -421,7 +421,7 @@ export default {
print_entire_stream: function (str, stream, fn = result => result) { print_entire_stream: function (str, stream, fn = result => result) {
const teed = stream.tee(); const teed = stream.tee();
teed[1].readToEnd().then(result => { stream.readToEnd(teed[1]).then(result => {
console.log(str + ': ', fn(result)); console.log(str + ': ', fn(result));
}); });
return teed[0]; return teed[0];

View File

@ -9,7 +9,7 @@ const input = require('./testInputs.js');
function stringify(array) { function stringify(array) {
if (openpgp.util.isStream(array)) { if (openpgp.util.isStream(array)) {
return array.readToEnd().then(stringify); return openpgp.stream.readToEnd(array).then(stringify);
} }
if (!openpgp.util.isUint8Array(array)) { if (!openpgp.util.isUint8Array(array)) {

View File

@ -21,7 +21,7 @@ describe('Streaming', function() {
data, data,
passwords: ['test'], passwords: ['test'],
}); });
const msgAsciiArmored = util.Uint8Array_to_str(await encrypted.data.readToEnd()); const msgAsciiArmored = util.Uint8Array_to_str(await openpgp.stream.readToEnd(encrypted.data));
const message = await openpgp.message.readArmored(msgAsciiArmored); const message = await openpgp.message.readArmored(msgAsciiArmored);
const decrypted = await openpgp.decrypt({ const decrypted = await openpgp.decrypt({
passwords: ['test'], passwords: ['test'],
@ -48,7 +48,7 @@ describe('Streaming', function() {
data, data,
passwords: ['test'], passwords: ['test'],
}); });
const msgAsciiArmored = util.Uint8Array_to_str(await encrypted.data.readToEnd()); const msgAsciiArmored = util.Uint8Array_to_str(await openpgp.stream.readToEnd(encrypted.data));
const message = await openpgp.message.readArmored(msgAsciiArmored); const message = await openpgp.message.readArmored(msgAsciiArmored);
const decrypted = await openpgp.decrypt({ const decrypted = await openpgp.decrypt({
passwords: ['test'], passwords: ['test'],
@ -85,6 +85,6 @@ describe('Streaming', function() {
format: 'binary' format: 'binary'
}); });
expect(util.isStream(decrypted.data)).to.be.true; expect(util.isStream(decrypted.data)).to.be.true;
expect(await decrypted.data.readToEnd()).to.deep.equal(util.concatUint8Array(plaintext)); expect(await openpgp.stream.readToEnd(decrypted.data)).to.deep.equal(util.concatUint8Array(plaintext));
}); });
}); });