Skip to content

Commit

Permalink
Merge d71fb88 into 6f5de1f
Browse files Browse the repository at this point in the history
  • Loading branch information
leorossi committed Sep 29, 2021
2 parents 6f5de1f + d71fb88 commit c3b5aeb
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 10 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ On which methods should the connection be retried in case of socket hang up.

By default: `['GET', 'HEAD', 'OPTIONS', 'TRACE' ]`

This plugin will always retry on 503 errors, _unless_ `retryMethods` does not contain `GET`.

---

### `reply.from(source, [opts])`
Expand Down
30 changes: 20 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,8 @@ module.exports = fp(function from (fastify, opts, next) {
const requestHeaders = rewriteRequestHeaders(req, headers)
const contentLength = requestHeaders['content-length']
let requestImpl

if (retriesCount && retryMethods.has(req.method) && !contentLength) {
requestImpl = createRequestRetry(request, this, retriesCount, retryOnError)
if (retryMethods.has(req.method) && !contentLength) {
requestImpl = createRequestRetry(request, this, retriesCount, retryOnError, 503)
} else {
requestImpl = request
}
Expand Down Expand Up @@ -213,25 +212,36 @@ function isFastifyMultipartRegistered (fastify) {
return fastify.hasContentTypeParser('multipart') && fastify.hasRequestDecorator('multipart')
}

function createRequestRetry (requestImpl, reply, retriesCount, retryOnError) {
function createRequestRetry (requestImpl, reply, retriesCount, retryOnError, retryOnCode = 503) {
function requestRetry (req, cb) {
let retries = 0

function run () {
requestImpl(req, function (err, res) {
if (err && !reply.sent && retriesCount > retries) {
if (err.code === retryOnError) {
retries += 1
// Magic number, so why not 42? We might want to make this configurable.
let retryAfter = 42 * Math.random() * (retries + 1)

run()
return
}
if (res && res.headers['retry-after']) {
retryAfter = res.headers['retry-after']
}

if (!reply.sent) {
// always retry on 503 errors
if (res && res.statusCode === retryOnCode && req.method === 'GET') {
return retry(retryAfter)
} else if (retriesCount > retries && err && err.code === retryOnError) {
return retry(retryAfter)
}
}
cb(err, res)
})
}

function retry (after) {
retries += 1
setTimeout(run, after)
}

run()
}

Expand Down
49 changes: 49 additions & 0 deletions test/retry-on-503.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use strict'

const t = require('tap')
const Fastify = require('fastify')
const From = require('..')
const http = require('http')
const get = require('simple-get').concat

const instance = Fastify()

t.plan(6)
t.teardown(instance.close.bind(instance))
let requestCount = 0
const target = http.createServer((req, res) => {
if (requestCount++ === 0) {
res.statusCode = 503
res.setHeader('Content-Type', 'text/plain')
res.setHeader('Retry-After', 1000)
return res.end('This Service Unavailable')
}
res.statusCode = 205
res.setHeader('Content-Type', 'text/plain')
return res.end('Hello World Twice!')
})

instance.get('/', (request, reply) => {
reply.from()
})

t.teardown(target.close.bind(target))

target.listen(0, (err) => {
t.error(err)

instance.register(From, {
base: `http://localhost:${target.address().port}`
})

instance.listen(0, (err) => {
t.error(err)

get(`http://localhost:${instance.server.address().port}`, (err, res, data) => {
t.error(err)
t.equal(res.headers['content-type'], 'text/plain')
t.equal(res.statusCode, 205)
t.equal(data.toString(), 'Hello World Twice!')
})
})
})

0 comments on commit c3b5aeb

Please sign in to comment.