Branch data Line data Source code
1 : : // Copyright 2026 HPActor Contributors
2 : : //
3 : : // Licensed under the Apache License, Version 2.0 (the "License");
4 : : // you may not use this file except in compliance with the License.
5 : : // You may obtain a copy of the License at
6 : : //
7 : : // http://www.apache.org/licenses/LICENSE-2.0
8 : : //
9 : : // Unless required by applicable law or agreed to in writing, software
10 : : // distributed under the License is distributed on an "AS IS" BASIS,
11 : : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : : // See the License for the specific language governing permissions and
13 : : // limitations under the License.
14 : :
15 : : #include <hpactor/net/acceptor.hpp>
16 : :
17 : : #include <arpa/inet.h>
18 : : #include <cstring>
19 : : #include <fcntl.h>
20 : : #include <netinet/in.h>
21 : : #include <netinet/tcp.h>
22 : : #include <sys/socket.h>
23 : : #include <sys/un.h>
24 : : #include <unistd.h>
25 : :
26 : : namespace hpactor {
27 : :
28 : : namespace net {
29 : :
30 : : // -----------------------------------------------------------------------------
31 : : // Acceptor (abstract base)
32 : : // -----------------------------------------------------------------------------
33 : :
34 : 51 : Acceptor::Acceptor(EventLoop* loop) : loop_(loop), listening_fd_(-1) {}
35 : :
36 : 51 : Acceptor::~Acceptor() {
37 : 51 : close();
38 : 51 : }
39 : :
40 : 116 : void Acceptor::close() {
41 : 116 : if (listening_fd_ >= 0) {
42 : 42 : if (loop_ != nullptr) {
43 : 42 : loop_->clear_read_handler(listening_fd_);
44 : 42 : loop_->remove_fd(listening_fd_);
45 : : }
46 : 42 : ::close(listening_fd_);
47 : 42 : listening_fd_ = -1;
48 : : }
49 : 116 : }
50 : :
51 : 35 : void Acceptor::set_accept_handler(accept_handler handler) {
52 : 35 : accept_handler_ = std::move(handler);
53 : 35 : }
54 : :
55 : : // -----------------------------------------------------------------------------
56 : : // TcpAcceptor
57 : : // -----------------------------------------------------------------------------
58 : :
59 : 17 : bool TcpAcceptor::listen(uint16_t port, uint16_t port_range,
60 : : const std::string& bind_address) {
61 : 17 : int fd = ::socket(AF_INET, SOCK_STREAM, 0);
62 : 17 : if (fd < 0) {
63 : 0 : return false;
64 : : }
65 : :
66 : : // Set SO_REUSEADDR
67 : 17 : int reuse = 1;
68 : 17 : setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
69 : :
70 : : // Set non-blocking
71 : 17 : int flags = fcntl(fd, F_GETFL, 0);
72 : 17 : fcntl(fd, F_SETFL, flags | O_NONBLOCK);
73 : :
74 : 17 : struct sockaddr_in addr{};
75 : 17 : addr.sin_family = AF_INET;
76 : 17 : if (bind_address == "0.0.0.0") {
77 : 17 : addr.sin_addr.s_addr = INADDR_ANY;
78 : : } else {
79 : 0 : inet_pton(AF_INET, bind_address.c_str(), &addr.sin_addr);
80 : : }
81 : 17 : addr.sin_port = htons(port);
82 : :
83 : : // Try binding, with port range fallback
84 : 17 : bool bound = false;
85 : 17 : for (uint16_t p = port; p < port + port_range + 1; ++p) {
86 : 17 : addr.sin_port = htons(p);
87 : 17 : if (::bind(fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) == 0) {
88 : 17 : bound = true;
89 : 17 : bound_port_ = p;
90 : 17 : break;
91 : : }
92 : : }
93 : :
94 : 17 : if (!bound) {
95 : 0 : ::close(fd);
96 : 0 : return false;
97 : : }
98 : :
99 : : // Listen
100 : 17 : if (::listen(fd, SOMAXCONN) < 0) {
101 : 0 : ::close(fd);
102 : 0 : return false;
103 : : }
104 : :
105 : 17 : listening_fd_ = fd;
106 : :
107 : : // Register with event loop
108 : 17 : if (loop_ != nullptr) {
109 : 17 : loop_->add_fd(listening_fd_, EventLoop::Event::Read);
110 : 17 : loop_->set_read_handler(listening_fd_, [this](int) {
111 : 32 : handle_read();
112 : 32 : });
113 : : }
114 : :
115 : 17 : return true;
116 : : }
117 : :
118 : 32 : void TcpAcceptor::handle_read() {
119 : 32 : struct sockaddr_storage client_addr{};
120 : 32 : socklen_t client_len = sizeof(client_addr);
121 : :
122 : : int client_fd =
123 : 32 : accept(listening_fd_, reinterpret_cast<struct sockaddr*>(&client_addr),
124 : : &client_len);
125 : :
126 : 32 : if (client_fd < 0) {
127 : 0 : return;
128 : : }
129 : :
130 : : // Set TCP_NODELAY for low latency
131 : 32 : int nodelay = 1;
132 : 32 : setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));
133 : :
134 : : // Set non-blocking
135 : 32 : int flags = fcntl(client_fd, F_GETFL, 0);
136 : 32 : fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);
137 : :
138 : 32 : if (accept_handler_) {
139 : 32 : EndPoint endpoint;
140 : 32 : if (client_addr.ss_family == AF_INET) {
141 : 32 : auto* in4 = reinterpret_cast<struct sockaddr_in*>(&client_addr);
142 : 32 : endpoint = Ipv4Endpoint{in4->sin_addr.s_addr, in4->sin_port};
143 : 0 : } else if (client_addr.ss_family == AF_INET6) {
144 : 0 : auto* in6 = reinterpret_cast<struct sockaddr_in6*>(&client_addr);
145 : 0 : std::array<uint8_t, 16> addr{};
146 : 0 : std::memcpy(addr.data(), &in6->sin6_addr, 16);
147 : 0 : endpoint = Ipv6Endpoint{addr, in6->sin6_port};
148 : : } else {
149 : 0 : ::close(client_fd);
150 : 0 : return;
151 : : }
152 : 32 : accept_handler_(client_fd, endpoint);
153 : : } else {
154 : 0 : ::close(client_fd);
155 : : }
156 : : }
157 : :
158 : : // -----------------------------------------------------------------------------
159 : : // UnixDomainAcceptor
160 : : // -----------------------------------------------------------------------------
161 : :
162 : 25 : bool UnixDomainAcceptor::listen(const std::string& path) {
163 : : // Remove stale socket file if it exists
164 : 25 : ::unlink(path.c_str());
165 : :
166 : 25 : int fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
167 : 25 : if (fd < 0) {
168 : 0 : return false;
169 : : }
170 : :
171 : : // Set non-blocking
172 : 25 : int flags = fcntl(fd, F_GETFL, 0);
173 : 25 : fcntl(fd, F_SETFL, flags | O_NONBLOCK);
174 : :
175 : 25 : struct sockaddr_un addr{};
176 : 25 : addr.sun_family = AF_UNIX;
177 : 25 : std::strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1);
178 : :
179 : 25 : if (::bind(fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0) {
180 : 0 : ::close(fd);
181 : 0 : return false;
182 : : }
183 : :
184 : 25 : if (::listen(fd, SOMAXCONN) < 0) {
185 : 0 : ::close(fd);
186 : 0 : return false;
187 : : }
188 : :
189 : : // Only set after successful listen to avoid fd leak on failure
190 : 25 : listening_fd_ = fd;
191 : 25 : uds_path_ = path;
192 : :
193 : 25 : if (loop_ != nullptr) {
194 : 25 : loop_->add_fd(listening_fd_, EventLoop::Event::Read);
195 : 25 : loop_->set_read_handler(listening_fd_, [this](int) {
196 : 0 : handle_read();
197 : 0 : });
198 : : }
199 : :
200 : 25 : return true;
201 : : }
202 : :
203 : 25 : void UnixDomainAcceptor::close() {
204 : 25 : if (!uds_path_.empty()) {
205 : 25 : ::unlink(uds_path_.c_str());
206 : 25 : uds_path_.clear();
207 : : }
208 : 25 : Acceptor::close();
209 : 25 : }
210 : :
211 : 0 : void UnixDomainAcceptor::handle_read() {
212 : 0 : struct sockaddr_un client_addr{};
213 : 0 : socklen_t client_len = sizeof(client_addr);
214 : :
215 : : int client_fd =
216 : 0 : accept(listening_fd_, reinterpret_cast<struct sockaddr*>(&client_addr),
217 : : &client_len);
218 : :
219 : 0 : if (client_fd < 0) {
220 : 0 : return;
221 : : }
222 : :
223 : : // Set non-blocking
224 : 0 : int flags = fcntl(client_fd, F_GETFL, 0);
225 : 0 : fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);
226 : :
227 : 0 : if (accept_handler_) {
228 : : // No UDS variant in EndPoint; use default Ipv4Endpoint as hint
229 : 0 : accept_handler_(client_fd, Ipv4Endpoint{});
230 : : } else {
231 : 0 : ::close(client_fd);
232 : : }
233 : : }
234 : :
235 : : } // namespace net
236 : : } // namespace hpactor
|