11/// <reference lib="webworker" />
22/// <reference no-default-lib="true"/>
33
4- export type { } ; // let typescript shut up
4+ export type { } ; // make typescript shut up, this line should be deleted after transpiled
55declare let self : ServiceWorkerGlobalScope ;
66
77const CACHE_KEY = "v1.0.1" ;
@@ -36,22 +36,56 @@ async function decrypt_file_part(
3636 return plain ;
3737}
3838
39- self . addEventListener ( "install" , function ( event ) {
40- event . waitUntil (
41- ( async function ( ) {
42- let cache = await caches . open ( CACHE_KEY ) ;
43- return cache . addAll ( [
44- "/assets/favicon.ico" ,
45- "/" ,
46- "/assets/homepage.js" ,
47- "/assets/homepage.css" ,
48- "/s/*" ,
49- "/assets/receive.js" ,
50- "/assets/receive.css" ,
51- ] ) ;
52- } ) ( )
53- ) ;
54- } ) ;
39+ class Chunker {
40+ done = false ;
41+ private remaining : Uint8Array | undefined ;
42+ remainingSize = 0 ;
43+ private reader : ReadableStreamDefaultReader < Uint8Array > ;
44+
45+ constructor ( stream : ReadableStream < Uint8Array > , private size = 16 ) {
46+ this . reader = stream . getReader ( ) ;
47+ }
48+
49+ async read ( ) : Promise <
50+ { done : true ; value : undefined } | { done : false ; value : Uint8Array }
51+ > {
52+ if ( this . done ) {
53+ return { done : true , value : undefined } ;
54+ }
55+ const { done, value } = await this . reader . read ( ) ;
56+ if ( done || value === undefined ) {
57+ this . done = true ;
58+ if ( this . remaining === undefined ) {
59+ return { done : true , value : undefined } ;
60+ } else {
61+ return { done : false , value : this . remaining } ;
62+ }
63+ }
64+ const inSize = value . byteLength + this . remainingSize ;
65+ const remainingSize = inSize % this . size ;
66+ const outSize = inSize - remainingSize ;
67+ let out : Uint8Array ;
68+ if ( this . remaining !== undefined ) {
69+ out = new Uint8Array ( outSize ) ;
70+ out . set ( this . remaining ) ;
71+ out . set (
72+ value . slice ( 0 , value . byteLength - remainingSize ) ,
73+ this . remainingSize
74+ ) ;
75+ } else {
76+ out = value . slice ( 0 , value . byteLength - remainingSize ) ;
77+ }
78+
79+ this . remainingSize = remainingSize ;
80+ if ( remainingSize > 0 ) {
81+ this . remaining = value . slice ( value . byteLength - remainingSize ) ;
82+ } else {
83+ this . remaining = undefined ;
84+ }
85+
86+ return { done : false , value : out } ;
87+ }
88+ }
5589
5690self . addEventListener ( "activate" , function ( event ) {
5791 event . waitUntil ( self . clients . claim ( ) ) ;
@@ -63,17 +97,6 @@ self.addEventListener("message", function (event) {
6397 }
6498} ) ;
6599
66- async function try_fetch ( input , init , tries = 3 ) {
67- try {
68- return await fetch ( input , init ) ;
69- } catch ( e ) {
70- if ( tries > 0 ) {
71- return try_fetch ( input , init , tries - 1 ) ;
72- }
73- throw e ;
74- }
75- }
76-
77100self . addEventListener ( "fetch" , function ( event ) {
78101 let request = event . request ;
79102 let url = new URL ( request . url ) ;
@@ -82,7 +105,7 @@ self.addEventListener("fetch", function (event) {
82105 }
83106 let path = url . pathname ;
84107 if ( path . startsWith ( "/s/download" ) ) {
85- event . respondWith ( virtual_downloading_response ( path ) ) ;
108+ event . respondWith ( virtual_downloading_response ( request ) ) ;
86109 return ;
87110 }
88111 if ( path . startsWith ( "/s/" ) ) {
@@ -91,7 +114,22 @@ self.addEventListener("fetch", function (event) {
91114 event . respondWith ( cached_response ( request ) ) ;
92115} ) ;
93116
94- async function virtual_downloading_response ( path : string ) {
117+ function rangeOf ( request : Request ) {
118+ let range = request . headers . get ( "Range" ) ;
119+ if ( range === null ) {
120+ return null ;
121+ }
122+ let range_match = range . match ( / ^ b y t e s = ( \d + ) - ( \d + ) $ / ) ;
123+ if ( range_match === null ) {
124+ return null ;
125+ }
126+ let start = parseInt ( range_match [ 1 ] ) ;
127+ let end = parseInt ( range_match [ 2 ] ) ;
128+ return [ start , end ] ;
129+ }
130+
131+ async function virtual_downloading_response ( request : Request ) {
132+ const path = new URL ( request . url ) . pathname ;
95133 let path_list = path . split ( "/" ) ;
96134 let file_path = path_list [ path_list . length - 1 ] ;
97135 let file_info = FilesData [ file_path ] ;
@@ -101,53 +139,64 @@ async function virtual_downloading_response(path: string) {
101139 statusText : "Not Found" ,
102140 } ) ;
103141 }
142+ let headers = new Headers ( ) ;
143+ // let range = rangeOf(request);
144+ // let start: number;
145+ // if (range !== null) {
146+ // start = range[0];
147+ // } else {
148+ // start = 0;
149+ // }
150+ // if (range !== null) {
151+ // headers.set("Range", `bytes=${range[0]}-${range[1]}`);
152+ // }
153+ //// TODO: handle cases when range does not start from multiple of 16
154+ let { abort, signal } = new AbortController ( ) ;
155+ let response = await fetch ( file_info . download_url , { headers, signal } ) ;
156+ let body = response . body ;
157+ if ( body === null ) {
158+ return response ;
159+ }
160+ let reader = new Chunker ( body , 16 ) ; // chunk stream to size of multiple of 16 bytes
104161 let decrypted_readable_stream = new ReadableStream ( {
105162 async start ( controller ) {
106- const chunk_size = 1310720 ;
107- let chunk_number = Math . ceil ( file_info . file_size / chunk_size ) ;
108- let fetched = 0 ;
109- let fetch_queue : Promise < Uint8Array | null > [ ] = [ ] ;
110- async function next_fetch ( ) {
111- if ( fetched >= chunk_number ) {
112- return null ;
163+ let offset = 0 ;
164+ while ( true ) {
165+ let readResult = await reader . read ( ) ;
166+ if ( readResult . done ) {
167+ break ;
113168 }
114- let i = fetched ;
115- fetched += 1 ;
116- let start = i * chunk_size ;
117- let end ;
118- if ( i === chunk_number - 1 ) {
119- end = file_info . file_size - 1 ;
120- } else {
121- end = start + chunk_size - 1 ;
122- }
123- let response = await try_fetch ( file_info . download_url , {
124- headers : { Range : `bytes=${ start } -${ end } ` } ,
125- } ) ;
126- let data = await response . arrayBuffer ( ) ;
127169 let plain = await decrypt_file_part (
128170 file_info . key ,
129- data ,
171+ readResult . value ,
130172 file_info . nonce ,
131173 file_info . file_id ,
132- start / 16
174+ offset / 16
133175 ) ;
134- return new Uint8Array ( plain ) ;
135- }
136- fetch_queue . push ( next_fetch ( ) ) ;
137- setTimeout ( function ( ) {
138- // 4 concurrent download
139- fetch_queue . push ( next_fetch ( ) ) ;
140- fetch_queue . push ( next_fetch ( ) ) ;
141- fetch_queue . push ( next_fetch ( ) ) ;
142- } , 1000 ) ;
143- for ( let j = 0 ; j < chunk_number ; j ++ ) {
144- let chunk = await fetch_queue . shift ( ) ;
145- controller . enqueue ( chunk ) ;
146- fetch_queue . push ( next_fetch ( ) ) ;
176+ offset += readResult . value . byteLength ;
177+ controller . enqueue ( new Uint8Array ( plain ) ) ;
147178 }
148179 controller . close ( ) ;
149180 } ,
181+ cancel ( ) {
182+ abort ( ) ;
183+ } ,
150184 } ) ;
185+ // let decrypted_readable_stream = body.pipeThrough(
186+ // new TransformStream({
187+ // async transform(chunk, controller) {
188+ // let plain = await decrypt_file_part(
189+ // file_info.key,
190+ // chunk,
191+ // file_info.nonce,
192+ // file_info.file_id,
193+ // start / 16
194+ // );
195+ // start += chunk.byteLength;
196+ // controller.enqueue(new Uint8Array(plain));
197+ // },
198+ // })
199+ // );
151200 return new Response ( decrypted_readable_stream , {
152201 headers : {
153202 "Content-Length" : file_info . file_size ,
0 commit comments