Skip to content

Commit

Permalink
fix: add missing error classes to types (#3316)
Browse files Browse the repository at this point in the history
* add missing error classes to types

* test new types

* test error includes data with count when etag mismatch

* test content-range mismatch error

* remove test debugging
  • Loading branch information
maxbeatty authored Jun 12, 2024
1 parent 78a0c24 commit f9997bb
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 5 deletions.
4 changes: 2 additions & 2 deletions lib/handler/retry-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ class RetryHandler {
this.abort(
new RequestRetryError('Content-Range mismatch', statusCode, {
headers,
count: this.retryCount
data: { count: this.retryCount }
})
)
return false
Expand All @@ -213,7 +213,7 @@ class RetryHandler {
this.abort(
new RequestRetryError('ETag mismatch', statusCode, {
headers,
count: this.retryCount
data: { count: this.retryCount }
})
)
return false
Expand Down
111 changes: 108 additions & 3 deletions test/retry-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ test('Should handle 206 partial content', async t => {
onBodySent () {
t.ok(true, 'pass')
},
onHeaders (status, _rawHeaders, resume, _statusMessage) {
onHeaders (status, _rawHeaders, _resume, _statusMessage) {
t.strictEqual(status, 200)
return true
},
Expand Down Expand Up @@ -636,7 +636,7 @@ test('Should handle 206 partial content', async t => {
})

test('Should handle 206 partial content - bad-etag', async t => {
t = tspl(t, { plan: 6 })
t = tspl(t, { plan: 8 })

const chunks = []

Expand Down Expand Up @@ -683,7 +683,7 @@ test('Should handle 206 partial content - bad-etag', async t => {
onBodySent () {
t.ok(true, 'pass')
},
onHeaders (status, _rawHeaders, resume, _statusMessage) {
onHeaders (_status, _rawHeaders, _resume, _statusMessage) {
t.ok(true, 'pass')
return true
},
Expand All @@ -697,6 +697,8 @@ test('Should handle 206 partial content - bad-etag', async t => {
onError (err) {
t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'abc')
t.strictEqual(err.code, 'UND_ERR_REQ_RETRY')
t.strictEqual(err.message, 'ETag mismatch')
t.deepEqual(err.data, { count: 2 })
}
}
}
Expand Down Expand Up @@ -1083,6 +1085,7 @@ test('Should be able to properly pass the minTimeout to the RetryContext when co

await t.completed
})

test('Issue#2986 - Handle custom 206', async t => {
t = tspl(t, { plan: 8 })

Expand Down Expand Up @@ -1504,3 +1507,105 @@ test('Weak etags are ignored on range-requests', async t => {

await t.completed
})

test('Should throw RequestRetryError when Content-Range mismatch', async t => {
t = tspl(t, { plan: 10 })

const chunks = []

// Took from: https://github.com/nxtedition/nxt-lib/blob/4b001ebc2f22cf735a398f35ff800dd553fe5933/test/undici/retry.js#L47
let x = 0
const server = createServer((req, res) => {
if (x === 0) {
t.ok(true, 'pass')
res.setHeader('etag', 'asd')
res.write('abc')
setTimeout(() => {
res.destroy()
}, 1e2)
} else if (x === 1) {
t.deepStrictEqual(req.headers.range, 'bytes=3-')
res.setHeader('content-range', 'bytes bad') // intentionally bad to trigger error
res.setHeader('etag', 'asd')
res.statusCode = 206
res.end('def')
}
x++
})

const dispatchOptions = {
retryOptions: {
retry: function (err, _, done) {
if (err.code && err.code === 'UND_ERR_DESTROYED') {
return done(false)
}

if (err.statusCode === 206) return done(err)

setTimeout(done, 800)
}
},
method: 'GET',
path: '/',
headers: {
'content-type': 'application/json'
}
}

server.listen(0, () => {
const client = new Client(`http://localhost:${server.address().port}`)
const handler = new RetryHandler(dispatchOptions, {
dispatch: (...args) => {
return client.dispatch(...args)
},
handler: {
onRequestSent () {
t.ok(true, 'pass')
},
onConnect () {
t.ok(true, 'pass')
},
onBodySent () {
t.ok(true, 'pass')
},
onHeaders (status, _rawHeaders, _resume, _statusMessage) {
t.strictEqual(status, 200)
return true
},
onData (chunk) {
chunks.push(chunk)
return true
},
onComplete () {
t.ifError('should not complete')
},
onError (err) {
t.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'abc')
t.strictEqual(err.code, 'UND_ERR_REQ_RETRY')
t.strictEqual(err.message, 'Content-Range mismatch')
t.deepEqual(err.data, { count: 2 })
}
}
})

client.dispatch(
{
method: 'GET',
path: '/',
headers: {
'content-type': 'application/json'
}
},
handler
)

after(async () => {
await client.close()

server.close()
await once(server, 'close')
})
})

await t.completed
})
10 changes: 10 additions & 0 deletions test/types/errors.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ expectAssignable<errors.ResponseExceededMaxSizeError>(new errors.ResponseExceede
expectAssignable<'ResponseExceededMaxSizeError'>(new errors.ResponseExceededMaxSizeError().name)
expectAssignable<'UND_ERR_RES_EXCEEDED_MAX_SIZE'>(new errors.ResponseExceededMaxSizeError().code)

expectAssignable<errors.UndiciError>(new errors.RequestRetryError('', 0))
expectAssignable<errors.RequestRetryError>(new errors.RequestRetryError('', 0))
expectAssignable<'RequestRetryError'>(new errors.RequestRetryError('', 0).name)
expectAssignable<'UND_ERR_REQ_RETRY'>(new errors.RequestRetryError('', 0).code)

expectAssignable<errors.UndiciError>(new errors.SecureProxyConnectionError())
expectAssignable<errors.SecureProxyConnectionError>(new errors.SecureProxyConnectionError())
expectAssignable<'SecureProxyConnectionError'>(new errors.SecureProxyConnectionError().name)
expectAssignable<'UND_ERR_PRX_TLS'>(new errors.SecureProxyConnectionError().code)

{
// @ts-ignore
function f (): errors.HeadersTimeoutError | errors.ConnectTimeoutError { return }
Expand Down
21 changes: 21 additions & 0 deletions types/errors.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,25 @@ declare namespace Errors {
name: 'ResponseExceededMaxSizeError';
code: 'UND_ERR_RES_EXCEEDED_MAX_SIZE';
}

export class RequestRetryError extends UndiciError {
constructor (
message: string,
statusCode: number,
headers?: IncomingHttpHeaders | string[] | null,
body?: null | Record<string, any> | string
);
name: 'RequestRetryError';
code: 'UND_ERR_REQ_RETRY';
statusCode: number;
data: {
count: number;
};
headers: Record<string, string | string[]>;
}

export class SecureProxyConnectionError extends UndiciError {
name: 'SecureProxyConnectionError';
code: 'UND_ERR_PRX_TLS';
}
}

0 comments on commit f9997bb

Please sign in to comment.