feat(web): add pluggable storage backends for RDP file downloads#1221
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a pluggable download storage layer to iron-remote-desktop-rdp so large RDP file downloads can stream to persistent storage (OPFS) instead of buffering all chunks in RAM, while keeping a universal in-memory Blob fallback.
Changes:
- Introduces
FileStorageBackend/FileWriteHandleabstractions plusBlobStorageBackendandOpfsStorageBackendimplementations. - Adds
detectStorageBackend()and re-exports new storage APIs from the package entry point. - Refactors
RdpFileTransferProviderdownload path to write chunks via a backend and adds extensive Vitest coverage.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| web-client/iron-remote-desktop-rdp/src/storage/storage.test.ts | New unit tests for Blob/OPFS backends and backend detection. |
| web-client/iron-remote-desktop-rdp/src/storage/index.ts | Barrel exports for the new storage APIs. |
| web-client/iron-remote-desktop-rdp/src/storage/detect.ts | Implements auto-detection logic (OPFS probe with Blob fallback). |
| web-client/iron-remote-desktop-rdp/src/storage/OpfsStorageBackend.ts | OPFS streaming backend + session directory lifecycle and stale cleanup. |
| web-client/iron-remote-desktop-rdp/src/storage/FileStorageBackend.ts | Defines the backend and write-handle interfaces (public API types). |
| web-client/iron-remote-desktop-rdp/src/storage/BlobStorageBackend.ts | In-memory fallback backend mirroring prior behavior. |
| web-client/iron-remote-desktop-rdp/src/main.ts | Re-exports storage types/classes/functions for package consumers. |
| web-client/iron-remote-desktop-rdp/src/RdpFileTransferProvider.ts | Integrates storage backend into download flow and adds new option validation. |
| web-client/iron-remote-desktop-rdp/src/RdpFileTransferProvider.test.ts | Adds tests for storageBackend option validation and download integration/error paths. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
febac42 to
d9213c5
Compare
d9213c5 to
8b3c7e0
Compare
Benoît Cortier (CBenoit)
left a comment
There was a problem hiding this comment.
Heads up: I intended to review this sooner, but ran out of time, I'm sorry! I'll be away until next week, I'll get through it then =)
|
Hey Benoît Cortier (@CBenoit), gentle bump, do you mind taking a look at this PR? |
|
Yuval Marcus (@ymarcus93) Ping acknowledged. I’ll go through important PRs, including this one by Friday. |
Benoît Cortier (CBenoit)
left a comment
There was a problem hiding this comment.
I’m sorry for the delay, I finally went through this! It’s looking pretty good to me 👍
Yuval Marcus (@ymarcus93) are you in position of pushing a commit to fix the conflict, or should I handle that?
Thank you! I can handle the conflict, no worries. Will wait for #1381 to be merged first just in case |
|
Hmm actually, I don't think I can since I don't own the source branch. I could make a duplicate PR thats cherry-picked and rebased with conflicts resolved |
|
Thank you for proposing; I think it will be fine, Gabriel Bauman (@gabrielbauman) enabled maintainers to push to his branch, I’ll try that. |
Replace the in-memory Uint8Array[] chunk accumulation in RdpFileTransferProvider with a FileStorageBackend abstraction that supports two implementations: - BlobStorageBackend: in-memory accumulation (universal fallback, same behavior as before). - OpfsStorageBackend: streams chunks to the Origin Private File System, reducing peak download RAM from ~2x file size to ~chunk size (typically 64 KB). Backend selection is automatic by default: a full OPFS round-trip probe detects browser support at first download, falling back to Blob transparently. Consumers can override via the new optional storageBackend field on RdpFileTransferProviderOptions, passing 'auto', 'blob', or a custom FileStorageBackend instance. The OPFS backend manages per-session temp directories under ironrdp-transfers/, cleans up stale sessions from crashed tabs on startup, and handles quota exhaustion with actionable errors. All new types (FileStorageBackend, FileWriteHandle, StorageBackendPreference, BlobStorageBackend, OpfsStorageBackend, detectStorageBackend) are re-exported from the package entry point. No new runtime dependencies. 97 tests cover both backends, detection logic, download flow integration, and edge cases.
8b3c7e0 to
12ac73c
Compare
|
Looks like I was able to address the conflicts on my side 🙂 Thank you Gabriel Bauman (@gabrielbauman)! |
af2706d
into
Devolutions:master
Right now,
RdpFileTransferProvideraccumulates every download chunk in aUint8Array[]in memory. For a 500 MB file, that means ~1 GB of RAM (the chunks plus the final Blob copy). This PR introduces aFileStorageBackendabstraction with two implementations so we can do better when the browser supports it.What's new
BlobStorageBackend- wraps the existing in-memory chunk accumulation behind the new interface. This is the universal fallback and behaves identically to the old code path.OpfsStorageBackend- streams download chunks directly to the Origin Private File System viaFileSystemWritableFileStream. Peak RAM drops from ~2x file size to roughly one chunk (~64 KB). On finalize,getFile()returns a lazyFilehandle backed by disk -- no bulk read into memory.Automatic detection - by default, the first download triggers an OPFS probe (create file, open writable, close, delete). If it works, OPFS is used for the session. If not, Blob kicks in silently. Consumers can also force a backend via the new
storageBackendoption onRdpFileTransferProviderOptions:OPFS housekeeping
ironrdp-transfers/in the OPFS root.Browser support
The writable stream APIs this depends on reached Baseline across all major browsers in September 2025 (Chrome 86+, Firefox 111+, Safari 17.2+, Edge 86+). Older browsers get the Blob fallback automatically via the probe -- no breakage.
API surface
All new types are re-exported from the package entry point:
FileStorageBackend(interface)FileWriteHandle(interface)StorageBackendPreference(type:'auto' | 'blob')BlobStorageBackend(class)OpfsStorageBackend(class)detectStorageBackend(function)The only change to the existing public API is the new optional
storageBackendfield onRdpFileTransferProviderOptions. No breaking changes.Testing
97 tests covering both backends, detection/fallback logic, download flow integration, sanitization of adversarial file names, dispose/abort lifecycle edge cases, and error paths (quota, timeouts, init failures). No new runtime dependencies.