Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

async plugins support #775

Closed
MateuszKikmunter opened this issue Sep 1, 2021 · 8 comments
Closed

async plugins support #775

MateuszKikmunter opened this issue Sep 1, 2021 · 8 comments

Comments

@MateuszKikmunter
Copy link

Version: 8.0.3

Environment:

  • Operating system: macOS Big Sur 11.5.2
  • Browser: Google Chrome Version 92.0.4515.159 (Official Build) (x86_64)
  • Node.js: 13.14.0

Expected result: Native support for async plugins.

Hi,

I'm using Primus with websockets and I'm wondering if you support async plugins. I looked at your docs but I didn't find what I need.

Basically I'm trying to pass an async function into Primus constructor like this:

 this.primus = new Primus(server, {
      pathname: '/my-path', plugin: {
        'my-validator': {
          server: myAsyncPlugin //this should run only on the server
        }
      }
    });

The issue is that if I register plugin like that, it does not work and my plugin is always called after other handlers but I want it to be executed before other stuff happens as it validates some incoming parameters. (I also looked at middleware but I couldn't get it working)

So if I register my plugin in constructor and then do the following, my plugin will not work:

this.primus.on('connection', (spark) => {
      //...do stuff
 });

But if I remove plugin from constructor and use following code, everything works as expected as I'm using an async function like so:

this.primus.on('connection', async (spark) => {
      //await myPlugin.doStuff();
      //other stuff
});

And here's how example plugin code looks like:

public doPluginStuff = (primus, options) => {
        primus.on('connection', async (spark) => {
            await exectuteAsyncStuffHere();
        });
    };

Please let me know if there's a way to achieve what I need or I have to call my plugin directly in the 'connection' handler.

Thanks!

@lpinca
Copy link
Member

lpinca commented Sep 3, 2021

I think it's better to use a middleware if the validation should be done before the 'connection' event is emitted.

@MateuszKikmunter
Copy link
Author

I think it's better to use a middleware if the validation should be done before the 'connection' event is emitted.

Hi,

Yes, I looked at middleware and tried to implement that but I had some problems with that.

What I tried looked something like this:

  this.primus.use(() => {
      return async (req, res) => {
        if(await doStuff()) {
          req.socket.destroy();
          return;
        }
      }
    });

And it worked as I wanted to (connection was closed if wrong params were provided) but the client kept creating new connections and I couldn't find out why.

That's why I tried with plugin where I have access to spark and it worked in the way I described in the issue.

Is there a way I use middleware and tell the client to not open new connections?

@lpinca
Copy link
Member

lpinca commented Sep 3, 2021

One issue I see in the above example is that Primus does not support middleware that return a promise. Asynchronous middleware take an additional next argument that should be called. You could do something like this

primus.use(() => {
  return async (req, res, next) => {
    let ret;

    try {
      ret = await doStuff();
    } catch(e) {
      res.destroy();
      return;
    }

    if (ret) {
      res.destroy();
      return;
    }

    next();
  }
});

@MateuszKikmunter
Copy link
Author

One issue I see in the above example is that Primus does not support middleware that return a promise. Asynchronous middleware take an additional next argument that should be called. You could do something like this

primus.use(() => {
  return async (req, res, next) => {
    let ret;

    try {
      ret = await doStuff();
    } catch(e) {
      res.destroy();
      return;
    }

    if (ret) {
      res.destroy();
      return;
    }

    next();
  }
});

I tried it but the issue is that I'm using TypeScript and definition in the declaration file does not allow addional next parameter as it looks this:

type Middleware = (req: http.IncomingMessage, res: http.ServerResponse) => void;

And use function looks like this:

use(fn: () => Middleware | Middleware, options?: object, level?: number): this;

Aynyway I overwrote it and I tried something like this:

    this.primus.use(() => {
      return async (req, res, next) => {

        try {
          if (!await doStuff()) {
            res.destroy();
            return;
          }
        } catch (error) {
          res.destroy();
          return;
        }

        next();
      }
    });

Unfortunately it still didn't work so most likely I'll have to stick to calling the plugin directly in the 'connection' handler. Unless there's another solution.

@lpinca
Copy link
Member

lpinca commented Sep 3, 2021

What is the issue? The client keeps connecting when "validation" fails? If so it might be a reconnection issue. The client does not know if the disconnection was caused by a validation failure. In that case you might want to disable automatic reconnection.

@MateuszKikmunter
Copy link
Author

MateuszKikmunter commented Sep 3, 2021

What is the issue? The client keeps connecting when "validation" fails? If so it might be a reconnection issue. The client does not know if the disconnection was caused by a validation failure. In that case you might want to disable automatic reconnection.

Yes, this is exactly the issue and I think that you're right about the reconnection. The issue is that I'm not sure if I can disable it but I would say that we can close this issue with that conclusion. Thank you very much for your help!

@lpinca
Copy link
Member

lpinca commented Sep 3, 2021

You can try to remove the 'timeout' strategy and see it makes any difference. See https://github.com/primus/primus#strategy.

See also https://github.com/primus/mirage for an example of an "async" plugin but I don't think it makes much sense in your case.

@MateuszKikmunter
Copy link
Author

You can try to remove the 'timeout' strategy and see it makes any difference. See https://github.com/primus/primus#strategy.

See also https://github.com/primus/mirage for an example of an "async" plugin but I don't think it makes much sense in your case.

Yeah, I tried removing the strategy after I closed the issue and it worked like I wanted to but unfortunately we need some reconnection strategy in our app so this is not applicable in my case.

I'll have look at the async plugin example you referenced, thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants