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 <cstdint>
20 : : #include <string>
21 : :
22 : : #include "wirestead/base/constants.hpp"
23 : : #include "wirestead/util/input_validator.hpp"
24 : :
25 : : namespace wirestead {
26 : : namespace config {
27 : :
28 : : struct TcpServerConfig {
29 : : std::string bind_address = "0.0.0.0";
30 : : uint16_t port = 9000;
31 : : size_t backpressure_threshold = base::constants::DEFAULT_BACKPRESSURE_THRESHOLD;
32 : : base::constants::BackpressureStrategy backpressure_strategy = base::constants::BackpressureStrategy::Reliable;
33 : : bool enable_memory_pool = true;
34 : : // #437: 0 (unlimited) used to be the default, risking unbounded memory
35 : : // growth from a large number of slow/malicious clients. 0 still means
36 : : // unlimited for callers who explicitly opt into it.
37 : : int max_connections = static_cast<int>(base::constants::DEFAULT_MAX_CONNECTIONS);
38 : :
39 : : // Port binding retry configuration
40 : : bool enable_port_retry = false; // Enable port binding retry
41 : : int max_port_retries = 3; // Maximum number of retry attempts
42 : : int port_retry_interval_ms = 1000; // Retry interval in milliseconds
43 : :
44 : : // TLS. Both must be set for the server to serve TLS; leaving them empty keeps
45 : : // the connection in plaintext, which is the default. Only honoured in a build
46 : : // configured with WIRESTEAD_ENABLE_TLS - a build without it rejects a
47 : : // configured certificate at start() rather than silently serving plaintext.
48 : : std::string tls_certificate_file;
49 : : std::string tls_private_key_file;
50 : :
51 [ + + + - ]: 137 : [[nodiscard]] bool tls_enabled() const { return !tls_certificate_file.empty() && !tls_private_key_file.empty(); }
52 : :
53 : : int idle_timeout_ms = static_cast<int>(base::constants::DEFAULT_IDLE_TIMEOUT_MS); // 0 = disabled
54 : : bool tcp_no_delay = base::constants::DEFAULT_TCP_NO_DELAY;
55 : : bool keep_alive = base::constants::DEFAULT_KEEP_ALIVE;
56 : : size_t send_buffer_size = 0;
57 : : size_t receive_buffer_size = 0;
58 : : // Size of the per-connection userspace read buffer that each
59 : : // async_read_some() fills. Distinct from receive_buffer_size, which is the
60 : : // kernel's SO_RCVBUF. Raising this reduces read completions and callback
61 : : // dispatches on bulk transfers, at the cost of that much memory per
62 : : // connection.
63 : : size_t read_buffer_size = base::constants::DEFAULT_READ_BUFFER_SIZE;
64 : :
65 : : // Opt into the shared IoContextManager singleton instead of a dedicated
66 : : // io_context + thread (the default since #440). Only meaningful for
67 : : // deliberately trading per-instance parallelism for reduced thread/memory
68 : : // overhead across many instances in one process.
69 : : bool use_shared_context = false;
70 : :
71 : : // Validation methods
72 : 10 : bool is_valid() const {
73 : 19 : return read_buffer_size >= base::constants::MIN_READ_BUFFER_SIZE &&
74 [ + + ]: 9 : read_buffer_size <= base::constants::MAX_READ_BUFFER_SIZE &&
75 [ + + - + ]: 8 : (util::InputValidator::is_valid_ipv4(bind_address) || util::InputValidator::is_valid_ipv6(bind_address)) &&
76 [ + - + - ]: 7 : port > 0 && backpressure_threshold >= base::constants::MIN_BACKPRESSURE_THRESHOLD &&
77 [ + - + - ]: 7 : backpressure_threshold <= base::constants::MAX_BACKPRESSURE_THRESHOLD && max_connections >= 0 &&
78 [ + + + - ]: 7 : (idle_timeout_ms == 0 || (idle_timeout_ms >= static_cast<int>(base::constants::MIN_IDLE_TIMEOUT_MS) &&
79 [ - + ]: 1 : idle_timeout_ms <= static_cast<int>(base::constants::MAX_IDLE_TIMEOUT_MS))) &&
80 [ + + + + ]: 6 : (send_buffer_size == 0 || (send_buffer_size >= base::constants::MIN_SOCKET_BUFFER_SIZE &&
81 [ + + + - ]: 20 : send_buffer_size <= base::constants::MAX_SOCKET_BUFFER_SIZE)) &&
82 [ + + + - ]: 5 : (receive_buffer_size == 0 || (receive_buffer_size >= base::constants::MIN_SOCKET_BUFFER_SIZE &&
83 [ + - ]: 11 : receive_buffer_size <= base::constants::MAX_SOCKET_BUFFER_SIZE));
84 : : }
85 : :
86 : : // Apply validation and clamp values to valid ranges
87 : 145 : void validate_and_clamp() {
88 [ + + ]: 145 : if (read_buffer_size < base::constants::MIN_READ_BUFFER_SIZE) {
89 : 1 : read_buffer_size = base::constants::MIN_READ_BUFFER_SIZE;
90 [ + + ]: 144 : } else if (read_buffer_size > base::constants::MAX_READ_BUFFER_SIZE) {
91 : 2 : read_buffer_size = base::constants::MAX_READ_BUFFER_SIZE;
92 : : }
93 [ - + ]: 145 : if (backpressure_threshold < base::constants::MIN_BACKPRESSURE_THRESHOLD) {
94 : 0 : backpressure_threshold = base::constants::MIN_BACKPRESSURE_THRESHOLD;
95 [ - + ]: 145 : } else if (backpressure_threshold > base::constants::MAX_BACKPRESSURE_THRESHOLD) {
96 : 0 : backpressure_threshold = base::constants::MAX_BACKPRESSURE_THRESHOLD;
97 : : }
98 : :
99 [ - + ]: 145 : if (max_connections < 0) {
100 : 0 : max_connections = 0;
101 [ - + ]: 145 : } else if (max_connections > static_cast<int>(base::constants::MAX_MAX_CONNECTIONS)) {
102 : 0 : max_connections = static_cast<int>(base::constants::MAX_MAX_CONNECTIONS);
103 : : }
104 : :
105 [ - + ]: 145 : if (idle_timeout_ms < 0) {
106 : 0 : idle_timeout_ms = 0;
107 [ + + ]: 145 : } else if (idle_timeout_ms != 0) {
108 [ - + ]: 2 : if (idle_timeout_ms < static_cast<int>(base::constants::MIN_IDLE_TIMEOUT_MS)) {
109 : 0 : idle_timeout_ms = static_cast<int>(base::constants::MIN_IDLE_TIMEOUT_MS);
110 [ + + ]: 2 : } else if (idle_timeout_ms > static_cast<int>(base::constants::MAX_IDLE_TIMEOUT_MS)) {
111 : 1 : idle_timeout_ms = static_cast<int>(base::constants::MAX_IDLE_TIMEOUT_MS);
112 : : }
113 : : }
114 : :
115 [ + + + + ]: 145 : if (send_buffer_size != 0 && send_buffer_size < base::constants::MIN_SOCKET_BUFFER_SIZE) {
116 : 1 : send_buffer_size = base::constants::MIN_SOCKET_BUFFER_SIZE;
117 [ - + ]: 144 : } else if (send_buffer_size > base::constants::MAX_SOCKET_BUFFER_SIZE) {
118 : 0 : send_buffer_size = base::constants::MAX_SOCKET_BUFFER_SIZE;
119 : : }
120 : :
121 [ + + - + ]: 145 : if (receive_buffer_size != 0 && receive_buffer_size < base::constants::MIN_SOCKET_BUFFER_SIZE) {
122 : 0 : receive_buffer_size = base::constants::MIN_SOCKET_BUFFER_SIZE;
123 [ + + ]: 145 : } else if (receive_buffer_size > base::constants::MAX_SOCKET_BUFFER_SIZE) {
124 : 1 : receive_buffer_size = base::constants::MAX_SOCKET_BUFFER_SIZE;
125 : : }
126 : 145 : }
127 : : };
128 : :
129 : : } // namespace config
130 : : } // namespace wirestead
|