186 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			186 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
|  | /// <reference path="../imports/imports_shared.d.ts" />
 | ||
|  | 
 | ||
|  | 
 | ||
|  | window["require_setup"](module); | ||
|  | import * as native from "teaclient_connection"; | ||
|  | import * as path from "path"; | ||
|  | 
 | ||
|  | namespace _transfer { | ||
|  |     class NativeFileDownload implements transfer.DownloadTransfer { | ||
|  |         readonly key: transfer.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: transfer.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(): transfer.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 transfer.UploadTransfer { | ||
|  |         readonly transfer_key: transfer.UploadKey; | ||
|  |         private _handle: native.ft.NativeFileTransfer; | ||
|  | 
 | ||
|  |         private _result: Promise<void>; | ||
|  | 
 | ||
|  |         private _result_success: () => any; | ||
|  |         private _result_error: (error: any) => any; | ||
|  | 
 | ||
|  |         constructor(key: transfer.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(): transfer.UploadKey { | ||
|  |             return this.transfer_key; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  | 
 | ||
|  |     export function spawn_download_transfer(key: transfer.DownloadKey) : transfer.DownloadTransfer { | ||
|  |         return new NativeFileDownload(key); | ||
|  |     } | ||
|  | 
 | ||
|  | 
 | ||
|  |     export function spawn_upload_transfer(key: transfer.UploadKey) : transfer.UploadTransfer { | ||
|  |         return new NativeFileUpload(key); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | Object.assign(window["transfer"] || (window["transfer"] = {} as any), _transfer); |