Receive non HTTP requests in node.js - javascript

I need to receive requests in Node JS that almost identical to HTTP requests, but have a different word to HTTP in the header for example the first line of the request is:
POST / RTSP/1.0
(instead of POST / HTTP/1.0)
The rest of the request format is identical to HTTP in every way.
Is there a way of making the http server parser ignore that the protocol is called HTTP in the first line of the request? So I can use http.createServer etc to receive and respond to these "non-HTTP" requests?
(I know I could use the net module rather than the http module, but then I'd have to implement the header parsing etc, all myself. )

Simplest way would be to implement tcp server in node that acts as a proxy replacing initial POST:
( note: I haven't tested this code, but it should be good enough to illustrate idea)
var net = require('net');
net.createServer(function(s)
{
var sawRequest = false;
var buff = "";
var requestText = "";
var connected = false;
var cli = net.createConnection(yourHttpServerPort);
s.on('data', function(d) {
if (!sawRequest) {
requestText += d.toString();
if (requestText.match(/POST \/ RTSP\/1.0/)) {
requestText.replace('POST / RTSP/1.0', 'POST / HTTP/1.0');
buff = requestText;
sawRequest = true;
}
} else {
buff += d.toString();
}
if (connected)
{
if (buff != '')
cli.write(buff);
cli.write(d);
} else {
buff += d.toString();
}
});
cli.once('connect', function() {
connected = true;
cli.write(buff);
});
cli.pipe(s);
}).listen(publicFacingRTSPport);
You could probably do the same with HAProxy

Related

Usage of web-worker for lobby updating (and maybe chat)?

I created a web version of a board game and made some kind of lobby that people can join.
Once the player joined the lobby a webworker (https://www.w3schools.com/html/html5_webworkers.asp) will start and check the current players within the lobby to display them.
calling part:
<script>
var w;
function startWorker() {
if (typeof(Worker) !== "undefined") {
if (typeof(w) == "undefined") {
//w = new Worker("demo_workers.js");
w = new Worker("js/lobbyUpdater.js");
w.postMessage(localStorage.groupID);
}
w.onmessage = function(event) {
var response = JSON.parse(event.data);
var player = response.player;
var playerarry = player.split(":");
document.getElementById("playerlist").innerHTML = "";
var i = 0;
for (i; i < response.playerCount; i++) {
var singleplayer = playerarry[i].split(",");
if (singleplayer[1] == localStorage.playerNumber) {
document.getElementById("playerlist").innerHTML += '<li><b>' + playerarry[i] + '</b></li>';
} else {
document.getElementById("playerlist").innerHTML += '<li>' + playerarry[i] + '</li>';
}
}
document.getElementById("currentPlayerAmount").innerHTML = response.playerCount;
if (response.closed == 1) {
window.location.href = "playerpage.html";
}
console.log(event.data);
};
} else {
document.getElementById("result").innerHTML = "Sorry, your browser does not support Web Workers...";
}
}
function stopWorker() {
w.terminate();
w = undefined;
}
startWorker();
</script>
webworker:
function updateLobby(groupID) {
var Http = new XMLHttpRequest();
var url = '../php/checkLobby.php';
var preGroupID = '?groupID=';
url = url.concat(preGroupID, groupID);
Http.open("GET", url);
Http.send();
Http.onreadystatechange = (e) => {
//console.log(Http.responseText)
postMessage(Http.responseText);
}
var string = "updateLobby('";
string = string.concat(groupID, "')");
setTimeout(string, 1500);
}
onmessage = function (e) {
var groupID = '';
groupID = e.data;
updateLobby(groupID);
};
The weborker is running every 1.5 seconds but i thought that would be very often (every round will create a query on the backend).
Now i thought about creating a chatroom where player can talk to each other. To receive messages i though about starting an another webworker that will check for the messages.
Is the usage of webworker generally ok or am i using a "unwanted" technology for that purpose.
Is there better solution for this ?
Will the usage of such frequent sql querys in the backend lead to extreme performance peaks ? I am not experience when it comes to "how many querys can my 2c/4GB database server handle".
Thanks in advance!
In my opinion, the best solution would be to use the WebSocket API. It allows you to connect with the server then the server is able to send requests to the client! So the client does not spam the server with checking requests and he always gets fresh information when something changed.
Browser's WebSocket API is supported by each modern browser expect Opera Mini: https://caniuse.com/#feat=websockets
Integration Client-side is pretty easy. About Server-side - I did not try to create WebSocket connection with PHP so I cannot say much about that.
Alternative solution, might be Firebase Realtime database. There you can read tutorial about it: https://css-tricks.com/building-a-real-time-chat-app-with-react-and-firebase/

How can I receive data on client side before calling .end() on the server side for a gRPC stream

I am currently trying to setup a server stream with the gRPC Node.js API. For that I want to achieve that when I write on server side to the stream that the client immediately receives the data event.
At the moment I don't receive anything on client side if I only call write on server side. However as soon as I call the end function on the server the client receives all data events.
To test this I used an endless while loop for writing messages on server side. Then the client does not receive messages (data events). If instead I use a for loop and call end afterwards the client receives all the messages (data events) when end is called.
My .proto file:
syntax = "proto3";
message ControlMessage {
enum Control {
Undefined = 0;
Start = 1;
Stop = 2;
}
Control control = 1;
}
message ImageMessage {
enum ImageType {
Raw = 0;
Mono8 = 1;
RGB8 = 2;
}
ImageType type = 1;
int32 width = 2;
int32 height = 3;
bytes image = 4;
}
service StartImageTransmission {
rpc Start(ControlMessage) returns (stream ImageMessage);
}
On the server side I implement the start function and try to endlessly write messages to the call:
function doStart(call) {
var imgMsg = {type: "Mono8", width: 600, height: 600, image: new ArrayBuffer(600*600)};
//for(var i = 0; i < 10; i++) {
while(true) {
call.write(imgMsg);
console.log("Message sent");
}
call.end();
}
I register the function as service in the server:
var server = new grpc.Server();
server.addService(protoDescriptor.StartImageTransmission.service, {Start: doStart});
On client side I generate an appropriate call and register the data and end event:
var call = client.Start({control: 0});
call.on('data', (imgMessage) => {
console.log('received image message');
});
call.read();
call.on('end', () => {console.log('end');});
I also tried to write the server side in python. In this case the node client instantly receives messages and not only after stream was ended on server side. So I guess this should be also possible for the server written with the Node API.
It seems that the problem was that the endless while loop is blocking all background tasks in node. A possible solution is to use setTimeout to create the loop. The following code worked for me:
First in the gRPC call store the call object in an array:
function doStart(call) {
calls.push(call);
}
For sending to all clients I use a setTimeout:
function sendToAllClients() {
calls.forEach((call) => {
call.write(imgMsg);
});
setTimeout(sendToAllClients, 10);
}
setTimeout(sendToAllClients, 10);
Helpful stackoverflow atricle: Why does a while loop block the event loop?
I was able to use uncork which comes from Node.js's Writable.
Here is an example. Pseudocode, but pulled from across a working implementation:
import * as grpc from '#grpc/grpc-js';
import * as proto from './src/proto/generated/organizations'; // via protoc w/ ts-proto
const OrganizationsGrpcServer: proto.OrganizationsServer = {
async getMany(call: ServerWritableStream<proto.Empty, proto.OrganizationCollection>) {
call.write(proto.OrganizationCollection.fromJSON({ value: [{}] }));
call.uncork();
// do some blocking stuff
call.write(proto.OrganizationCollection.fromJSON({ value: [{}] }));
call.uncork();
// call.end(), or client.close() below, at some point?
},
ping(call, callback) {
callback(null);
}
};
const client = new proto.OrganizationsClient('127.0.0.1:5000', grpc.credentials.createInsecure());
const stream = client.getMany(null);
stream.on('data', data => {
// this cb should run twice
});
export default OrganizationsGrpcServer;
//.proto
service Organizations {
rpc GetMany (google.protobuf.Empty) returns (stream OrganizationCollection) {}
}
message OrganizationCollection {
repeated Organization value = 1;
}
Versions:
#grpc/grpc-js 1.4.4
#grpc/proto-loader 0.6.7
ts-proto 1.92.1
npm 8.1.4
node 17

Node js Error: Protocol "https:" not supported. Expected "http:"

I am using IBM Bluemix to make a web service for a school project.
My project needs to request a JSON from an API, so I can use the data it provides. I use the http get method for a data set, and I am not sure if it is working properly.
When I run my code, I get the message:
Error: Protocol "https:" not supported. Expected "http:"
What is causing it and how can I solve it?
Here is my .js file:
// Hello.
//
// This is JSHint, a tool that helps to detect errors and potential
// problems in your JavaScript code.
//
// To start, simply enter some JavaScript anywhere on this page. Your
// report will appear on the right side.
//
// Additionally, you can toggle specific options in the Configure
// menu.
function main() {
return 'Hello, World!';
}
main();/*eslint-env node*/
//------------------------------------------------------------------------------
// node.js starter application for Bluemix
//------------------------------------------------------------------------------
// HTTP request - duas alternativas
var http = require('http');
var request = require('request');
// cfenv provides access to your Cloud Foundry environment
// for more info, see: https://www.npmjs.com/package/cfenv
var cfenv = require('cfenv');
//chama o express, que abre o servidor
var express = require('express');
// create a new express server
var app = express();
// serve the files out of ./public as our main files
app.use(express.static(__dirname + '/public'));
// get the app environment from Cloud Foundry
var appEnv = cfenv.getAppEnv();
// start server on the specified port and binding host
app.listen(appEnv.port, '0.0.0.0', function() {
// print a message when the server starts listening
console.log("server starting on " + appEnv.url);
});
app.get('/home1', function (req,res) {
http.get('http://developers.agenciaideias.com.br/cotacoes/json', function (res2) {
var body = '';
res2.on('data', function (chunk) {
body += chunk;
});
res2.on('end', function () {
var json = JSON.parse(body);
var CotacaoDolar = json["dolar"]["cotacao"];
var VariacaoDolar = json["dolar"]["variacao"];
var CotacaoEuro = json["euro"]["cotacao"];
var VariacaoEuro = json["euro"]["variacao"];
var Atualizacao = json["atualizacao"];
obj=req.query;
DolarUsuario=obj['dolar'];
RealUsuario=Number(obj['dolar'])*CotacaoDolar;
EuroUsuario=obj['euro'];
RealUsuario2=Number(obj['euro'])*CotacaoEuro;
Oi=1*VariacaoDolar;
Oi2=1*VariacaoEuro;
if (VariacaoDolar<0) {
recomend= "Recomenda-se, portanto, comprar dólares.";
}
else if (VariacaoDolar=0){
recomend="";
}
else {
recomend="Recomenda-se, portanto, vender dólares.";
}
if (VariacaoEuro<0) {
recomend2= "Recomenda-se, portanto, comprar euros.";
}
else if (VariacaoEuro=0){
recomend2="";
}
else {
recomend2="Recomenda-se,portanto, vender euros.";
}
res.render('cotacao_response.jade', {
'CotacaoDolar':CotacaoDolar,
'VariacaoDolar':VariacaoDolar,
'Atualizacao':Atualizacao,
'RealUsuario':RealUsuario,
'DolarUsuario':DolarUsuario,
'CotacaoEuro':CotacaoEuro,
'VariacaoEuro':VariacaoEuro,
'RealUsuario2':RealUsuario2,
'recomend':recomend,
'recomend2':recomend2,
'Oi':Oi,
'Oi2':Oi2
});
app.get('/home2', function (req,res) {
http.get('https://www.quandl.com/api/v3/datasets/BCB/432.json?api_key=d1HxqKq2esLRKDmZSHR2', function (res3) {
var body = '';
res3.on('data', function (chunk) {
body += chunk;
});
res3.on('end', function () {
var x=json.dataset.data[0][1];
console.log("My JSON is "+x); });
});
});
});
});
});
Here is a print of the error screen I get:
When you want to request an https resource, you need to use https.get, not http.get.
https://nodejs.org/api/https.html
As a side note to anyone looking for a solution from Google... make sure you are not using an http.Agent with an https request or you will get this error.
The reason for this error is that you are trying to call a HTTPS URI from a HTTP client. The ideal solution would have been for a generic module to figure out the URI protocol and take the decision to use HTTPS or HTTP internally.
The way I overcame this problem is by using the switching logic on my own.
Below is some code which did the switching for me.
var http = require('http');
var https = require('https');
// Setting http to be the default client to retrieve the URI.
var url = new URL("https://www.google.com")
var client = http; /* default client */
// You can use url.protocol as well
/*if (url.toString().indexOf("https") === 0){
client = https;
}*/
/* Enhancement : using the URL.protocol parameter
* the URL object , provides a parameter url.protocol that gives you
* the protocol value ( determined by the protocol ID before
* the ":" in the url.
* This makes it easier to determine the protocol, and to support other
* protocols like ftp , file etc)
*/
client = (url.protocol == "https:") ? https : client;
// Now the client is loaded with the correct Client to retrieve the URI.
var req = client.get(url, function(res){
// Do what you wanted to do with the response 'res'.
console.log(res);
});
Not sure why, but the issue for me happened after updating node to version 17, i was previously using version 12.
In my setup, i have node-fetch using HttpsProxyAgent as an agent in the options object.
options['agent'] = new HttpsProxyAgent(`http://${process.env.AWS_HTTP_PROXY}`)
response = await fetch(url, options)
Switching back to node 12 fixed the problem:
nvm use 12.18.3
I got this error while deploying the code.
INFO error=> TypeError [ERR_INVALID_PROTOCOL]: Protocol "https:" not supported. Expected "http:"
at new NodeError (node:internal/errors:372:5)
To fix this issue, I have updated the "https-proxy-agent" package version to "^5.0.0"
Now the error was gone and it's working for me.

How to detect client disconnection from node.js server

I am new to node.js. How to detect client is disconnected from node.js server .
Here is my code:
var net = require('net');
var http = require('http');
var host = '192.168.1.77';
var port = 12345;//
var server = net.createServer(function (stream) {
stream.setEncoding('utf8');
stream.on('data', function (data) {
var comm = JSON.parse(data);
if (comm.action == "Join_Request" && comm.gameId =="game1") // join request getting from client
{
var reply0 = new Object();
reply0.message = "WaitRoom";
stream.write(JSON.stringify(reply0) + "\0");
}
});
stream.on('disconnect', function() {
});
stream.on('close', function () {
console.log("Close");
});
stream.on('error', function () {
console.log("Error");
});
});
server.listen(port,host);
How to know client side internet disconnection.
The best way to detect "dead sockets" is to send periodic application-level ping/keepalive messages. What that message looks like depends on the protocol you're using for communicating over the socket. Then it's just a matter of using a timer or other means of checking if you've received a "ping response" within a certain period of time after you sent the ping/keepalive message to the client.
On a semi-related note, it looks like you're using JSON messages for communication, but you're assuming a complete JSON string on every data event which is a bad assumption. Try using a delimiter (a newline is pretty common for something like this, and it makes debugging the communication more human-readable) instead.
Here is a simple example of how to achieve this:
var PING_TIMEOUT = 5000, // how long to wait for client to respond
WAIT_TIMEOUT = 5000; // duration of "silence" from client until a ping is sent
var server = net.createServer(function(stream) {
stream.setEncoding('utf8');
var buffer = '',
pingTimeout,
waitTimeout;
function send(obj) {
stream.write(JSON.stringify(obj) + '\n');
}
stream.on('data', function(data) {
// stop our timers if we've gotten any kind of data
// from the client, whether it's a ping response or
// not, we know their connection is still good.
clearTimeout(waitTimeout);
clearTimeout(pingTimeout);
buffer += data;
var idx;
// because `data` can be a chunk of any size, we could
// have multiple messages in our buffer, so we check
// for that here ...
while (~(idx = buffer.indexOf('\n'))) {
try {
var comm = JSON.parse(buffer.substring(0, idx));
// join request getting from client
if (comm.action === "Join_Request" && comm.gameId === "game1") {
send({ message: 'WaitRoom' });
}
} catch (ex) {
// some error occurred, probably from trying to parse invalid JSON
}
// update our buffer
buffer = buffer.substring(idx + 1);
}
// we wait for more data, if we don't see anything in
// WAIT_TIMEOUT milliseconds, we send a ping message
waitTimeout = setTimeout(function() {
send({ message: 'Ping' });
// we sent a ping, now we wait for a ping response
pingTimeout = setTimeout(function() {
// if we've gotten here, we are assuming the
// connection is dead because the client did not
// at least respond to our ping message
stream.destroy(); // or stream.end();
}, PING_TIMEOUT);
}, WAIT_TIMEOUT);
});
// other event handlers and logic ...
});
You could also just have one interval instead of two timers that checks a "last data received" timestamp against the current timestamp and if it exceeds some length of time and we have sent a ping message recently, then you assume the socket/connection is dead. You could also instead send more than one ping message and if after n ping messages are sent and no response is received, close the connection at that point (this is basically what OpenSSH does).
There are many ways to go about it. However you may also think about doing the same on the client side, so that you know the server didn't lose its connection either.

Winsock.SendData equivalent with Javascript?

Can the following VB Script to open an IP cash drawer be done in Javascript instead?
Private Sub CashDrawerConnect_Click()
Winsock1.Close
ipaddr = "192.168.2.5"
Winsock1.RemoteHost = ipaddr
Winsock1.RemotePort = 30998
Winsock1.Connect
Sleep 250
TxtOpStatus = "Connection to the cash drawer at " & ipaddr & " is established..."
TxtOpStatus.Refresh
End Sub
Private Sub CashDrawerOpen_Click()
If Winsock1.State = sckConnected Then
Winsock1.SendData "opendrawer\0a"
Else
TxtOpStatus = "Not connected to the device"
TxtOpStatus.Refresh
End If
End Sub
You could do it on javascript, but not while running on a browser.
You would need to install nodejs and run your js file directly from the console.
This is a small example that would connect you the the drawer and send the "opendrawer" command on your example:
var net = require('net');
var client = net.connect({port: 30998, host: "yourip"}, function() {
client.write("opendrawer\0a");
});
If however the server has access to the drawer the javascript code could just make a request to the server which would be on charge of opening the connection to the drawer and sending the payload (opendrawer).
If you use php you can take a look at the sockets documentation.
Using VB and JavaScript the calls are mostly the same, you just jhave to adapt it to the language. http://www.ostrosoft.com/oswinsck/oswinsck_javascript.asp
The following is a snippet that uses WinSock from JScript
var oWinsock;
var sServer = "192.168.2.5";
var nPort = 3098;
var bClose = false;
oWinsock = new ActiveXObject("OSWINSCK.Winsock");
// Hooking up handlers
WScript.ConnectObject(oWinsock, "oWinsock_");
oWinsock.Connect(sServer, nPort);
WScript.Echo("Invalid URL");
bClose = true;
function oWinsock_OnConnect() {
oWinsock.SendData('Your data');
}
function oWinsock_OnDataArrival(bytesTotal) {
var sBuffer = oWinsock.GetDataBuffer();
sSource = sSource + sBuffer;
}
function oWinsock_OnError(Number, Description, Scode, Source,
HelpFile, HelpContext, CancelDisplay) {
WScript.Echo(Number + ': ' + Description);
}
function oWinsock_OnClose() {
oWinsock.CloseWinsock();
WScript.Echo(sSource);
oWinsock = null;
bClose = true;
}
while (!bClose) {
WScript.Sleep(1);
}
In the browser? Not really, but you can use WebSockets http://en.wikipedia.org/wiki/WebSocket
You'll need to implement a WebSocket server, so if you need to talk directly to a socket, you can't do it from a browser. But you could implement a proxy server that relays information between the socket server and the WebSocket server.
If you don't need two way communication, the best thing would be for your server to provide a webservice that wraps that socket request. Then your client can just make an AJAX call.

Categories