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 : : #include "wirestead/builder/tcp_server_builder.hpp"
18 : :
19 : : #include <boost/asio/io_context.hpp>
20 : :
21 : : #include "wirestead/builder/auto_initializer.hpp"
22 : : #include "wirestead/config/validation.hpp"
23 : : #include "wirestead/diagnostics/exceptions.hpp"
24 : : #include "wirestead/util/input_validator.hpp"
25 : :
26 : : namespace wirestead {
27 : : namespace builder {
28 : :
29 : 119 : TcpServerBuilder::TcpServerBuilder(uint16_t port)
30 : 119 : : port_(port),
31 : 119 : bind_address_("0.0.0.0"),
32 : 119 : auto_start_(false),
33 : 119 : independent_context_(false),
34 : 119 : shared_context_(false),
35 : 119 : max_clients_(0),
36 : 119 : client_limit_enabled_(false),
37 : 119 : port_retry_enabled_(false),
38 : 119 : max_port_retries_(3),
39 : 119 : port_retry_interval_ms_(1000),
40 : 238 : idle_timeout_(0),
41 : 119 : idle_timeout_set_(false),
42 : 119 : tcp_no_delay_(true),
43 : 119 : keep_alive_(false),
44 : 119 : send_buffer_size_(0),
45 : 119 : receive_buffer_size_(0),
46 : 238 : read_buffer_size_(base::constants::DEFAULT_READ_BUFFER_SIZE) {
47 [ + + + - : 131 : if (port == 0) throw diagnostics::BuilderException("Invalid port number: 0");
+ - + - +
- ]
48 : :
49 : : // Ensure background IO service is running
50 : 117 : AutoInitializer::ensure_io_context_running();
51 : 125 : }
52 : :
53 : 116 : std::unique_ptr<wrapper::TcpServer> TcpServerBuilder::build() {
54 : 116 : std::unique_ptr<wrapper::TcpServer> server;
55 [ + + ]: 116 : if (independent_context_) {
56 : 1 : server = std::make_unique<wrapper::TcpServer>(port_, std::make_shared<boost::asio::io_context>());
57 : 1 : server->manage_external_context(true);
58 : : } else {
59 : 115 : server = std::make_unique<wrapper::TcpServer>(port_);
60 : : }
61 [ + + + - ]: 116 : if (shared_context_) server->shared_context(true);
62 : :
63 [ + + + - : 116 : if (this->on_data_) server->on_data(this->on_data_);
+ - ]
64 [ + + + - : 116 : if (this->on_data_batch_) server->on_data_batch(this->on_data_batch_);
+ - ]
65 [ + + + - : 116 : if (this->on_connect_) server->on_connect(this->on_connect_);
+ - ]
66 [ + + + - : 116 : if (this->on_disconnect_) server->on_disconnect(this->on_disconnect_);
+ - ]
67 [ + + + - : 116 : if (this->on_error_) server->on_error(this->on_error_);
+ - ]
68 [ + + + - : 116 : if (this->on_backpressure_) server->on_backpressure(this->on_backpressure_);
+ - ]
69 : :
70 [ + + ]: 116 : if (client_limit_enabled_) {
71 : 13 : server->max_clients(max_clients_);
72 : : }
73 : :
74 [ + + + - ]: 116 : if (bind_address_set_) server->bind_address(bind_address_);
75 [ + + + - : 116 : if (port_retry_enabled_set_ || max_port_retries_set_ || port_retry_interval_set_) {
- + ]
76 : 4 : server->port_retry(port_retry_enabled_, static_cast<int>(max_port_retries_),
77 : 4 : static_cast<int>(port_retry_interval_ms_));
78 : : }
79 [ + + + - ]: 116 : if (idle_timeout_set_) server->idle_timeout(idle_timeout_);
80 [ + + + - ]: 116 : if (tcp_no_delay_set_) server->tcp_no_delay(tcp_no_delay_);
81 [ + + + - ]: 116 : if (keep_alive_set_) server->keep_alive(keep_alive_);
82 [ + - - + : 116 : if (!tls_certificate_file_.empty() || !tls_private_key_file_.empty())
- + ]
83 : 0 : server->tls(tls_certificate_file_, tls_private_key_file_);
84 [ + + + - ]: 116 : if (send_buffer_size_set_) server->send_buffer_size(send_buffer_size_);
85 [ + + + - ]: 116 : if (receive_buffer_size_set_) server->receive_buffer_size(receive_buffer_size_);
86 [ + + + - ]: 116 : if (read_buffer_size_set_) server->read_buffer_size(read_buffer_size_);
87 : :
88 [ + + + - ]: 116 : if (this->bp_strategy_set_) server->backpressure_strategy(this->bp_strategy_);
89 : 116 : server->backpressure_threshold(this->get_effective_backpressure_threshold());
90 : :
91 [ + + ]: 116 : if (this->framer_factory_) {
92 : 2 : server->framer(this->framer_factory_);
93 : : }
94 [ + + ]: 116 : if (this->on_message_) {
95 : 2 : server->on_message(this->on_message_);
96 : : }
97 [ + + ]: 116 : if (this->on_message_batch_) {
98 : 1 : server->on_message_batch(this->on_message_batch_);
99 : : }
100 : :
101 [ + + ]: 116 : if (auto_start_) {
102 : 1 : server->auto_start(true);
103 : : }
104 : :
105 : 116 : return server;
106 : 0 : }
107 : :
108 : 2 : TcpServerBuilder& TcpServerBuilder::auto_start(bool auto_start) {
109 : 2 : auto_start_ = auto_start;
110 : 2 : return *this;
111 : : }
112 : :
113 : 1 : TcpServerBuilder& TcpServerBuilder::bind_address(const std::string& address) {
114 [ - + - - ]: 1 : config::detail::require(util::InputValidator::is_valid_ipv4(address) || util::InputValidator::is_valid_ipv6(address),
115 : : "invalid bind address");
116 : 1 : bind_address_ = address;
117 : 1 : bind_address_set_ = true;
118 : 1 : return *this;
119 : : }
120 : :
121 : 2 : TcpServerBuilder& TcpServerBuilder::independent_context(bool use_independent) {
122 : 2 : independent_context_ = use_independent;
123 : 2 : return *this;
124 : : }
125 : :
126 : 2 : TcpServerBuilder& TcpServerBuilder::shared_context(bool use_shared) {
127 : 2 : shared_context_ = use_shared;
128 : 2 : return *this;
129 : : }
130 : :
131 : 11 : TcpServerBuilder& TcpServerBuilder::max_clients(uint32_t max_clients) {
132 : 11 : config::detail::range(max_clients, 0, base::constants::MAX_MAX_CONNECTIONS, "invalid client limit");
133 : 11 : max_clients_ = max_clients;
134 : 11 : client_limit_enabled_ = true;
135 : 11 : return *this;
136 : : }
137 : :
138 : 1 : TcpServerBuilder& TcpServerBuilder::enable_port_retry(bool enable) {
139 : 1 : port_retry_enabled_ = enable;
140 : 1 : port_retry_enabled_set_ = true;
141 : 1 : return *this;
142 : : }
143 : :
144 : 1 : TcpServerBuilder& TcpServerBuilder::max_port_retries(uint32_t max_retries) {
145 : 1 : max_port_retries_ = max_retries;
146 : 1 : max_port_retries_set_ = true;
147 : 1 : return *this;
148 : : }
149 : :
150 : 1 : TcpServerBuilder& TcpServerBuilder::port_retry_interval(std::chrono::milliseconds interval) {
151 : 1 : port_retry_interval_ms_ = static_cast<uint32_t>(interval.count());
152 : 1 : port_retry_interval_set_ = true;
153 : 1 : return *this;
154 : : }
155 : :
156 : 1 : TcpServerBuilder& TcpServerBuilder::tcp_no_delay(bool enable) {
157 : 1 : tcp_no_delay_ = enable;
158 : 1 : tcp_no_delay_set_ = true;
159 : 1 : return *this;
160 : : }
161 : :
162 : 1 : TcpServerBuilder& TcpServerBuilder::keep_alive(bool enable) {
163 : 1 : keep_alive_ = enable;
164 : 1 : keep_alive_set_ = true;
165 : 1 : return *this;
166 : : }
167 : :
168 : 1 : TcpServerBuilder& TcpServerBuilder::tls(const std::string& certificate_file, const std::string& private_key_file) {
169 : 1 : config::detail::require(certificate_file.empty() == private_key_file.empty(), "TLS requires certificate and key");
170 : 0 : tls_certificate_file_ = certificate_file;
171 : 0 : tls_private_key_file_ = private_key_file;
172 : 0 : return *this;
173 : : }
174 : :
175 : 1 : TcpServerBuilder& TcpServerBuilder::send_buffer_size(size_t bytes) {
176 [ + - ]: 1 : if (bytes != 0)
177 : 1 : config::detail::range(bytes, base::constants::MIN_SOCKET_BUFFER_SIZE, base::constants::MAX_SOCKET_BUFFER_SIZE,
178 : : "invalid send_buffer_size");
179 : 1 : send_buffer_size_ = bytes;
180 : 1 : send_buffer_size_set_ = true;
181 : 1 : return *this;
182 : : }
183 : :
184 : 1 : TcpServerBuilder& TcpServerBuilder::receive_buffer_size(size_t bytes) {
185 [ + - ]: 1 : if (bytes != 0)
186 : 1 : config::detail::range(bytes, base::constants::MIN_SOCKET_BUFFER_SIZE, base::constants::MAX_SOCKET_BUFFER_SIZE,
187 : : "invalid receive_buffer_size");
188 : 1 : receive_buffer_size_ = bytes;
189 : 1 : receive_buffer_size_set_ = true;
190 : 1 : return *this;
191 : : }
192 : :
193 : 2 : TcpServerBuilder& TcpServerBuilder::read_buffer_size(size_t bytes) {
194 : 2 : config::detail::range(bytes, base::constants::MIN_READ_BUFFER_SIZE, base::constants::MAX_READ_BUFFER_SIZE,
195 : : "invalid read_buffer_size");
196 : 2 : read_buffer_size_ = bytes;
197 : 2 : read_buffer_size_set_ = true;
198 : 2 : return *this;
199 : : }
200 : :
201 : : // Backward compatibility implementations
202 : 3 : TcpServerBuilder& TcpServerBuilder::port_retry(bool enable, int max_retries, int retry_interval_ms) {
203 : 3 : config::detail::range(max_retries, 0, base::constants::MAX_RETRIES_LIMIT, "invalid port retry count");
204 : 3 : config::detail::range(retry_interval_ms, base::constants::MIN_RETRY_INTERVAL_MS,
205 : : base::constants::MAX_RETRY_INTERVAL_MS, "invalid port retry interval");
206 : 3 : port_retry_enabled_ = enable;
207 : 3 : port_retry_enabled_set_ = true;
208 : 3 : max_port_retries_ = static_cast<uint32_t>(max_retries);
209 : 3 : max_port_retries_set_ = true;
210 : 3 : port_retry_interval_ms_ = static_cast<uint32_t>(retry_interval_ms);
211 : 3 : port_retry_interval_set_ = true;
212 : 3 : return *this;
213 : : }
214 : :
215 : 2 : TcpServerBuilder& TcpServerBuilder::idle_timeout(std::chrono::milliseconds timeout) {
216 : 2 : config::detail::duration(timeout, base::constants::MIN_IDLE_TIMEOUT_MS, base::constants::MAX_IDLE_TIMEOUT_MS, true,
217 : : "invalid idle_timeout");
218 : 2 : idle_timeout_ = timeout;
219 : 2 : idle_timeout_set_ = true;
220 : 2 : return *this;
221 : : }
222 : :
223 : 1 : TcpServerBuilder& TcpServerBuilder::single_client() {
224 : 1 : max_clients_ = 1;
225 : 1 : client_limit_enabled_ = true;
226 : 1 : return *this;
227 : : }
228 : :
229 : 2 : TcpServerBuilder& TcpServerBuilder::multi_client(size_t max) {
230 [ + + ]: 2 : if (max == 0) {
231 : 7 : throw diagnostics::BuilderException("multi_client max must be greater than 0");
232 : : }
233 : 1 : max_clients_ = static_cast<uint32_t>(max);
234 : 1 : client_limit_enabled_ = true;
235 : 1 : return *this;
236 : : }
237 : :
238 : : // Explicit template instantiations
239 : :
240 : : } // namespace builder
241 : : } // namespace wirestead
|