How do I catch object creation using a proxy? - javascript

Thank you for taking the time to read my problem. A website's javascript I'm reverse engineering executes the following line:
this.socket = new WebSocket(o.host)
The line is buried in functions of other functions, etc. so I am not able to access it normally during runtime. I would like to use a Proxy object to "inject" code whenever new Websocket() is called in order to notify me of its creation and save it to a global variable I would prepare prior.
My greasemonkey script would look something like this:
var caught_socket; // prepare variable
handler = {
apply: function(target, thisArg, argumentsList) {
console.log("WebSocket was created!");
caught_socket = target;
}
}
WebSocket = new Proxy(WebSocket, handler);
This obviously does not work and I would be grateful for any help or demonstration of how Proxies actually work!

Use Proxy.construct() instead, it will execute when new is called.
// Create a new WebSocket proxy
WebSocket = new Proxy(WebSocket, {
construct: (target, args) => {
console.log('New WebSocket Instance!')
return new target(...args)
}
})
// Test the WebSocket proxy by connecting to a remote server
const ws = new WebSocket('wss://echo.websocket.org')
// Listen for an open connection
// Send a message to the connection
ws.addEventListener('open', () => {
console.log('Connection Open!')
ws.send('hello')
})
// Listen for messages from the remote server
ws.addEventListener('message', msg => console.log('Response:', msg.data))
If you add a second proxy both proxies execute:
// Create a new WebSocket proxy
WebSocket = new Proxy(WebSocket, {
construct: (target, args) => {
console.log('New WebSocket Instance!')
return new target(...args)
}
})
WebSocket = new Proxy(WebSocket, {
construct: (target, args) => {
console.log('New WebSocket Override!')
return new target(...args)
}
})
// Test the WebSocket proxy by connecting to a remote server
const ws = new WebSocket('wss://echo.websocket.org')
// Listen for an open connection
// Send a message to the connection
ws.addEventListener('open', () => {
console.log('Connection Open!')
ws.send('hello')
})
// Listen for messages from the remote server
ws.addEventListener('message', msg => console.log('Response:', msg.data))
You could even return a completely different class:
class MyCoolWebSocket {
addEventListener(event, callback) {
console.log('override event:', event)
}
}
// Create a new WebSocket proxy
WebSocket = new Proxy(WebSocket, {
construct: (target, args) => {
console.log('New WebSocket Instance!')
return new WebSocket(...args)
}
})
// Create a new WebSocket proxy
WebSocket = new Proxy(WebSocket, {
construct: (target, args) => {
console.log('Greasemonkey override!')
return new MyCoolWebSocket(...args)
}
})
// Test the WebSocket proxy by connecting to a remote server
const ws = new WebSocket('wss://echo.websocket.org')
// Listen for an open connection
// Send a message to the connection
ws.addEventListener('open', () => {
console.log('Connection Open!')
ws.send('hello')
})
// Listen for messages from the remote server
ws.addEventListener('message', msg => console.log('Response:', msg.data))

Related

Websocket is unable to reconnect after restarting the server in Javascript

I have a simple client-side script like this:
function connect() {
const { contextBridge } = require('electron');
var ws = new WebSocket('ws://localhost:3000');
ws.onerror = (error) => {
console.error(`Lost connection to server. Reason: ${error.message}`);
console.error('Attempting to reconnect...');
ws.close();
}
ws.onclose = (e) => {
setTimeout({
connect();
}, 500);
}
ws.addEventListener('open', () => {
console.log('Connected to server!');
});
// Some other stuff to call functions via the browser console
const API = {
ws_isOpen: () => { return ws.readyState === ws.OPEN }
}
contextBridge.exposeInMainWorld('api', API);
function send_msg(msg) {
// Process some data...
ws.send(msg);
}
}
connect();
It works normally when the server is running and it's trying to connect, or when the server is rebooting and it's trying to connect for the first time, but not while it's connected. What I mean is that, if I were to suddenly shut the server down while the client is being connected to it, it attempts to try to reconnect as usual and the success message does pop up. However, if I type in window.api.ws_isOpen() in the browser console, it returns false. When I try to send a message, an error pops up saying something like Websocket is already in CLOSING or CLOSED stage. I tried changing the ws variable type to let and const but it doesn't work.
Turns out the answer is really simple. For some reason, when I put the ws variable outside the connect() function and modify it in the function, it works. I'm guessing it kinda re-declares/re-new the ws variable. It looks something like this:
var ws = null;
function connect() {
ws = new WebSocket('ws://localhost:3000');
// the exact same as above here....
}
connect();
After rebooting the server and letting it reconnect:
>> window.api.ws_isOpen()
true
I feel like I'm supposed to know how this works...

React Websocket gets inactive after some time

I am using Azure Pub-Sub Service for Chatting module in a ReactApplication, I am creating this connection using Websocket.
let ws = new WebSocket(token.url);
ws.onmessage = (data) => {
//Messages Logic
}
when i am in other tabs, or in the sametab for longer time(more than 40-45 mins). I am not receiving messages, but when i refresh the page and websocket initialization code gets executed again and then i receive messages again. Any Suggestions?
Use this technique :
function connect() {
var ws = new WebSocket('ws://localhost:8080');
ws.onopen = function() {
// subscribe to some channels
ws.send(JSON.stringify({
//.... some message the I must send when I connect ....
}));
};
ws.onclose = function(e) {
console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason);
setTimeout(function() {
connect();
}, 1000);
};

Javascript: How can I interact with a TCP Socket API using net.connect properly?

I'm fairly new to Javascript and am trying to wrap my head around async, promises, etc.
I have an application running a TCP API (non-HTTP) on the localhost. I'm building an Electron app to interact with this API. I need to send a single request to the API every second and retrieve a single JSON object it returns.
I'm able to do this successfully (for while) by running something like this:
const net = require('net');
function apiCall() {
if (running) {
setTimeout(() => {
// Send the request
request = '{"id":1,"jsonrpc":"2.0","method":"getdetails"}'
socketClient = net.connect({host:'localhost', port:8888}, () => {
socketClient.write(request + '\r\n');
});
// Listen for the response
var response;
socketClient.on('data', (data) => {
response = JSON.parse(data).result;
updateUI(response);
socketClient.end();
});
// On disconnect
socketClient.on('end', () => {
console.log('Disconnected from API');
});
apiCall();
}, refreshRate)
}
}
After running this for an extended amount of time, it appears that the API server is crashing:
Error: connect ECONNREFUSED 127.0.0.1:8888
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1146)
Unfortunately, I have no control over the API server or its source code. I'd like some clarification on whether my client might be causing the API server to crash by sending requests this way.
Should I be opening and closing the connection for each request or keep it open and send requests only every second?
If I should be keeping the connection open, how can I do this, and do I need to worry about keep-alive?
It looks like that every time you call apiCall you are creating a new socket client and you are not removing the old socket client instances. This is a memory leak and it will cause the application to crash after running for some time
You can keep a running connection instead like below
const net = require("net");
const { once } = require("events");
let socketClient;
function apiCall() {
if (running) {
setTimeout(async () => {
const request = '{"id":1,"jsonrpc":"2.0","method":"getdetails"}';
// Create the socket client if it was not already created
if (!socketClient) {
socketClient = net.connect({ host: "localhost", port: 8888 });
// On disconnect
socketClient.on("end", () => {
console.log("Disconnected from API");
socketClient.destroy();
socketClient = null;
});
// Wait until connection is established
await once(socketClient, "connect");
}
// Send the request
socketClient.write(request + "\r\n");
// Listen for the response
const data = await once(socketClient, "data");
const response = JSON.parse(data).result;
updateUI(response);
apiCall();
}, refreshRate);
}
}

Create multiple ftp clients to fetch files from different servers respectively. Is this possible in node?

So in my setup I have about 7 different ftp servers running on my network, and I want to be able to create multiple ftp clients in my node app to fetch some files from these servers respectively. I was wondering if that is even possible or not? I am looking at using the ftp npm package: https://www.npmjs.com/package/ftp
I have tried a couple things like the following where I create multiple instances of the client in an array and then spawn them off by doing a connect at the end of the iteration.
// Create all the clients for each host
// Note that config here is just a JSON object I read at the start
numAddresses = Object.keys(config.addresses).length; //7
clients=[];
for (address in config.addresses) {
// current address being processed
console.log(config.addresses[address]);
// create a new client for this address
clients[address] = createClient(path);
// update the settings for this new client based on address
// note we keep the default anonymous login credential
// you can change it here though
connectionSettings.host = config.addresses[address].ip;
connectionSettings.port = config.addresses[address].port;
console.log(connectionSettings);
// connect the current client with the associated settings
clients[address].connect(connectionSettings);
}
// creates a ftp client and returns it
function createClient(path) {
var client = new Client();
// define wait for the server handshake (greeting)
client.on('greeting', afterGreeting);
return client;
}
// handles the flow after server handshake (greeting)
function afterGreeting(msg, client) {
console.log(msg);
// define wait for the server 'ready' state
client.on('ready', afterReady);
}
// handles the flow after server 'ready' state
function afterReady() {
console.log('ready');
performListing(path);
}
// handles the listing of the files
function performListing(path) {
console.log('fetching from:'+path);
client.list(path, performFetch);
}
// handles the fetching of the files
function performFetch(err, list){
if (err) throw err;
list.forEach(function (element, index, array) {
console.log('now copying: '+element.name);
});
client.end();
}
I expected this to just spawn off all the instances of those clients in the loop but I get this error:
D:\node_workspace\ftp\main.js:52
client.on('ready', afterReady);
TypeError: Cannot read property 'on' of undefined
I think I am running into some very basic error with my assumptions of how node works (I am a beginner). Any help would be appreciated.
Try the following code...
var Client = require("ftp");
numAddresses = Object.keys(config.addresses).length;
clients = [];
for (address in config.addresses) {
// current address being processed
console.log(config.addresses[address]);
// create a new client for this address
clients[address] = createClient(path);
// update the settings for this new client based on address
// note we keep the default anonymous login credential
// you can change it here though
connectionSettings.host = config.addresses[address].ip;
connectionSettings.port = config.addresses[address].port;
console.log(connectionSettings);
// connect the current client with the associated settings
clients[address].connect(connectionSettings);
}
function createClient(path) {
var c = new Client();
c.on('greeting', (msg) => {
afterGreeting(msg, c);
});
return c;
}
function afterGreeting(msg, client) {
console.log(msg);
client.on('ready', () => {
afterReady(client);
});
}
function afterReady(client) {
console.log('ready');
performListing(path, client);
}
function performListing(path, client) {
console.log('fetching from:' + path);
client.list(path, (err, list) => {
performFetch(err, list, client);
});
}
function performFetch(err, list, client) {
if (err) throw err;
list.forEach(function (element, index, array) {
console.log('now copying: ' + element.name);
});
client.end();
}
Updated
var Client = require("ftp");
numAddresses = Object.keys(config.addresses).length;
clients = [];
for (address in config.addresses) {
// current address being processed
console.log(config.addresses[address]);
// create a new client for this address
clients[address] = createClient(path);
// update the settings for this new client based on address
// note we keep the default anonymous login credential
// you can change it here though
connectionSettings.host = config.addresses[address].ip;
connectionSettings.port = config.addresses[address].port;
console.log(connectionSettings);
// connect the current client with the associated settings
clients[address].connect(connectionSettings);
}
function createClient(path) {
// initialize new client
var c = new Client();
// official npm documentation https://www.npmjs.com/package/ftp#events
// tells us, that greeting returns a string so we need to pass a callback
// to c.on('greeting', callback)
// we could either write function(msg) { } or short form (msg) => { }
c.on('greeting', (msg) => {
afterGreeting(msg, c);
});
return c;
}
function afterGreeting(msg, client) {
console.log(msg);
// ready needs a callback too but returns nothing, only calls this callback
// if ready is ready. Kappa.
client.on('ready', () => {
afterReady(client);
});
}
function afterReady(client) {
console.log('ready');
performListing(path, client);
}
function performListing(path, client) {
console.log('fetching from:' + path);
// client.list returns an err if we encounter an error and a list
// our callback is written in shortform (err, list) => { } so we're
// passing those callback data to this anonymous function
client.list(path, (err, list) => {
performFetch(err, list, client);
});
}
function performFetch(err, list, client) {
if (err) throw err;
list.forEach(function (element, index, array) {
console.log('now copying: ' + element.name);
});
client.end();
}
Answer to question:
Depends on your program. You always can rewrite and optimize a program.

How to reconnect a websocket connection after manual close - Vue

Here's my methods to open/close socket connection:
methods: {
connect () {
this.$socket.onopen = () => {
this.status = 'connected'
this.$socket.onmessage = ({data}) => {
this.$socket.send(this.message)
console.log({ event: "Recieved message", data })
}
}
this.$socket.onclose = (e) => {
console.log('Socket is closed. Reconnect will be attempted in 1 second.')
setTimeout(() => {
this.connect()
}, 1000)
}
},
disconnect () {
this.$socket.close()
this.status = 'disconnected'
}
}
I'm not using socket.io etc, just built in Websocket object.
When i manually call disconnect () method - it closes the connection as expected, but when i send message again - it says that connection is closed. Since i call connect () in a mounted, then it won't reconnect if i don't refresh the page.
I've tried using watcher:
watch: {
'$socket': 'connect'
}
But no effect. Is there a way to watch for websocket connection status? If it's closed - call connect (), if it's error'ed - call connect () to reconnect it.
Your connect() method does nothing to "reconnect" to the WebSocket server. So calling this.connect() simply rewrites your onopen handler.
You have to take the steps necessary to reconnect to the WebSocket server. The most excellent answer to this question does a great job of explaining a great structure for your code:
vue: emitting global events from websocket listener
Unfortunately, it doesn't answer your specific question. So I've forked the sandbox from that answer and added the modified code below that allows you to achieve your goal.
import Vue from "vue";
const url = "wss://echo.websocket.org";
let socket;
const emitter = new Vue({
methods: {
send(message) {
if (1 === socket.readyState) socket.send(message);
},
close() {
if (1 === socket.readyState) {
emitter.$emit("message", "Closing Socket.");
socket.close();
socket = null; // prevent memory leak
}
},
connect() {
socket = new WebSocket(url);
socket.onmessage = function(msg) {
emitter.$emit("message", msg.data);
};
socket.onerror = function(err) {
emitter.$emit("error", err);
};
emitter.$emit("message", "Openning Socket.");
}
}
});
emitter.connect();
export default emitter;
To see how this service is used, check out index.js in the running sample is here:
https://codesandbox.io/s/ry4993q654
You should check the close status code before reconnecting.
e.code === 1e3 || e.code === 1001 || e.code === 1005

Categories

Resources