Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
77 changes: 77 additions & 0 deletions .github/workflows/spec-watch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Monitor IETF Resumable Uploads Spec Updates

on:
schedule:
- cron: "0 0 * * 6" # Run weekly on Saturday at midnight UTC
workflow_dispatch: # Allow manual trigger

jobs:
check-spec-update:
runs-on: ubuntu-latest
permissions:
issues: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Check IETF Datatracker API for Spec Revision
id: spec_check
run: |
# Fetch spec document metadata from IETF Datatracker API
API_RESPONSE=$(curl -s https://datatracker.ietf.org/api/v1/doc/document/draft-ietf-httpbis-resumable-upload/)
LATEST_REV=$(echo "$API_RESPONSE" | jq -r '.rev // empty')

if [ -z "$LATEST_REV" ]; then
echo "Failed to fetch latest revision from IETF Datatracker"
exit 1
fi

echo "Latest IETF Draft Revision: $LATEST_REV"
echo "latest_rev=$LATEST_REV" >> $GITHUB_OUTPUT

# Currently implemented baseline revision in tus-java-server: 11
BASELINE_REV="11"
echo "baseline_rev=$BASELINE_REV" >> $GITHUB_OUTPUT

if [ "$LATEST_REV" != "$BASELINE_REV" ]; then
echo "spec_updated=true" >> $GITHUB_OUTPUT
else
echo "spec_updated=false" >> $GITHUB_OUTPUT
fi

- name: Create GitHub Issue if Spec Updated
if: steps.spec_check.outputs.spec_updated == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
LATEST_REV: ${{ steps.spec_check.outputs.latest_rev }}
BASELINE_REV: ${{ steps.spec_check.outputs.baseline_rev }}
run: |
TITLE="IETF Resumable Uploads Spec Updated: draft-ietf-httpbis-resumable-upload-$LATEST_REV"

# Check if an issue for this revision already exists
EXISTING_ISSUE=$(gh issue list --search "$TITLE" --json number --jq '.[0].number')

if [ -n "$EXISTING_ISSUE" ]; then
echo "An issue (#$EXISTING_ISSUE) for revision $LATEST_REV already exists."
exit 0
fi

BODY=$(cat <<EOF
A new revision of the IETF Resumable Uploads specification has been published: **draft-ietf-httpbis-resumable-upload-$LATEST_REV** (current baseline: draft-$BASELINE_REV).

- **Spec Datatracker**: https://datatracker.ietf.org/doc/draft-ietf-httpbis-resumable-upload/
- **Spec Diff**: https://author-tools.ietf.org/diff?doc_1=draft-ietf-httpbis-resumable-upload-$BASELINE_REV&doc_2=draft-ietf-httpbis-resumable-upload-$LATEST_REV

### Recommended Actions for Developers & Agents:
1. Review the specification diff link above.
2. Update the verbatim spec quotes in the compliance test Javadocs (\`src/test/java/me/desair/tus/server/rufh/*\`).
3. Adjust protocol logic in \`ResumableUploadsForHttpProtocol.java\`, \`HttpHeader.java\`, \`StructuredHeaderUtil.java\`, or \`HttpProblemDetails.java\`.
4. Run test verification and coverage check:
\`\`\`bash
mvn verify -Pcheck-coverage -Djacoco.compare.branch=master
\`\`\`
EOF
)

gh issue create --title "$TITLE" --body "$BODY" --label "documentation,enhancement"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,6 @@ buildNumber.properties

# End of https://www.toptal.com/developers/gitignore/api/java,maven,intellij,visualstudiocode
.vscode/
__pycache__/
.venv/
*.pyc
72 changes: 62 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# 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).

## GitHub CLI (`gh`) Usage
When running `gh` commands in this project via an automated agent environment, ensure you bypass the default `GITHUB_TOKEN` environment variable. The agent environment may have an invalid `GITHUB_TOKEN` set, which `gh` prioritizes over valid keyring credentials, resulting in an `HTTP 401: Bad credentials` error.

Expand Down Expand Up @@ -30,36 +33,85 @@ When performing a release, please strictly follow the instructions outlined in t
- **Java Version**: The project is configured for **Java 17** (or newer) to align with Spring Boot 3.x requirements.
- **Jakarta EE / Servlets**: Always use `jakarta.servlet.*` package imports instead of the legacy `javax.servlet.*` packages.

### 2. Serializable UploadInfo & Backward Compatibility
### 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.

### 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"`).
- Pass dependencies such as `UploadLockingService` and `ProtocolVersion` explicitly as typed method parameters through `TusExtension` and `RequestHandler` interface methods. When expanding interfaces, always use Java `default` methods to preserve backward compatibility.

### 4. Serializable UploadInfo & Backward Compatibility
- The `UploadInfo` class is stored on disk serialized. If you modify fields in `UploadInfo`, you **must** preserve the `serialVersionUID = -8751200491586638308L` to ensure pre-existing uploads on disk do not trigger `InvalidClassException` upon deserialization.
- Backward compatibility is paramount for this project. Breaking changes should only be done if all other options lead to ugly code and design. Breaking changes require a new major version.
- When expanding interfaces like `UploadLockingService` or `UploadStorageService`, always use Java `default` methods to avoid breaking custom third-party implementations.
- **Release Scope for Backward Compatibility**: Only maintain backward compatibility for classes, methods, or public API signatures that are present in the latest official Git release tag. Signatures, classes, or helper methods introduced in unreleased commits or feature branches do not require backward compatibility and should be refactored or deleted directly.

### 5. Lock Contention Resolution & InterruptibleInputStream
- Request handlers that stream payload bytes to storage (`CorePatchRequestHandler`, `RufhCreationPostRequestHandler`, `RufhAppendPatchRequestHandler`) MUST wrap body input streams in `InterruptibleInputStream` and register them via `lockingService.registerInputStream(...)`. This ensures concurrent `HEAD` and `DELETE` requests can interrupt ongoing byte streams cleanly and resolve lock contention.

### 3. File Deduplication and Read/Write Safety
### 6. Problem Details Value Objects & Structured JSON Serialization
- Model RFC 7807 problem details as immutable domain value objects (`HttpProblemDetails`).
- Do NOT construct JSON strings using manual string concatenation or `StringBuilder` quote-stitching. Model JSON objects using structured maps (`Map<String, Object>`) or value objects and format them safely with proper JSON string escaping (handling quotes, backslashes, and control characters).

### 7. File Deduplication and Read/Write Safety
The deduplication mechanism links duplicate uploads (child) to the original upload (parent) using the `duplicatesUploadId` field in `UploadInfo`.
- **Read Operations**: Methods that read data (e.g., `getUploadedBytes`, `copyUploadTo` in `DiskStorageService`) should dynamically resolve `duplicatesUploadId` to the parent upload ID if it is set.
- **Write/Modify Operations**: Methods that write or truncate data (e.g., `append`, `removeLastNumberOfBytes` in `DiskStorageService`) **must not** resolve `duplicatesUploadId` recursively. They must only operate on the target upload's own physical files to guarantee parent files are never modified or truncated when handling child upload errors.

### 4. Checksum Index Storage & Self-Cleaning
### 8. Checksum Index Storage & Self-Cleaning
Completed parent uploads are indexed by checksum under the `<storagePath>/checksums/<algorithm>/<checksum_value>` file path containing the target `UploadId`.
- Index lookup includes a self-cleaning check: if the index points to an upload that is null or whose data file is missing (e.g., due to expiration), the index file is deleted on the fly, keeping the file system clean without needing a separate index sweeper.
- Child uploads (duplicates) are never indexed.
- On parent termination, the parent's index entry is explicitly deleted.

### 5. No Thread-Local Contexts
### 9. No Thread-Local Contexts
- Do not use `ThreadLocal` variables or thread-local request context to pass state between components. Always pass parameters explicitly or use request wrapping.

### 6. Unit Test Coverage
### 10. Unit Test Coverage & Verbatim Spec Quotes
- Unit test coverage must remain high. All new code (including background watchdog threads, helper methods, stream wrappers, and retry logic) must be thoroughly unit tested.
- Do not use reflection to test private helper methods. Always test code through public API boundaries instead of bypassing encapsulation.
- Compliance unit tests in `me.desair.tus.server.rufh` MUST contain verbatim specification quotes in method Javadocs based on the official specification.
- After finalizing any implementation, you MUST check the unit test coverage on new/modified lines by running:
```bash
mvn verify -Pcheck-coverage -Djacoco.compare.branch=master
```
If any added or modified lines are reported as uncovered (❌) or partially covered (⚠️), you must add extra unit tests to cover them before submitting.

### 7. No Code Duplication & Reusability
- Avoid code duplication wherever possible. Consolidate repetitive logic (such as path generation, header parsing, and lock acquisition/release) into reusable helper functions or utility classes. Keep file path resolutions consolidated.
### 11. Mandatory Javadocs & Code Formatting
- Always write thorough Javadoc comments for all new and modified public/protected classes, interfaces, and methods.
- Always remove unused imports across all modified and newly created Java source files.
- Run code formatting before committing:
```bash
mvn -P codestyle com.spotify.fmt:fmt-maven-plugin:format
```

### 8. Documentation & Comments
- All new classes, interfaces, and non-obvious code blocks (e.g., watchdog lifecycle, stream wrapping logic, thread-safety mechanisms) must have detailed comments describing their purpose, behavior, and design decisions.
### 12. String Comparisons & Avoiding Deprecated StringUtils
- Do not use deprecated `StringUtils` comparison methods such as `StringUtils.equals(...)` or `StringUtils.equalsIgnoreCase(...)`.
- Always use `org.apache.commons.lang3.Strings.CS` for case-sensitive operations (e.g., `Strings.CS.equals(...)`, `Strings.CS.startsWith(...)`) and `org.apache.commons.lang3.Strings.CI` for case-insensitive operations (e.g., `Strings.CI.equals(...)`, `Strings.CI.startsWith(...)`).

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

### 1. Spec Diff Review
When a new draft revision of the IETF Resumable Uploads specification (`draft-ietf-httpbis-resumable-upload`: https://datatracker.ietf.org/doc/draft-ietf-httpbis-resumable-upload/) is published:
- Compare the new draft against the current baseline (draft-11) using the official IETF Author Tools diff:
`https://author-tools.ietf.org/diff?doc_1=draft-ietf-httpbis-resumable-upload-11&doc_2=draft-ietf-httpbis-resumable-upload-<NEW_REV>`
- Identify any changed header names, structured field syntax changes, response status codes, or problem details schemas.

### 2. Spec-Driven Compliance Test Maintenance
Compliance unit tests located in `src/test/java/me/desair/tus/server/rufh/` (`RufhProtocolCreationTest`, `RufhProtocolAppendTest`, `RufhProtocolHeadTest`, `RufhProtocolCancellationTest`, `HttpProblemDetailsTest`) contain verbatim quotes from the specification in their method Javadocs.
- **Workflow**:
1. Update the verbatim spec quotes in test method Javadocs to reflect the new draft revision text.
2. Update test assertions and expected header/status formats.
3. Run `mvn test` to pinpoint which server components need code updates.

### 3. Protocol Update Skill & Execution Procedure
When updating the IETF protocol implementation for a new draft revision, follow this step-by-step procedure:
1. **Branching**: Ensure you are on a feature branch (e.g. `feature/ietf-spec-draft-<REV>`).
2. **Protocol Headers**: Update header definitions in `HttpHeader.java` if structured field keys or parameter names changed.
3. **Structured Header Utility**: Update `StructuredHeaderUtil.java` if RFC 9651 structured field parsing rules or data types changed.
4. **Problem Details**: Update `HttpProblemDetails.java` if RFC 7807 problem json type URIs or field keys changed.
5. **Protocol Logic**: Update `ResumableUploadsForHttpProtocol.java` validation and processing logic.
6. **Coverage Verification**: Verify code coverage and unit tests pass:
```bash
mvn verify -Pcheck-coverage -Djacoco.compare.branch=master
```
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

All notable changes to this project will be documented in this file.

## [2.0.0]

### Added
- **IETF Resumable Uploads for HTTP (RUFH) Protocol Compliance**: Implemented full support for the official IETF Resumable Uploads for HTTP specification (`draft-ietf-httpbis-resumable-upload`).
- **Dual Protocol Auto-Detection**: Added transparent protocol routing in `TusFileUploadService` supporting both legacy `TUS_1_0_0` (`Tus-Resumable: 1.0.0`) and `RUFH` (`ProtocolVersion.RUFH`) clients concurrently on the same endpoint.
- **RFC 9651 Structured Header Fields**: Implemented RFC 9651 parsing and serialization for `Upload-Offset`, `Upload-Complete`, `Upload-Length`, and `Upload-Limit` dictionary headers.
- **RFC 7807 Problem Details JSON**: Added support for standard `application/problem+json` error responses (`mismatching-upload-offset`, `completed-upload`, `inconsistent-upload-length`).
- **Dedicated Compliance & Security Test Suites**: Added comprehensive, spec-quoted unit tests under package `me.desair.tus.server.ietf` and security tests under `me.desair.tus.server.ietf.security` verifying Path Traversal protection, DoS limits, CRLF sanitization, and lock safety.
- **User Migration Documentation**: Added `docs/MIGRATION.md` detailing steps for transitioning from Tus 1.0.0 to IETF RUFH.

## [1.0.0-3.3]

### Added
Expand Down
Loading
Loading