feat: reject uploads with a synchronous validator#24925
Open
mcollovati wants to merge 3 commits into
Open
Conversation
knoobie
reviewed
Jul 7, 2026
| * Adds a validator that can reject the upload while it is being received. | ||
| * <p> | ||
| * The validator is invoked synchronously on the request thread: once before | ||
| * any data is read (for metadata-only checks) and then once per chunk of |
Contributor
There was a problem hiding this comment.
once before any data is read (for metadata-only checks) and then once per chunk
That means - it's possible that the validator run literally 1000 times? 😬 I doubt that there are validator that require to run so often. Would it make sense to have a way to define when a validator runs? e.g. once, meta + first chunk or at every chunk, last chunk? I'm looking at things like:
- once: typical file extension check
- meta + first chunk: mime type check on the magic bytes
- every chunk: possible disc threshold exceeded?
- last chunk: anti virus check
Collaborator
Author
There was a problem hiding this comment.
Good point. Allowing to configure when validation should happen makes sense to avoid calling the validator many times without a good reason
2a59bbe to
23c8741
Compare
The pre-made upload handlers offered no supported way to refuse an upload while it was being received; only a fully custom `UploadHandler` could call `UploadEvent.reject(...)`, and the handlers invoked the success callback unconditionally and left rejected files on disk. Add `UploadValidator`, invoked synchronously by the pre-made handlers in three phases: `validateMetadata` before any data is read, `validateHeader` over the first N bytes (e.g. magic-byte checks, aborting after only those bytes), and `validateComplete` over the fully received content (e.g. antivirus). Calling `UploadEvent.reject(...)` from any phase aborts the upload before it is stored, skips the success callback, deletes any written file, and reports the rejection to the client. Register validators additively via `withValidator(...)` or the fluent `validateMetadata`/`validateHeader(int, ...)`/`validateComplete` methods. Fixes #24923
23c8741 to
79cd68d
Compare
Constructing a real UploadEvent lets the rejection state machine (reject/isRejected/getRejectionMessage) run for real, removing the simulateReject() helper that stubbed those on the mock. Upload content is now served through a mocked Part.
Extract the in-memory read phase into a helper so metadata and header rejection become flat early returns instead of nested guards, and drop the redundant post-transfer isRejected() check inside the UI.access command in both handlers: rejection state is fully settled once all validators have run, so a rejected upload reports its terminal onError, cleans up, and returns without scheduling a no-op delivery.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



The pre-made upload handlers offered no supported way to refuse an upload while it was being received; only a fully custom
UploadHandlercould callUploadEvent.reject(...). Subclassing a pre-made handler to reject a captured event did not help: rejection was signalled asynchronously and never checked, so the success callback still ran and file handlers left the written file on disk.Add
UploadValidator, registered viawithValidator(...), which is invoked synchronously while the upload is received and can callUploadEvent.reject(...)to abort it before it is stored. Downloads are unaffected.Fixes #24923