LCOV - code coverage report
Current view: top level - wirestead/wrapper - iserver.hpp (source / functions) Coverage Total Hit
Test: Wirestead Coverage Report Lines: 28.6 % 7 2
Test Date: 2026-10-29 11:36:05 Functions: 40.0 % 5 2
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/fanout_result.hpp"
      31                 :             : #include "wirestead/wrapper/runtime_stats.hpp"
      32                 :             : #include "wirestead/wrapper/send_result.hpp"
      33                 :             : 
      34                 :             : namespace wirestead {
      35                 :             : namespace wrapper {
      36                 :             : 
      37                 :             : /**
      38                 :             :  * @brief Interface for 1:N server communication (e.g., TcpServer)
      39                 :             :  */
      40                 :             : class WIRESTEAD_API ServerInterface {
      41                 :             :  public:
      42                 :             :   using MessageHandler = std::function<void(const MessageContext&)>;
      43                 :             :   using BatchMessageHandler = std::function<void(const std::vector<MessageContext>&)>;
      44                 :             :   using ConnectionHandler = std::function<void(const ConnectionContext&)>;
      45                 :             :   using ErrorHandler = std::function<void(const ErrorContext&)>;
      46                 :             :   using FramerFactory = std::function<std::unique_ptr<framer::IFramer>()>;
      47                 :             : 
      48                 :         614 :   virtual ~ServerInterface() = default;
      49                 :             : 
      50                 :             :   // Lifecycle
      51                 :             :   /**
      52                 :             :    * @brief Start the server asynchronously.
      53                 :             :    *
      54                 :             :    * @return A future that resolves to true when listening, or false on failure
      55                 :             :    *         (e.g. bind failure, port already in use). This future always
      56                 :             :    *         resolves - including on a restart after stop() (#444) - never
      57                 :             :    *         blocks indefinitely.
      58                 :             :    */
      59                 :             :   [[nodiscard]] virtual std::future<bool> start() = 0;
      60                 :             : 
      61                 :             :   /**
      62                 :             :    * @brief Synchronously start the channel/server and wait for the result.
      63                 :             :    */
      64                 :          49 :   [[nodiscard]] virtual bool start_sync() { return start().get(); }
      65                 :             : 
      66                 :             :   /**
      67                 :             :    * @brief Request server shutdown and wait when the calling thread permits it.
      68                 :             :    *
      69                 :             :    * Outside callers wait for shutdown completion, including concurrent callers
      70                 :             :    * that find a stop already in progress. No user callback remains running or can
      71                 :             :    * start from that run when a waiting stop returns. Repeated completed stops are no-ops.
      72                 :             :    *
      73                 :             :    * On an executor needed by shutdown (including this object's own callback),
      74                 :             :    * stop() only requests shutdown and returns without waiting. Such a return does
      75                 :             :    * not permit destruction, restart, or stopped-only configuration. An outside
      76                 :             :    * caller must observe shutdown completion first. Destroying the object from its
      77                 :             :    * own callback and concurrent destruction/use or start/stop are unsupported.
      78                 :             :    * Externally managed executors must keep making progress while callers wait;
      79                 :             :    * user callbacks must return. Internal handlers may remain if they own their
      80                 :             :    * lifetime and cannot affect a later run.
      81                 :             :    *
      82                 :             :    * Restart contract (#444): completed shutdown fully tears down the underlying transport
      83                 :             :    * (acceptor, sessions, timers) rather than leaving it in a reusable
      84                 :             :    * half-alive state. Every on_*() callback and every config setter ever
      85                 :             :    * called on this wrapper remains in force across any number of stop()/
      86                 :             :    * start() cycles - a subsequent start() reconstructs the transport from
      87                 :             :    * this wrapper's retained configuration and re-registers all callbacks
      88                 :             :    * automatically, including config changes made while stopped. Connected-
      89                 :             :    * client state and stats() reset on restart. Callers never need to
      90                 :             :    * re-register callbacks or reconfigure after a stop()/start() cycle.
      91                 :             :    */
      92                 :             :   virtual void stop() = 0;
      93                 :             :   virtual bool listening() const = 0;
      94                 :             : 
      95                 :             :   /**
      96                 :             :    * @brief Snapshot of this server's runtime counters.
      97                 :             :    *
      98                 :             :    * The cumulative fields (`bytes_*`, `messages_*`, `failed_sends`, `dropped_*`,
      99                 :             :    * `backpressure_events`, `max_queued_bytes`) cover every session this server
     100                 :             :    * has accepted, including ones that have since disconnected or stopped.
     101                 :             :    * Native TCP/UDS send_accounting also covers those contributors. During stop,
     102                 :             :    * retiring sessions remain in the aggregate until their final totals transfer.
     103                 :             :    * The
     104                 :             :    * instantaneous fields (`queued_bytes`, `pending_bytes`,
     105                 :             :    * `backpressure_active`) describe only the sessions that are live right now.
     106                 :             :    *
     107                 :             :    * Cleared by reset_stats(), and by a stop()/start() cycle per the restart
     108                 :             :    * contract above. See docs/error_model.md.
     109                 :             :    */
     110                 :             :   virtual RuntimeStats stats() const = 0;
     111                 :             : 
     112                 :             :   /**
     113                 :             :    * @brief Counters for one connected client.
     114                 :             :    *
     115                 :             :    * Returns nullopt when the id is unknown - including for a client that has
     116                 :             :    * already disconnected, since a session's counters are folded into the
     117                 :             :    * server-wide stats() and the session itself is gone. Sample this while the
     118                 :             :    * client is connected if you need its numbers in isolation.
     119                 :             :    *
     120                 :             :    * Built-in UDP servers expose per-virtual-session traffic and accounting.
     121                 :             :    * Their backpressure state/events describe shared socket capacity. Custom
     122                 :             :    * servers may return nullopt when per-client statistics are unsupported.
     123                 :             :    */
     124                 :           0 :   virtual std::optional<RuntimeStats> client_stats(ClientId /*client_id*/) const { return std::nullopt; }
     125                 :             : 
     126                 :             :   virtual void reset_stats() = 0;
     127                 :             : 
     128                 :             :   // Transmission
     129                 :             :   //
     130                 :             :   // Strategy-aware API (recommended):
     131                 :             :   //
     132                 :             :   //   send_to() / broadcast()
     133                 :             :   //     Behaviour depends on the configured backpressure strategy:
     134                 :             :   //       BestEffort — non-blocking; drops data when the target client's queue is full.
     135                 :             :   //       Reliable   — blocks the calling thread until queue pressure is relieved,
     136                 :             :   //                    then enqueues for send_to().
     137                 :             :   //     broadcast() always performs non-blocking fan-out to connected clients.
     138                 :             :   //     It does not wait for one slow client to relieve backpressure.
     139                 :             :   //
     140                 :             :   // Explicit API (escape hatch):
     141                 :             :   //
     142                 :             :   //   try_send_to() / try_broadcast()
     143                 :             :   //     Always non-blocking and always drops on full queue, regardless of strategy.
     144                 :             :   //     Rejects payloads that would exceed the current non-blocking queue
     145                 :             :   //     threshold; payloads below MAX_BUFFER_SIZE can still be rejected when
     146                 :             :   //     they exceed the current backpressure high-water budget.
     147                 :             :   //
     148                 :             :   //   send_to_blocking()
     149                 :             :   //     Always blocks until queue pressure is relieved, regardless of strategy.
     150                 :             :   //     This call may block indefinitely while the server remains active and
     151                 :             :   //     backpressure does not clear. stop() from another thread is expected to
     152                 :             :   //     unblock waiting senders.
     153                 :             : 
     154                 :             :   /**
     155                 :             :    * @brief Send to a specific client, honouring the backpressure strategy.
     156                 :             :    *
     157                 :             :    * BestEffort: non-blocking, drops if the client's send queue is full.
     158                 :             :    * Reliable:   blocks until queue pressure is relieved, then enqueues.
     159                 :             :    *
     160                 :             :    * @return Local queue acceptance or a synchronous rejection reason; not delivery.
     161                 :             :    */
     162                 :             :   [[nodiscard]] virtual SendResult send_to(ClientId client_id, std::string_view data) = 0;
     163                 :             : 
     164                 :             :   /**
     165                 :             :    * @brief Send to all connected clients using non-blocking fan-out.
     166                 :             :    *
     167                 :             :    * Does not wait for slow clients to relieve backpressure. Use send_to_blocking()
     168                 :             :    * for per-client blocking queue admission.
     169                 :             :    *
     170                 :             :    * @return Counts over the fixed target set; empty() identifies zero targets.
     171                 :             :    */
     172                 :             :   [[nodiscard]] virtual FanoutResult broadcast(std::string_view data) = 0;
     173                 :             : 
     174                 :             :   /**
     175                 :             :    * @brief Block until queue pressure is relieved, then send to a client. Ignores strategy.
     176                 :             :    *
     177                 :             :    * This call may block indefinitely while the server remains active and
     178                 :             :    * backpressure does not clear. stop() from another thread is expected to unblock
     179                 :             :    * waiting senders.
     180                 :             :    *
     181                 :             :    * @return Local queue acceptance or a synchronous rejection reason. A stopped wait returns
     182                 :             :    * CancelledWhileWaiting; a lost target returns NotReady.
     183                 :             :    */
     184                 :             :   [[nodiscard]] virtual SendResult send_to_blocking(ClientId client_id, std::string_view data) = 0;
     185                 :             : 
     186                 :             :   /**
     187                 :             :    * @brief Non-blocking send_to that always drops on a full queue, ignoring strategy.
     188                 :             :    *
     189                 :             :    * Use as an escape hatch when you need drop-on-full behaviour on a Reliable channel.
     190                 :             :    * Rejects payloads that would exceed the current non-blocking queue threshold.
     191                 :             :    * A payload may be rejected even if it is below MAX_BUFFER_SIZE when it is larger
     192                 :             :    * than the current backpressure high-water budget. Use send_to() or
     193                 :             :    * send_to_blocking() when Reliable enqueue semantics are required for large
     194                 :             :    * payloads.
     195                 :             :    *
     196                 :             :    * @return Local queue acceptance or a synchronous rejection reason; not delivery.
     197                 :             :    */
     198                 :             :   [[nodiscard]] virtual SendResult try_send_to(ClientId client_id, std::string_view data) = 0;
     199                 :             : 
     200                 :             :   /**
     201                 :             :    * @brief Non-blocking broadcast that always drops on full queues, ignoring strategy.
     202                 :             :    *
     203                 :             :    * Uses the same non-blocking queue threshold policy as try_send_to().
     204                 :             :    *
     205                 :             :    * @return Counts over the fixed target set; empty() identifies zero targets.
     206                 :             :    */
     207                 :             :   [[nodiscard]] virtual FanoutResult try_broadcast(std::string_view data) = 0;
     208                 :             : 
     209                 :             :   /**
     210                 :             :    * @brief Send a line (data + "\n") to all clients without waiting for capacity.
     211                 :             :    */
     212                 :             :   [[nodiscard]] virtual FanoutResult broadcast_line(std::string_view line) = 0;
     213                 :             : 
     214                 :             :   /**
     215                 :             :    * @brief Send a line (data + "\n") to a specific client, honouring the backpressure strategy.
     216                 :             :    */
     217                 :             :   [[nodiscard]] virtual SendResult send_to_line(ClientId client_id, std::string_view line) = 0;
     218                 :             : 
     219                 :             :   /**
     220                 :             :    * @brief Non-blocking broadcast_line that always drops on a full queue, ignoring strategy.
     221                 :             :    */
     222                 :             :   [[nodiscard]] virtual FanoutResult try_broadcast_line(std::string_view line) = 0;
     223                 :             : 
     224                 :             :   /**
     225                 :             :    * @brief Non-blocking send_to_line that always drops on a full queue, ignoring strategy.
     226                 :             :    */
     227                 :             :   [[nodiscard]] virtual SendResult try_send_to_line(ClientId client_id, std::string_view line) = 0;
     228                 :             : 
     229                 :             :   // Event handlers
     230                 :             : 
     231                 :             :   /**
     232                 :             :    * @brief Fires when a connection is accepted.
     233                 :             :    *
     234                 :             :    * Accepted, not usable. On a TLS server this runs before the handshake, so a
     235                 :             :    * client that fails it still produces one on_connect - followed by an
     236                 :             :    * on_disconnect, with no on_data in between. The two callbacks stay paired
     237                 :             :    * either way, so connection bookkeeping keyed on them does not leak; treat
     238                 :             :    * the first byte of data, not this callback, as proof the peer got through.
     239                 :             :    */
     240                 :             :   virtual ServerInterface& on_connect(ConnectionHandler handler) = 0;
     241                 :             :   virtual ServerInterface& on_disconnect(ConnectionHandler handler) = 0;
     242                 :             :   virtual ServerInterface& on_data(MessageHandler handler) = 0;
     243                 :             : 
     244                 :             :   /** @brief Register a callback for batched data reception */
     245                 :             :   virtual ServerInterface& on_data_batch(BatchMessageHandler handler) = 0;
     246                 :             : 
     247                 :             :   virtual ServerInterface& on_error(ErrorHandler handler) = 0;
     248                 :             : 
     249                 :             :   /**
     250                 :             :    * @brief Register a callback to be notified when send queue congestion changes.
     251                 :             :    * @param handler Callback receiving the current number of queued bytes.
     252                 :             :    *
     253                 :             :    * Default implementation is a no-op. Concrete implementations that support
     254                 :             :    * backpressure reporting must override this method; otherwise the handler
     255                 :             :    * is silently discarded.
     256                 :             :    */
     257                 :           0 :   virtual ServerInterface& on_backpressure(std::function<void(size_t)> handler) {
     258         [ #  # ]:           0 :     if (handler) {
     259                 :           0 :       WIRESTEAD_LOG_WARNING("server_interface", "on_backpressure",
     260                 :             :                             "Backpressure reporting is not supported by this server; handler is discarded.");
     261                 :             :     }
     262                 :           0 :     return *this;
     263                 :             :   }
     264                 :             : 
     265                 :             :   /**
     266                 :             :    * @brief Set a factory function to create a new framer for each client connection.
     267                 :             :    * @param factory Function that returns a unique_ptr to a new framer.
     268                 :             :    */
     269                 :             :   virtual ServerInterface& framer(FramerFactory factory) = 0;
     270                 :             : 
     271                 :             :   /**
     272                 :             :    * @brief Set a handler for complete messages extracted by the framer.
     273                 :             :    * @param handler callback taking MessageContext (where data is the framed payload).
     274                 :             :    */
     275                 :             :   virtual ServerInterface& on_message(MessageHandler handler) = 0;
     276                 :             : 
     277                 :             :   /** @brief Register a callback for batched framed message reception */
     278                 :             :   virtual ServerInterface& on_message_batch(BatchMessageHandler handler) = 0;
     279                 :             : 
     280                 :             :   // Management
     281                 :             :   virtual ServerInterface& auto_start(bool manage = true) = 0;
     282                 :             :   virtual size_t client_count() const = 0;
     283                 :             :   virtual std::vector<ClientId> connected_clients() const = 0;
     284                 :             : };
     285                 :             : 
     286                 :             : }  // namespace wrapper
     287                 :             : }  // namespace wirestead
        

Generated by: LCOV version 2.0-1