Program Listing for File HttpWsServer.h

Program Listing for File HttpWsServer.h#

Return to documentation for file (include/Karana/WebUI/HttpWsServer.h)

/*
 * Copyright (c) 2024-2026 Karana Dynamics Pty Ltd. All rights reserved.
 *
 * NOTICE TO USER:
 *
 * This source code and/or documentation (the "Licensed Materials") is
 * the confidential and proprietary information of Karana Dynamics Inc.
 * Use of these Licensed Materials is governed by the terms and conditions
 * of a separate software license agreement between Karana Dynamics and the
 * Licensee ("License Agreement"). Unless expressly permitted under that
 * agreement, any reproduction, modification, distribution, or disclosure
 * of the Licensed Materials, in whole or in part, to any third party
 * without the prior written consent of Karana Dynamics is strictly prohibited.
 *
 * THE LICENSED MATERIALS ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
 * KARANA DYNAMICS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND
 * FITNESS FOR A PARTICULAR PURPOSE.
 *
 * IN NO EVENT SHALL KARANA DYNAMICS BE LIABLE FOR ANY DAMAGES WHATSOEVER,
 * INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS, DATA, OR USE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, WHETHER IN CONTRACT, TORT,
 * OR OTHERWISE ARISING OUT OF OR IN CONNECTION WITH THE LICENSED MATERIALS.
 *
 * U.S. Government End Users: The Licensed Materials are a "commercial item"
 * as defined at 48 C.F.R. 2.101, and are provided to the U.S. Government
 * only as a commercial end item under the terms of this license.
 *
 * Any use of the Licensed Materials in individual or commercial software must
 * include, in the user documentation and internal source code comments,
 * this Notice, Disclaimer, and U.S. Government Use Provision.
 */

#pragma once
#include <filesystem>
#include <functional>
#include <memory>
#include <string>
#include <vector>

namespace Karana::WebUI {

    using HeaderList = std::vector<std::pair<std::string, std::string>>;

    /**
     * @class HttpWsServer
     * @brief Generic http/websocket server.
     *
     * See \sref{webui_sec} for more discussion on WebUI.
     */
    class HttpWsServer {
      public:
        /**
         * @brief HttpWsServer constructor.
         * @param port - the port to bind to.
         */
        HttpWsServer(unsigned short port);

        /**
         * @brief HttpWsServer destructor.
         */
        virtual ~HttpWsServer();

        /**
         * @brief Get the port the server is bound to.
         * @return The port number.
         */
        unsigned short getPort() const;

        /**
         * @brief Get the url to access the server.
         * @return The url.
         */
        std::string getUrl() const;

        /**
         * @brief Serve a give file for GET requests
         *
         * If headers is empty, Content-Type will automatically be added
         * based on MIME type associated with the filename extension
         *
         * @param url - The url to serve at
         * @param path - Path to the file to serve
         * @param headers - Response headers
         */
        void serveFile(std::string_view url,
                       const std::filesystem::path &path,
                       const HeaderList &headers = {});

        /**
         * @brief Serve the given string for GET requests
         *
         * If headers is empty, Content-Type will automatically be added
         * with the type, application/octet-stream
         *
         * @param url - The url to serve at
         * @param content - The data to serve
         * @param headers - Response headers
         */
        void
        serveData(std::string_view url, std::string_view content, const HeaderList &headers = {});

        /**
         * @brief Idempotently serve a give file at a content-addressed URL
         *
         * If headers is empty, automatically determines MIME_TYPE based on
         * file extension and uses the headers:
         * - Content-Type: $MIME_TYPE
         * - Cache-Control: public, max-age=31536000, immutable
         *
         * @param path - Path to the file to serve
         * @param url_prefix - A url to prepend to the CAS url
         * @param headers - Response headers
         * @return The content-addressed URL
         */
        std::string serveCasFile(const std::filesystem::path &path,
                                 std::string_view url_prefix = "/objects/",
                                 const HeaderList &headers = {});

        /**
         * @brief Idempotently serve the given string at a content-addressed URL
         *
         * If headers is empty, automatically uses the headers:
         * - Content-Type: application/octet-stream
         * - Cache-Control: public, max-age=31536000, immutable
         *
         * @param content - The data to serve
         * @param url_prefix - A url to prepend to the CAS url
         * @param headers - Response headers
         * @return The content-addressed URL
         */
        std::string serveCasData(std::string_view content,
                                 std::string_view url_prefix = "/objects/",
                                 const HeaderList &headers = {});

        /**
         * @brief Send a message to all connected websocket clients
         * @param msg - The message to send
         */
        void broadcastMessage(std::string_view msg);

        /**
         * @brief Send a message to a given websocket client
         * @param msg - The message to send
         * @param client_id - The id for the client to send to
         */
        void sendMessage(std::string_view msg, int client_id);

        /**
         * @brief Set a function called on websocket client connection
         * @param callback - The callback function
         */
        void setOnConnect(std::function<void(int)> callback = {});

        /**
         * @brief Set a function called on receiving a websocket message
         * @param callback - The callback function
         */
        void setOnMessage(std::function<void(std::string_view, int)> callback = {});

        /**
         * @brief Set a function called on websocket client disconnect
         * @param callback - The callback function
         */
        void setOnDisconnect(std::function<void(int)> callback = {});

        /**
         * @brief Wait for a given number of clients to connect.
         * @param clients - Number of clients to wait for
         * @param timeout_seconds - Timeout, if positive
         * @return True if the requested client count was reached
         */
        bool waitForClients(int clients = 1, float timeout_seconds = 0);

        /**
         * @brief Submit a function to be called in the event loop
         * @param callback - The callback function
         */
        void defer(std::function<void()> callback);

        /**
         * @brief Block for deferred calls
         *
         * This places a deferred callback on the event loop and waits
         * for the callback to be called, ensuring any prior callbacks
         * have also been called. This does _not_ wait for the event
         * loop to reach quiescence.
         */
        void sync();

        /**
         * @brief Print info to stdout about how to connect
         */
        void printConnectionInfo();

        /**
         * @brief Shut down the server
         */
        void close();

      private:
        std::unique_ptr<struct HttpWsServerImpl> _impl;
    };

} // namespace Karana::WebUI