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>
24 : : #include <string_view>
25 : : #include <vector>
26 : :
27 : : #include "wirestead/base/constants.hpp"
28 : : #include "wirestead/base/visibility.hpp"
29 : : #include "wirestead/wrapper/iserver.hpp"
30 : :
31 : : namespace boost {
32 : : namespace asio {
33 : : class io_context;
34 : : }
35 : : } // namespace boost
36 : :
37 : : namespace wirestead {
38 : :
39 : : namespace interface {
40 : : class Channel;
41 : : }
42 : :
43 : : namespace wrapper {
44 : :
45 : : /**
46 : : * @brief Modernized TCP Server Wrapper
47 : : */
48 : : class WIRESTEAD_API TcpServer : public ServerInterface {
49 : : public:
50 : : explicit TcpServer(uint16_t port);
51 : : TcpServer(uint16_t port, std::shared_ptr<boost::asio::io_context> external_ioc);
52 : : explicit TcpServer(std::shared_ptr<interface::Channel> channel);
53 : : ~TcpServer() override;
54 : :
55 : : // Move semantics
56 : : TcpServer(TcpServer&&) noexcept;
57 : : TcpServer& operator=(TcpServer&&) noexcept;
58 : :
59 : : // Disable copy
60 : : TcpServer(const TcpServer&) = delete;
61 : : TcpServer& operator=(const TcpServer&) = delete;
62 : :
63 : : // ServerInterface implementation
64 : : [[nodiscard]] std::future<bool> start() override;
65 : : void stop() override;
66 : : bool listening() const override;
67 : : RuntimeStats stats() const override;
68 : : void reset_stats() override;
69 : :
70 : : // Transmission
71 : : bool broadcast(std::string_view data) override;
72 : : bool send_to(ClientId client_id, std::string_view data) override;
73 : : bool send_to_blocking(ClientId client_id, std::string_view data) override;
74 : : bool try_send_to(ClientId client_id, std::string_view data) override;
75 : : bool try_broadcast(std::string_view data) override;
76 : : bool broadcast_line(std::string_view line) override;
77 : : bool send_to_line(ClientId client_id, std::string_view line) override;
78 : : bool try_broadcast_line(std::string_view line) override;
79 : : bool try_send_to_line(ClientId client_id, std::string_view line) override;
80 : :
81 : : // Event handlers
82 : : TcpServer& on_connect(ConnectionHandler handler) override;
83 : : TcpServer& on_disconnect(ConnectionHandler handler) override;
84 : : TcpServer& on_data(MessageHandler handler) override;
85 : : TcpServer& on_data_batch(BatchMessageHandler handler) override;
86 : : TcpServer& on_error(ErrorHandler handler) override;
87 : : TcpServer& on_backpressure(std::function<void(size_t)> handler) override;
88 : :
89 : : TcpServer& framer(FramerFactory factory) override;
90 : : TcpServer& on_message(MessageHandler handler) override;
91 : : TcpServer& on_message_batch(BatchMessageHandler handler) override;
92 : :
93 : : // Client count and management
94 : : size_t client_count() const override;
95 : : std::vector<ClientId> connected_clients() const override;
96 : : std::optional<RuntimeStats> client_stats(ClientId client_id) const override;
97 : :
98 : : // Configuration (Fluent API)
99 : : TcpServer& auto_start(bool manage = true) override;
100 : : TcpServer& bind_address(const std::string& address);
101 : : // Opt into the shared IoContextManager singleton instead of the default
102 : : // dedicated io_context + thread (#440). Must be set before the first
103 : : // start() call to take effect. Only meaningful for deliberately trading
104 : : // per-instance parallelism for reduced thread/memory overhead across many
105 : : // servers in one process; most callers should not need this.
106 : : TcpServer& shared_context(bool use_shared = true);
107 : : TcpServer& port_retry(bool enable = true, int max_retries = 3, int retry_interval_ms = 1000);
108 : : /**
109 : : * @brief Configure application-level idle timeout for accepted sessions.
110 : : *
111 : : * A value of 0ms disables idle timeout. When enabled, only the idle client
112 : : * session is closed; the server keeps listening for new connections.
113 : : */
114 : : TcpServer& idle_timeout(std::chrono::milliseconds timeout);
115 : : TcpServer& max_clients(size_t max);
116 : : TcpServer& backpressure_threshold(size_t threshold);
117 : : TcpServer& backpressure_strategy(base::constants::BackpressureStrategy strategy);
118 : :
119 : : /// Returns the configured backpressure threshold in bytes.
120 : : size_t backpressure_threshold() const;
121 : : /// Returns the configured backpressure strategy.
122 : : base::constants::BackpressureStrategy backpressure_strategy() const;
123 : : TcpServer& tcp_no_delay(bool enable = true);
124 : : TcpServer& keep_alive(bool enable = true);
125 : : /**
126 : : * @brief Serve TLS using this certificate chain and private key (PEM).
127 : : *
128 : : * Must be set before start(). Requires a build with WIRESTEAD_ENABLE_TLS;
129 : : * without it start() fails rather than quietly serving plaintext.
130 : : */
131 : : TcpServer& tls(const std::string& certificate_file, const std::string& private_key_file);
132 : :
133 : : TcpServer& send_buffer_size(size_t bytes);
134 : : TcpServer& receive_buffer_size(size_t bytes);
135 : :
136 : : /**
137 : : * @brief Size of the userspace buffer each read fills, in bytes.
138 : : *
139 : : * Distinct from receive_buffer_size(), which sets the kernel's SO_RCVBUF.
140 : : * Raising this reduces read completions and callback dispatches on bulk
141 : : * transfers, at the cost of that much memory per connection - a server
142 : : * multiplies it by the number of accepted connections. Clamped to
143 : : * [MIN_READ_BUFFER_SIZE, MAX_READ_BUFFER_SIZE]; takes effect on the next
144 : : * start().
145 : : */
146 : : TcpServer& read_buffer_size(size_t bytes);
147 : : TcpServer& manage_external_context(bool manage);
148 : : TcpServer& batch_size(size_t size);
149 : : TcpServer& batch_latency(std::chrono::milliseconds latency);
150 : :
151 : : private:
152 : : struct Impl;
153 : 2074 : const Impl* get_impl() const { return impl_.get(); }
154 : : Impl* get_impl() { return impl_.get(); }
155 : : // #450: shared_ptr (not unique_ptr) so in-flight callbacks on an
156 : : // externally-owned io_context can extend Impl's lifetime for the
157 : : // duration of their invocation via weak_from_this(), rather than only
158 : : // checking a staleness flag that says nothing about whether Impl itself
159 : : // still exists.
160 : : std::shared_ptr<Impl> impl_;
161 : : };
162 : :
163 : : } // namespace wrapper
164 : : } // namespace wirestead
|