Blog
Zato WebSockets channels are an attractive counterpart to REST-based APIs. Their main characteristics are:
Zato offers a dedicated client for WebSocket services that hides all the low-level details under a familiar API, as below:
# Zato
from zato.wsx_client import Client
# Prepare credentials
address = 'ws://my.server:33055/api/v1/channel'
username = 'my.username'
password = 'my.password'
# Our service to invoke
service = 'zato.ping'
# Request to send - note that it is a regular dict
request = {'Hello': 'World'}
# Create a client object
client = Client(address, username, password)
# Connect to a Zato server - opens a long-running session and sends credentials
client.connect()
# Invoke the service defined above - this will block until a response arrives
response = client.invoke(request)
# Log what we received
print('Response ->', response)
WebSocket channels created in Dashboard are programming language-agnostic, which means that they may be used to invoke services and publish or receive messages from any application that speaks WebSockets and JSON, e.g. Python, JavaScript or any other.
To utilize WebSocket channels, applications (API clients) need to follow a protocol described in this chapter.
Clients establish an initial WebSocket connection to the address a given channel utilizes, possibly taking into account the fact that there may be a load-balancer performing URL transformations or port mappings in front of Zato servers.
Once a connection is created, clients need to create a session and receive a session token. This step needs to be done even if for a given WebSocket channel no client credentials are needed.
Clients must use the session token in all requests over the same TCP connection. The token cannot be used any other connections, it is specific to one TCP stream.
Right after a TCP connection is established, before logging in, an information entry is stored in server logs. This includes data about from what IP address and domain name the connection comes from as well as the address and name of the WebSocket channel.
If a client connects but does not initiate the create-session request within the expected time, the TCP connection will be closed with an accompanying warning message in server logs:
WARNING - Peer 127.0.0.1:51738 (localhost) did not create session within 5s,
closing its connection to 127.0.0.1:48902 (my.wsx.channel), cid:`23b3cae088382f62106edd67`
Otherwise, if the client creates a session successfully, an informational message will be saved to logs:
INFO - Client 127.0.0.1:57288 (localhost ws.eee4b49d6fd835b424175ed5)
logged in successfully to 127.0.0.1:48902 (my.wsx.channel)
Element | Datatype | Optional | Notes |
---|---|---|---|
meta | dict | --- | A dictionary of metadata for the request |
meta.action | string | --- | A constant value of "create-session" |
meta.username | string | Yes | Username to authenticate with (if needed) |
meta.secret | string | Yes | Password or other channel-specific secret to authenticate with (if needed) |
meta.id | string | --- | Client-generated request ID - must be globally unique (e.g. UUID4). Returned in responses in the in_reply_to element to let clients know in reply to which request a given response is returned. |
meta.timestamp | datetime | --- | When the message was generated by client, must be in UTC using ISO-8601 YYYY-MM-DDTHH:mm:ss.ssssss |
meta.client_id | string | --- | An arbitrary business ID of the client - any value identifying the client can be used, e.g. ID of a business application such as crm.prod.1 or ID of an IoT device connecting to Zato such as printer.mx2.3910 |
meta.client_name | string | Yes | Human-friendly identifier of the client (in addition to client_id) |
{"meta": {
"action": "create-session",
"username": "user1",
"secret": "PNVmGkLejnhsAZ5VzzfdHQkvGg",
"id": "238dc406351444d0869390af9541da59",
"timestamp": "2022-11-16T15:53:25.717215",
"client_id": "p.33915",
"client_name": "Printer #33915, Fifth floor"
}}
Element | Datatype | Optional | Notes |
---|---|---|---|
meta | dict | --- | A dictionary of metadata for the response |
meta.status | integer | --- | An overall status code, using HTTP status code, e.g. 200 is OK |
meta.timestamp | datetime | --- | When the response was produced by Zato, in UTC |
id | string | --- | Response ID - its last element is a correlation ID that can be used to look up details in server log |
in_reply_to | string | Yes | In reply to what request ID the response is returned |
data | any | --- | Business data related to the response. In case of an error it will be an error message. Otherwise, it will contain an element with the session token. |
data.token | string | Yes | A randomly generated session token - returned only if meta status is 200 |
Samples:
{"meta":{
"status":200,
"timestamp":"2022-04-11T09:17:16.954645",
"in_reply_to":"238dc406351444d0869390af9541da59",
"id":"zato.ws.srv.rsp-auth.39d6e397054e410810afeac2"},
"data":{
"token":"ws.token.b76561f8c145cd6baf086d04"}}
{"meta": {
"status":403,
"timestamp":"2022-04-11T09:02:20.918796",
"id":"zato.ws.srv.msg-err.82608509fb56261fd8c90910"},
"data":"You are not authorized to access this resource"}
Clients with a session token can invoke the service that is mounted on a WebSocket channel they are connected to.
It means that if a client wants to invoke multiple services, the service from the channel needs to forward messages to the actual ones using self.invoke
and send the response back to the calling client by assigning it to self.response.payload
. That allows one to implement white-lists where only selected services can be made accessible to the caller and attempts to invoke any other one will be rejected.
There is also a default WebSockets gateway service that can be used to invoke any service, it is documented in this chapter below.
Element | Datatype | Optional | Notes |
---|---|---|---|
meta | dict | --- | A dictionary of metadata for the request |
meta.action | string | --- | A constant value of "invoke-service" |
meta.id | string | --- | Same as in "create-session" action |
meta.timestamp | datetime | --- | ” |
meta.token | string | --- | Session token returned by "create-session" action |
data | any | --- | Arbitrary data required by the channel's service - can be a string, integer, dict or anything else that the service expects |
This sample assumes that service zato.helpers.echo
is mounted on the channel - it is a built-in service that echoes back everything in receives on input.
{"meta": {
"action": "invoke-service",
"id": "36df91fca2444dcaadc7199691217cfd",
"timestamp": "2016-11-16T15:53:25.717215",
"token": "ws.token.7a1729f99acbfe21b0cca337"},
"data": {
"customer_id": "123",
"account_id": "456"
}}
Element | Datatype | Optional | Notes |
---|---|---|---|
meta | dict | --- | Same as in "create-session" action |
meta.status | integer | --- | ” |
meta.timestamp | datetime | --- | ” |
id | string | --- | ” |
in_reply_to | string | Yes | ” |
data | any | --- | Business data related to the response. In case of an error it will be an error message. Otherwise, it will contain a response that the channel's service returned - may be a string, integer, dict or any other datatype |
Again, the sample shows what zato.helpers.echo may return if it is assigned to a WebSocket channel.
{"meta":{
"status":200,
"timestamp":"2022-04-11T11:38:34.400736",
"in_reply_to":"36df91fca2444dcaadc7199691217cfd",
"id":"zato.ws.srv.rsp-ok.05308f81590751038cc9167f"},
"data":{
"customer_id":"123",
"account_id":"456"
}}
There is no separate API call to close a session and log out. It suffices to close the WebSocket connection along with its TCP stream - this will be immediately recognized by Zato and all the relevant shutdown and cleanup actions will be performed.
Afterwards, the session token cannot be used anymore even by the same client; a new session needs to be created with its own token instead.
A built-in service called helpers.web-sockets-gateway
can be mounted on a WebSocket channel to make it possible for clients to invoke any other arbitrary service.
Note, however, that clients will truly have access to any service deployed on the server they will connect to, including all the internal ones. If this is not desirable, the gateway service can be subclassed to implement a white-listing logic, i.e. to filter out requests that attempt to invoke a service outside of the list of permitted ones.
Book a demo with an expert who will help you build meaningful systems that match your ambitions
"For me, Zato Source is the only technology partner to help with operational improvements."