DDP, Node, and Chrome Browserify - node.js

I am using the ddp and browserify packages in a chrome extension to talk via the ddp client to a Meteor app.
However, when I instantiate the connection, I am running across the error:
Uncaught SyntaxError: Failed to execute 'connect' on 'WebSocket': The subprotocol '[object Object]' is invalid.
This occurs when creating a new Websocket(uri='ws://localhost:3000/websocket', protocols={})
I see a similar error at How to access app hosted on meteor.com by DDP (WebSocket) protocol?, but the solution they had to change the port to 443 did not work.
Does anyone have a workaround?

The protocols parameter is optional and must be either a string or an array of protocols as described here (in your code you are providing an object)
Once this solved, obviously you will need to attach all the other mandatory callbacks, typically:
exampleSocket.onopen = function (event) {
exampleSocket.send("Here's some text that the server is urgently awaiting!");
};
And the callback for handling the server's messages
exampleSocket.onmessage = function (event) {
console.log(event.data);
}

Related

How can you force #google-cloud/datastore to use 'http' when you query your datastore emulator

I have a node + vuejs app from which I'm trying to query my local db (created using datastore emulator).
My code is something like this
const { Datastore } = require('#google-cloud/datastore')
const datastore = new Datastore({
apiEndpoint: "http://localhost:<port>",
projectId: <my_project_name>
})
I then try to run one of the sample queries on Google's documentation
My output shows there is a successful connection but then I get the following error in the console
POST https://localhost:<port>/$rpc/google.datastore.v1.Datastore/RunQuery net::ERR_SSL_PROTOCOL_ERROR
From the error, I see that it has changed http://localhost:<port> to https://localhost:<port> i.e it is forcing a protocol of https for my local host which then obviously fails.
Is there something/option that I have to specify to ensure that it uses http when I'm making local calls or using the datastore emulator?
I found the solution and am posting it in case someone else has the issue.
I started going through the code itself and discovered a reference to this link. The page says
.....By default, the client library will use gRPC, which is a binary tranport based on HTTP/2. It's Node.js implementation, #grpc/grpc-js, uses Node.js http2 module.
If you need to use the client library in non-Node.js environment or when gRPC cannot be used for any reason, you can use the HTTP/1 fallback mode. In this mode, a special browser-compatible transport implementation is used instead of gRPC transport.
In browser context (if the window object is defined) the fallback mode is enabled automatically; set options.fallback to false if you need to override this behavior....
When I added fallback:false to the options, everything worked i.e. it no longer redirected to https and it connected to my data emulator.
I thought about it further and I believe this behavior is because my code was written in the renderer process for a Vuejs App which means there's a windows object. To confirm, I moved the code to the main process and did not include the fallback option and it worked.

Redis Error "max number of clients reached"

I am running a nodeJS application using forever npm module.
Node application also connects to Redis DB for cache check. Quite often the API stops working with the following error on the forever log.
{ ReplyError: Ready check failed: ERR max number of clients reached
at parseError (/home/myapp/core/node_modules/redis/node_modules/redis-parser/lib/parser.js:193:12)
at parseType (/home/myapp/core/node_modules/redis/node_modules/redis-parser/lib/parser.js:303:14)
at JavascriptRedisParser.execute (/home/myapp/ecore/node_modules/redis/node_modules/redis-parser/lib/parser.js:563:20) command: 'INFO', code: 'ERR' }
when I execute the client list command on the redis server it shows too many open connections. I have also set the timeout = 3600 in my Redis configuration.
I do not have any unclosed Redis connection object on my application code.
This happens once or twice in a week depending on the application load, as a stop gap solution I am restarting the node server( it works ).
What could be the permanent solution in this case?
I have figured out why. This has nothing to do with Redis. Increasing the OS file descriptor limit was just a temporary solution. I was using Redis in a web application and the connection was created for every new request.
When the server was restarted occasionally, all the held-up connections by the express server were released.
I solved this by creating a global connection object and re-using the same. The new connection is created only when necessary.
You could do so by creating a global connection object, make a connection once, and make sure it is connected before every time you use that. Check if there is an already coded solution depending on your programming language. In my case it was perl with dancer framework and I used a module called Dancer2::Plugin::Redis
redis_plugin
Returns a Dancer2::Plugin::Redis instance. You can use redis_plugin to
pass the plugin instance to 3rd party modules (backend api) so you can
access the existing Redis connection there. You will need to access
the actual methods of the the plugin instance.
In case if you are not running a web-server and you are running a worker process or any background job process, you could do this simple helper function to re-use the connection.
perl example
sub get_redis_connection {
my $redis = Redis->new(server => "www.example.com:6372" , debug => 0);
$redis->auth('abcdefghijklmnop');
return $redis;
}
...
## when required
unless($redisclient->ping) {
warn "creating new redis connection";
$redisclient = get_redis_connection();
}
I was running into this issue in my chat app because I was creating a new Redis instance each time something connected rather than just creating it once.
// THE WRONG WAY
export const getRedisPubSub = () => new RedisPubSub({
subscriber: new Redis(REDIS_CONNECTION_CONFIG),
publisher: new Redis(REDIS_CONNECTION_CONFIG),
});
and where I wanted to use the connection I was calling
// THE WRONG WAY
getNewRedisPubsub();
I fixed it by just creating the connection once when my app loaded.
export const redisPubSub = new RedisPubSub({
subscriber: new Redis(REDIS_CONNECTION_CONFIG),
publisher: new Redis(REDIS_CONNECTION_CONFIG),
});
and then I passed the one-time initialized redisPubSub object to my createServer function.
It was this article here that helped me see my error: https://docs.upstash.com/troubleshooting/max_concurrent_connections

Connect to a socket.io server from a Node.js server using the 'ws' package

I have a Node.js server which utilizes the popular ws package for using web sockets. I'd like to use this library to connect to an third party server which is running socket.io.
If I were to use socket.io on my server, the connection code would be something like this:
const socket = socketIo('https://api.example.com/1.0/scores')
I've attempted to connect to the same service using the ws package, and modifying the url:
const wsClient = new WebSocket('wss://api.example.com/1.0/scores');
but this results in the following:
Error: Unexpected server response: 200
Question:
What needs to be done to connect to a third party server running socket.io from a server running the ws package?
Additional Info:
I've noticed in my searches that some people have suggested appending
/socket.io/?EIO=3&transport=websocket to the end of the url. This
does not throw the same error as above (> Error: Unexpected server
response: 200) nor throw any visible error, but does not appear to
work (no data is received from the remote server).
Using new WebSocket('ws://api.example.com/1.0/scores?EIO=3&transport=websocket'); to open the connection (via ws) results in the following stack trace:
{ Error: Parse Error
at Socket.socketOnData
at emitOne
at Socket.emit
// ...
}
The socket.io api utilizes websockets but it also has a lot of other functions built on top of it in order to do things such as HTTP handshakes, session ids, and it can even handle fail overs to other protocols when needed.
You got half of the issue so far. Adding the line socket.io/?EIO=3&transport=websocket you're specifying parameters for the socket.io server to take.
EIO=3 specifies the version number for engine.io in which socket.io is using. In this case you are saying engine.io version = 3
transport=websocket specifies which transport protocol to use. As i said earlier, socket.io uses other protocols in cases such as fail overs. This portion forces socket.io to use websocket as the preferred protocol.
Now the next half is the WebSocket. WebSocket allows for Extensions which includes different kinds of compression that are commonly used when sending data. Which I believe is what is causing your Parse Error
Try this (found here):
const WebSocket = require('ws');
const ws = new WebSocket('ws://server/socket.io/?EIO=3&transport=websocket', {
perMessageDeflate: false
});
By setting perMessageDeflate: false you are specifying "Do not compress data". Since as i said this is a WebSocket Extension there are different variations as well. Try these instead if it doesn't work
x-webkit-deflate-frame
perframe-deflate
As a disclaimer this information is from the research that I have done. Im not a "socket.io specialist" so if there's anything incorrect please comment and i'll edit the post.
Because Socket.IO doesn't guarantee that there will be a WebSockets server hosted like you're seeming to expect, you should instead use their standard client package.
npm i socket.io-client
Then use the package in your code:
const ioClient = require('socket.io-client')('https://example.com/1.0/scores')
The full docs for socket.io-client are available on their GitHub repo.
Note: Honestly, though, it's just better at this point to use WebSockets instead if possible. WebSockets has become well-supported in browsers and is quite standard. Socket.IO is rarely necessary and could add some overhead.

Node JS: net.Socket is not a constructor

The below code works fine if I run it from my command line, but if I run this code anywhere in my project I get this error:
net.Socket is not a constructor
I've tried making the below code an object and importing / requiring it into my project and I still get that error.
var net = require('net');
var client = new net.Socket();
client.connect(3000, '127.0.0.1', function() {
console.log('Connected');
client.write('Hello, server! Love, Client.');
});
Am I miss understanding what require does, I also tried using import and import * as to obtain 'net'.
I'm not too sure what information would be useful in the situation. Any suggestions would be great.
There are no plain TCP sockets in the browser, so that is why trying to use net.Socket in the browser (via webpack, browserify, etc.) won't work.
There could be a "polyfill" of sorts that requires a server to make the TCP connection on the browser's behalf though (or perhaps via some bridge to a Flash or Java applet).
use node 16 version.
these features are currently under Node.js v16.9.1
https://nodejs.org/dist/latest-v16.x/docs/api/net.html

Websocket server running fine but cannot connect from client (what url should I use?)

OK this is very simple to anyone who's used websocket and nodejs.
I have created a websocket server named ws_server.js and put it in C:\Program Files (x86)\nodejs where I have installed the nodejs framework. I started the server and it is running and it says it's listening on port 8080. So far so good, I have the server running.
Now I simply want to connect to it from client code so that I can do all that lovely stuff about capturing events using event listeners etc. The problem is, embarassingly, I cannot figure out what URL to use to connect to my websocket server.
function init() {
testWebSocket();
}
function testWebSocket() {
websocket = new WebSocket("ws://localhost:8080/"); // WHAT URL SHOULD BE USED HERE?
websocket.onopen = function(evt) { alert("OPEN") };
websocket.onclose = function(evt) { alert("CLOSE") };
websocket.onmessage = function(evt) { alert("MESSAGE") };
websocket.onerror = function(evt) { alert("ERROR") };
}
function doSend(message) {
// this would be called by user pressing a button somewhere
websocket.send(message);
alert("SENT");
}
window.addEventListener("load", init, false);
When I use ws://localhost:8080 the only events that trigger are CLOSE and ERROR. I cannot get the client to connect. I must be missing something very simple. Do I need to set up my nodejs folder in IIS for example and then use that as the URL?
Just to reiterate, the websocket server is running fine, I just don't know what URL to use to connect to it from the client.
EDIT: The websocket server reports the following error.
Specified protocol was not requested by the client.
I think I have got it working by doing the following.
var websocket = new WebSocket("ws://localhost:8080/","echo-protocol");
The problem being that I needed to specify a protocol. At least now I get the onopen event. ...if nothing much else
I was seeing the same error, the entire web server goes down. Adding the protocol fixes it but leaves me wondering why it was implemented this way. I mean, one bad request should not bring down your server.
You definitely have to encase it a try/catch, but the example code provided here https://www.npmjs.com/package/websocket (2019-08-07) does not. This issue can be easily avoided.
I just wanted to share a crazy issue that I had. I was able to connect to a websocket of an old version of a 3rd party app in one computer, but not to a newer version of the app in another.
Moreever, even in new computer with the new version of the app, The app was able to connect to the websocket, but no matter what I did, when I tried to connect with my own code, I kept getting the error message that the websocket connection failed
Long story short, They changed an apache configuration that allowed connecting to the websocket via a proxy.
In the old version, apache config was:
ProxyPass /socket/ ws://localhost:33015/ retry=10
ProxyPass /socket ws://localhost:33015/ retry=10
In the new version, apache config was changed to:
ProxyPass /socket/ ws://localhost:33015/ retry=10
By bad luck, I was trying to connect to ws://localhost/socket and not to ws://localhost/socket/. As a result, proxy was not found, and connection returned an error.
Moral of the story: Make sure that you are trying to connect to a websocket url that exists.
For me, the solution was to change the URL from ws:// to wss://. This is because the server I was connecting to had updated its security, and now only accepted wss.

Resources