CoolFace
Apppublic

strong-tie/inbound-calls

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
skip-reply-send.test.js323 linesDownload Raw Back to test
1'use strict'2 3const { test } = require('tap')4const split = require('split2')5const net = require('node:net')6const Fastify = require('../fastify')7 8process.removeAllListeners('warning')9 10const lifecycleHooks = [11  'onRequest',12  'preParsing',13  'preValidation',14  'preHandler',15  'preSerialization',16  'onSend',17  'onTimeout',18  'onResponse',19  'onError'20]21 22test('skip automatic reply.send() with reply.hijack and a body', (t) => {23  const stream = split(JSON.parse)24  const app = Fastify({25    logger: {26      stream27    }28  })29 30  stream.on('data', (line) => {31    t.not(line.level, 40) // there are no errors32    t.not(line.level, 50) // there are no errors33  })34 35  app.get('/', (req, reply) => {36    reply.hijack()37    reply.raw.end('hello world')38 39    return Promise.resolve('this will be skipped')40  })41 42  return app.inject({43    method: 'GET',44    url: '/'45  }).then((res) => {46    t.equal(res.statusCode, 200)47    t.equal(res.body, 'hello world')48  })49})50 51test('skip automatic reply.send() with reply.hijack and no body', (t) => {52  const stream = split(JSON.parse)53  const app = Fastify({54    logger: {55      stream56    }57  })58 59  stream.on('data', (line) => {60    t.not(line.level, 40) // there are no error61    t.not(line.level, 50) // there are no error62  })63 64  app.get('/', (req, reply) => {65    reply.hijack()66    reply.raw.end('hello world')67 68    return Promise.resolve()69  })70 71  return app.inject({72    method: 'GET',73    url: '/'74  }).then((res) => {75    t.equal(res.statusCode, 200)76    t.equal(res.body, 'hello world')77  })78})79 80test('skip automatic reply.send() with reply.hijack and an error', (t) => {81  const stream = split(JSON.parse)82  const app = Fastify({83    logger: {84      stream85    }86  })87 88  let errorSeen = false89 90  stream.on('data', (line) => {91    if (line.level === 50) {92      errorSeen = true93      t.equal(line.err.message, 'kaboom')94      t.equal(line.msg, 'Promise errored, but reply.sent = true was set')95    }96  })97 98  app.get('/', (req, reply) => {99    reply.hijack()100    reply.raw.end('hello world')101 102    return Promise.reject(new Error('kaboom'))103  })104 105  return app.inject({106    method: 'GET',107    url: '/'108  }).then((res) => {109    t.equal(errorSeen, true)110    t.equal(res.statusCode, 200)111    t.equal(res.body, 'hello world')112  })113})114 115function testHandlerOrBeforeHandlerHook (test, hookOrHandler) {116  const idx = hookOrHandler === 'handler' ? lifecycleHooks.indexOf('preHandler') : lifecycleHooks.indexOf(hookOrHandler)117  const previousHooks = lifecycleHooks.slice(0, idx)118  const nextHooks = lifecycleHooks.slice(idx + 1)119 120  test(`Hijacking inside ${hookOrHandler} skips all the following hooks and handler execution`, t => {121    t.plan(4)122    const test = t.test123 124    test('Sending a response using reply.raw => onResponse hook is called', t => {125      const stream = split(JSON.parse)126      const app = Fastify({127        logger: {128          stream129        }130      })131 132      stream.on('data', (line) => {133        t.not(line.level, 40) // there are no errors134        t.not(line.level, 50) // there are no errors135      })136 137      previousHooks.forEach(h => app.addHook(h, async (req, reply) => t.pass(`${h} should be called`)))138 139      if (hookOrHandler === 'handler') {140        app.get('/', (req, reply) => {141          reply.hijack()142          reply.raw.end(`hello from ${hookOrHandler}`)143        })144      } else {145        app.addHook(hookOrHandler, async (req, reply) => {146          reply.hijack()147          reply.raw.end(`hello from ${hookOrHandler}`)148        })149        app.get('/', (req, reply) => t.fail('Handler should not be called'))150      }151 152      nextHooks.forEach(h => {153        if (h === 'onResponse') {154          app.addHook(h, async (req, reply) => t.pass(`${h} should be called`))155        } else {156          app.addHook(h, async (req, reply) => t.fail(`${h} should not be called`))157        }158      })159 160      return app.inject({161        method: 'GET',162        url: '/'163      }).then((res) => {164        t.equal(res.statusCode, 200)165        t.equal(res.body, `hello from ${hookOrHandler}`)166      })167    })168 169    test('Sending a response using req.socket => onResponse not called', t => {170      const stream = split(JSON.parse)171      const app = Fastify({172        logger: {173          stream174        }175      })176      t.teardown(() => app.close())177 178      stream.on('data', (line) => {179        t.not(line.level, 40) // there are no errors180        t.not(line.level, 50) // there are no errors181      })182 183      previousHooks.forEach(h => app.addHook(h, async (req, reply) => t.pass(`${h} should be called`)))184 185      if (hookOrHandler === 'handler') {186        app.get('/', (req, reply) => {187          reply.hijack()188          req.socket.write('HTTP/1.1 200 OK\r\n\r\n')189          req.socket.write(`hello from ${hookOrHandler}`)190          req.socket.end()191        })192      } else {193        app.addHook(hookOrHandler, async (req, reply) => {194          reply.hijack()195          req.socket.write('HTTP/1.1 200 OK\r\n\r\n')196          req.socket.write(`hello from ${hookOrHandler}`)197          req.socket.end()198        })199        app.get('/', (req, reply) => t.fail('Handler should not be called'))200      }201 202      nextHooks.forEach(h => app.addHook(h, async (req, reply) => t.fail(`${h} should not be called`)))203 204      app.listen({ port: 0 }, err => {205        t.error(err)206        const client = net.createConnection({ port: (app.server.address()).port }, () => {207          client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')208 209          let chunks = ''210          client.setEncoding('utf8')211          client.on('data', data => {212            chunks += data213          })214 215          client.on('end', function () {216            t.match(chunks, new RegExp(`hello from ${hookOrHandler}`, 'i'))217            t.end()218          })219        })220      })221    })222 223    test('Throwing an error does not trigger any hooks', t => {224      const stream = split(JSON.parse)225      const app = Fastify({226        logger: {227          stream228        }229      })230      t.teardown(() => app.close())231 232      let errorSeen = false233      stream.on('data', (line) => {234        if (hookOrHandler === 'handler') {235          if (line.level === 40) {236            errorSeen = true237            t.equal(line.err.code, 'FST_ERR_REP_ALREADY_SENT')238          }239        } else {240          t.not(line.level, 40) // there are no errors241          t.not(line.level, 50) // there are no errors242        }243      })244 245      previousHooks.forEach(h => app.addHook(h, async (req, reply) => t.pass(`${h} should be called`)))246 247      if (hookOrHandler === 'handler') {248        app.get('/', (req, reply) => {249          reply.hijack()250          throw new Error('This wil be skipped')251        })252      } else {253        app.addHook(hookOrHandler, async (req, reply) => {254          reply.hijack()255          throw new Error('This wil be skipped')256        })257        app.get('/', (req, reply) => t.fail('Handler should not be called'))258      }259 260      nextHooks.forEach(h => app.addHook(h, async (req, reply) => t.fail(`${h} should not be called`)))261 262      return Promise.race([263        app.inject({ method: 'GET', url: '/' }),264        new Promise((resolve, reject) => setTimeout(resolve, 1000))265      ]).then((err, res) => {266        t.error(err)267        if (hookOrHandler === 'handler') {268          t.equal(errorSeen, true)269        }270      })271    })272 273    test('Calling reply.send() after hijacking logs a warning', t => {274      const stream = split(JSON.parse)275      const app = Fastify({276        logger: {277          stream278        }279      })280 281      let errorSeen = false282 283      stream.on('data', (line) => {284        if (line.level === 40) {285          errorSeen = true286          t.equal(line.err.code, 'FST_ERR_REP_ALREADY_SENT')287        }288      })289 290      previousHooks.forEach(h => app.addHook(h, async (req, reply) => t.pass(`${h} should be called`)))291 292      if (hookOrHandler === 'handler') {293        app.get('/', (req, reply) => {294          reply.hijack()295          reply.send('hello from reply.send()')296        })297      } else {298        app.addHook(hookOrHandler, async (req, reply) => {299          reply.hijack()300          return reply.send('hello from reply.send()')301        })302        app.get('/', (req, reply) => t.fail('Handler should not be called'))303      }304 305      nextHooks.forEach(h => app.addHook(h, async (req, reply) => t.fail(`${h} should not be called`)))306 307      return Promise.race([308        app.inject({ method: 'GET', url: '/' }),309        new Promise((resolve, reject) => setTimeout(resolve, 1000))310      ]).then((err, res) => {311        t.error(err)312        t.equal(errorSeen, true)313      })314    })315  })316}317 318testHandlerOrBeforeHandlerHook(test, 'onRequest')319testHandlerOrBeforeHandlerHook(test, 'preParsing')320testHandlerOrBeforeHandlerHook(test, 'preValidation')321testHandlerOrBeforeHandlerHook(test, 'preHandler')322testHandlerOrBeforeHandlerHook(test, 'handler')323