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

Discuss: passing in own http(s) server #20

Open
rikkertkoppes opened this issue Mar 28, 2020 · 5 comments
Open

Discuss: passing in own http(s) server #20

rikkertkoppes opened this issue Mar 28, 2020 · 5 comments

Comments

@rikkertkoppes
Copy link
Contributor

To enable running a websocket server on an already existing http(s) server (like an express server), consider passing in an optional server instance

I would propose allowing a server instance in WSServerOptions

export interface WSServerOptions extends TlsOptions {
	type: "websocket";
	port?: number; // default 13900 (ws) or 13901 (wss)
	server?: http.Server | https.Server;	//optional server to pass in
}

And using that in startWebSocketServer or creating one if not given

This allows running http and ws over the same port as the rest of the application

Thoughts?

@poelstra
Copy link
Owner

poelstra commented Mar 28, 2020

Hmyeah, my original idea with this kind of stuff is different: the hub is the real core of the thing, and can be used by anyone who wants to publish/subscribe. This hub is then used by both the TCP and websocket transport, of which a 'default' implementation is used in the MServer class. The plan was that if you want to do your own, more custom stuff, you'd basically create your own MyMServer class, and do whatever you like with the Hub in there.

Thus, in a way, the MServer class is 'only' intended to be used with the simple command-line program mhub-server, assuming that if you want to embed it in your own app, you'd rather want to use the Hub and XXXConnection classes directly.

Alternatively, if you want to have the ease-of-use of the MServer config object, you can instantiate your own Hub, and pass it to the constructor of MServer, leave out the websocket transport option in its config, then set up your own websocket transport and connect it to the Hub.

The actual code of such websocket<->hub connection is trivial:

const wss = new ws.Server({ server: yourHttpServer, path: "/" });
wss.on("connection", (conn: ws) => {
    new WSConnection(hub, conn, "websocket" + this.connectionId++);
});

So I think it's already possible to do without further changes today, and that way is also more in the direction of the design.

Curious what you think, though.

@poelstra
Copy link
Owner

I've improved a few things and now added an example on how to set up a fully custom server.

See https://github.com/poelstra/mhub/blob/master/src/example/custom-server.ts and please let me know if this would work for you.

@rikkertkoppes
Copy link
Contributor Author

I did not realize I had all the LEGO pieces available to build the entire server and shove in my own http server, so the example is much appreciated.

This approach does work for what I am currently trying to do (host a simple mhub + http server on heroku)

I do feel attaching your own http server would be a pretty common use case though. It kind of feels like an all or nothing scenario with regard to creating an MServer. You can either use MServer with configuration or build it all up yourself

I like the modular approach though, but you might want to add an in-between level. Two approaches come to mind:

  • create helpers like createHub(config) as in many cases all options provided by the config are sufficient for the hub
  • create a more useful baseclass for MServer, (with protected init* methods), like so:
// express server
const app = express();
const server = require("http").createServer(app);

class MyMServer extends MServer {
    protected startWebSocketServer(
        hub: Hub,
        options: WSServerOptions
    ): Promise<void> {
        return new Promise<void>((resolve, reject) => {
            const wss = new ws.Server({ server: <any>server, path: "/" });
            wss.on("connection", (conn: ws) => {
                // tslint:disable-next-line:no-unused-expression
                new WSConnection(hub, conn, "websocket" + this.connectionId++);
            });

            server.on("error", (e: Error): void => {
                reject(e);
            });
        });
    }
}

// mhub server
let mserver = new MyMServer({
    listen: [
        {
            type: "websocket"
        }
    ],
    bindings: [],
    nodes: {
        default: "HeaderStore"
    },
    storage: "./storage",
    users: {},
    rights: {
        "": true
    }
});

// start mhub server
mserver.init();

// start web server
server.listen(port, () => {
    console.log(`server running on port ${port}`);
});

@poelstra
Copy link
Owner

Agreed, while creating the example I was also thinking about making the MServer methods protected instead of private, such that this kind of subclassing is trivially possible too.

Are you in for creating a PR for that?

@poelstra
Copy link
Owner

Version 2.1.0 is published on NPM which contains the necessary changes to easily run your own custom server as demonstrated in the example 🎉

I'll leave this issue open to implement your other suggestion (protected instead of private methods on MServer class).

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