Class AppDirectoryHandler
- All Implemented Interfaces:
Handler, Handler.Container, Handler.Singleton, Request.Handler, Container, Destroyable, Dumpable, Dumpable.DumpableContainer, LifeCycle, Invocable
/app/<appID>/ at the head of the handler chain (work order CC-PWA-V5A-APPDIR-ROUTE, design CC-DESIGN-APP-URL-V2 sections 2.2, 2.3, 2.4 and 2.7, rulings AU-2 to AU-5 of 2026-09-29). Nothing exists on disk under /app/. For a GET or HEAD whose path starts with /app/:/app/<id>without a slash, and/app/<alias>/...: 302 to the canonical directory, the rest of the path and the query kept (AU-3)./app/<segment>/...for a segment the server refuses or does not know: 404, fromIExternalBrowserPages.getAppDirectory(String), which validates the segment before any lookup./app/<id>/...and/app/<alias>/...for an application that is not served on the port configuration the request arrived on (IExternalBrowserPages.isAppRefusedOn(String,com.iizix.comm.PortConfig), CC-FIX-APP-DEFINITIONS): the same 404 as for an unknown segment, before any redirect, decided on the application the directory names - so an alias is refused with its application./app/<id>/and/app/<id>/iiziApp.html: the application page for thelangentry parameter, both served, no redirect (AU-2), behind the same Browser gate as the legacy page./app/<id>/iiziAppSorry.html: the sorry page, the application implied by the path./app/<id>/sw.js,offline.html,manifest-<lang>.webmanifest(ormanifest.webmanifestwithout a language) and the icons: the directory's generated files (work order CC-PWA-V5E-APPDIR-PWA), asIAppDirectory.getFile(String,String)answers them - the worker and the offline page for every application, the manifest and the icons only for an installable one - and 404 for a name the directory does not hold; never a fall-through, so that no file of the web root can take their place./app/<id>/<anything else>: passed on to the chain as/<anything else>by anAppPathRequest, so the resources handler, the servlet context, the precompressed variants and the CORS filter behave for it exactly as for the web root's path (AU-4). The rest is passed on encoded as it came, its dot segments resolved by the canonical path; a rest that, decoded, would re-enter/app/or carries':'or".."is 404.
/app/ is passed on untouched, as WebServerResources passes it: OPTIONS and POST belong to the servlet context.The page. Per response, on the directory's page for the language: the client gate's BrowserGate.APP_ID_MARKER, the Web Analytics line of AppAnalytics.script(AppAnalytics.Settings, boolean) (the user's decision lives on the device only since CC-CLIENT-STORAGE-V5: the server reads no cookie), and the registration of the APPLICATION's Service Worker, /app/<id>/sw.js with scope /app/<id>/ (design 2.5), in the same places and the same order as CustomDefaultServlet puts the legacy page's. Then Content-Type: text/html;charset=utf-8, Cache-Control: no-cache, Last-Modified from the directory's version time, Referrer-Policy: same-origin, no Vary, and an ETag over the FINAL bytes (AU-5): "<stamp>-<12 hexadecimal digits of SHA-256 over the response body>", since the substituted page varies per request - its language, the application's name, until version 5 the consent cookie - and a stamp-only ETag would answer 304 to a page that changed (design F-7). If-None-Match answers 304 with the same headers and no body; If-Modified-Since alone is not honoured, for the same reason. HEAD gets the headers of the GET.
The generated files. Sent as the directory holds them, with the same Last-Modified, ETag and 304: the file's media type (application/manifest+json, text/javascript, text/html or image/png), Cache-Control: max-age=86400 for an icon and no-cache for the others, and the page's Referrer-Policy on the offline page, which the worker shows at the URL of the page it stands in for. No Browser gate: it guards the application page only. Compression follows Accept-Encoding through the compression handler that wraps this one in production.
Why a wrapper and not a member of the sequence. A member of a Handler.Sequence receives the request the sequence was given and cannot hand the members after it a rewritten one, so this handler wraps the sequence: it is the head of the chain, ahead of WebServerResources, and everything it does not answer itself goes to the sequence, rewritten or untouched.
- Author:
- Christopher Mindus
Nested Class Summary
Nested classes/interfaces inherited from class Handler.Abstract
Handler.Abstract.NonBlockingNested classes/interfaces inherited from class AbstractLifeCycle
AbstractLifeCycle.AbstractLifeCycleListener, AbstractLifeCycle.StopExceptionNested classes/interfaces inherited from interface Container
Container.InheritedListener, Container.ListenerNested classes/interfaces inherited from interface Dumpable
Dumpable.DumpableContainer, Dumpable.DumpAppendableNested classes/interfaces inherited from interface Handler
Handler.Abstract, Handler.AbstractContainer, Handler.Collection, Handler.Container, Handler.Sequence, Handler.Singleton, Handler.WrapperNested classes/interfaces inherited from interface Invocable
Invocable.Callable, Invocable.InvocationType, Invocable.ReadyTask, Invocable.TaskNested classes/interfaces inherited from interface LifeCycle
LifeCycle.ListenerNested classes/interfaces inherited from interface Request.Handler
Request.Handler.AbortExceptionField Summary
FieldsFields inherited from interface Invocable
__nonBlocking, NOOPConstructor Summary
ConstructorsConstructorDescriptionAppDirectoryHandler(String root, IExternalBrowserPages extPages, Handler handler) Creates the handler at the head of the chain.Method Summary
Methods inherited from class Handler.Wrapper
getHandler, getInvocationType, setHandlerMethods inherited from class Handler.AbstractContainer
findContainerOf, getDescendant, getDescendants, isDynamic, setDynamic, setServerMethods inherited from class Handler.Abstract
destroy, doStart, doStop, getServerMethods inherited from class ContainerLifeCycle
addBean, addBean, addEventListener, addManaged, addManaged, contains, dump, dump, dump, dumpObjects, dumpStdErr, getBean, getBeans, getBeans, getContainedBeans, getContainedBeans, installBean, installBean, isAuto, isManaged, isUnmanaged, manage, removeBean, removeBeans, removeEventListener, setBeans, start, stop, unmanage, updateBean, updateBean, updateBeans, updateBeansMethods inherited from class AbstractLifeCycle
getEventListeners, getState, getState, isFailed, isRunning, isStarted, isStarting, isStopped, isStopping, setEventListeners, start, stop, toStringMethods inherited from class Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, waitMethods inherited from interface Container
getCachedBeans, getEventListenersMethods inherited from interface Destroyable
destroyMethods inherited from interface Dumpable.DumpableContainer
isDumpableMethods inherited from interface Handler.Container
getContainer, getDescendant, getDescendants, getDescendantsMethods inherited from interface Handler.Singleton
getHandlers, getTail, insertHandler, setHandlerMethods inherited from interface LifeCycle
addEventListener, isFailed, isRunning, isStarted, isStarting, isStopped, isStopping, removeEventListener, start, stop
Field Details
PREFIX
The prefix of the virtual directories: "/app/". Changing it after one installation orphans that installation, whose manifestidis its directory (design section 5, Q1). It is the one constant ofcom.iizix.pwa, which the manifest, the worker and the offline page build their URLs from (decision V5B-D-9).- See Also:
Constructor Details
AppDirectoryHandler
Creates the handler at the head of the chain.- Parameters:
root- The web server root.extPages- The external pages, which answer the directories, the sorry page's information and the Analytics settings; null for none.handler- The handler chain that follows: the production sequence.
Method Details
handle
- Specified by:
handlein interfaceRequest.Handler- Overrides:
handlein classHandler.Wrapper- Throws:
Exception