SignalR with React through NodeJS proxy - node.js

I'm trying to use SignalR on a React application through a NodeJS proxy using the NPM package 'http-proxy-middleware'. However, it appears that the connection is falling to connect. Below is the console log from the react application.
[2022-11-29T23:02:24.022Z] Information: Normalizing '/hubs/Chat' to 'https://localhost:3000/hubs/Chat'.
[2022-11-29T23:02:24.022Z] Debug: Starting connection with transfer format 'Text'.
[2022-11-29T23:02:24.023Z] Debug: Sending negotiation request: https://localhost:3000/hubs/Chat/negotiate?negotiateVersion=1.
[2022-11-29T23:02:24.642Z] Debug: Selecting transport 'WebSockets'.
[2022-11-29T23:02:24.643Z] Trace: (WebSockets transport) Connecting.
WebSocket connection to 'wss://localhost:3000/hubs/Chat?id=DykJoTMv9LiYrtjYHUCQVg' failed:
[2022-11-29T23:06:24.671Z] Information: (WebSockets transport) There was an error with the transport.
[2022-11-29T23:06:24.672Z] Error: Failed to start the transport 'WebSockets': Error: WebSocket failed to connect. The connection could not be found on the server, either the endpoint may not be a SignalR endpoint, the connection ID is not present on the server, or there is a proxy blocking WebSockets. If you have multiple servers check that sticky sessions are enabled.
[2022-11-29T23:06:24.672Z] Debug: Skipping transport 'ServerSentEvents' because it was disabled by the client.
[2022-11-29T23:06:24.672Z] Debug: Skipping transport 'LongPolling' because it was disabled by the client.
[2022-11-29T23:06:24.672Z] Error: Failed to start the connection: Error: Unable to connect to the server with any of the available transports. Error: WebSockets failed: Error: WebSocket failed to connect. The connection could not be found on the server, either the endpoint may not be a SignalR endpoint, the connection ID is not present on the server, or there is a proxy blocking WebSockets. If you have multiple servers check that sticky sessions are enabled. ServerSentEvents failed: Error: 'ServerSentEvents' is disabled by the client. LongPolling failed: Error: 'LongPolling' is disabled by the client.
Error establishing the connection: Error: Unable to connect to the server with any of the available transports. Error: WebSockets failed: Error: WebSocket failed to connect. The connection could not be found on the server, either the endpoint may not be a SignalR endpoint, the connection ID is not present on the server, or there is a proxy blocking WebSockets. If you have multiple servers check that sticky sessions are enabled. ServerSentEvents failed: Error: 'ServerSentEvents' is disabled by the client. LongPolling failed: Error: 'LongPolling' is disabled by the client.
at HttpConnection._createTransport (HttpConnection.ts:407:1)
at async HttpConnection._startInternal (HttpConnection.ts:283:1)
at async HttpConnection.start (HttpConnection.ts:137:1)
at async HubConnection._startInternal (HubConnection.ts:207:1)
at async HubConnection._startWithStateTransitions (HubConnection.ts:181:1)
If I connect directly to the .NET application, which is running the server-side part of SignalR, it works fine. So it's not a problem with that.
This is the code snippet from the React App which performs the connection.
this.hubConnection = new HubConnectionBuilder()
.withUrl("/hubs/chat",{
transport: HttpTransportType.WebSockets,
logger: LogLevel.Trace
})
.withAutomaticReconnect()
.build();
this.hubConnection.start().catch(error => console.log("Error establishing the connection: ", error));
The proxy is running in NodeJS through nodemon. Below is the code.
const PORT = 8000;
const express = require('express');
const https = require('https');
const fs = require('fs')
const {createProxyMiddleware} = require('http-proxy-middleware');
const app = express();
require('dotenv').config({ path: `.env.${process.env.NODE_ENV.trim()}` });
const httpsOptions = {
key: fs.readFileSync('./certs/cert.key'),
cert: fs.readFileSync('./certs/cert.pem')
}
app.use('/hubs/chat', createProxyMiddleware({
target: process.env.REACT_APP_APISERVICEURL,
changeOrigin: false ,
secure: false,
ws: true,
logger: console
}));
var secure = https.createServer(httpsOptions, app);
secure.listen(8000, () => console.log(`Server is running on port ${PORT}`));
The proxy does work for all other API calls to the .NET Application. The React application has the proxy attribute set in the package.json. The proxy then re-writes the URL and adds an API Token header to send to the API. Hence the use of http-proxy-middleware.
I have tried the below as well, but it still produces the same result.
const signlrRProxy = createProxyMiddleware({
target: process.env.REACT_APP_APISERVICEURL,
changeOrigin: false ,
secure: false,
ws: true,
logger: console
});
app.use('/hubs/chat', signlrRProxy);
var secure = https.createServer(httpsOptions, app);
secure.listen(8000, () => console.log(`Server is running on port ${PORT}`));
secure.on('upgrade', signlrRProxy.upgrade);
I am using the latest package versions to everything.

Related

Ngrok reconnecting (failed to fetch CRL. errors encountered: asn1: structure error: length too large)

I can't run ngrok successfully, I'm using a node.js app with the command
app.listen(3000, () => {
console.log('Listening on port', 3000);
});
but yet everytime I start ngrok using ngrok http 3000, it just show up these error messages
reconnecting (failed to fetch CRL. errors encountered: asn1: structure error: length too large)
reconnecting (jsonHTTP.Lookup: No such host: tunnel.ngrok.com)
reconnecting (resolved tunnel.ngrok.com has no records)
I also got the exact same error.
The problem for me was the internet connection - ngrok requires a stable connection, as soon as I changed the wifi network the problem was solved

How to debug ECONNRESET with socket.io and express encountered when running a Nessus scan?

I'm encountering ECONNRESET errors that are crashing my node server when I run a Nessus Essentials basic network scan:
node:events:505
throw er; // Unhandled 'error' event
^
Error: read ECONNRESET
at TCP.onStreamRead (node:internal/stream_base_commons:217:20)
Emitted 'error' event on Socket instance at:
at emitErrorNT (node:internal/streams/destroy:157:8)
at emitErrorCloseNT (node:internal/streams/destroy:122:3)
at processTicksAndRejections (node:internal/process/task_queues:83:21) {
errno: -54,
code: 'ECONNRESET',
syscall: 'read'
}
I don't know exactly what Nessus is doing and I haven't managed to reproduce the error any other way, but I've identified two interesting facts:
The problem only occurs when running on Node versions < 16.16.0
It also goes away if I remove socket.io, or if I instantiate socket.io using a different port or HTTP server than my express server
For context, I've simplified my application down to a minimal project still exhibits the error:
import express from 'express';
import { Server } from 'socket.io';
import morgan from 'morgan';
import winston from 'winston';
const logger = winston.createLogger({
level: 'debug',
format: winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }),
winston.format.splat(),
winston.format.prettyPrint(),
winston.format(info => {
info.level = `[${info.level.toUpperCase()}]`;
return info;
})(),
winston.format.printf(info => {
if (typeof info.message === 'object') {
info.message = JSON.stringify(info.message, null, 3);
}
return `${info.timestamp} ${info.level} ${info.message}`;
}),
),
transports: [new winston.transports.Console()]
});
const morganMiddleware = morgan(
':remote-addr :method :url :status :response-time ms',
{
stream: {
write: message => logger.http(message.trim()),
},
});
const app = express();
app.use(morganMiddleware);
const port = 3001;
const httpServer = app.listen(port, () => {
logger.info(
`Server is now listening on port ${port} ✓`,
);
new Server(httpServer);
});
export default app;
If I remove the new Server(httpServer); line, the app doesn't crash. So the error seems to be linked to socket.io and Express sharing a connection, and something going wrong during the vulnerability scan (something causing the socket to be dropped?), but I haven't managed to debug it further.
I have tried all sorts of ways of catching the error, but the only way that works is using process.on('uncaughtException'). That's not helpful however, because at that point, there is no safe way to recover from the error. The error bypasses all the error handling of both Express and socket.io.
I could of course "solve" the problem by upgrading to a more recent version of node, but I need to understand the problem in order to be sure that it's actually fixed and won't surface again in some other form. Also, I would like to be able to make my app more resilient by catching this sort of error if they were to occur in the future.
Or perhaps there's some way to separate socket.io from express that without using separate ports (which wouldn't be practical in my use-case). Can I use proxy websocket related requests through Express to socket.io without sharing an HTTP server?
Any suggestions, either to help understand/debug the problem, or to work around it, would be welcome.
After much investigation, I figured out what was going on.
The ECONNRESET error I was getting occurred when Socket.io received an HTTP2 upgrade request: upgrade events are intercepted by Socket.io so that websocket upgrades can be handled, but for HTTP2 upgrades, the socket was incorrectly being disconnected without any error handler to catch the error, which in turn led to the the application crashing.
I was able to fix the bug, which was released as engine.io 6.2.1.
The reason I was not seeing the issue with versions of Node older than 16.16.0 was actually unrelated: Nessus was, for some reason, sending malformed HTTP requests for the HTTP2 upgrade, using LF as line separators instead of CRLF. Starting with v16.16.0, Node now rejects such requests with a 400 Bad Request, which avoids the Socket.io issue.

Can't connect node to SQL Server

I'm trying to connect a node app with a SQL Server database, but I keep getting this error:
Error while connecting database: ConnectionError:
Error: [Microsoft][SQL Server Native Client 11.0]SQL Server Network Interfaces: Connection string is not valid [87].
Error: [Microsoft][SQL Server Native Client 11.0]Login timeout expired
Error: [Microsoft][SQL Server Native Client 11.0]A network-related or instance-specific error has occurred while establishing a connection to SQL Server. Server is not found or not accessible. Check if instance name is correct and if SQL Server is configured to allow remote connections. For more information see SQL Server Books Online.
I use the node package mssql with driver msnodesqlv8, and this is my db config:
const dbConfig = {
user: "user",
password: "password",
driver: 'msnodesqlv8',
server: 'DESKTOP-QG3R51Q',
database: 'WeighingScales',
options: {
instanceName: 'SQLEXPRESS',
trustedConnection: true,
}
};

Sentry error logging with express does not send any errors

When I am trying to send errors to Sentry for a simple node.js Express server, I don't get any errors logged in Sentry itself. There are no inbound filters set up and it does not report anything filtered in the past 30 days. Allowed domains is set to *, which is I think the default. The code that I use is more or less the same as the example code in the documentation. However, when calling the endpoint nothing appears in Sentry and the debug lines do not show anything about an error even being sent.
How do I get Sentry to properly capture errors?
This is test.js
'use strict';
const express = require('express');
const Sentry = require('#sentry/node');
const app = express();
// TODO dotenv setting
const SENTRY_NODE_DSN = process.env.SENTRY_NODE_DSN || 'the ingest url from settings > client keys';
console.log(`Started logging errors at sentry on DSN ${SENTRY_NODE_DSN}`);
// Sentry
Sentry.init({
dsn: SENTRY_NODE_DSN,
debug: true,
});
// The request handler must be the first middleware on the app
app.use(Sentry.Handlers.requestHandler());
const port = process.argv[2];
if (port === undefined) {
console.log(`Please specify a port as first argument`);
process.exit(0);
}
app.get('/test', () => {
throw new Error('test');
});
// The error handler must be before any other error middleware and after all controllers
app.use(Sentry.Handlers.errorHandler({
shouldHandleError(error) {
return true;
}
}));
app.listen(port);
We start it with node ./test.js 3000
Then from a different window we execute wget -- localhost:3000/test which gives the following output.
--2021-07-29 14:03:01-- http://localhost:3000/test
Resolving localhost (localhost)... ::1, 127.0.0.1
Connecting to localhost (localhost)|::1|:3000... connected.
HTTP request sent, awaiting response... 500 Internal Server Error
2021-07-29 14:03:01 ERROR 500: Internal Server Error.
The output from the express server is this:
$ node ./test.js 3000
Started logging errors at sentry on DSN same url as in the code above
Sentry Logger [Log]: Integration installed: InboundFilters
Sentry Logger [Log]: Integration installed: FunctionToString
Sentry Logger [Log]: Integration installed: Console
Sentry Logger [Log]: Integration installed: Http
Sentry Logger [Log]: Integration installed: OnUncaughtException
Sentry Logger [Log]: Integration installed: OnUnhandledRejection
Sentry Logger [Log]: Integration installed: LinkedErrors
Error: test
at /var/www/vhosts/myproject/test.js:28:8
at Layer.handle [as handle_request] (/var/www/vhosts/myproject/node_modules/express/lib/router/layer.js:95:5)
at next (/var/www/vhosts/myproject/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/var/www/vhosts/myproject/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/var/www/vhosts/myproject/node_modules/express/lib/router/layer.js:95:5)
at /var/www/vhosts/myproject/node_modules/express/lib/router/index.js:281:22
at Function.process_params (/var/www/vhosts/myproject/node_modules/express/lib/router/index.js:335:12)
at next (/var/www/vhosts/myproject/node_modules/express/lib/router/index.js:275:10)
at Domain.<anonymous> (/var/www/vhosts/myproject/node_modules/#sentry/node/dist/handlers.js:321:13)
at Domain.run (domain.js:370:15)
In my case this code was correct, but the issue was that I was running this script in wsl while on a VPN, and the request was being sent, but hang. Experimentally it appears that Sentry does not log anything when trying to send errors when in debug mode, so you can't infer anything from that.
To test if Sentry does actually send anything, try the even simpler version
const Sentry = require('#sentry/node');
const SENTRY_NODE_DSN = 'the ingest url';
Sentry.init({
dsn: SENTRY_NODE_DSN,
debug: true,
});
Sentry.captureException(new Error('test exception'));
If the script hangs (does not terminate), it means that the http-request to the sentry servers is not being completed. This is likely due to a network issue on your side.

Azure + Cassandra certified by Bitnami VM + nodejs app: Cannot connect to cassandra from remote app

Using datastax nodejs driver: 'cassandra-driver'
Connecting to database in my nodejs app server backend as:
const cassandra = require('cassandra-driver');
const client = new cassandra.Client({ contactPoints: [ '${azure_vm_ip}' ] });
Output log:
{ [Error: All host(s) tried for query failed. First host tried, azure_vm_ip:9042: Error: Connection timeout. See innerErrors.]
innerErrors:
{ 'azure_vm_ip:9042':
{ [Error: Connection timeout]
message: 'Connection timeout',
info: 'Cassandra Driver Error' } },
info: 'Represents an error when a query cannot be performed because no host is available or could be reached by the driver.',
message: 'All host(s) tried for query failed. First host tried, azure_vm_ip:9042: Error: Connection timeout. See innerErrors.' }
Questions:
Should I edit something on the default cassandra.yaml file? If
so, what?
Should I do something with firewall? If so, what?
Should I pass in more options in new cassandra.Client({
contactPoints: [ '${azure_vm_ip}' ] })? If so, What?
Make sure you have set rule for 9042 port in NSG. Also check whether the same port is in listening mode inside the VM.

Resources