How to fix "Firefox can’t establish a connection to the server at wss://example.domain/subscription" issue?

169 Views Asked by At

This is how I have setup app.js:

/**
 *  Importing Packages
 */
require("dotenv").config({ path: `./env/.env-${process.env.NODE_ENV}` });
const express                                 = require("express");
const logger                                  = require("morgan");
const moment                                  = require("moment");
const cors                                    = require("cors");
const path                                    = require("path");
const bodyParser                              = require('body-parser');
const app                                     = express();
const fs                                      = require("fs");
const { ApolloServer, gql }                   = require("apollo-server-express");
const { mergeGraphQLTypes, mergeResolvers }   = require("@graphql-tools/merge");
const { makeExecutableSchema }                = require("@graphql-tools/schema");
const { applyMiddleware }                     = require("graphql-middleware");
const { createServer }                        = require("http"); // newly added
const { 
  ApolloServerPluginDrainHttpServer, 
  ApolloServerPluginLandingPageLocalDefault } = require("apollo-server-core");
const { WebSocketServer }                     = require("ws");
const { useServer }                           = require("graphql-ws/lib/use/ws");
const graphqlUploadExpress                    = require("graphql-upload/graphqlUploadExpress.js");
const GraphQLUpload                           = require("graphql-upload/GraphQLUpload.js");

require("./database/db");
require("./helper/function");
const Stripe                                  = require("./stripe/stripeRoute");
const Cron                                    = require("./scheduleCron/scheduleCronRoute");
const generateFolder                          = Helper("generate-folder");

/* Get the Queries & Mutation type and merge them togather for the schema */
const Query                                   = mergeGraphQLTypes(require("./queries")(gql));

/* Get the resolvers array and merge them togather for the schema */
const Resolvers                               = mergeResolvers(require("./resolvers"));
Resolvers.Upload                              = GraphQLUpload;

/* Get all the middlewares.
   Note:- This we can return as array of middleware object
   The object pattern are mentioned in the middleware folder index.js file */
const Middlewares                             = require("./middlewares");

/* For the use of middleware we must create an executable schema and pass the
   query and resolvers we acquired before. */
const schema                                  = makeExecutableSchema({ typeDefs: Query, resolvers: Resolvers });

/* Apply middlewares to the schema. */
const schemaWithMiddleware                    = applyMiddleware(schema, Middlewares.verifyUser);

/* Create the main Apollo Server from the schema */
// Added for Websocket Subscriptions Starts //
const httpServer                              = createServer(app);
// Set up WebSocket server.
const wsServer = new WebSocketServer({
  //port: 8443,
  server: httpServer,
  path: "/",
});
const serverCleanup = useServer({schema}, wsServer);
// Added for Websocket Subscriptions Ends //

const server = new ApolloServer({
  schema: schemaWithMiddleware,
  context: ({ req, res }) => {
    return { req, res };
  },
  formatError: (err) => {
    // logger.info(err);
    return err;
  },
  uploads: false,
  plugins: [
    // Proper shutdown for the HTTP server.
    ApolloServerPluginDrainHttpServer({httpServer}),
    {
      async serverWillStart() {
        return {
          async renderLandingPage() {
            const html = `<!DOCTYPE html>
                          <html>
                            <head>
                            <title>Welcome To TestDrive</title>
                            </head>
                            <body>
                              <img src="https://testdrive.co/images/api-screen.jpg" style="display: block; margin-left: auto; margin-right: auto; width:auto;">
                            </body>
                          </html>`;
            return { html };
          },
          async drainServer() {
            await serverCleanup.dispose();
          },
        }
      }
    }
  ]
});

/* Setting up port */
const port = process.env.PORT || 8000;

/* Generating logs */
var accessLogStream = fs.createWriteStream(
  path.join(
    __dirname,
    `${generateFolder.generateLogFolder()}/access-${moment().format(
      "YYYY-MM-DD"
    )}.log`
  ),
  { flags: "a" }
);

// setting up the logger
app.use(logger("combined", { stream: accessLogStream }));
app.use(function (err, req, res, next) {
  logger.error(
    `${req.method} - ${err.message}  - ${req.originalUrl} - ${req.ip}`
  );
  next(err);
});

/* Setting up stripe payment */
app.use(
  express.json({
    // We need the raw body to verify webhook signatures.
    // Let's compute it only when hitting the Stripe webhook endpoint.
    verify: function (req, res, buf) {
      if (req.originalUrl.startsWith("/webhook")) {
        req.rawBody = buf.toString();
      }
    },
  })
);

/* Setting up graphql upload */
app.use(
  graphqlUploadExpress({
    maxFileSize: 30000000,
    maxFiles: 20,
  })
);

/* Cors Setup */
var corsOptions = {
  origin: process.env.CORS_ALLOW_URL,
  allowedHeaders: [
    "Content-Type",
    "Authorization",
    "Accept",
    "x-www-form-urlencoded",
    "x-access-token",
  ],
  credentials: true,
};
app.use(cors(corsOptions));
app.use(express.static("public"));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(Stripe);
app.use(Cron);

/* Initiating the Apollo server */
server
  .start()
  .then((r) => {
    /**
     * The following line is require as we are using the apollo-server-express
     */
    server.applyMiddleware({ app, path: "/" });

    // Now that our HTTP server is fully set up, actually listen.
    httpServer.listen(port, () => {
      console.log(` Query endpoint ready at http://localhost:${port}${server.graphqlPath}`);
      console.log(` Subscription endpoint ready at ws://localhost:${port}${server.graphqlPath}`);
    });
  })
  .catch((err) => {
    console.log("Could not start the apollo server", err);
  });

This is my nginx setup:-

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

upstream sockettest.testdrive.co {
  server 100.242.100.242:5555; #  host and port of local running server instance
}

server {
    ssl on;
    listen 443 ssl http2;
    server_name apistg.testdrive.co;
    ssl_certificate /etc/testdrives/testcert.pem;
    ssl_certificate_key /etc/testdrives/testkey.pem;

    root /var/www/apistg.testdrive.co/html;

    # Add index.php to the list if you are using PHP
    index index.html index.htm index.nginx-debian.html;

    location / {
        proxy_pass http://100.242.100.242:5555/;
        proxy_buffering off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
    }

    location /subscription { # websocket endpoint
      proxy_pass http://sockettest.testdrive.co/;

      # upstream url and host
      
      proxy_redirect     default;
      proxy_http_version 1.1;

      proxy_set_header   Connection        $connection_upgrade;
      proxy_set_header   Upgrade           $http_upgrade; 

      proxy_set_header   Host              $host;
      proxy_set_header   X-Real-IP         $remote_addr;
      proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
      proxy_set_header   X-Forwarded-Proto $scheme;

      client_max_body_size       10m;
      client_body_buffer_size    128k;

      proxy_connect_timeout      90;
      proxy_send_timeout         90;
      proxy_read_timeout         90;
  }

    location /graphql {
        proxy_pass http://100.242.100.242:5555/graphql;
        proxy_buffering off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
   }

    # Secure Headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "origin-when-cross-origin" always;
    add_header "X-XSS-Protection" "1; mode=block";
}

The websocket is running fine in chrome, opera, etc. Only in Firefox, I am getting this error:-

Firefox can’t establish a connection to the server at wss://testdrive.co/subscription.

No. It is not always happening in Firefox. 2 out of 5 times, Firefox isn't displaying this error. But 3 out of 5 times, this issue is taking place.

How can I fix this issue? Someone said that it could be fixed by proxy_pass in nginx (in the comment section of the accepted answer), but I can't fix it. Another work around which I found is that manually visiting the link http://testdrive.co/subscription in Firefox and accepting the certificate warning. But the project is a SAAS application. I can't ask all the users to open the link and accept certificate warning like this:

1

There are 1 best solutions below

0
On

Please check the link below. https://www.nginx.com/blog/websocket-nginx/?amp=1

If you have a SaaS service you need to activate https, http not secure and browser doesn't work fine.

You have to check if WS and WSS is ok out of the browser, if the connexion is ok, check the browser and certificates.

Hop that help