Difference between revisions of "WebSocket Module"

From BaseX Documentation
Jump to navigation Jump to search
m (Text replacement - "</syntaxhighlight>" to "</pre>")
 
(42 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
This [[Module Library|XQuery Module]] contains functions for accessing specific WebSocket functions. This module is mainly useful in the context of [[WebSockets]].  
 
This [[Module Library|XQuery Module]] contains functions for accessing specific WebSocket functions. This module is mainly useful in the context of [[WebSockets]].  
Within the WebSocket module you can access functions in the context of the specific client. You can access the clientid, the clientpath and broadcast a message to all connected clients without the caller/client. If you have to access functions with more rights, like getting the clientids of other connected clients, you should checkout the [[WebSockets Module]]
 
  
 
=Conventions=
 
=Conventions=
  
* The {{Code|basex-api}} package must be included in the classpath. This is always the case if you use one of the complete distributions (zip, exe, war) of BaseX.
+
* The module will be available if the {{Code|basex-api}} library is found in the classpath. This is the case if you use one of the complete distributions of BaseX (zip, exe, war).
* All functions and errors are assigned to the <code><nowiki>http://basex.org/modules/websocket</nowiki></code> namespace. The module must be imported in the query prolog:
+
* All functions and errors are assigned to the <code><nowiki>http://basex.org/modules/ws</nowiki></code> namespace, which is statically bound to the {{Code|ws}} prefix.
<pre class="brush:xquery">
+
* As sessions are side-effecting operations, all functions are flagged as ''nondeterministic''. As a result, some query optimizations will be suppressed.
import module namespace websocket = "http://basex.org/modules/Websocket";
+
 
...
+
=General Functions=
 +
 
 +
==ws:id==
 +
 
 +
{| width='100%'
 +
|- valign="top"
 +
| width='120' | '''Signature'''
 +
|<pre>ws:id() as xs:string</pre>
 +
|- valign="top"
 +
| '''Summary'''
 +
|Returns the ID of the current WebSocket.
 +
|- valign="top"
 +
| '''Errors'''
 +
|{{Error|not-found|#Errors}} No WebSocket with the specified id exists.
 +
|}
 +
 
 +
==ws:ids==
 +
 
 +
{| width='100%'
 +
|- valign="top"
 +
| width='120' | '''Signature'''
 +
|<pre>ws:ids() as xs:string*</pre>
 +
|- valign="top"
 +
| '''Summary'''
 +
|Returns the ids of all currently registered WebSockets.
 +
|}
 +
 
 +
==ws:path==
 +
 
 +
{| width='100%'
 +
|- valign="top"
 +
| width='120' | '''Signature'''
 +
|<pre>ws:path(
 +
  $id  as xs:string
 +
) as xs:string</pre>
 +
|- valign="top"
 +
| '''Summary'''
 +
|Returns the path of the WebSocket with the specified {{Code|$id}}.
 +
|- valign="top"
 +
| '''Errors'''
 +
|{{Error|not-found|#Errors}} No WebSocket with the specified id exists.
 +
|}
 +
 
 +
==ws:close==
 +
 
 +
{| width='100%'
 +
|- valign="top"
 +
| width='120' | '''Signature'''
 +
|<pre>ws:close(
 +
  $id  as xs:string
 +
) as empty-sequence()</pre>
 +
|- valign="top"
 +
| '''Summary'''
 +
|Closes the connection of the WebSocket with the specified {{Code|$id}}.
 +
|- valign="top"
 +
| '''Errors'''
 +
|{{Error|not-found|#Errors}} No WebSocket with the specified id exists.
 +
|}
 +
 
 +
=Sending Data=
 +
 
 +
==ws:send==
 +
 
 +
{| width='100%'
 +
|- valign="top"
 +
| width='120' | '''Signature'''
 +
|<pre>ws:send(
 +
  $message  as item(),
 +
  $ids      as xs:string*
 +
) as empty-sequence()</pre>
 +
|- valign="top"
 +
| '''Summary'''
 +
|Sends a <code>$message</code> to the clients with the specified <code>$ids</code>. Ids that cannot be assigned to clients will be ignored. The message will be handled as follows:
 +
* Items of type {{Code|xs:base64Binary}} and {{Code|xs:hexBinary}} will be transmitted as binary messages.
 +
* Function items (maps, arrays) will be serialized as JSON and transmitted as string messages.
 +
* All other items will be serialized with the default serialization options and transmitted as string messages.
 +
|}
 +
 
 +
==ws:broadcast==
 +
 
 +
{| width='100%'
 +
|- valign="top"
 +
| width='120' | '''Signature'''
 +
|<pre>ws:broadcast(
 +
  $message  as xs:anyAtomicType
 +
) as empty-sequence()</pre>
 +
|- valign="top"
 +
| '''Summary'''
 +
|Broadcasts a <code>$message</code> to all connected clients except to the caller. Invocations of this convenience function are equivalent to <code>ws:send($message, ws:ids()[. != ws:id()])</code>. See {{Function||ws:send}} for more details on the message handling.
 +
|}
 +
 
 +
==ws:emit==
 +
 
 +
{| width='100%'
 +
|- valign="top"
 +
| width='120' | '''Signature'''
 +
|<pre>ws:emit(
 +
  $message  as xs:anyAtomicType
 +
) as empty-sequence()</pre>
 +
|- valign="top"
 +
| '''Summary'''
 +
|Emits a <code>$message</code> to all connected clients. Invocations of this function are equivalent to <code>ws:send($message, ws:ids())</code>. See {{Function||ws:send}} for more details on the message handling.
 +
|}
 +
 
 +
==ws:eval==
 +
 
 +
{| width='100%'
 +
|- valign="top"
 +
| width='120' | '''Signature'''
 +
|<pre>ws:eval(
 +
  $query    as xs:anyAtomicType,
 +
  $bindings  as map(*)?          := (),
 +
  $options  as map(*)?          := map { }
 +
) as xs:string</pre>
 +
|- valign="top"
 +
| '''Summary'''
 +
|Schedules the evaluation of the supplied {{Code|$query}} and returns the result to the calling WebSocket client. The query can be a URI or a string, and variables and context items can be declared via {{Code|$bindings}} (see {{Function|XQuery|xquery:eval}} for more details). The following {{Code|$options}} can be supplied:
 +
* {{Code|base-uri}}: sets the [https://www.w3.org/TR/xquery-31/#dt-static-base-uri base-uri property] for the query. This URI will be used when resolving relative URIs, such as with {{Code|fn:doc}}.
 +
* {{Code|id}}: sets a custom job id. The id must not start with the standard <code>job</code> prefix, and it can only be assigned if no job with the same name exists.
 +
Query scheduling is recommendable if the immediate query execution might be too time consuming and lead to a timeout.
 +
|- valign="top"
 +
| '''Errors'''
 +
|{{Error|overflow|#Errors}} Query execution is rejected, because too many jobs are queued or being executed. <br/>{{Error|id|#Errors}} The specified id is invalid or has already been assigned.
 +
|- valign="top"
 +
| '''Examples'''
 +
|
 +
* Schedule a second query that will notify the client 10 seconds later that a message was processed:
 +
<pre lang='xquery'>
 +
declare
 +
  %ws:message('/tasks', '{$message}')
 +
function local:message($message) {
 +
  ws:eval('prof:sleep(10000), "Your message has been processed."')
 +
};
 
</pre>
 
</pre>
 +
|}
 +
 +
=WebSocket Attributes=
  
=Functions=
+
==ws:get==
  
==websocket:broadcast==
 
 
{| width='100%'
 
{| width='100%'
|-
+
|- valign="top"
| width='120' | '''Signatures'''
+
| width='120' | '''Signature'''
|{{Func|websocket:broadcast|$message as xs:anyAtomicType|empty-sequence()}}
+
|<pre>ws:get(
|-
+
  $id      as xs:string,
 +
  $name    as xs:string,
 +
  $default  as item()*    := ()
 +
) as item()*</pre>
 +
|- valign="top"
 
| '''Summary'''
 
| '''Summary'''
|Broadcasts <code>message</code> which may be of type xs:string, xs:base64Binary, or xs:hexBinary to all connected members except to the caller.  
+
|Returns the value of an attribute with the specified {{Code|$name}} for the WebSocket with the specified {{Code|$id}}. If the attribute is unknown, an empty sequence or the optionally specified {{Code|$default}} value will be returned instead.
 +
|- valign="top"
 +
| '''Errors'''
 +
|{{Error|not-found|#Errors}} No WebSocket with the specified id exists.
 
|}
 
|}
  
==websocket:id==
+
==ws:set==
 +
 
 
{| width='100%'
 
{| width='100%'
|-
+
|- valign="top"
| width='120' | '''Signatures'''
+
| width='120' | '''Signature'''
|{{Func|websocket:id||xs:string}}
+
|<pre>ws:set(
|-
+
  $id     as xs:string,
 +
  $name  as xs:string,
 +
  $value  as item()*
 +
) as empty-sequence()</pre>
 +
|- valign="top"
 
| '''Summary'''
 
| '''Summary'''
|Returns the ID of the current WebSocket connection.
+
|Assigns the specified {{Code|$value}} to the attribute with the specified {{Code|$name}} for the WebSocket with the specified {{Mono|$id}}.
 +
|- valign="top"
 +
| '''Errors'''
 +
|{{Error|not-found|#Errors}} No WebSocket with the specified id exists.
 
|}
 
|}
  
==websocket:path==
+
==ws:delete==
 +
 
 
{| width='100%'
 
{| width='100%'
|-
+
|- valign="top"
| width='120' | '''Signatures'''
+
| width='120' | '''Signature'''
|{{Func|websocket:path||xs:string}}
+
|<pre>ws:delete(
|-
+
  $id    as xs:string,
 +
  $name  as xs:string
 +
) as empty-sequence()</pre>
 +
|- valign="top"
 
| '''Summary'''
 
| '''Summary'''
|Returns the path of the current WebSocketClient. If the <code>$id</code> parameter is set, the path of a specific user with the ID <code>$id</code> will be returned.
+
|Deletes an attribute with the specified {{Code|$name}} from the WebSocket with the specified {{Mono|$id}}.
 +
|- valign="top"
 +
| '''Errors'''
 +
|{{Error|not-found|#Errors}} No WebSocket with the specified id exists.
 
|}
 
|}
  
=Usage Tips=
+
=Examples=
* <code>websocket:id</code> returns your current session id. You can use all [[Session Module]] and [[Sessions Module]] functions within the websocket context.
+
 
 +
==Example 1==
 +
 
 +
<pre lang='xquery'>
 +
import module namespace ws = "http://basex.org/modules/ws";
  
=Examples=
 
==Code==
 
<pre class="brush:xquery">
 
 
declare
 
declare
module namespace websocketexample = 'http://basex.org/modules/web-page';
+
  %ws:connect('/')
(: Import the WebSocket module :)
+
function local:connect() as empty-sequence() {
import module namespace websocket = "http://basex.org/modules/Websocket";
+
  let $id := ws:id()
 +
  let $message := json:serialize(map {
 +
    'type': 'Connect',
 +
    'id': $id
 +
  })
 +
  return ws:broadcast($message)
 +
};
 +
</pre>
 +
 
 +
'''Explanation:'''
 +
 
 +
* The function has a <code>%ws:connect</code> annotation. It gets called if a client successfully creates a WebSocket connection to the path <code>/</code> (check out [[WebSockets]] for further information).
 +
* A JSON response is generated, which contains the new client id and a <code>Connect</code> string.
 +
* This response will be sent to all other connected clients.
 +
 
 +
==Example 2==
 +
 
 +
<pre lang='xquery'>
 +
import module namespace ws = "http://basex.org/modules/ws";
  
   %ws:connect("/")
+
declare
  function websocketexample:connect(
+
   %ws:message('/', '{$message}')
   )  {
+
function local:message(
    let $client-id := websocket:id()
+
   $message as xs:string
    let $client-path := websocket:path()
+
) as empty-sequence() {
    let $response := json:serialize(
+
  let $message := json:serialize(map { 'message': $message })
                              <json type="object">
+
  return ws:emit($message)
                                <messageType>UserConnected</messageType>
+
};
                                <clientId>{$client-id}</clientId>
 
                                <clientPath>{$client-path}</clientPath>
 
                              </json>
 
                            )
 
    return websocket:broadcast($response)
 
  };
 
 
</pre>
 
</pre>
==Explanation==
+
 
* First of all: include the websocket module
+
'''Explanation:'''
* The <code>$ws:connect("/")</code> annotation gets called if a client successfully creates a websocket to the path "/"
+
 
* Get the <code>client-id</code> and the <code>client-path</code> with <code>websocket:id()</code> and <code>websocket:path()</code>
+
* The function has a <code>%ws:message</code> annotation. It gets called if a client sends a new message.
* Create a json-result
+
* A JSON response is generated, which contains the message string.
* Broadcast the result to all connected clients without the calling client
+
* This response will be sent to all connected clients (including the calling client).
 +
 
 +
=Errors=
 +
 
 +
{| class="wikitable" width="100%"
 +
! width="110"|Code
 +
|Description
 +
|- valign="top"
 +
|{{Code|not-found}}
 +
|No WebSocket with the specified id exists.
 +
|}
 +
 
 +
=Changelog=
 +
 
 +
;Version 9.2
 +
 
 +
* Added: {{Function||ws:eval}}
 +
 
 +
This module was introduced with Version 9.1.

Latest revision as of 17:35, 1 December 2023

Conventions[edit]

  • The module will be available if the basex-api library is found in the classpath. This is the case if you use one of the complete distributions of BaseX (zip, exe, war).
  • All functions and errors are assigned to the http://basex.org/modules/ws namespace, which is statically bound to the ws prefix.
  • As sessions are side-effecting operations, all functions are flagged as nondeterministic. As a result, some query optimizations will be suppressed.

General Functions[edit]

ws:id[edit]

Signature
ws:id() as xs:string
Summary Returns the ID of the current WebSocket.
Errors not-found: No WebSocket with the specified id exists.

ws:ids[edit]

Signature
ws:ids() as xs:string*
Summary Returns the ids of all currently registered WebSockets.

ws:path[edit]

Signature
ws:path(
  $id  as xs:string
) as xs:string
Summary Returns the path of the WebSocket with the specified $id.
Errors not-found: No WebSocket with the specified id exists.

ws:close[edit]

Signature
ws:close(
  $id  as xs:string
) as empty-sequence()
Summary Closes the connection of the WebSocket with the specified $id.
Errors not-found: No WebSocket with the specified id exists.

Sending Data[edit]

ws:send[edit]

Signature
ws:send(
  $message  as item(),
  $ids      as xs:string*
) as empty-sequence()
Summary Sends a $message to the clients with the specified $ids. Ids that cannot be assigned to clients will be ignored. The message will be handled as follows:
  • Items of type xs:base64Binary and xs:hexBinary will be transmitted as binary messages.
  • Function items (maps, arrays) will be serialized as JSON and transmitted as string messages.
  • All other items will be serialized with the default serialization options and transmitted as string messages.

ws:broadcast[edit]

Signature
ws:broadcast(
  $message  as xs:anyAtomicType
) as empty-sequence()
Summary Broadcasts a $message to all connected clients except to the caller. Invocations of this convenience function are equivalent to ws:send($message, ws:ids()[. != ws:id()]). See ws:send for more details on the message handling.

ws:emit[edit]

Signature
ws:emit(
  $message  as xs:anyAtomicType
) as empty-sequence()
Summary Emits a $message to all connected clients. Invocations of this function are equivalent to ws:send($message, ws:ids()). See ws:send for more details on the message handling.

ws:eval[edit]

Signature
ws:eval(
  $query     as xs:anyAtomicType,
  $bindings  as map(*)?           := (),
  $options   as map(*)?           := map { }
) as xs:string
Summary Schedules the evaluation of the supplied $query and returns the result to the calling WebSocket client. The query can be a URI or a string, and variables and context items can be declared via $bindings (see xquery:eval for more details). The following $options can be supplied:
  • base-uri: sets the base-uri property for the query. This URI will be used when resolving relative URIs, such as with fn:doc.
  • id: sets a custom job id. The id must not start with the standard job prefix, and it can only be assigned if no job with the same name exists.

Query scheduling is recommendable if the immediate query execution might be too time consuming and lead to a timeout.

Errors overflow: Query execution is rejected, because too many jobs are queued or being executed.
id: The specified id is invalid or has already been assigned.
Examples
  • Schedule a second query that will notify the client 10 seconds later that a message was processed:
declare
  %ws:message('/tasks', '{$message}')
function local:message($message) {
  ws:eval('prof:sleep(10000), "Your message has been processed."')
}; 

WebSocket Attributes[edit]

ws:get[edit]

Signature
ws:get(
  $id       as xs:string,
  $name     as xs:string,
  $default  as item()*    := ()
) as item()*
Summary Returns the value of an attribute with the specified $name for the WebSocket with the specified $id. If the attribute is unknown, an empty sequence or the optionally specified $default value will be returned instead.
Errors not-found: No WebSocket with the specified id exists.

ws:set[edit]

Signature
ws:set(
  $id     as xs:string,
  $name   as xs:string,
  $value  as item()*
) as empty-sequence()
Summary Assigns the specified $value to the attribute with the specified $name for the WebSocket with the specified $id.
Errors not-found: No WebSocket with the specified id exists.

ws:delete[edit]

Signature
ws:delete(
  $id    as xs:string,
  $name  as xs:string
) as empty-sequence()
Summary Deletes an attribute with the specified $name from the WebSocket with the specified $id.
Errors not-found: No WebSocket with the specified id exists.

Examples[edit]

Example 1[edit]

import module namespace ws = "http://basex.org/modules/ws";

declare
  %ws:connect('/')
function local:connect() as empty-sequence() {
  let $id := ws:id()
  let $message := json:serialize(map {
    'type': 'Connect',
    'id': $id
  })
  return ws:broadcast($message)
};

Explanation:

  • The function has a %ws:connect annotation. It gets called if a client successfully creates a WebSocket connection to the path / (check out WebSockets for further information).
  • A JSON response is generated, which contains the new client id and a Connect string.
  • This response will be sent to all other connected clients.

Example 2[edit]

import module namespace ws = "http://basex.org/modules/ws";

declare
  %ws:message('/', '{$message}')
function local:message(
  $message as xs:string
) as empty-sequence() {
  let $message := json:serialize(map { 'message': $message })
  return ws:emit($message)
};

Explanation:

  • The function has a %ws:message annotation. It gets called if a client sends a new message.
  • A JSON response is generated, which contains the message string.
  • This response will be sent to all connected clients (including the calling client).

Errors[edit]

Code Description
not-found No WebSocket with the specified id exists.

Changelog[edit]

Version 9.2

This module was introduced with Version 9.1.