mirror of
https://github.com/LLukas22/Jellyswarrm.git
synced 2026-06-14 18:49:40 -04:00
125 lines
5.5 KiB
Markdown
125 lines
5.5 KiB
Markdown
# Request and Response Processing
|
|
|
|
Jellyswarrm is a Jellyfin-aware reverse proxy. Clients see Jellyswarrm user IDs, media IDs, server IDs, and API keys. Upstream Jellyfin servers receive their original IDs and credentials.
|
|
|
|
The code keeps three concerns separate:
|
|
|
|
- Request preprocessing chooses the upstream server/session and rewrites the outgoing URL/auth.
|
|
- Request and response JSON processing rewrites IDs inside bodies.
|
|
- URL processing rewrites IDs and credentials in request URLs and embedded media delivery URLs.
|
|
|
|
Handlers should use the `extractors` module, `ProxyProcessors`, and `AppState::process_response_json` instead of calling low-level processors directly.
|
|
|
|
## Request Flow
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A[Incoming Axum request] --> B{Route handler type}
|
|
|
|
B -->|Routed handler| C[Preprocessed / RequireUser / RequireSession / RequireUserSession]
|
|
B -->|Catch-all proxy_handler| D[Static asset check]
|
|
D -->|Asset found| E[Return asset]
|
|
D -->|No asset| F[preprocess_request]
|
|
C --> F
|
|
|
|
F --> G[axum_to_reqwest]
|
|
G --> H[extract_request_infos]
|
|
H --> I[Parse auth headers/query]
|
|
H --> J[Resolve user and device]
|
|
H --> K{JSON request body?}
|
|
|
|
K -->|yes| L[RequestAnalyzer]
|
|
L --> M[Find media/user/session hints]
|
|
K -->|no| N[No body hints]
|
|
M --> O[Load user sessions]
|
|
N --> O
|
|
J --> O
|
|
|
|
O --> P[resolve_server]
|
|
P --> Q[UrlProcessor.server_from_client_url]
|
|
Q --> R[Pick upstream server/session]
|
|
R --> S[remap_authorization]
|
|
S --> T[apply_to_request]
|
|
T --> U[Remove hop-by-hop headers]
|
|
T --> V[Set Host/Auth headers]
|
|
T --> W[UrlProcessor.client_to_server_url]
|
|
W --> X[Virtual IDs/API key -> upstream IDs/token in path/query]
|
|
|
|
X --> Y{Forwarded body is JSON?}
|
|
Y -->|yes| Z[ProxyProcessors.process_request_body]
|
|
Z --> AA[RequestProcessor rewrites JSON virtual IDs -> upstream IDs]
|
|
AA --> AB[set_json_body if modified]
|
|
Y -->|no| AC[Forward request]
|
|
AB --> AC
|
|
```
|
|
|
|
## Response Flow
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A[Upstream response] --> B{Handler path}
|
|
|
|
B -->|Item/media JSON handlers| C[Deserialize as serde_json::Value]
|
|
B -->|Fallback JSON response| D[Deserialize as serde_json::Value]
|
|
B -->|Typed special handlers| E[Deserialize typed model]
|
|
B -->|Non-JSON response| F[Pass through]
|
|
|
|
E --> G[Run typed behavior]
|
|
G --> H[Example: playback session tracking]
|
|
G --> I[Serialize typed response to JSON if rewriting is needed]
|
|
|
|
C --> J[process_response_json profile: Media]
|
|
D --> K[process_response_json profile: BestEffortMedia]
|
|
I --> J
|
|
|
|
J --> L[ResponseProcessor]
|
|
K --> L
|
|
L --> M[Rewrite upstream media IDs -> virtual IDs]
|
|
L --> N[Rewrite ServerId -> proxy server id]
|
|
L --> O[Disable CanDelete/CanDownload]
|
|
L --> P[Optionally suffix names with server name]
|
|
L --> Q[DeliveryUrl/StreamUrl/TranscodingUrl]
|
|
Q --> R[UrlProcessor.server_to_client_delivery_url]
|
|
R --> S[Rewrite embedded path/query IDs and api_key]
|
|
|
|
S --> T[Processed JSON]
|
|
M --> T
|
|
N --> T
|
|
O --> T
|
|
P --> T
|
|
T --> U{Typed handler?}
|
|
U -->|yes| V[Deserialize JSON back to typed model]
|
|
U -->|no| W[Serialize JSON body]
|
|
V --> X[Return response]
|
|
W --> Y[Update Content-Length]
|
|
Y --> X
|
|
F --> X
|
|
```
|
|
|
|
## Response Profiles
|
|
|
|
- `Media`: full media response rewriting for explicitly routed media/item/playback/federated handlers.
|
|
- `BestEffortMedia`: full media-like rewriting for catch-all JSON responses. This keeps less common Jellyfin endpoints working even when they are not explicitly routed.
|
|
- `Disabled`: no response rewriting.
|
|
|
|
`Media` and `BestEffortMedia` currently rewrite the same fields. The main difference is how they are selected: explicit handlers use `Media`; the generic fallback uses `BestEffortMedia`. Name suffixing is still controlled separately by `should_change_name` and config.
|
|
|
|
## Component Boundaries
|
|
|
|
- `extractors.rs`: Axum extractors that run preprocessing and optionally require a resolved user, session, or both.
|
|
- `request_preprocessing.rs`: request identity extraction, session lookup, server selection, auth remapping, and outgoing URL/header rewriting.
|
|
- `processors/url_processor.rs`: all path/query URL rewriting for client-to-server request URLs, server-to-client delivery URLs, and request server detection.
|
|
- `processors/request_analyzer.rs`: scans incoming JSON bodies for media IDs, user IDs, and session IDs that help pick the upstream server/session.
|
|
- `processors/request_processor.rs`: rewrites JSON request body IDs from Jellyswarrm virtual IDs to upstream Jellyfin IDs.
|
|
- `processors/response_processor.rs`: rewrites JSON response fields from upstream Jellyfin IDs to Jellyswarrm virtual IDs and delegates embedded URL rewriting to `UrlProcessor`.
|
|
- `processors/json_processor.rs`: generic recursive JSON walker used by analyzers and processors.
|
|
- `processors/field_matcher.rs`: centralized field-name groups for JSON rewrite rules.
|
|
- `ProxyProcessors`: facade that constructs and coordinates request, response, analyzer, and URL processors.
|
|
|
|
## Design Rules
|
|
|
|
- Prefer `serde_json::Value` for pass-through media/item responses so unknown Jellyfin schema changes are preserved.
|
|
- Keep typed models where the proxy performs behavior beyond simple transformation, such as playback session tracking and federated item interleaving.
|
|
- Keep URL rewriting centralized in `UrlProcessor`; request URL rules and embedded delivery URL rules should not drift.
|
|
- Keep handler signatures expressive: use `Preprocessed`, `RequireUser`, `RequireSession`, or `RequireUserSession` instead of manually calling `preprocess_request` in routed handlers.
|