Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
877a7e4
feat: async chunk upload, dynamic part sizing for multi-TB uploads, a…
tomdesair Sep 28, 2026
c2e7020
fix(rufh): do not invoke append on creation requests without content
tomdesair Sep 28, 2026
fe63589
perf(s3): optimize S3 lock acquisition and multi-object release
tomdesair Sep 28, 2026
89da876
feat(s3): configurable locking jitter, HTTP 501 non-CAS fallback, and…
tomdesair Sep 29, 2026
a4ec6b8
feat(s3): add native S3 multipart copy helper and connection paramete…
tomdesair Oct 3, 2026
762f39c
feat: unify lock wait timeout and cloud chunk drain timeout
tomdesair Oct 3, 2026
7b843fd
feat(s3): decouple S3 storage, locking, and concatenation services fr…
tomdesair Oct 4, 2026
b69948b
Merge branch 'master' into feature/async-chunk-upload
tomdesair Oct 4, 2026
6a9fae7
Merge branch 'master' into feature/async-chunk-upload
tomdesair Oct 4, 2026
e17625d
fix(cloud): improve pause/resume byte integrity, lock contention reso…
tomdesair Oct 10, 2026
80e1988
test: increase test coverage for S3 and Azure storage services
tomdesair Oct 10, 2026
506b474
feat(lock): automatically clean up locks on upload completion, termin…
tomdesair Oct 10, 2026
b4498b4
docs: add architecture guide and overhaul project documentation
tomdesair Oct 11, 2026
a34a899
ci: fix include_fragments string enum in lychee.toml
tomdesair Oct 11, 2026
4e49a92
docs: fix broken section anchor links in DEPLOYMENT.md
tomdesair Oct 11, 2026
fae60b7
ci: add lychee markdown link check pre-commit hook
tomdesair Oct 11, 2026
4b0d73b
fix(s3): remove unused storeIncompletePartToS3 and cover mid-stream m…
tomdesair Oct 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/docs-link-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Checks that links and anchors in the Markdown documentation are not broken.
# Pull requests only verify local files/anchors (offline, fast and never flaky);
# the weekly run also checks external URLs.

name: Documentation Link Check

permissions:
contents: read

on:
pull_request:
paths: ["**.md", ".github/workflows/docs-link-check.yml", "lychee.toml"]
schedule:
- cron: "0 6 * * 1"
workflow_dispatch:

jobs:
link-check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Check Markdown links
uses: lycheeverse/lychee-action@v2
with:
# Offline (local links and #anchors only) on pull requests, full check otherwise
args: >-
--config lychee.toml
${{ github.event_name == 'pull_request' && '--offline' || '' }}
README.md CONTRIBUTING.md CHANGELOG.md 'docs/**/*.md'
fail: true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,4 @@ SFTP_STORAGE_ANALYSIS.md
NFS_LOCKING.md
RELEASE-REVIEW.md
ASYNC_REVIEW.md
ASYNC_REVIEW_2.md
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,10 @@ repos:
language: system
files: \.java$
pass_filenames: false
- repo: https://github.com/lycheeverse/lychee
rev: lychee-v0.24.2
hooks:
- id: lychee
name: Lychee Markdown Link Check
args: ["--config", "lychee.toml", "--offline", "--no-progress"]
types: [markdown]
19 changes: 15 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Agent Instructions

## Project Context
When working on this project, always read the [`README.md`](README.md) file to obtain full context on project architecture, features, configuration options, and dual protocol version support (Tus 1.0.0 & IETF RUFH).
When working on this project, always read the [`README.md`](README.md) and [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) files to obtain full context on project architecture, extension pipelines, features, configuration options, and dual protocol version support (Tus 1.0.0 & IETF RUFH).

## GitHub CLI (`gh`) & Git Usage
- Always run `git` commands (e.g., `git status`, `git diff`, `git add`, `git commit`, `git push`) and `gh` commands unsandboxed (setting `BypassSandbox: true` when calling `run_command`) to ensure git hooks, local tools, and remote repository authentication work without sandbox errors.
Expand All @@ -23,7 +23,7 @@ Any new feature, bugfix, or improvement must be developed in a separate branch t
When a new feature is introduced, the `README.md` file must be updated with information on this new feature (e.g. configuration, usage).

### CHANGELOG.md
For any new feature, big improvements, or fixes, the `CHANGELOG.md` file must be updated to describe the changes added in this version. Use a release version header (e.g., `## [1.0.0-3.2]`) instead of `## [Unreleased]`. Derive this next release version from the SNAPSHOT version declared in the `pom.xml` file by removing the `-SNAPSHOT` suffix. Make sure to not add duplicate headers.
For any new feature, big improvements, or fixes, the `CHANGELOG.md` file must be updated to describe the changes added in this version. Use a release version header (e.g., `## [2.0.0]`) instead of `## [Unreleased]`. Derive this next release version from the SNAPSHOT version declared in the `pom.xml` file by removing the `-SNAPSHOT` suffix. Make sure to not add duplicate headers.

### Release Process
When performing a release, please strictly follow the instructions outlined in the [docs/RELEASE.md](docs/RELEASE.md) documentation file.
Expand All @@ -35,8 +35,9 @@ When performing a release, please strictly follow the instructions outlined in t
- **Jakarta EE / Servlets**: Always use `jakarta.servlet.*` package imports instead of the legacy `javax.servlet.*` packages.

### 2. Extension Architecture & Protocol Applicability
- Every protocol extension MUST extend `AbstractTusExtension` and declare its applicability via `isApplicable(HttpMethod, ProtocolVersion)`.
- `TusFileUploadService` MUST NOT contain protocol-specific conditionals, version branching, or hardcoded error handling; all protocol-specific validation and execution logic belongs inside dedicated `RequestValidator` and `RequestHandler` implementations.
- Read the definitive architectural design in [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) before implementing new protocol logic.
- `TusFileUploadService` is the single facade service class orchestrating protocols composed of modular extensions. It MUST NOT contain protocol-specific conditionals, version branching, or hardcoded error handling.
- Every protocol extension MUST extend `AbstractTusExtension` and declare its applicability via `isApplicable(HttpMethod, ProtocolVersion)`. All protocol-specific validation and execution logic belongs inside dedicated `RequestValidator` and `RequestHandler` implementations.

### 3. Explicit Parameter Passing & No Request Attributes
- Do NOT use magic string servlet request attributes (such as `"me.desair.tus.uploadLockingService"` or `"me.desair.tus.protocolVersion"`).
Expand Down Expand Up @@ -111,6 +112,7 @@ Whenever a new setter or configuration property (such as `setMinAppendSize`, `se
- A corresponding `with...` builder method (e.g. `withMinAppendSize`, `withMinSize`) MUST be added to `TusFileUploadService` with thorough Javadoc comments.
- `TusFileUploadService.withUploadStorageService(...)` MUST be updated to copy the setting from the old `UploadStorageService` instance to the new one.
- `ThreadLocalCachedStorageAndLockingService` MUST delegate the setter and getter methods to `storageServiceDelegate`.
- **ThreadLocalCache Wrapper Safety**: When `.withThreadLocalCache(true)` is enabled, `getUploadStorageService()` returns `ThreadLocalCachedStorageAndLockingService` (a wrapper), not concrete classes like `S3StorageService` or `AzureBlobStorageService`. Never cast `getUploadStorageService()` directly in downstream code; always inject or retain the concrete storage service bean directly.

### 15. Typed Exceptions & HttpServletResponse Status Codes
- Do NOT throw generic `TusException` directly when throwing protocol errors or request validation failures.
Expand Down Expand Up @@ -147,6 +149,7 @@ To maintain a clear separation between fast, offline unit tests and containerize
- Focus strictly on business processes, end-to-end user flows, protocol interactions, and backend service capability flows (such as `ITAzureBlobStorageService`, `ITAzureBlobLockingService`, `ITAzureBlobConcatenationService`, `ITAzureBlobRufhProtocol`, `ITAzureBlobTusFileUploadService`).
- StorageService, LockingService, and ConcatenationService integration tests are maintained because they represent key capability flows that can be combined across different backend types (e.g. Azure storage combined with S3 locking).
- Internal helper objects or component handles that do NOT represent an independent business process (such as `UploadLock` handles) MUST NOT have dedicated `IT*` integration test classes (e.g. `ITAzureBlobUploadLock` is omitted in favor of testing `AzureBlobUploadLockTest` offline and testing lock lifecycles end-to-end via `ITAzureBlobLockingService` / `ITAzureBlobTusFileUploadService`).
- **Automated Container Lifecycle (Podman & Docker)**: Integration tests automatically manage container lifecycles via Testcontainers using either **Podman** or **Docker**. Never invoke `docker` CLI directly or instruct developers to manually start containers with `docker run` or `podman run`.
- **Naming & Execution Rules**:
- Unit test classes MUST end with `Test.java` (executed during `mvn test`).
- Integration test classes MUST start with `IT` and MUST NOT end with `Test` or `Test.java` (executed during `mvn verify`).
Expand Down Expand Up @@ -180,6 +183,13 @@ To maintain a clear separation between fast, offline unit tests and containerize
- Do NOT use legacy `java.io.File.createTempFile(...)`. On POSIX systems, it creates files with overly permissive default umask permissions (often world-readable or group-readable, CWE-378 / SonarQube java:S5443).
- Always use `java.nio.file.Files.createTempFile(...)` (or `Utils.createTempSiblingPath(...)` / `Utils.createTempSibling(...)`), which creates temporary files with restricted owner-only permissions (`rw-------` / `0600`) by default.

### 23. Thread Safety: Prevent Volatile Class Variables & Use ReentrantReadWriteLock
- Do NOT use `volatile` class variables for mutable state, lazy compilation, or feature flags. `volatile` references on complex objects or mutable fields can trigger static analysis warnings (e.g. SonarQube java:S3077) and cannot guard multi-step compound actions, non-atomic transitions, or lazy initializations.
- Guard concurrent mutable state using a `ReadWriteLock` (`ReentrantReadWriteLock`).
- Consistently name the lock field `private final ReadWriteLock thisObjectLock = new ReentrantReadWriteLock();` in every class for pattern consistency across the codebase.
- Use `thisObjectLock.readLock()` for fast, concurrent reads, and `thisObjectLock.writeLock()` for state mutations, lazy pattern compilation, or feature capability degradation.
- Always acquire and release locks using `try ... finally` blocks (e.g. `thisObjectLock.readLock().lock(); try { return field; } finally { thisObjectLock.readLock().unlock(); }`).


## IETF Resumable Uploads for HTTP (RUFH) Spec Maintenance & Update Playbook

Expand Down Expand Up @@ -211,6 +221,7 @@ When updating the IETF protocol implementation for a new draft revision, follow
### 4. Conformity Test Suite Maintenance & Subagent Isolation
Whenever a new draft revision of the RUFH specification is published, the repository's Python conformity test suite (`scripts/rufh_conformity_test.py`) MUST be reviewed and updated by a separate, dedicated subagent.
- **Strict Isolation Rule**: The subagent tasked with updating `scripts/rufh_conformity_test.py` MUST ONLY consult the official IETF specification document (and RFC 9530) and MUST NOT inspect the Java server implementation code under `src/main/java/`. This ensures the conformity test suite remains an independent, unbiased specification benchmark.
- **Default Multi-Backend Execution**: When executing the conformity test suite (`scripts/rufh_conformity_test.py`), tests MUST be run by default against all three supported storage backend types (Disk: `/test/api/upload`, S3: `/test-s3/api/upload`, and Azure Blob: `/test-azure/api/upload`) as documented in [`docs/CONFORMITY_TESTING.md`](docs/CONFORMITY_TESTING.md).

### 5. Conformity Test Suite Audit — Repeatable Procedure
Use this procedure to audit `scripts/rufh_conformity_test.py` against the current (or a new) specification revision. The goal is to identify untested MUST/SHOULD/MAY requirements and produce an actionable improvement report.
Expand Down
Loading
Loading