question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

When Fastify is listening on IPv4+6, routes return 404 when connecting from IPv6 addresses

See original GitHub issue

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

4.0.1

Plugin version

6.0.1

Node.js version

16.15.0

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

12.4

Description

When configured to listen on localhost, Fastify spawns two http.Servers: one on IPv4 address localhost on startup, which is exposed as fastify.server (fastify/server.js:15), and a second one on IPv6 address ::, some time after the first one has finished listening, exposed through symbol kServerBindings (fastify/server.js:133).

However, fastify-websocket only listens to upgrade events on the first server (fastify-websocket/index.js:41), which means connections from IPv6 addresses are never upgraded, and so never handled, and reach the fallback 404 handler.

On my machine, where IPv6 is available, Node ws connections have a remoteAddress of 127.0.0.1, and reach the first http.Server, triggering the proper upgrade handling. However, browser WebSocket connections come from ::1, and reach the second, which has no upgrade handler set up. In the following extract from the logs, the only difference in the requests is remoteAddress:

{"level":30,"time":1655248914754,"pid":86869,"hostname":"<my hostname>","reqId":"req-1","req":{"method":"GET","url":"/connect/worker","hostname":"localhost:3001","remoteAddress":"127.0.0.1","remotePort":55029},"msg":"incoming request"}
☝️ No failure, and my route handler logs successfully afterwards

{"level":30,"time":1655248918573,"pid":86869,"hostname":"<my hostname>","reqId":"req-2","req":{"method":"GET","url":"/connect/worker","hostname":"localhost:3001","remoteAddress":"::1","remotePort":55032},"msg":"incoming request"}
{"level":30,"time":1655248918577,"pid":86869,"hostname":"<my hostname>","reqId":"req-2","res":{"statusCode":404},"responseTime":3.638867139816284,"msg":"request completed"}
☝️ 404 on the same route

Because the second http.Server is only created some time after the first has finished listening, and is still not ready even when the onReady hook triggers, a clean fix for this is not obvious, without changing the Fastify API. Additionally, the second server is only exposed through a symbol from an untyped module, which complicates things further.

My current work-around is to forward upgrade events from the second server to the first during the listen callback:

// @ts-ignore untyped module
import symbols from 'fastify/lib/symbols.js';

fastify.listen({port: 3001}, () => {
  // @ts-ignore unknown index access
  fastify[symbols.kServerBindings].forEach((server) => {
    server.on('upgrade', (...args: any[]) => {
      fastify.server.emit('upgrade', ...args);
    });
  });
});

Steps to Reproduce

  • Set up a simple websocket route
  • Listen on localhost, with ipv6 enabled, causing a multiple server binding
  • Trigger a WebSocket call from remoteAddress: ::1
  • → 404

Expected Behavior

  • All connections are handled properly

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:2
  • Comments:13 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
jsumnerscommented, Jun 15, 2022

cc @jsumners we did not think about this use case when doing the multiple interface fix.

I genuinely never even think about the websocket module.

1reaction
mcollinacommented, Jun 15, 2022

Good spot! I will take a look as soon as I can (but it might take a few days).

Read more comments on GitHub >

github_iconTop Results From Across the Web

Server - Fastify
When set to "idle" , upon close the server will iterate the current persistent connections which are not sending a request or waiting...
Read more >
Detect 404s in preHandler middleware · Issue #65 · fastify/help
We want to have a fastify server that defaults to secured routes, and only disables authentication when explicitly told so.
Read more >
How to reverse port biding (Phusion Passenger and Fastify)?
Phusion passenger can only call listen once. Since 'localhost' runs both IPv4 and IPv6 it crashes. Setting to '127.0.0.1' solves this. I asked ......
Read more >
address JavaScript and Node.js code examples - Tabnine
console.log(`server listening on ${fastify.server.address().port}`) ... .filter(info => info.family === 'IPv6') .map(info => info.address) .shift().
Read more >
SOLVED - Ipv6 address returns 404, ipv4 is good
I have a VPS with Linode and has both ipv4 and ipv6. I installed WHM as normal way (I have another servers with...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found