LCOV - code coverage report
Current view: top level - wirestead/wrapper - iserver.hpp (source / functions) Coverage Total Hit
Test: Wirestead Coverage Report Lines: 42.9 % 7 3
Test Date: 2026-08-30 10:35:09 Functions: 60.0 % 5 3
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0.0 % 2 0

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * Copyright 2025 Jinwoo Sung
       3                 :             :  *
       4                 :             :  * Licensed under the Apache License, Version 2.0 (the "License");
       5                 :             :  * you may not use this file except in compliance with the License.
       6                 :             :  * You may obtain a copy of the License at
       7                 :             :  *
       8                 :             :  *     http://www.apache.org/licenses/LICENSE-2.0
       9                 :             :  *
      10                 :             :  * Unless required by applicable law or agreed to in writing, software
      11                 :             :  * distributed under the License is distributed on an "AS IS" BASIS,
      12                 :             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13                 :             :  * See the License for the specific language governing permissions and
      14                 :             :  * limitations under the License.
      15                 :             :  */
      16                 :             : 
      17                 :             : #pragma once
      18                 :             : 
      19                 :             : #include <functional>
      20                 :             : #include <future>
      21                 :             : #include <memory>
      22                 :             : #include <optional>
      23                 :             : #include <string_view>
      24                 :             : #include <vector>
      25                 :             : 
      26                 :             : #include "wirestead/base/visibility.hpp"
      27                 :             : #include "wirestead/diagnostics/logger.hpp"
      28                 :             : #include "wirestead/framer/iframer.hpp"
      29                 :             : #include "wirestead/wrapper/context.hpp"
      30                 :             : #include "wirestead/wrapper/runtime_stats.hpp"
      31                 :             : 
      32                 :             : namespace wirestead {
      33                 :             : namespace wrapper {
      34                 :             : 
      35                 :             : /**
      36                 :             :  * @brief Interface for 1:N server communication (e.g., TcpServer)
      37                 :             :  */
      38                 :             : class WIRESTEAD_API ServerInterface {
      39                 :             :  public:
      40                 :             :   using MessageHandler = std::function<void(const MessageContext&)>;
      41                 :             :   using BatchMessageHandler = std::function<void(const std::vector<MessageContext>&)>;
      42                 :             :   using ConnectionHandler = std::function<void(const ConnectionContext&)>;
      43                 :             :   using ErrorHandler = std::function<void(const ErrorContext&)>;
      44                 :             :   using FramerFactory = std::function<std::unique_ptr<framer::IFramer>()>;
      45                 :             : 
      46                 :         185 :   virtual ~ServerInterface() = default;
      47                 :             : 
      48                 :             :   // Lifecycle
      49                 :             :   /**
      50                 :             :    * @brief Start the server asynchronously.
      51                 :             :    *
      52                 :             :    * @return A future that resolves to true when listening, or false on failure
      53                 :             :    *         (e.g. bind failure, port already in use). This future always
      54                 :             :    *         resolves - including on a restart after stop() (#444) - never
      55                 :             :    *         blocks indefinitely.
      56                 :             :    */
      57                 :             :   [[nodiscard]] virtual std::future<bool> start() = 0;
      58                 :             : 
      59                 :             :   /**
      60                 :             :    * @brief Synchronously start the channel/server and wait for the result.
      61                 :             :    */
      62                 :           7 :   [[nodiscard]] virtual bool start_sync() { return start().get(); }
      63                 :             : 
      64                 :             :   /**
      65                 :             :    * @brief Stop the server and block until all active sessions are closed.
      66                 :             :    *
      67                 :             :    * Safe to call from any thread. After stop() returns, no further callbacks will fire
      68                 :             :    * and it is safe to destroy the object. Calling stop() more than once is a no-op.
      69                 :             :    *
      70                 :             :    * Restart contract (#444): stop() fully tears down the underlying transport
      71                 :             :    * (acceptor, sessions, timers) rather than leaving it in a reusable
      72                 :             :    * half-alive state. Every on_*() callback and every config setter ever
      73                 :             :    * called on this wrapper remains in force across any number of stop()/
      74                 :             :    * start() cycles - a subsequent start() reconstructs the transport from
      75                 :             :    * this wrapper's retained configuration and re-registers all callbacks
      76                 :             :    * automatically, including config changes made while stopped. Connected-
      77                 :             :    * client state and stats() reset on restart. Callers never need to
      78                 :             :    * re-register callbacks or reconfigure after a stop()/start() cycle.
      79                 :             :    */
      80                 :             :   virtual void stop() = 0;
      81                 :             :   virtual bool listening() const = 0;
      82                 :             : 
      83                 :             :   /**
      84                 :             :    * @brief Snapshot of this server's runtime counters.
      85                 :             :    *
      86                 :             :    * The cumulative fields (`bytes_*`, `messages_*`, `failed_sends`, `dropped_*`,
      87                 :             :    * `backpressure_events`, `max_queued_bytes`) cover every session this server
      88                 :             :    * has accepted, including ones that have since disconnected. The
      89                 :             :    * instantaneous fields (`queued_bytes`, `pending_bytes`,
      90                 :             :    * `backpressure_active`) describe only the sessions that are live right now.
      91                 :             :    *
      92                 :             :    * Cleared by reset_stats(), and by a stop()/start() cycle per the restart
      93                 :             :    * contract above. See docs/error_model.md.
      94                 :             :    */
      95                 :             :   virtual RuntimeStats stats() const = 0;
      96                 :             : 
      97                 :             :   /**
      98                 :             :    * @brief Counters for one connected client.
      99                 :             :    *
     100                 :             :    * Returns nullopt when the id is unknown - including for a client that has
     101                 :             :    * already disconnected, since a session's counters are folded into the
     102                 :             :    * server-wide stats() and the session itself is gone. Sample this while the
     103                 :             :    * client is connected if you need its numbers in isolation.
     104                 :             :    *
     105                 :             :    * Not every server can answer. UDP servers group datagrams into virtual
     106                 :             :    * sessions that have no queues or counters of their own, so they always
     107                 :             :    * return nullopt; their traffic is only visible in the aggregate.
     108                 :             :    */
     109                 :           2 :   virtual std::optional<RuntimeStats> client_stats(ClientId /*client_id*/) const { return std::nullopt; }
     110                 :             : 
     111                 :             :   virtual void reset_stats() = 0;
     112                 :             : 
     113                 :             :   // Transmission
     114                 :             :   //
     115                 :             :   // Strategy-aware API (recommended):
     116                 :             :   //
     117                 :             :   //   send_to() / broadcast()
     118                 :             :   //     Behaviour depends on the configured backpressure strategy:
     119                 :             :   //       BestEffort — non-blocking; drops data when the target client's queue is full.
     120                 :             :   //       Reliable   — blocks the calling thread until queue pressure is relieved,
     121                 :             :   //                    then enqueues for send_to().
     122                 :             :   //     broadcast() always performs non-blocking fan-out to connected clients.
     123                 :             :   //     It does not wait for one slow client to relieve backpressure.
     124                 :             :   //
     125                 :             :   // Explicit API (escape hatch):
     126                 :             :   //
     127                 :             :   //   try_send_to() / try_broadcast()
     128                 :             :   //     Always non-blocking and always drops on full queue, regardless of strategy.
     129                 :             :   //     Rejects payloads that would exceed the current non-blocking queue
     130                 :             :   //     threshold; payloads below MAX_BUFFER_SIZE can still be rejected when
     131                 :             :   //     they exceed the current backpressure high-water budget.
     132                 :             :   //
     133                 :             :   //   send_to_blocking()
     134                 :             :   //     Always blocks until queue pressure is relieved, regardless of strategy.
     135                 :             :   //     This call may block indefinitely while the server remains active and
     136                 :             :   //     backpressure does not clear. stop() from another thread is expected to
     137                 :             :   //     unblock waiting senders.
     138                 :             : 
     139                 :             :   /**
     140                 :             :    * @brief Send to a specific client, honouring the backpressure strategy.
     141                 :             :    *
     142                 :             :    * BestEffort: non-blocking, drops if the client's send queue is full.
     143                 :             :    * Reliable:   blocks until queue pressure is relieved, then enqueues.
     144                 :             :    *
     145                 :             :    * @return true Data was accepted. @return false Dropped or client not found.
     146                 :             :    */
     147                 :             :   virtual bool send_to(ClientId client_id, std::string_view data) = 0;
     148                 :             : 
     149                 :             :   /**
     150                 :             :    * @brief Send to all connected clients using non-blocking fan-out.
     151                 :             :    *
     152                 :             :    * Does not wait for slow clients to relieve backpressure. Use send_to_blocking()
     153                 :             :    * for strict per-client blocking delivery.
     154                 :             :    *
     155                 :             :    * @return true At least one client accepted the data.
     156                 :             :    */
     157                 :             :   virtual bool broadcast(std::string_view data) = 0;
     158                 :             : 
     159                 :             :   /**
     160                 :             :    * @brief Block until queue pressure is relieved, then send to a client. Ignores strategy.
     161                 :             :    *
     162                 :             :    * This call may block indefinitely while the server remains active and
     163                 :             :    * backpressure does not clear. stop() from another thread is expected to unblock
     164                 :             :    * waiting senders.
     165                 :             :    *
     166                 :             :    * @return true Data was accepted. @return false Server stopped while waiting.
     167                 :             :    */
     168                 :             :   virtual bool send_to_blocking(ClientId client_id, std::string_view data) = 0;
     169                 :             : 
     170                 :             :   /**
     171                 :             :    * @brief Non-blocking send_to that always drops on a full queue, ignoring strategy.
     172                 :             :    *
     173                 :             :    * Use as an escape hatch when you need drop-on-full behaviour on a Reliable channel.
     174                 :             :    * Rejects payloads that would exceed the current non-blocking queue threshold.
     175                 :             :    * A payload may be rejected even if it is below MAX_BUFFER_SIZE when it is larger
     176                 :             :    * than the current backpressure high-water budget. Use send_to() or
     177                 :             :    * send_to_blocking() when Reliable enqueue semantics are required for large
     178                 :             :    * payloads.
     179                 :             :    *
     180                 :             :    * @return true Data was accepted. @return false Dropped or client not found.
     181                 :             :    */
     182                 :             :   virtual bool try_send_to(ClientId client_id, std::string_view data) = 0;
     183                 :             : 
     184                 :             :   /**
     185                 :             :    * @brief Non-blocking broadcast that always drops on full queues, ignoring strategy.
     186                 :             :    *
     187                 :             :    * Uses the same non-blocking queue threshold policy as try_send_to().
     188                 :             :    *
     189                 :             :    * @return true At least one client accepted the data.
     190                 :             :    */
     191                 :             :   virtual bool try_broadcast(std::string_view data) = 0;
     192                 :             : 
     193                 :             :   /**
     194                 :             :    * @brief Send a line (data + "\n") to all clients, honouring the backpressure strategy.
     195                 :             :    */
     196                 :             :   virtual bool broadcast_line(std::string_view line) = 0;
     197                 :             : 
     198                 :             :   /**
     199                 :             :    * @brief Send a line (data + "\n") to a specific client, honouring the backpressure strategy.
     200                 :             :    */
     201                 :             :   virtual bool send_to_line(ClientId client_id, std::string_view line) = 0;
     202                 :             : 
     203                 :             :   /**
     204                 :             :    * @brief Non-blocking broadcast_line that always drops on a full queue, ignoring strategy.
     205                 :             :    */
     206                 :             :   virtual bool try_broadcast_line(std::string_view line) = 0;
     207                 :             : 
     208                 :             :   /**
     209                 :             :    * @brief Non-blocking send_to_line that always drops on a full queue, ignoring strategy.
     210                 :             :    */
     211                 :             :   virtual bool try_send_to_line(ClientId client_id, std::string_view line) = 0;
     212                 :             : 
     213                 :             :   // Event handlers
     214                 :             : 
     215                 :             :   /**
     216                 :             :    * @brief Fires when a connection is accepted.
     217                 :             :    *
     218                 :             :    * Accepted, not usable. On a TLS server this runs before the handshake, so a
     219                 :             :    * client that fails it still produces one on_connect - followed by an
     220                 :             :    * on_disconnect, with no on_data in between. The two callbacks stay paired
     221                 :             :    * either way, so connection bookkeeping keyed on them does not leak; treat
     222                 :             :    * the first byte of data, not this callback, as proof the peer got through.
     223                 :             :    */
     224                 :             :   virtual ServerInterface& on_connect(ConnectionHandler handler) = 0;
     225                 :             :   virtual ServerInterface& on_disconnect(ConnectionHandler handler) = 0;
     226                 :             :   virtual ServerInterface& on_data(MessageHandler handler) = 0;
     227                 :             : 
     228                 :             :   /** @brief Register a callback for batched data reception */
     229                 :             :   virtual ServerInterface& on_data_batch(BatchMessageHandler handler) = 0;
     230                 :             : 
     231                 :             :   virtual ServerInterface& on_error(ErrorHandler handler) = 0;
     232                 :             : 
     233                 :             :   /**
     234                 :             :    * @brief Register a callback to be notified when send queue congestion changes.
     235                 :             :    * @param handler Callback receiving the current number of queued bytes.
     236                 :             :    *
     237                 :             :    * Default implementation is a no-op. Concrete implementations that support
     238                 :             :    * backpressure reporting must override this method; otherwise the handler
     239                 :             :    * is silently discarded.
     240                 :             :    */
     241                 :           0 :   virtual ServerInterface& on_backpressure(std::function<void(size_t)> handler) {
     242         [ #  # ]:           0 :     if (handler) {
     243                 :           0 :       WIRESTEAD_LOG_WARNING("server_interface", "on_backpressure",
     244                 :             :                             "Backpressure reporting is not supported by this server; handler is discarded.");
     245                 :             :     }
     246                 :           0 :     return *this;
     247                 :             :   }
     248                 :             : 
     249                 :             :   /**
     250                 :             :    * @brief Set a factory function to create a new framer for each client connection.
     251                 :             :    * @param factory Function that returns a unique_ptr to a new framer.
     252                 :             :    */
     253                 :             :   virtual ServerInterface& framer(FramerFactory factory) = 0;
     254                 :             : 
     255                 :             :   /**
     256                 :             :    * @brief Set a handler for complete messages extracted by the framer.
     257                 :             :    * @param handler callback taking MessageContext (where data is the framed payload).
     258                 :             :    */
     259                 :             :   virtual ServerInterface& on_message(MessageHandler handler) = 0;
     260                 :             : 
     261                 :             :   /** @brief Register a callback for batched framed message reception */
     262                 :             :   virtual ServerInterface& on_message_batch(BatchMessageHandler handler) = 0;
     263                 :             : 
     264                 :             :   // Management
     265                 :             :   virtual ServerInterface& auto_start(bool manage = true) = 0;
     266                 :             :   virtual size_t client_count() const = 0;
     267                 :             :   virtual std::vector<ClientId> connected_clients() const = 0;
     268                 :             : };
     269                 :             : 
     270                 :             : }  // namespace wrapper
     271                 :             : }  // namespace wirestead
        

Generated by: LCOV version 2.0-1