[quickjs-devel] Re: tcp/udp sockets

  • From: Roman Senn <roman.l.senn@xxxxxxxxx>
  • To: quickjs-devel@xxxxxxxxxxxxx
  • Date: Mon, 20 Feb 2023 05:07:09 +0100

Hi Pranav!

You could've been more specific without sharing any of your code, it would
have been sufficient to mention you're running a nodejs/express server.

I see that you are accepting a POST request with a JSON body, which is a
similar requirement I was seeking to fullfil

I was figuring to implement nodejs 'net' module with the same semantics to
have express.js running ontop of it would not fit quickjs's I/O loop very
well and would inherit nodejs's bad design changes (code debt).

If you are looking to replace your solution with a quickjs one, you would
have to replace all of express.js with another http server.
qjs-net (https://github.com/rsenn/qjs-net) would probably fit that, it's
quite up to the job, even supporting HTTP multipart uploads and dynamic
content serving.

It is based off minnet-quickjs (https://github.com/khanhas/minnet-quickjs)
which utilizes libwebsockets for HTTP server/client functionality.
The original module was very limited though, all blocking I/O, which means
once you started the server the QuickJS main loop didn't run anymore, so no
async functions or timers.

My version is integrated os.setReadHandler from QuickJS, so what you do in
express.js should be achievable with it.
(A code sample for a server.
https://github.com/rsenn/qjs-net/blob/main/tests/server.js)

It is however still very experimental and has a completely different API.
If you're developing production code I can not recommend it at this time,
because if bugs were encountered you would need to go back coding at the
module level in C.

For your database client you'd probably not need sockets directly (you'd
need to implement the database protocol on top of them) but rather binding
the database client library in a QuickJS module providing the necessary
functionality.
For this to work the database client library, which probably does the
socket communication using them directly in C, would need to be integrated
with os.setReadHandler into the I/O loop of QuickJS, or else the whole HTTP
server would block while doing a database query. This is not so trivial,
but would be interesting to do.

What database are you using?
I would be interested to try this, as I'm looking too to use a database
from within QuickJS...

Regards,
Roman

On Fri, Feb 17, 2023 at 1:02 PM Pranav Samvith Bharadwaja <pranav@xxxxxxxxxx>
wrote:

Hi Jeremy, guest271314, Roman
  I am sorry I was away from my mail. I should have responded much
sooner. I can't share the exact code segment, so I will share a
modified version.

const request = require("request");
const http = require('http');
const express = require("express");
const logger = require("morgan");
const bodyparser = require("body-parser");
const debug = require('debug')('MSI:server');

const cmdBroker = express();
cmdBroker.use(logger("tiny"));
cmdBroker.use(bodyparser.json({ limit: "1mb" }));
cmdBroker.use(bodyparser.urlencoded({ limit: "1mb", extended: true }));

cmdBroker.use('/command', async function(req,res){
  var err = '';
  if(!req.body){
      err = 'empty request body';
  } else if(!req.body.port){
      err = 'no port';
  }
  if(err){
      console.error(err);
      return res.status(400).send(err);
  }
  var options = {
    url: `http://localhost:${req.body.port}/command`,
    headers: {
       "Content-Type": "application/json",
    },
    body: req.body,
    json: true,
  };
    request.get(options, function (err, httpResponse, body) {
        res.send(body);
    })

});

cmdBroker.use(function(req,res){
  res.status(404).send("url not found");
});

const port = 2000;

cmdBroker.set('port', port);

const server = http.createServer(cmdBroker);

cmdBroker.set('server', server)

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  const bind = typeof port === 'string'
  ? 'Pipe ' + port
  : 'Port ' + port;

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
        console.error(bind + ' requires elevated privileges');
        process.exit(1);
        break;
    case 'EADDRINUSE':
        console.error(bind + ' is already in use');
        process.exit(1);
        break;
    default:
        throw error;
  }
}

function onListening() {
  const addr = server.address();
  const bind = typeof addr === 'string'
  ? 'pipe ' + addr
  : 'port ' + addr.port;
  debug('Listening on ' + bind);
  console.log('Listening on ' + bind);
}


This is just a featureless proxy, but the code I could not share does
much more including db access, and message queues and ws (all of which
can be bundles with esbuild as mentioned previously by Jeremy). In a
bigger usecase I need to make this take http requests and translate it
into a json.stringified data pushed into a tcp socket. This particular
script is for serving on port 2000 as a proxy to many other servers
running on other ports which are blocked from external access for
security reasons.

Thanks and Regards.

On Fri, Feb 3, 2023 at 9:27 PM Roman Senn <roman.l.senn@xxxxxxxxx> wrote:

Hi Pranav,

I've made two modules which I am using for full network functionality.

The first one implements BSD socket functionality (async and it uses
os.setReadHandler of quickjs):
https://github.com/rsenn/qjs-modules

Altought the classes are named "Socket" and "SockAddr", similar to
nodejs I named the module 'sockets', because the API differs from nodejs
'net': it needs to be incorporated on the event loop of quickjs by
os.setReadHandler().

The second one using libwebsockets (can do clients/servers for UDP raw
sockets but isn't tested):
https://github.com/rsenn/qjs-net

Or it could be done using libffi on unix (winsock would be another
story):
https://github.com/rsenn/qjs-ffi/tree/main/examples

Regards,
Roman

On Mon, Jan 30, 2023 at 7:49 AM Pranav Samvith Bharadwaja <
pranav@xxxxxxxxxx> wrote:


Greetings,
  I have seen this mailchain

https://www.freelists.org/post/quickjs-devel/Newbie-Getting-something-like-require-to-work,1
  I think it is reasonable that net, fs and path are not implemented in
qjs. But is there an alternative by which we could create tcp/udp sockets?
  I am trying to compile a node.js project and with some custom edits
to some node modules, able to bundle them all into a single file (because
require doesnt work with qjs). A database driver would inevitably use the
net built-in module. If an alternative is available i could make
customization for qjs backend versus pre-existing nodejs. Any help is
appreciated.
--
Thanks and Regards,
Pranav.



Other related posts: