Skip to content

Commit

Permalink
feat: add support for weak etag check
Browse files Browse the repository at this point in the history
  • Loading branch information
metcoder95 committed Oct 27, 2023
1 parent b8fbef0 commit 5a159cc
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 12 deletions.
20 changes: 11 additions & 9 deletions lib/handler/RetryHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class RetryHandler {
this.retryAfter = null
this.head = 0
this.tail = null
this.etag = ''
this.etag = null
this.resume = null

// Handle possible onConnect duplication
Expand Down Expand Up @@ -183,14 +183,15 @@ class RetryHandler {
return true
}

// TODO (fix): etag
// if (this.etag == null || this.etag !== etag) {
// throw this.error
// }

const contentRange = parseRangeHeader(headers['content-range'])
if (!contentRange) {
throw err
let contentRange
if (
// Let's start with a weak etag check
(this.etag != null && this.etag !== headers.etag) ||
// If no content range
!(contentRange = parseRangeHeader(headers['content-range']))
) {
this.abort(err)
return false
}

const { start, size, end = size } = contentRange
Expand Down Expand Up @@ -245,6 +246,7 @@ class RetryHandler {
)

this.resume = resume
this.etag = headers.etag != null ? headers.etag : null

return this.handler.onHeaders(
statusCode,
Expand Down
98 changes: 95 additions & 3 deletions test/retry-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ const tap = require('tap')

const { RetryHandler, Client } = require('..')

// TODO: rewrite tests to not use explicit Promise handling
// TODO: add tests for retry-after on 429

tap.test('Should retry status code', t => {
let counter = 0
const chunks = []
Expand Down Expand Up @@ -663,3 +660,98 @@ tap.test('Should handle 206 partial content', t => {
})
})
})
tap.test('Should handle 206 partial content - bad-etag', { only: true }, t => {
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.pass()
res.setHeader('etag', 'asd')
res.write('abc')
setTimeout(() => {
res.destroy()
}, 1e2)
} else if (x === 1) {
t.same(req.headers.range, 'bytes=3-')
res.setHeader('content-range', 'bytes 3-6/6')
res.setHeader('etag', 'erwsd')
res.statusCode = 206
res.end('def')
}
x++
})

const dispatchOptions = {
method: 'GET',
path: '/',
headers: {
'content-type': 'application/json'
}
}

t.plan(6)

server.listen(0, () => {
const client = new Client(`http://localhost:${server.address().port}`)
const handler = new RetryHandler(
dispatchOptions,
{
dispatch: (...args) => {
return client.dispatch(...args)
},
handler: {
onConnect () {
t.pass()
},
onBodySent () {
t.pass()
},
onHeaders (status, _rawHeaders, resume, _statusMessage) {
t.pass()
return true
},
onData (chunk) {
chunks.push(chunk)
return true
},
onComplete () {
t.error('should not complete')
},
onError (err) {
t.equal(Buffer.concat(chunks).toString('utf-8'), 'abc')
t.equal(err.code, 'UND_ERR_REQ_RETRY')
}
}
},
{
retry: function (err) {
if (err.code && err.code === 'UND_ERR_DESTROYED') {
return null
}

return err.statusCode === 206 ? null : 800
}
}
)

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

t.teardown(async () => {
await client.close()

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

0 comments on commit 5a159cc

Please sign in to comment.