TeaSpeak-Client/modules/renderer/connection/FileTransfer.ts

180 lines
5.4 KiB
TypeScript

import * as native from "tc-native/connection";
import * as path from "path";
import {DownloadKey, DownloadTransfer, UploadKey, UploadTransfer} from "tc-shared/FileManager";
import {base64_encode_ab, str2ab8} from "tc-shared/utils/buffers";
class NativeFileDownload implements DownloadTransfer {
readonly key: DownloadKey;
private _handle: native.ft.NativeFileTransfer;
private _buffer: Uint8Array;
private _result: Promise<void>;
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<Response> {
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<void> {
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<void>;
private _result_success: () => any;
private _result_error: (error: any) => any;
constructor(key: UploadKey) {
this.transfer_key = key;
}
async put_data(data: BlobPart | File) : Promise<void> {
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 = <BufferSource>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;
}
}
export function spawn_download_transfer(key: DownloadKey) : DownloadTransfer {
return new NativeFileDownload(key);
}
export function spawn_upload_transfer(key: UploadKey) : UploadTransfer {
return new NativeFileUpload(key);
}