mattcasters opened a new issue, #8297: URL: https://github.com/apache/hop/issues/8297
### Apache Hop version? 2.20.0 ### Java version? OpenJDK 21 ### Operating system Docker ### What happened? ## Description Opening any `.html` / `.htm` file in the **File explorer** perspective on **Hop Web** shows the markup but not the site. Stylesheets, images, scripts, and relative links do not load. This is not a bug in the HTML files. Desktop Hop GUI is fine (`file:` URLs resolve against the file on disk). Hop Web injects the file with RAP `Browser.setText()`, which has **no document URL**, so the browser resolves relative references against the Hop Web UI origin. Typical 404s in the browser network tab: ``` https://<hop-web-host>/ui/assets/css/hop-doc.css https://<hop-web-host>/assets/css/hop-doc.css https://<hop-web-host>/ui/images/logo.png ``` The files live next to the HTML on the **Tomcat host**, not under the RAP entry point. Self-contained documentation sets (external CSS/JS, nested pages, relative `../` links) are unusable in explorer. Plugin help HTML with relative images/links has the same problem. `http://` / `https://` filenames already call `Browser.setUrl()` and work. Local / VFS files do not. ## Environment - Hop 2.19.x / 2.20.0-SNAPSHOT - Hop Web (RAP) behind a TLS reverse proxy (Caddy/nginx) is the production case; same failure on a direct `http://localhost:8080/ui` for relative assets - Reproduced by opening any multi-file HTML tree from explorer (project docs, plugin docs, a folder with `index.html` + `assets/css/…`) ## Steps to reproduce 1. Start Hop Web. 2. Open a project that contains an HTML file with a relative stylesheet, for example: ```html <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="assets/css/site.css"> </head> <body> <h1>Hello</h1> <p>This should be styled.</p> <p><a href="other.html">other page</a></p> </body> </html> ``` with `assets/css/site.css` next to it (or one directory up with `../assets/css/site.css`). 3. In **File explorer**, double-click the HTML file. 4. Optionally open DevTools → Network. ## Expected The page renders with its CSS, images, and in-page links, as it does when the same folder is opened in a desktop browser via `file:` / `http:`. ## Actual Unstyled HTML. CSS/image requests go to the Hop Web UI origin and 404. Clicking a relative link leaves the RAP app or 404s. Nested pages with `../../assets/css/…` fail the same way; fixing the number of `../` segments in the generator does not help. ## Root cause `HtmlExplorerFileTypeHandler.reload()`: ```java // plugins/transforms/textfile/.../HtmlExplorerFileTypeHandler.java if (filename starts with http:// or https://) { wBrowser.setUrl(filename); return; } String htmlContent = readTextFileContent(UTF_8); wBrowser.setText(htmlContent); // no document base ``` On RAP, `Browser.setText()` puts markup in an iframe without a URL for that document. Relative `href` / `src` resolve against `https://<host>/ui/…` (or `/`). The HTML on disk is never used as the base. A `<base href="file:…">` tag would not help on Hop Web: the files are on the server, and the user’s browser cannot read `file:` URLs on Tomcat. ## Related (same class of bug) `PdfExplorerFileTypeHandler` uses `wBrowser.setUrl(HopVfs.getFileObject(filename).getURL())`, i.e. `file://` on the server. That also cannot work in the user’s browser on Hop Web. Markdown preview that `setText()`s generated HTML with relative assets would hit the same limit. ## What does *not* work as a plugin workaround A plugin can register a RAP `ServiceHandler` and try `Browser.setUrl(handlerUrl)` for its own docs. That is incomplete: 1. Explorer still binds `*.html` to `HtmlExplorerFileType`. A custom type that only claims a fake extension never wins on double-click. 2. Paint-listener swaps of `TreeItemFolder.fileType` are racy (`asyncExec`, and listeners run before `setTreeItemData`). 3. Turning `RWT.getServiceManager().getServiceHandlerUrl(id)` into an **absolute** URL via `HttpServletRequest.getRequestURL()` breaks behind TLS-terminating reverse proxies. Hop already documents this in `RapHopWebUrlUpdater`: the browser origin is `https://…` while `getRequestURL()` is often `http://<upstream-host>/ui`, so the explorer if rame is mixed-content-blocked. Hop’s canvas already does this correctly with a **relative** handler URL: ```javascript // rap/.../canvas-svg.js connection.getUrl() + "?servicehandler=" + encodeURIComponent(serviceId) + "&cid=" + encodeURIComponent(connection.getConnectionId()); ``` registered from `HopWeb` as `application.addServiceHandler("canvasRender", …)`. ## Proposed fix Add a Hop Web **explorer file** RAP `ServiceHandler` (same pattern as `CanvasRenderServiceHandler`) and use it from the HTML (and PDF) explorer handlers when `EnvironmentUtils.getInstance().isWeb()`. Suggested shape: - Register once from `HopWeb` / RAP application config, e.g. `servicehandler=explorerFile`. - Query parameters: current RAP `cid` plus a **relative** file path (or an opaque, session-scoped token — not a raw filesystem path in the URL if that is a concern). - Handler reads the file through `HopVfs` from the **current project / last-opened explorer root** (or a session map of allowed roots registered when the tab opens). - Reject `..`, absolute paths, and unknown extensions. Allow a small set: `html`, `htm`, `css`, `js`, `png`, `jpg`, `svg`, `gif`, `webp`, `ico`, `woff`, `woff2`, `ttf`, `pdf`, … - Set `Content-Type` correctly (`text/html`, `text/css`, `application/javascript`, `application/pdf`, image types). - For HTML/CSS responses, either: - **A (preferred):** `Browser.setUrl(relativeHandlerUrl + "&file=" + encodedRelativePath)` so the iframe **has** a document URL. Relative `href`/`src` then resolve against the handler; nested `../assets/css/foo.css` just works; **or** - **B:** rewrite relative `href`/`src`/`url()` in HTML/CSS to handler URLs before writing the response (needed if the iframe URL cannot preserve a directory base). **URL construction must stay relative** (path + query, including `cid`). Do not absolutize with `request.getRequestURL()`. If a public absolute URL is ever needed, follow `RapHopWebUrlUpdater.publicOrigin()` (`X-Forwarded-Proto` / `X-Forwarded-Host`). Desktop SWT path stays as today (`setText` is acceptable there only if SWT WebKit still resolves against `file:`; otherwise desktop can keep using `file:` via `setUrl`). ## Security The handler is reachable from the user’s browser. It must not become an arbitrary file read: - Scope to the Hop project folder / explorer root for that RAP session. - Canonicalize and reject paths that escape the root. - Allow-list extensions. - Require a valid RAP connection id (`cid`), same as `canvasRender`. ## Tests - Unit: path sandbox (reject `../`, absolute, unknown extension); content-type map; relative URL join for nested HTML (`workflows/workflows/page.html` + `../../assets/css/x.css` → `assets/css/x.css`). - Unit: handler URL is relative (starts with `?` or `/ui?`, never `http://`). - Hop Web IT (if feasible): open an HTML fixture with a sibling CSS file from explorer; assert the stylesheet request hits `servicehandler=explorerFile` and returns 200. ## Workaround Open the same HTML in a real browser (desktop `file:`, or copy the folder to a static HTTP root). The Hop Web explorer tab cannot host a multi-file HTML site until this lands. ### Issue Priority Priority: 2 ### Issue Component Component: Hop Web -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
