.. index:: single: http_websocket_server_service(HTTPTransport)
.. _http_websocket_server_service/1:

.. rst-class:: right

**object**

``http_websocket_server_service(HTTPTransport)``
================================================

Server-side convenience for callback-driven WebSocket sessions with atom text representation, including registry-backed broadcast helpers.

| **Availability:** 
|    ``logtalk_load(http_websocket_service(loader))``

| **Author:** Paulo Moura
| **Version:** 1:0:0
| **Date:** 2026-07-13

| **Compilation flags:**
|    ``static, context_switching_calls, threaded``


| **Extends:**
|    ``public`` :ref:`http_websocket_service(HTTPTransport,server,atom) <http_websocket_service/3>`
| **Uses:**
|    :ref:`http_websocket_service_registry <http_websocket_service_registry/0>`
|    :ref:`user <user/0>`

| **Remarks:**
|    (none)

| **Inherited public predicates:**
|     :ref:`http_websocket_session/2::initial_state/1`  :ref:`http_websocket_messages/1::is_message/1`  :ref:`http_websocket_session/2::is_state/1`  :ref:`http_websocket_messages/1::message/3`  :ref:`http_websocket_messages/1::payload/2`  :ref:`http_websocket_messages/1::read_message/2`  :ref:`http_websocket_session/2::read_message/4`  :ref:`http_websocket_session/2::read_message/5`  :ref:`http_websocket_session/2::read_message/6`  :ref:`http_websocket_service/3::run_session/3`  :ref:`http_websocket_service/3::run_session/4`  :ref:`http_websocket_messages/1::type/2`  :ref:`http_websocket_session/2::write_message/2`  :ref:`http_websocket_session/2::write_message/3`  :ref:`http_websocket_session/2::write_message/4`  :ref:`http_websocket_session/2::write_message/5`  

.. contents::
   :local:
   :backlinks: top

Public predicates
-----------------

.. index:: serve_once/6
.. _http_websocket_server_service/1::serve_once/6:

``serve_once/6``
^^^^^^^^^^^^^^^^

Accepts one incoming socket connection on the given listener created by the selected transport parameterization, serves one WebSocket opening handshake using the given HTTP handler, then runs one callback-driven WebSocket session using the given session handler until the close handshake completes or the peer closes the stream.

| **Compilation flags:**
|    ``static``

| **Template:**
|    ``serve_once(Listener,HandshakeHandler,SessionHandler,Response,State,ClientInfo)``
| **Mode and number of proofs:**
|    ``serve_once(+compound,+object_identifier,+object_identifier,-compound,-compound,-compound)`` - ``one_or_error``

| **Exceptions:**
|    The listener is invalid for the selected transport:
|        ``domain_error(http_socket_transport_listener,Listener)``
|    The WebSocket opening request does not exist:
|        ``existence_error(http_socket_transport_websocket_request,end_of_file)``
|    The WebSocket opening response is invalid:
|        ``domain_error(http_socket_transport_websocket_response,Response)``
|    ``SessionHandler`` is a variable:
|        ``instantiation_error``
|    ``SessionHandler`` is not a valid WebSocket service handler:
|        ``domain_error(http_websocket_service_handler,SessionHandler)``
|    The upgraded connection handle is invalid:
|        ``domain_error(http_socket_transport_connection,Connection)``
|    The delegated session loop raises a WebSocket session error:
|        ``domain_error(http_websocket_session_sequence,Frame)``
|    ``SessionHandler`` returns an invalid reply:
|        ``domain_error(http_websocket_service_handler_reply,Reply)``


------------

.. index:: serve_once/7
.. _http_websocket_server_service/1::serve_once/7:

``serve_once/7``
^^^^^^^^^^^^^^^^

Accepts one incoming socket connection on the given listener created by the selected transport parameterization, serves one WebSocket opening handshake using the given HTTP handler, then runs one callback-driven WebSocket session using the given session handler and the given session-loop options.

| **Compilation flags:**
|    ``static``

| **Template:**
|    ``serve_once(Listener,HandshakeHandler,SessionHandler,Response,State,ClientInfo,Options)``
| **Mode and number of proofs:**
|    ``serve_once(+compound,+object_identifier,+object_identifier,-compound,-compound,-compound,+list)`` - ``one_or_error``

| **Exceptions:**
|    The listener is invalid for the selected transport:
|        ``domain_error(http_socket_transport_listener,Listener)``
|    The WebSocket opening request does not exist:
|        ``existence_error(http_socket_transport_websocket_request,end_of_file)``
|    The WebSocket opening response is invalid:
|        ``domain_error(http_socket_transport_websocket_response,Response)``
|    ``SessionHandler`` is a variable:
|        ``instantiation_error``
|    ``SessionHandler`` is not a valid WebSocket service handler:
|        ``domain_error(http_websocket_service_handler,SessionHandler)``
|    ``Options`` is a variable or a partial list:
|        ``instantiation_error``
|    ``Options`` is neither a variable nor a list:
|        ``type_error(list,Options)``
|    An element ``Option`` of the list ``Options`` is neither a variable nor a compound term:
|        ``type_error(compound,Option)``
|    An element ``Option`` of the list ``Options`` is a compound term but not a valid option:
|        ``domain_error(option,Option)``
|    ``Options`` contains an invalid WebSocket service loop option:
|        ``domain_error(http_websocket_service_option,Option)``
|    Timed session-loop options are not available on this backend:
|        ``existence_error(http_websocket_service,timing)``
|    The upgraded connection handle is invalid:
|        ``domain_error(http_socket_transport_connection,Connection)``
|    The delegated session loop raises a WebSocket session error:
|        ``domain_error(http_websocket_session_sequence,Frame)``
|    ``SessionHandler`` returns an invalid reply:
|        ``domain_error(http_websocket_service_handler_reply,Reply)``

| **Remarks:**

    - Option ``auto_pong(on)``: Automatically writes pong replies while still forwarding ping messages to the session handler.
    - Option ``auto_pong(off)``: Disables automatic pong replies. This is the default.
    - Option ``keepalive_interval(Seconds)``: Schedules empty ping messages when the peer stays silent for the given positive number of seconds. This option requires backend thread support.
    - Option ``idle_timeout(Seconds)``: Closes the session with ``status(1001, idle_timeout)`` after the given positive number of seconds without an inbound message. This option requires backend thread support.
    - Option ``max_payload_length(Bytes)``: Rejects inbound frames whose declared payload length is greater than ``Bytes`` before allocating payload storage. Oversized frames are treated as ``1009`` close errors in the session loop. Use a non-negative integer.


------------

.. index:: serve_until_shutdown/5
.. _http_websocket_server_service/1::serve_until_shutdown/5:

``serve_until_shutdown/5``
^^^^^^^^^^^^^^^^^^^^^^^^^^

Accepts WebSocket opening handshakes on the given listener created by the selected transport parameterization until request_shutdown/1 is called for the specified control term, runs one callback-driven session loop per accepted upgraded connection, registers active sessions in the given registry for queued broadcasts, and closes the listener before returning.

| **Compilation flags:**
|    ``static``

| **Template:**
|    ``serve_until_shutdown(Listener,HandshakeHandler,SessionHandler,Registry,Control)``
| **Mode and number of proofs:**
|    ``serve_until_shutdown(+compound,+object_identifier,+object_identifier,+compound,+nonvar)`` - ``one_or_error``

| **Exceptions:**
|    Registry-backed serving is not available on this backend:
|        ``existence_error(http_websocket_server_service,registry)``
|    ``Registry`` is not an open WebSocket service registry handle:
|        ``domain_error(http_websocket_service_registry,Registry)``
|    ``Registry`` refers to a closed WebSocket service registry handle:
|        ``existence_error(http_websocket_service_registry,session_registry(RegistryId))``
|    ``Control`` is a variable:
|        ``instantiation_error``
|    ``Control`` is already registered for another open-ended WebSocket server loop:
|        ``permission_error(reuse,http_websocket_service_shutdown_control,Control)``
|    The WebSocket opening response is invalid:
|        ``domain_error(http_socket_transport_websocket_response,Response)``
|    ``SessionHandler`` is a variable:
|        ``instantiation_error``
|    ``SessionHandler`` is not a valid WebSocket service handler:
|        ``domain_error(http_websocket_service_handler,SessionHandler)``
|    The delegated session loop raises a WebSocket session error:
|        ``domain_error(http_websocket_session_sequence,Frame)``
|    ``SessionHandler`` returns an invalid registry action:
|        ``domain_error(http_websocket_service_handler_action,Action)``

| **Remarks:**

    - Thread support: This helper requires backend thread support so that multiple sessions can stay active concurrently.
    - Handler actions: Plain normalized messages are written back to the originating session. ``broadcast(Message)`` queues ``Message`` for all registered sessions and ``broadcast_others(Message)`` queues it for all registered sessions except the originating one.


------------

.. index:: serve_until_shutdown/6
.. _http_websocket_server_service/1::serve_until_shutdown/6:

``serve_until_shutdown/6``
^^^^^^^^^^^^^^^^^^^^^^^^^^

Accepts WebSocket opening handshakes on the given listener created by the selected transport parameterization until request_shutdown/1 is called for the specified control term, runs one registry-backed callback session per accepted connection, applies the given session-loop options to every active session, and closes the listener before returning.

| **Compilation flags:**
|    ``static``

| **Template:**
|    ``serve_until_shutdown(Listener,HandshakeHandler,SessionHandler,Registry,Control,Options)``
| **Mode and number of proofs:**
|    ``serve_until_shutdown(+compound,+object_identifier,+object_identifier,+compound,+nonvar,+list)`` - ``one_or_error``

| **Exceptions:**
|    Registry-backed serving is not available on this backend:
|        ``existence_error(http_websocket_server_service,registry)``
|    ``Registry`` is not an open WebSocket service registry handle:
|        ``domain_error(http_websocket_service_registry,Registry)``
|    ``Registry`` refers to a closed WebSocket service registry handle:
|        ``existence_error(http_websocket_service_registry,session_registry(RegistryId))``
|    ``Control`` is a variable:
|        ``instantiation_error``
|    ``Control`` is already registered for another open-ended WebSocket server loop:
|        ``permission_error(reuse,http_websocket_service_shutdown_control,Control)``
|    ``Options`` is a variable or a partial list:
|        ``instantiation_error``
|    ``Options`` is neither a variable nor a list:
|        ``type_error(list,Options)``
|    ``Options`` contains an invalid WebSocket service loop option:
|        ``domain_error(http_websocket_service_option,Option)``
|    Timed session-loop options are not available on this backend:
|        ``existence_error(http_websocket_service,timing)``
|    The WebSocket opening response is invalid:
|        ``domain_error(http_socket_transport_websocket_response,Response)``
|    ``SessionHandler`` is a variable:
|        ``instantiation_error``
|    ``SessionHandler`` is not a valid WebSocket service handler:
|        ``domain_error(http_websocket_service_handler,SessionHandler)``
|    The delegated session loop raises a WebSocket session error:
|        ``domain_error(http_websocket_session_sequence,Frame)``
|    ``SessionHandler`` returns an invalid registry action:
|        ``domain_error(http_websocket_service_handler_action,Action)``

| **Remarks:**

    - Option ``auto_pong(on|off)``: Controls automatic pong replies in each active session loop.
    - Option ``keepalive_interval(Seconds)``: Schedules empty ping messages when a peer stays silent for the given positive number of seconds.
    - Option ``idle_timeout(Seconds)``: Closes a session with ``status(1001, idle_timeout)`` after the given positive number of seconds without an inbound message.
    - Option ``max_payload_length(Bytes)``: Rejects inbound frames whose declared payload length is greater than ``Bytes`` before allocating payload storage. Oversized frames are treated as ``1009`` close errors in each session loop. Use a non-negative integer.


------------

.. index:: request_shutdown/1
.. _http_websocket_server_service/1::request_shutdown/1:

``request_shutdown/1``
^^^^^^^^^^^^^^^^^^^^^^

Requests shutdown of a registry-backed server loop started with serve_until_shutdown/5-6 for the specified control term and wakes any blocked accept call so the loop can terminate portably.

| **Compilation flags:**
|    ``static``

| **Template:**
|    ``request_shutdown(Control)``
| **Mode and number of proofs:**
|    ``request_shutdown(+nonvar)`` - ``one_or_error``

| **Exceptions:**
|    Registry-backed serving is not available on this backend:
|        ``existence_error(http_websocket_server_service,registry)``
|    ``Control`` is a variable:
|        ``instantiation_error``
|    ``Control`` is not registered for an open-ended WebSocket server loop:
|        ``existence_error(http_websocket_service_shutdown_control,Control)``


------------

Protected predicates
--------------------

(no local declarations; see entity ancestors if any)

Private predicates
------------------

.. index:: shutdown_seed_/1
.. _http_websocket_server_service/1::shutdown_seed_/1:

``shutdown_seed_/1``
^^^^^^^^^^^^^^^^^^^^

Last allocated shutdown run identifier for open-ended WebSocket server loops.

| **Compilation flags:**
|    ``dynamic``

| **Template:**
|    ``shutdown_seed_(RunId)``
| **Mode and number of proofs:**
|    ``shutdown_seed_(?positive_integer)`` - ``zero_or_one``


------------

.. index:: shutdown_control_/3
.. _http_websocket_server_service/1::shutdown_control_/3:

``shutdown_control_/3``
^^^^^^^^^^^^^^^^^^^^^^^

Registered shutdown control terms and their associated listeners.

| **Compilation flags:**
|    ``dynamic``

| **Template:**
|    ``shutdown_control_(Control,Listener,RunId)``
| **Mode and number of proofs:**
|    ``shutdown_control_(?nonvar,?compound,?positive_integer)`` - ``zero_or_more``


------------

.. index:: shutdown_requested_/2
.. _http_websocket_server_service/1::shutdown_requested_/2:

``shutdown_requested_/2``
^^^^^^^^^^^^^^^^^^^^^^^^^

Recorded shutdown requests indexed by control term and run identifier.

| **Compilation flags:**
|    ``dynamic``

| **Template:**
|    ``shutdown_requested_(Control,RunId)``
| **Mode and number of proofs:**
|    ``shutdown_requested_(?nonvar,?positive_integer)`` - ``zero_or_more``


------------

.. index:: active_worker_/3
.. _http_websocket_server_service/1::active_worker_/3:

``active_worker_/3``
^^^^^^^^^^^^^^^^^^^^

Active WebSocket session worker records indexed by control term and run identifier.

| **Compilation flags:**
|    ``dynamic``

| **Template:**
|    ``active_worker_(Control,RunId,Worker)``
| **Mode and number of proofs:**
|    ``active_worker_(?nonvar,?positive_integer,?compound)`` - ``zero_or_more``


------------

Operators
---------

(none)

