import * as native from "tc-native/connection"; import * as path from "path"; import {DownloadKey, DownloadTransfer, UploadKey, UploadTransfer} from "tc-shared/file/FileManager"; import {base64_encode_ab, str2ab8} from "tc-shared/utils/buffers"; import {set_transfer_provider, TransferKey, TransferProvider} from "tc-shared/file/FileManager"; class NativeFileDownload implements DownloadTransfer { readonly key: DownloadKey; private _handle: native.ft.NativeFileTransfer; private _buffer: Uint8Array; private _result: Promise; private _response: Response; private _result_success: () => any; private _result_error: (error: any) => any; constructor(key: DownloadKey) { this.key = key; this._buffer = new Uint8Array(key.total_size); this._handle = native.ft.spawn_connection({ client_transfer_id: key.client_transfer_id, server_transfer_id: key.server_transfer_id, remote_address: key.peer.hosts[0], remote_port: key.peer.port, transfer_key: key.key, object: native.ft.download_transfer_object_from_buffer(this._buffer.buffer) }); } get_key(): DownloadKey { return this.key; } async request_file(): Promise { if(this._response) return this._response; try { await (this._result || this._start_transfer()); } catch(error) { throw error; } if(this._response) return this._response; const buffer = this._buffer.buffer.slice(this._buffer.byteOffset, this._buffer.byteOffset + Math.min(64, this._buffer.byteLength)); /* may another task has been stepped by and already set the response */ return this._response || (this._response = new Response(this._buffer, { status: 200, statusText: "success", headers: { "X-media-bytes": base64_encode_ab(buffer) } })); } _start_transfer() : Promise { return this._result = new Promise((resolve, reject) => { this._result_error = (error) => { this._result_error = undefined; this._result_success = undefined; reject(error); }; this._result_success = () => { this._result_error = undefined; this._result_success = undefined; resolve(); }; this._handle.callback_failed = this._result_error; this._handle.callback_finished = aborted => { if(aborted) this._result_error("aborted"); else this._result_success(); }; this._handle.start(); }); } } class NativeFileUpload implements UploadTransfer { readonly transfer_key: UploadKey; private _handle: native.ft.NativeFileTransfer; private _result: Promise; private _result_success: () => any; private _result_error: (error: any) => any; constructor(key: UploadKey) { this.transfer_key = key; } async put_data(data: BlobPart | File) : Promise { if(this._result) { await this._result; return; } let buffer: native.ft.FileTransferSource; if(data instanceof File) { if(data.size != this.transfer_key.total_size) throw "invalid size"; buffer = native.ft.upload_transfer_object_from_file(path.dirname(data.path), data.name); } else if(typeof(data) === "string") { if(data.length != this.transfer_key.total_size) throw "invalid size"; buffer = native.ft.upload_transfer_object_from_buffer(str2ab8(data)); } else { let buf = data; if(buf.byteLength != this.transfer_key.total_size) throw "invalid size"; if(ArrayBuffer.isView(buf)) buf = buf.buffer.slice(buf.byteOffset); buffer = native.ft.upload_transfer_object_from_buffer(buf); } this._handle = native.ft.spawn_connection({ client_transfer_id: this.transfer_key.client_transfer_id, server_transfer_id: this.transfer_key.server_transfer_id, remote_address: this.transfer_key.peer.hosts[0], remote_port: this.transfer_key.peer.port, transfer_key: this.transfer_key.key, object: buffer }); await (this._result = new Promise((resolve, reject) => { this._result_error = (error) => { this._result_error = undefined; this._result_success = undefined; reject(error); }; this._result_success = () => { this._result_error = undefined; this._result_success = undefined; resolve(); }; this._handle.callback_failed = this._result_error; this._handle.callback_finished = aborted => { if(aborted) this._result_error("aborted"); else this._result_success(); }; this._handle.start(); })); } get_key(): UploadKey { return this.transfer_key; } } set_transfer_provider(new class implements TransferProvider { spawn_upload_transfer(key: TransferKey): UploadTransfer { return new NativeFileUpload(key); } spawn_download_transfer(key: TransferKey): DownloadTransfer { return new NativeFileDownload(key); } });