@@ -13,6 +13,7 @@ import (
1313 "os"
1414 "path/filepath"
1515 "regexp"
16+ "strconv"
1617 "strings"
1718 "text/template"
1819
@@ -233,11 +234,12 @@ func ServeIndex(indexPath string) func(*App, http.ResponseWriter, *http.Request)
233234 sign := signature ()
234235 base := WithBase ("/" )
235236 templateData := map [string ]any {
236- "base" : base ,
237- "version" : BUILD_REF ,
238- "license" : LICENSE ,
239- "hash" : sign ,
240- "favicon" : favicon (),
237+ "base" : base ,
238+ "version" : BUILD_REF ,
239+ "license" : LICENSE ,
240+ "hash" : sign ,
241+ "favicon" : favicon (),
242+ "bundle_size" : len (preload ),
241243 }
242244 calculatedEtag := QuickHash (base + BUILD_REF + LICENSE + sign , 10 )
243245 head .Set ("ETag" , calculatedEtag )
@@ -258,8 +260,8 @@ func ServeIndex(indexPath string) func(*App, http.ResponseWriter, *http.Request)
258260 }
259261}
260262
261- func ServeBundle () func ( * App , http. ResponseWriter , * http. Request ) {
262- paths := [] string {
263+ var preload = [][] string {
264+ {
263265 "/assets/" + BUILD_REF + "/boot/ctrl_boot_frontoffice.js" ,
264266 "/assets/" + BUILD_REF + "/boot/router_frontoffice.js" ,
265267 "/assets/" + BUILD_REF + "/boot/common.js" ,
@@ -303,6 +305,9 @@ func ServeBundle() func(*App, http.ResponseWriter, *http.Request) {
303305 "/assets/" + BUILD_REF + "/helpers/sdk.js" ,
304306
305307 "/assets/" + BUILD_REF + "/lib/rx.js" ,
308+ "/assets/" + BUILD_REF + "/lib/ajax.js" ,
309+ },
310+ {
306311 "/assets/" + BUILD_REF + "/lib/vendor/rxjs/rxjs.min.js" ,
307312 "/assets/" + BUILD_REF + "/lib/vendor/rxjs/rxjs-ajax.min.js" ,
308313 "/assets/" + BUILD_REF + "/lib/vendor/rxjs/rxjs-shared.min.js" ,
@@ -311,7 +316,6 @@ func ServeBundle() func(*App, http.ResponseWriter, *http.Request) {
311316 "/assets/" + BUILD_REF + "/lib/path.js" ,
312317 "/assets/" + BUILD_REF + "/lib/random.js" ,
313318 "/assets/" + BUILD_REF + "/lib/settings.js" ,
314- "/assets/" + BUILD_REF + "/lib/ajax.js" ,
315319 "/assets/" + BUILD_REF + "/lib/animate.js" ,
316320 "/assets/" + BUILD_REF + "/lib/assert.js" ,
317321 "/assets/" + BUILD_REF + "/lib/dom.js" ,
@@ -321,16 +325,16 @@ func ServeBundle() func(*App, http.ResponseWriter, *http.Request) {
321325 "/assets/" + BUILD_REF + "/lib/error.js" ,
322326
323327 "/assets/" + BUILD_REF + "/locales/index.js" ,
324-
325328 "/assets/" + BUILD_REF + "/model/config.js" ,
326329 "/assets/" + BUILD_REF + "/model/chromecast.js" ,
327330 "/assets/" + BUILD_REF + "/model/session.js" ,
328331 "/assets/" + BUILD_REF + "/model/plugin.js" ,
329332
330333 "/assets/" + BUILD_REF + "/pages/ctrl_logout.js" ,
331334 "/assets/" + BUILD_REF + "/pages/ctrl_error.js" ,
335+ },
336+ {
332337 "/assets/" + BUILD_REF + "/pages/ctrl_homepage.js" ,
333-
334338 "/assets/" + BUILD_REF + "/pages/ctrl_connectpage.js" ,
335339 "/assets/" + BUILD_REF + "/pages/ctrl_connectpage.css" ,
336340 "/assets/" + BUILD_REF + "/pages/connectpage/ctrl_form.css" ,
@@ -341,18 +345,24 @@ func ServeBundle() func(*App, http.ResponseWriter, *http.Request) {
341345 "/assets/" + BUILD_REF + "/pages/connectpage/model_config.js" ,
342346 "/assets/" + BUILD_REF + "/pages/connectpage/ctrl_form_state.js" ,
343347
348+ "/assets/" + BUILD_REF + "/pages/filespage/thing.js" ,
349+ "/assets/" + BUILD_REF + "/pages/filespage/thing.css" ,
350+ },
351+ {
344352 "/assets/" + BUILD_REF + "/pages/ctrl_filespage.js" ,
345353 "/assets/" + BUILD_REF + "/pages/ctrl_filespage.css" ,
346- "/assets/" + BUILD_REF + "/pages/filespage/ctrl_submenu.css" ,
347354 "/assets/" + BUILD_REF + "/pages/filespage/model_acl.js" ,
348355 "/assets/" + BUILD_REF + "/pages/filespage/cache.js" ,
349- "/assets/" + BUILD_REF + "/pages/filespage/thing.js" ,
350- "/assets/" + BUILD_REF + "/pages/filespage/thing.css" ,
351356 "/assets/" + BUILD_REF + "/pages/filespage/ctrl_filesystem.js" ,
352357 "/assets/" + BUILD_REF + "/pages/filespage/ctrl_filesystem.css" ,
353358 "/assets/" + BUILD_REF + "/pages/filespage/ctrl_upload.js" ,
354359 "/assets/" + BUILD_REF + "/pages/filespage/ctrl_upload.css" ,
360+ "/assets/" + BUILD_REF + "/pages/filespage/ctrl_newitem.js" ,
361+ "/assets/" + BUILD_REF + "/pages/filespage/ctrl_newitem.css" ,
362+ },
363+ {
355364 "/assets/" + BUILD_REF + "/pages/filespage/ctrl_submenu.js" ,
365+ "/assets/" + BUILD_REF + "/pages/filespage/ctrl_submenu.css" ,
356366 "/assets/" + BUILD_REF + "/pages/filespage/state_config.js" ,
357367 "/assets/" + BUILD_REF + "/pages/filespage/helper.js" ,
358368 "/assets/" + BUILD_REF + "/pages/filespage/model_files.js" ,
@@ -367,8 +377,6 @@ func ServeBundle() func(*App, http.ResponseWriter, *http.Request) {
367377 "/assets/" + BUILD_REF + "/pages/filespage/modal_delete.js" ,
368378 "/assets/" + BUILD_REF + "/pages/filespage/state_selection.js" ,
369379 "/assets/" + BUILD_REF + "/pages/filespage/state_newthing.js" ,
370- "/assets/" + BUILD_REF + "/pages/filespage/ctrl_newitem.js" ,
371- "/assets/" + BUILD_REF + "/pages/filespage/ctrl_newitem.css" ,
372380
373381 // "/assets/" + BUILD_REF + "/pages/ctrl_viewerpage.js", // TODO: dynamic imports
374382 "/assets/" + BUILD_REF + "/pages/ctrl_viewerpage.css" ,
@@ -379,66 +387,90 @@ func ServeBundle() func(*App, http.ResponseWriter, *http.Request) {
379387 "/assets/" + BUILD_REF + "/pages/viewerpage/application_downloader.css" ,
380388 "/assets/" + BUILD_REF + "/pages/viewerpage/component_menubar.js" ,
381389 "/assets/" + BUILD_REF + "/pages/viewerpage/component_menubar.css" ,
382- }
390+ },
391+ }
383392
393+ func ServeBundle () func (* App , http.ResponseWriter , * http.Request ) {
384394 var isDebug = os .Getenv ("DEBUG" ) == "true"
385395
386- build := func (quality int ) (bundlePlain []byte , bundleBr []byte , etag string ) {
387- var buf bytes.Buffer
388- for _ , path := range paths {
389- curPath := "/assets/" + strings .TrimPrefix (path , "/assets/" + BUILD_REF + "/" )
390- f := applyPatch (curPath )
391- if f == nil {
392- file , err := WWWPublic .Open (curPath )
393- if err != nil {
394- Log .Warning ("static::bundler failed to find file %s" , err .Error ())
395- continue
396+ buildChunks := func (quality int ) (chunks [][]byte , chunksBr [][]byte , etags []string ) {
397+ numChunks := len (preload )
398+ chunks = make ([][]byte , numChunks + 1 )
399+ chunksBr = make ([][]byte , numChunks + 1 )
400+ etags = make ([]string , numChunks + 1 )
401+ var fullBuf bytes.Buffer
402+ for i := 0 ; i < numChunks ; i ++ {
403+ var chunkBuf bytes.Buffer
404+ for _ , path := range preload [i ] {
405+ curPath := "/assets/" + strings .TrimPrefix (path , "/assets/" + BUILD_REF + "/" )
406+ f := applyPatch (curPath )
407+ if f == nil {
408+ file , err := WWWPublic .Open (curPath )
409+ if err != nil {
410+ Log .Warning ("static::bundler failed to find file %s" , err .Error ())
411+ continue
412+ }
413+ f = new (bytes.Buffer )
414+ if _ , err := io .Copy (f , file ); err != nil {
415+ Log .Warning ("static::bundler msg=copy_error err=%s" , err .Error ())
416+ continue
417+ }
418+ file .Close ()
396419 }
397- f = new (bytes. Buffer )
398- if _ , err := io . Copy ( f , file ); err != nil {
399- Log .Warning ("static::bundler msg=copy_error err=%s" , err .Error ())
420+ code , err := json . Marshal ( f . String () )
421+ if err != nil {
422+ Log .Warning ("static::bundle msg=marshal_failed path=%s err=%s" , path , err .Error ())
400423 continue
401424 }
402- file .Close ()
425+ bundleCall := fmt .Sprintf ("bundler.register(%q, %s);\n " , WithBase (path ), code )
426+ chunkBuf .WriteString (bundleCall )
427+ fullBuf .WriteString (bundleCall )
403428 }
404- code , err := json .Marshal (f .String ())
405- if err != nil {
406- Log .Warning ("static::bundle msg=marshal_failed path=%s err=%s" , path , err .Error ())
407- continue
408- }
409- fmt .Fprintf (& buf , "bundler.register(%q, %s);\n " , WithBase (path ), code )
410- }
411- etag = QuickHash (string (bundlePlain ), 10 )
412- bundlePlain = buf .Bytes ()
413- if quality > 0 {
414- bundleBr , _ = cbrotli .Encode (bundlePlain , cbrotli.WriterOptions {Quality : quality })
429+ chunks [i + 1 ] = chunkBuf .Bytes ()
430+ etags [i + 1 ] = QuickHash (string (chunks [i + 1 ]), 10 )
431+ chunksBr [i + 1 ], _ = cbrotli .Encode (chunks [i + 1 ], cbrotli.WriterOptions {Quality : quality })
415432 }
416- return bundlePlain , bundleBr , etag
433+ chunks [0 ] = fullBuf .Bytes ()
434+ etags [0 ] = QuickHash (string (chunks [0 ]), 10 )
435+ chunksBr [0 ], _ = cbrotli .Encode (chunks [0 ], cbrotli.WriterOptions {Quality : quality })
436+ return chunks , chunksBr , etags
417437 }
418438
419439 quality := 11
420440 if isDebug {
421441 quality = 8
422442 }
423- bundlePlain , bundleBr , etag := build (quality )
443+ chunks , chunksBr , etags := buildChunks (quality )
424444
425445 return func (ctx * App , res http.ResponseWriter , req * http.Request ) {
426446 if isDebug {
427- bundlePlain , bundleBr , etag = build (quality )
447+ chunks , chunksBr , etags = buildChunks (quality )
428448 }
449+
450+ chunkIndex := 0
451+ if parsed , err := strconv .Atoi (req .URL .Query ().Get ("chunk" )); err == nil && parsed < len (chunks ) {
452+ chunkIndex = parsed
453+ }
454+
455+ if chunkIndex >= len (chunks ) {
456+ http .NotFound (res , req )
457+ return
458+ }
459+
429460 head := res .Header ()
430461 head .Set ("Content-Type" , "application/javascript" )
431462 head .Set ("Cache-Control" , "no-cache" )
432- head .Set ("Etag" , etag )
433- if req .Header .Get ("If-None-Match" ) == etag && etag != "" {
463+ head .Set ("Etag" , etags [chunkIndex ])
464+
465+ if req .Header .Get ("If-None-Match" ) == etags [chunkIndex ] && etags [chunkIndex ] != "" {
434466 res .WriteHeader (http .StatusNotModified )
435467 return
436- } else if strings .Contains (req .Header .Get ("Accept-Encoding" ), "br" ) && len (bundleBr ) > 0 {
468+ } else if strings .Contains (req .Header .Get ("Accept-Encoding" ), "br" ) && len (chunksBr [ chunkIndex ] ) > 0 {
437469 head .Set ("Content-Encoding" , "br" )
438- res .Write (bundleBr )
470+ res .Write (chunksBr [ chunkIndex ] )
439471 return
440472 }
441- res .Write (bundlePlain )
473+ res .Write (chunks [ chunkIndex ] )
442474 }
443475}
444476
0 commit comments