karjud marjoram virtuous

0 views
Skip to first unread message

Placido Teofilo

unread,
Aug 3, 2024, 1:13:40 AM8/3/24
to scholovmero

Primus, the creator god of transformers but now also known as universal wrapperfor real-time frameworks. There are a lot of real-time frameworks available forNode.js and they all have different opinions on how real-time should be done.Primus provides a common low level interface to communicate in real-time usingvarious real-time frameworks.

If you deploy your application behind a reverse proxy (Nginx, HAProxy, etc.) youmight need to add WebSocket specific settings to its configuration files. Ifyou intend to use WebSockets, please ensure that these settings have been added.There are some example configuration files available in theobserving/balancerbattlerepository.

Primus doesn't ship with real-time frameworks as dependencies, it assumes thatyou as user add them yourself as a dependency. This is done to keep the moduleas lightweight as possible. This works because require in will walk throughyour directories searching for node_module folders that have these matchingdependencies.

Primus needs to be "attached" to a HTTP compatible server. These includes thebuilt-in http and https servers but also the spdy module as it has thesame API as node servers. Creating a new Primus instance is relativelystraightforward:

The options that are prefixed with cors are supplied to ouraccess-control module which handlesHTTP Access Control (CORS), so for a more detailed explanation of these optionscheck it out.

The transport option allows you to use any configuration option supported bythe underlying real-time framework. Its use is discouraged as these optionsare framework specific and no longer work if you change transformer. Our adviseis to use it only if you know what you are doing and if you need fine-grainedcontrol over the real-time framework. Please also keep in mind that some ofthese options are overriden by Primus.

The idGenerator option can be used to define a function which will be calledto set each spark.id. The generator function should returna unique string each time it is invoked. If idGenerator is not defined, Primuswill try to use ids provided by the transformer. If the transformer does notprovide ids, Primus will use nanoid to generateSpark ids.

In the above example we automatically create a HTTP server which will listenon port 8080, a primus instance with the websockets transformer and startlistening for incoming connections. The supplied function in thePrimus.createServer method is optional. You can just listen for incomingconnections your self using the returned Primus instance. If you want to listen toa HTTPS or SPDY server, which is recommended, you can directly pass the SPDY andHTTPS certs/keys/pfx files in the options object:

Which returns the client-side library as a string (which can then be minified oreven have more code added to it). It does not come pre-minified as that is outof the scope of this project. You can store this on a CDN or on your static server.Do whatever you want with it, but remember to regenerate it every time you changePrimus server options. This is important because some properties of the clientare set using the server configuration. For example if you change thepathname, the client should be regenerated to reflect that change and workcorrectly. We advise you to regenerate the library every time you redeploy soyou always have a client compatible with your back-end. To save the file youcan use:

But to make it easier for you during development we've automatically added anextra route to the supplied HTTP server, this will serve the library for you soyou don't have to save it. Please note, that this route isn't optimised forserving static assets and should only be used during development. In your HTMLpage add:

As you can see, it will use the /primus pathname by default. Primus needs toown the whole path/namespace in order to function properly as it will forwardall other requests directly in to the transformers so they can work their magic.If you already have a static folder with the name primus you can change thepathname to something different and still make this work. But you would ofcourse need to update the src attribute of the script tag to set the correctlocation. It's always available at:

Here is the pathname set in server options above. The clientis cross domain compatible so you don't have to serve it from thesame domain you're running Primus on. But please note, that the real-timeframework you're using might be tied to same domain restrictions.

The spark argument is the actual real-time socket/connection. Sparks have areally low level interface and only expose a couple properties that are crossengine supported. The interface is modeled towards a Node.js stream compatibleinterface. So this will include all methods that are available on the streaminterface including Spark#pipe.

The spark.headers property contains the headers of either the requestthat started a handshake with the server or the headers of the actual real-timeconnection. This depends on the module you are using.

Please note that sending custom headers from the client to the server isimpossible as not all transports that these transformers support can add customheaders to a request (JSONP for example). If you need to send custom data, use aquery string when connecting

The spark.address property contains the ip and port of theconnection. If you're running your server behind a reverse proxy it willautomatically use the x-forwarded-for header. This way you will always havethe address of the connecting client and not the IP address of your proxy.

This is a unique id that we use to identify this single connection with. Normallythe frameworks refer to this as a sessionid, which is confusing as it's onlyused for the duration of one single connection. You should not see this as a"session id", and rather expect it to change between disconnects and reconnects.

The spark.request gives you access to the HTTP request that was used toinitiate the real-time connection with the server. Please note that this requestis already answered and closed (in most cases) so do not attempt to write oranswer it anyway. But it might be useful to access methods that get added bymiddleware layers, etc.

You can use the spark.write method to send data over the socket. The data isautomatically encoded for you using the parser that you've set while creatingthe Primus server instance. This method always returns true on success andfalse on failure so back pressure isn't handled.

You can use spark.end to close the connection. This method takes two optionalarguments. The first, if provided, is the data to send to the client beforeclosing the connection. The second is an options object used to customize thebehavior of the method. By default the spark.end method closes the connectionin a such way that the client knows it was intentional and it doesn't attempt areconnection.

This method is mostly used internally. It works similarly to the native bindfunction, returning a function that emits the assigned event every time it'scalled. If the last argument is a function, it will be used to parse thearguments of the returned function. The parser is optional and always async,its first argument is a callback that follows the usual error first pattern,all successive arguments are the ones to parse. Using the parser you canreduce the arguments down to a single value, remove them completely or preventthe event from being emitted. See emits fordetailed usage instructions.

When the connection goes down unexpectedly an automatic reconnect process isstarted. It uses a randomised exponential back-off algorithm to prevent clientsfrom DDoSing your server when you reboot as they will all be re-connecting atdifferent times. The reconnection can be configured using the options argumentin Primus and you should add these options to the reconnect property:

Please note that when we reconnect, we will receive a new connection event onthe server and a new open event on the client, as the previous connection wascompletely dead and should therefore be considered a new connection.

The strategy allows you to configure when you want a reconnect operation tokick in. We're providing some sane defaults for this but we still want toprovide users with highest level of customization:

If you are using authentication you should disable the timeout strategy asthere is no way of detecting the difference between a failed authorization and afailed connect. If you leave this enabled with authorization every unauthorizedaccess will try to reconnect again.

But there are always use cases where reconnection is not advised for yourapplication. In these cases we've provided a way to completely disable thereconnection, this is done by setting the strategy to false:

If you want to manually control the reconnection you can call primus.end()to close the connection and primus.open() to establish a new one. Be sureto use primus.open() correctly, see below for details.

This method opens a connection with the server. By default it is calledautomatically when the Primus instance is created, but there are cases whereit's desirable to open the connection manually. To do this set the manualoption to true and when you have the Primus instance call the method:

When you are sending messages to the server, you don't have to wait for theopen event to happen, the client will automatically buffer all the data you'vesend and automatically write it to the server once it's connected. The clientsupports a couple of different events.

The error event is emitted when something breaks that is out of our control.Unlike Node.js, we do not throw an error if no error event listener isspecified. In general, when there is an active connection, it is not directlyclosed when an error event is emitted. The cause of an error, in fact, couldbe that the parser failed to encode or decode a message. In this case we onlyemit the error, discard the message and keep the connection alive. An errorevent can also be emitted when a connection fails to establish. When thishappens the client automatically tries to reconnect, unless the connection getsclosed for some other reason. The only exception is when there is anauthorization hook. If we get an error when connecting to a server whereauthorization is required, we simply close the connection, as we can'tdeterminate if the error is the result of an unauthorized access or not.

c01484d022
Reply all
Reply to author
Forward
0 new messages