klaemo opened a new issue, #375:
URL: https://github.com/apache/couchdb-nano/issues/375
## Expected Behavior
If the connection breaks off after CouchDB (or a proxy in front of it) has
sent the response headers but before the body is complete, the request promise
should reject. nano 10 (axios) did this.
## Current Behavior
In nano 11 the result depends on the response's content type:
- **`application/json`** (e.g. `db.get`, `db.list`, `db.view`): the request
**resolves with `''`**. `responseHandler` in `lib/nano.js` wraps `await
response.json()` in `try { … } catch { // do nothing }`. That catch was meant
for bodies that aren't valid JSON, but it also catches undici's `TypeError:
terminated` when the body read fails. So `db.get()` of an existing document
resolves `""`. Callers see something like a missing document, crash later with
a `TypeError`, or, worst case, write `{ ...doc }` with `doc === ''` back as a
new document.
- **`text/*`, `multipart/related` and binary bodies** (e.g.
`db.attachment.get`): `response.text()` / `response.arrayBuffer()` throws
inside the async `responseHandler`. `relax()` doesn't await or return it
(`.then((response) => { responseHandler(…) })`), so the request promise **never
settles**. The rejection is unhandled, which crashes the process under Node's
default `--unhandled-rejections=throw`; if the app has an `unhandledRejection`
handler, the call just hangs.
Error responses (non-2xx) aren't affected: if reading the error body fails,
the request still rejects with the status code. Streaming requests
(`*AsStream`) take a separate code path.
## Possible Solution
1. In the 2xx JSON branch, read the body with `response.text()` and only
wrap `JSON.parse()` in the try/catch. A failed read then propagates, and a
complete body that isn't JSON still resolves `''` as it does today.
2. In `relax()`, return the `responseHandler(…)` promise from the `.then()`
callback so that the existing `.catch()` handles failures. A body-read failure
then rejects the same way a connection failure before the headers does: `error
happened in your connection. Reason: SocketError: other side closed`.
I have a patch with tests ready and will open a PR referencing this issue.
## Steps to Reproduce (for bugs)
1. Save this as `repro.js` next to an install of `[email protected]`:
```js
const http = require('node:http')
const Nano = require('nano')
const contentType = process.argv[2] || 'application/json'
// answer 200, promise 100 bytes, send part of a document, then drop the
connection
const server = http.createServer((req, res) => {
res.writeHead(200, { 'content-type': contentType, 'content-length': 100
})
res.write('{"_id":"meta","type":"ev')
setTimeout(() => res.socket.destroy(), 10)
})
server.listen(0, '127.0.0.1', async () => {
const db = Nano(`http://127.0.0.1:${server.address().port}`).use('db')
try {
console.log('resolved:', JSON.stringify(await db.get('meta')))
} catch (e) {
console.log('rejected:', e.message)
}
server.closeAllConnections()
server.close()
})
```
2. `node repro.js` prints `resolved: ""`.
3. `node repro.js text/plain` crashes with an unhandled `TypeError:
terminated` (`[cause]: SocketError: other side closed`). `db.get()` never
settles.
4. Expected in both cases: `rejected: …`.
## Context
I found this while upgrading an app from nano 10 to 11. On a dropped
connection, a `db.get()` of an existing document returned `""` instead of
throwing, and the app treated the document as missing. We're carrying a local
patch for now.
## Your Environment
* Version used: nano 11.0.7 (also current `main`, 53dea92)
* Node.js: 24.21.0 (also reproduced on 20.20.2)
* Operating System and version: Ubuntu 24.04 (Linux 6.8)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]