47 lines
2.8 KiB
Markdown
47 lines
2.8 KiB
Markdown
---
|
|
id: plugin-infrastructure/a-plugin-s-own-routes-lose-their-request-body
|
|
title: "A plugin that has a sidecar proxy loses the request body on its OWN routes"
|
|
theme: plugin-infrastructure
|
|
status: done
|
|
created: 2026-08-27
|
|
date_source: stated
|
|
order: 9
|
|
---
|
|
|
|
**`createSidecarProxy` registers the plugin's whole mount prefix with `registerProxiedPrefix`, and
|
|
`bodyParser` then skips every request under it — including routes the plugin serves itself.** Those
|
|
routes get `ctx.get('body') === {}` and every field reads as missing.
|
|
|
|
Found on 2026-08-27 extracting caldav: `POST /api/caldav/provision/ios` answered
|
|
`{"error":"deviceLabel is required"}` for a request that carried one. It had never happened before
|
|
because app passwords lived at `/api/dav` — a second top-level prefix that was not proxied — and one
|
|
prefix per plugin is the rule that ended that arrangement.
|
|
|
|
**The skip itself is correct and must not be removed.** Parsing a body that is only passing through is
|
|
wasted for JSON and *corrupting* for `multipart/form-data`: Hono caches the parsed `FormData` and
|
|
re-serialises it with a NEW boundary while the proxy forwards the ORIGINAL content-type header naming
|
|
the old one, so every upload fails with "Multipart: Unexpected end of form". That is why the mechanism
|
|
exists.
|
|
|
|
**The narrow fix is that the proxy should register only the sub-path it actually serves.** Caldav's
|
|
sidecar answers `_officer/*` and nothing else, so `/api/caldav/_officer` is the honest claim; the rest of
|
|
the prefix is the plugin's own and should be parsed normally. That means `createSidecarProxy` taking the
|
|
proxied sub-path rather than assuming the whole mount.
|
|
|
|
Not done on the night it was found: it is shared machinery every sidecar depends on, and the change was
|
|
diagnosed while a phone was waiting. **The caldav plugin works around it locally** — `jsonBody()` in
|
|
`api/app-passwords.ts` reads `ctx.req.json()` directly, with the reasoning inline.
|
|
|
|
**Who else is affected:** every plugin that mixes its own routes with a sidecar proxy under one prefix,
|
|
which is most of them. They have not hit it because their own routes are GETs, or because their writes go
|
|
to the sidecar. A plugin adding its first non-proxied POST will hit it, and the symptom — a required
|
|
field reported missing on a request that plainly has it — points at the caller rather than at this.
|
|
|
|
---
|
|
|
|
**Done 2026-08-27** (`f00abe81` in the platform, and caldav's own repo). `createSidecarProxy` takes
|
|
`proxiedSubPath`, so a proxy claims only what it forwards — caldav claims `/api/caldav/_officer` and its
|
|
own routes are parsed normally. Omitted still means the whole prefix, so no other plugin changed.
|
|
Caldav's local `jsonBody()` workaround is gone. Verified: a POST to the plugin's own route read its body,
|
|
and the proxied namespace still reaches the sidecar.
|