Class FileUploadRegistry
Quota is checked here and never at write time. A reservation holds its accepted files' declared sizes against the session and server quotas until each file reaches a terminal state; a never-started reservation is reclaimed by the start timeout, an abandoned one by the idle timeout.
Threading: reserve(String, String, List, UploadLimits, IFileUploadListener, boolean) synchronizes on the registry for the quota computation; every per-reservation change runs under that reservation's lock. Timeouts fire on the ITimeouts implementation's thread and take the reservation lock themselves. Listener dispatch is not done here: the FileUploadRegistry.IEvents callback is told, and the application-session service posts to its worker.
Pause: a file is paused by the App (pause(UploadReservation, int, String, boolean)) or by the client's notice, each with its own flag on the record; the state is UploadState.PAUSED while either is set and returns to where it was when both are clear. When no file of a reservation is receiving and at least one is paused, the reservation is paused as a whole: its start and idle timeouts are cancelled and the maximum pause timeout runs; when a file receives again, the idle timeout (or the start timeout, if no chunk has ever arrived) is re-armed from that moment.
No call waits for a chunk in flight. The servlet holds the reservation lock for the whole chunk it receives, which on a slow network is many seconds; pause(UploadReservation, int, String, boolean), resume(UploadReservation, int, boolean), cancel(UploadReservation, int, String) and markConsumed(UploadFileEntry, boolean) therefore try the lock, and when it is taken leave their change to the thread that holds it (UploadReservation.defer(Runnable)) and return. A pause by the App is told to the listeners and the client before it returns, and a pause or cancellation makes the servlet give up the chunk it is receiving: that chunk is answered as paused or gone, its bytes are dropped, and the file continues later from the offset the client was last acknowledged.
- Author:
- Christopher Mindus
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic interfaceWhat the registry reports to its owner (the application-session service), on the thread the change happened on; the owner hands it to the app worker.Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringRefusal reason: uploads are disabled.static final StringCancellation reason: the idle timeout expired.static final StringCancellation reason: paused longer than the maximum pause time.static final StringRefusal reason: the quota (session or server) is exhausted.static final StringCancellation reason: the start timeout expired.static final StringRefusal reason: the declared size exceeds the per-file maximum.static final StringRefusal reason: too many files in the reservation.static final StringRefusal reason: the declared type is not permitted.Constructor Summary
ConstructorsConstructorDescriptionFileUploadRegistry(UploadStagingStore store, ITimeouts timeouts, LongSupplier clock, FileUploadRegistry.IEvents events, SecureRandom random) Constructor.Method Summary
Modifier and TypeMethodDescriptionbooleanacceptChunk(UploadReservation r, UploadFileEntry f, long compressedLen, long plainLen, boolean last, String remoteAddress) Records an accepted chunk; the caller holds the reservation lock and has already appended and forced the bytes to the part.voidcancel(UploadReservation r, int fileIndex, String reason) Cancels one file or the whole reservation: bytes deleted immediately, quota released.voidCancels every live reservation (application session or server going away).voidcancelSession(String sessionKey, String reason) Cancels every live reservation of one application session.static booleancheckSecret(UploadFileEntry file, String presented) Checks a presented secret against a file's secret in constant time.voidCloses the reservation when every file is final, and drops it when every file is terminal: the second half ofmarkVerified(UploadFileEntry,String,long,boolean)withclosefalse, for a caller that had something to post in between.Gets a live reservation.voidmarkConsumed(UploadFileEntry f, boolean delete) Marks a verified file consumed: taken by the App (bytes kept, never swept) or deleted by it.voidmarkFailed(UploadFileEntry f, String reason) Marks a file failed and deletes its bytes; the caller holds the lock.voidmarkVerified(UploadFileEntry f, String vtype, long measured) Marks a complete file verified and closes the reservation when every file is final; the caller holds the lock.voidmarkVerified(UploadFileEntry f, String vtype, long measured, boolean close) Marks a complete file verified; the caller holds the lock.Mints a reservation ID: 16 random bytes as 32 lower-case hex characters.Mints a per-file secret: 32 random bytes as base64url without padding.voidpause(UploadReservation r, int fileIndex, String reason, boolean byApp) Pauses one file or every receiving file, by the App or by the client.refused(String sessionKey, List<UploadFileRequest> requests, UploadLimits limits, IFileUploadListener listener, String reason) Builds a reservation refused as a whole for a reason outside the quota rules (e.g.reservationsOf(String sessionKey) Gets the live reservations of one application session.reserve(String sessionKey, String userUUID, List<UploadFileRequest> requests, UploadLimits limits, IFileUploadListener listener, boolean enabled) Mints a reservation with the automatic failure notice on; see the seven-argument form.reserve(String sessionKey, String userUUID, List<UploadFileRequest> requests, UploadLimits limits, IFileUploadListener listener, boolean enabled, boolean notices) Mints a reservation.voidresume(UploadReservation r, int fileIndex, boolean byApp) Resumes one file or every file this party holds paused.static StringSHA-256 of a string, lower-case hex.intsize()Gets the number of live reservations.intsweepIdle(long now) Belt and braces beside the timers: cancels every open reservation whose last activity is older than its idle timeout, or, while it is paused as a whole, whose pause is older than its maximum pause time.longSums the bytes held by every live reservation.longusedSessionBytes(String sessionKey) Sums the bytes held by the live reservations of one application session.
Field Details
REASON_DISABLED
REASON_QUOTA
Refusal reason: the quota (session or server) is exhausted.- See Also:
REASON_TOO_MANY
Refusal reason: too many files in the reservation.- See Also:
REASON_TOO_LARGE
Refusal reason: the declared size exceeds the per-file maximum.- See Also:
REASON_TYPE
REASON_START
REASON_IDLE
REASON_MAX_PAUSE
Cancellation reason: paused longer than the maximum pause time.- See Also:
Constructor Details
FileUploadRegistry
public FileUploadRegistry(UploadStagingStore store, ITimeouts timeouts, LongSupplier clock, FileUploadRegistry.IEvents events, SecureRandom random) Constructor.- Parameters:
store- The staging store.timeouts- The timeouts seam.clock- The clock in milliseconds (e.g.System::currentTimeMillis).events- The owner's callback.random- The randomness source, or null for a newSecureRandom.
Method Details
newReservationID
Mints a reservation ID: 16 random bytes as 32 lower-case hex characters.- Returns:
- The ID.
newSecret
Mints a per-file secret: 32 random bytes as base64url without padding.- Returns:
- The secret.
sha256Hex
checkSecret
Checks a presented secret against a file's secret in constant time.- Parameters:
file- The file.presented- The header value, may be null.- Returns:
- true only if the file has a secret and the presented value equals it.
usedServerBytes
public long usedServerBytes()Sums the bytes held by every live reservation.- Returns:
- The server-wide held bytes.
usedSessionBytes
Sums the bytes held by the live reservations of one application session.- Parameters:
sessionKey- The application session UUID.- Returns:
- The session's held bytes.
reserve
public UploadReservation reserve(String sessionKey, String userUUID, List<UploadFileRequest> requests, UploadLimits limits, IFileUploadListener listener, boolean enabled) throws IOException Mints a reservation with the automatic failure notice on; see the seven-argument form.- Parameters:
sessionKey- The application session UUID (the quota key).userUUID- The user UUID for the sidecar, metadata only, may be empty.requests- The files, at least one.limits- The limits in force.listener- The per-reservation listener, or null.enabled- Whether new reservations are allowed at all.- Returns:
- The reservation, accepted or refused.
- Throws:
IOException- If the staging directory cannot be created.
reserve
public UploadReservation reserve(String sessionKey, String userUUID, List<UploadFileRequest> requests, UploadLimits limits, IFileUploadListener listener, boolean enabled, boolean notices) throws IOException Mints a reservation. Files are accepted in order until one no longer fits the quota; the rest are refused individually. The reservation as a whole is refused when the count exceeds the maximum, in which case no file is accepted and nothing is written to disk.- Parameters:
sessionKey- The application session UUID (the quota key).userUUID- The user UUID for the sidecar, metadata only, may be empty.requests- The files, at least one.limits- The limits in force.listener- The per-reservation listener, or null.enabled- Whether new reservations are allowed at all.notices- Whether the App wants the automatic failure notice for this reservation.- Returns:
- The reservation, accepted or refused.
- Throws:
IOException- If the staging directory cannot be created.
refused
public UploadReservation refused(String sessionKey, List<UploadFileRequest> requests, UploadLimits limits, IFileUploadListener listener, String reason) Builds a reservation refused as a whole for a reason outside the quota rules (e.g. the staging directory could not be created). Nothing is registered, nothing is on disk, no secret exists.- Parameters:
sessionKey- The application session UUID.requests- The files.limits- The limits in force.listener- The per-reservation listener, or null.reason- The refusal reason.- Returns:
- The refused reservation.
get
Gets a live reservation.- Parameters:
rid- The reservation ID.- Returns:
- The reservation, or null.
reservationsOf
Gets the live reservations of one application session.- Parameters:
sessionKey- The application session UUID.- Returns:
- The reservations.
size
public int size()Gets the number of live reservations.- Returns:
- The count.
acceptChunk
public boolean acceptChunk(UploadReservation r, UploadFileEntry f, long compressedLen, long plainLen, boolean last, String remoteAddress) throws IOException Records an accepted chunk; the caller holds the reservation lock and has already appended and forced the bytes to the part. Rewrites the file sidecar, then the reservation'slastActivity, clears the start timeout and re-arms the idle timeout.- Parameters:
r- The reservation.f- The file.compressedLen- The bytes appended to the part.plainLen- The uncompressed bytes the chunk carried.last- Whether this was the final chunk.remoteAddress- The client's remote address, may be null.- Returns:
- true if this chunk came from a different address than the first one AND that has not been reported before (log one WARNING, never block, never again).
- Throws:
IOException- For sidecar write errors.IllegalStateException- If the lock is not held or the file is not receiving.
markVerified
Marks a complete file verified and closes the reservation when every file is final; the caller holds the lock.- Parameters:
f- The file.vtype- The verified type, empty if not sniffable.measured- The measured uncompressed size.- Throws:
IOException- For sidecar write errors.
markVerified
public void markVerified(UploadFileEntry f, String vtype, long measured, boolean close) throws IOException Marks a complete file verified; the caller holds the lock.With
closefalse the reservation is NOT closed here, even when this file was the last to become final: the caller posts the file's completion to its listeners first and then callscloseWhenFinal(UploadReservation), so that a listener hears every file's completion before the reservation's end (IIZI-SRV-UPLOAD-CLOSED-BEFORE-COMPLETED).- Parameters:
f- The file.vtype- The verified type, empty if not sniffable.measured- The measured uncompressed size.close- true to close the reservation when every file is final, false to leave that tocloseWhenFinal(UploadReservation).- Throws:
IOException- For sidecar write errors.
closeWhenFinal
Closes the reservation when every file is final, and drops it when every file is terminal: the second half ofmarkVerified(UploadFileEntry,String,long,boolean)withclosefalse, for a caller that had something to post in between. The caller holds the lock. Harmless when nothing is final or the close has been done.- Parameters:
r- The reservation.
markFailed
Marks a file failed and deletes its bytes; the caller holds the lock.- Parameters:
f- The file.reason- The reason.- Throws:
IOException- For file errors.
markConsumed
Marks a verified file consumed: taken by the App (bytes kept, never swept) or deleted by it.The call does not wait for a chunk in flight: when the lock is taken, the file is recorded as consumed (and deleted, if asked) by the thread that holds it.
- Parameters:
f- The file.delete- Whether to delete the part now.- Throws:
IOException- For file errors.
pause
Pauses one file or every receiving file, by the App or by the client. A file that is not receiving and not already paused is left alone. A file already paused by the other party gets this party's flag as well, silently. Then the reservation's timers follow: paused as a whole means the start and idle timeouts are cancelled and the maximum pause timeout armed.The call does not wait for a chunk in flight. When the lock is taken, the pause is left to the thread that holds it; the App's pause is then told at once (
FileUploadRegistry.IEvents.onFilePaused(UploadReservation, UploadFileEntry, String, boolean), which reaches the listeners and the client), and the servlet gives up the chunk it is receiving and answers it as paused.- Parameters:
r- The reservation.fileIndex- The file index, or -1 for every file.reason- The reason, never null.byApp- true for the App's pause (the servlet then refuses chunks), false for the client's notice (a chunk from it is its resume).
resume
Resumes one file or every file this party holds paused. The file receives again only when the other party does not hold it too; the App's release of a file the client still holds is reported throughFileUploadRegistry.IEvents.onAppReleased(UploadReservation, UploadFileEntry)so the client learns it. Then the reservation's timers follow: receiving again means the idle timeout (or the start timeout, if no chunk has ever arrived) is re-armed from now.The call does not wait for a chunk in flight: when the lock is taken, the resume is left to the thread that holds it, after every change that was left to it before.
- Parameters:
r- The reservation.fileIndex- The file index, or -1 for every file.byApp- true for the App's resume, false for the client's notice or chunk.
cancel
Cancels one file or the whole reservation: bytes deleted immediately, quota released.The call does not wait for a chunk in flight: when the lock is taken, the cancellation is left to the thread that holds it, and the servlet gives up the chunk it is receiving and answers it as gone.
- Parameters:
r- The reservation.fileIndex- The file index, or -1 for all.reason- The reason.
cancelAll
Cancels every live reservation (application session or server going away).- Parameters:
reason- The reason.
cancelSession
sweepIdle
public int sweepIdle(long now) Belt and braces beside the timers: cancels every open reservation whose last activity is older than its idle timeout, or, while it is paused as a whole, whose pause is older than its maximum pause time.- Parameters:
now- The current time.- Returns:
- The number cancelled.