mtcute/packages/client/src/utils/stream-utils.ts

110 lines
3.1 KiB
TypeScript
Raw Normal View History

2021-04-08 12:19:38 +03:00
import { Readable, ReadableOptions } from 'stream'
// taken from https://github.com/JCCR/web-streams-node, licensed under Apache 2.0
class NodeReadable extends Readable {
private _webStream: ReadableStream
private _reader: ReadableStreamDefaultReader
private _reading: boolean
private _doneReading?: () => void
2021-04-08 12:19:38 +03:00
constructor(webStream: ReadableStream, opts?: ReadableOptions) {
super(opts)
this._webStream = webStream
this._reader = webStream.getReader()
this._reading = false
}
_read() {
if (this._reading) {
return
}
this._reading = true
2021-04-08 12:19:38 +03:00
const doRead = () => {
this._reader
.read()
.then((res) => {
if (this._doneReading) {
this._reading = false
this._reader.releaseLock()
this._doneReading()
}
if (res.done) {
this.push(null)
this._reading = false
this._reader.releaseLock()
return
}
if (this.push(res.value)) {
doRead()
return
}
this._reading = false
this._reader.releaseLock()
})
.catch((err) => this.emit('error', err))
2021-04-08 12:19:38 +03:00
}
doRead()
}
_destroy(err: Error | null, callback: (error?: Error | null) => void) {
if (this._reading) {
const promise = new Promise<void>((resolve) => {
2021-04-08 12:19:38 +03:00
this._doneReading = resolve
})
promise
.then(() => {
this._handleDestroy(err, callback)
})
.catch((err) => this.emit('error', err))
2021-04-08 12:19:38 +03:00
} else {
this._handleDestroy(err, callback)
}
}
2023-09-24 01:32:22 +03:00
_handleDestroy(err: Error | null, callback: (error?: Error | null) => void) {
this._webStream
.cancel()
.then(() => super._destroy(err, callback))
.catch((err: Error) => callback(err))
2021-04-08 12:19:38 +03:00
}
}
2023-09-24 01:32:22 +03:00
export function convertWebStreamToNodeReadable(webStream: ReadableStream, opts?: ReadableOptions): Readable {
2021-04-08 12:19:38 +03:00
return new NodeReadable(webStream, opts)
}
export function bufferToStream(buf: Buffer): Readable {
return new Readable({
read() {
this.push(buf)
this.push(null)
},
})
}
2023-09-24 01:32:22 +03:00
export async function readBytesFromStream(stream: Readable, size: number): Promise<Buffer | null> {
2021-04-08 12:19:38 +03:00
if (stream.readableEnded) return null
let res = stream.read(size) as Buffer
2021-04-08 12:19:38 +03:00
if (!res) {
return new Promise((resolve, reject) => {
2021-04-08 12:19:38 +03:00
stream.on('readable', function handler() {
res = stream.read(size) as Buffer
2021-04-08 12:19:38 +03:00
if (res) {
stream.off('readable', handler)
stream.off('error', reject)
2021-04-08 12:19:38 +03:00
resolve(res)
}
})
stream.on('error', reject)
2021-04-08 12:19:38 +03:00
})
}
return res
}