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/core/actor_system.hpp>
16 : : #include <hpactor/net/http_gateway.hpp>
17 : :
18 : : #include <unistd.h>
19 : :
20 : : #include <algorithm>
21 : : #include <cstring>
22 : :
23 : : namespace hpactor {
24 : : namespace net {
25 : :
26 : : // =============================================================================
27 : : // RouteRegistry Implementation
28 : : // =============================================================================
29 : :
30 : 3 : static std::vector<PatternSegment> parse_pattern(const std::string& pattern) {
31 : 3 : std::vector<PatternSegment> segments;
32 : 3 : if (pattern.empty() || pattern[0] != '/') return segments;
33 : :
34 : 3 : size_t pos = 1;
35 : 7 : while (pos < pattern.size()) {
36 : 4 : size_t end = pattern.find('/', pos);
37 : 4 : std::string seg = pattern.substr(pos, end == std::string::npos ? end : end - pos);
38 : 4 : if (seg.empty()) {
39 : 0 : pos = end == std::string::npos ? pattern.size() : end + 1;
40 : 0 : continue;
41 : : }
42 : 4 : if (seg[0] == '*') {
43 : 0 : if (seg.size() > 1) {
44 : 0 : segments.push_back({PatternSegmentType::MultiWildcard, seg.substr(1)});
45 : : } else {
46 : 0 : segments.push_back({PatternSegmentType::SingleWildcard, ""});
47 : : }
48 : 4 : } else if (seg[0] == ':') {
49 : 1 : segments.push_back({PatternSegmentType::NamedParam, seg.substr(1)});
50 : : } else {
51 : 3 : segments.push_back({PatternSegmentType::Literal, seg});
52 : : }
53 : 4 : pos = end == std::string::npos ? pattern.size() : end + 1;
54 : 4 : }
55 : 3 : return segments;
56 : : }
57 : :
58 : 1 : static bool match_pattern(const std::vector<PatternSegment>& segments,
59 : : const std::string& path, HttpRequest& req) {
60 : 1 : std::vector<std::string> path_segs;
61 : 1 : if (!path.empty() && path[0] == '/') {
62 : 1 : size_t pos = 1;
63 : 2 : while (pos < path.size()) {
64 : 1 : size_t end = path.find('/', pos);
65 : 1 : path_segs.push_back(
66 : 2 : path.substr(pos, end == std::string::npos ? end : end - pos));
67 : 1 : pos = end == std::string::npos ? path.size() : end + 1;
68 : : }
69 : : }
70 : :
71 : 1 : size_t pi = 0;
72 : 1 : size_t si = 0;
73 : 2 : while (si < segments.size() && pi < path_segs.size()) {
74 : 1 : const auto& seg = segments[si];
75 : 1 : switch (seg.type) {
76 : 1 : case PatternSegmentType::Literal:
77 : 1 : if (path_segs[pi] != seg.name) return false;
78 : 1 : ++pi; ++si;
79 : 1 : break;
80 : 0 : case PatternSegmentType::NamedParam:
81 : 0 : req.path_params[seg.name] = path_segs[pi];
82 : 0 : ++pi; ++si;
83 : 0 : break;
84 : 0 : case PatternSegmentType::SingleWildcard:
85 : 0 : ++pi; ++si;
86 : 0 : break;
87 : 0 : case PatternSegmentType::MultiWildcard: {
88 : 0 : std::string remaining;
89 : 0 : for (size_t i = pi; i < path_segs.size(); ++i) {
90 : 0 : if (i > pi) remaining += '/';
91 : 0 : remaining += path_segs[i];
92 : : }
93 : 0 : req.path_params[seg.name] = remaining;
94 : 0 : return true;
95 : 0 : }
96 : : }
97 : : }
98 : 1 : return si == segments.size() && pi == path_segs.size();
99 : 1 : }
100 : :
101 : 3 : void RouteRegistry::add(HttpMethod method, std::string pattern,
102 : : MessageBuilder builder, int priority) {
103 : 6 : routes_.push_back(
104 : 3 : {method, parse_pattern(pattern), std::move(builder), priority});
105 : 3 : std::sort(routes_.begin(), routes_.end(),
106 : 6 : [](const Route& a, const Route& b) {
107 : 6 : return a.priority < b.priority;
108 : : });
109 : 3 : }
110 : :
111 : : const RouteRegistry::MessageBuilder*
112 : 1 : RouteRegistry::match(HttpMethod method, const std::string& path,
113 : : HttpRequest& req) const {
114 : 1 : for (const auto& route : routes_) {
115 : 1 : if (route.method != method) continue;
116 : 1 : if (match_pattern(route.segments, path, req)) {
117 : 1 : return &route.builder;
118 : : }
119 : : }
120 : 0 : return nullptr;
121 : : }
122 : :
123 : : // =============================================================================
124 : : // HTTPGateway Implementation
125 : : // =============================================================================
126 : :
127 : 1 : HTTPGateway::HTTPGateway() = default;
128 : :
129 : 1 : HTTPGateway::~HTTPGateway() {
130 : 1 : stop();
131 : 1 : }
132 : :
133 : 1 : bool HTTPGateway::listen(uint16_t port, const std::string& bind_host) {
134 : 1 : bind_host_ = bind_host;
135 : 1 : port_ = port;
136 : :
137 : 1 : loop_.run();
138 : :
139 : 1 : acceptor_ = std::make_unique<TcpAcceptor>(&loop_);
140 : 1 : acceptor_->set_accept_handler([this](int client_fd, EndPoint remote_endpoint) {
141 : 1 : on_accept(client_fd, remote_endpoint);
142 : 1 : });
143 : :
144 : 1 : if (!acceptor_->listen(port_, 0, bind_host_)) {
145 : 0 : acceptor_.reset();
146 : 0 : return false;
147 : : }
148 : 1 : return true;
149 : : }
150 : :
151 : 3 : void HTTPGateway::stop() {
152 : : {
153 : 3 : std::lock_guard<std::mutex> lock(conn_mutex_);
154 : 4 : for (auto& conn : connections_) {
155 : 1 : conn->close();
156 : : }
157 : 3 : connections_.clear();
158 : 3 : }
159 : 3 : if (acceptor_) {
160 : 3 : acceptor_->close();
161 : : }
162 : 3 : loop_.stop();
163 : 3 : }
164 : :
165 : 1 : bool HTTPGateway::is_listening() const {
166 : 1 : return acceptor_ && acceptor_->is_listening();
167 : : }
168 : :
169 : 0 : uint16_t HTTPGateway::port() const {
170 : 0 : return acceptor_ ? acceptor_->port() : port_;
171 : : }
172 : :
173 : 6 : void HTTPGateway::run_once() {
174 : 6 : loop_.wait(100);
175 : 6 : loop_.process_completions();
176 : 6 : }
177 : :
178 : 1 : void HTTPGateway::set_request_handler(RequestHandler handler) {
179 : 1 : request_handler_ = std::move(handler);
180 : 1 : }
181 : :
182 : 1 : void HTTPGateway::set_error_handler(ErrorHandler handler) {
183 : 1 : error_handler_ = std::move(handler);
184 : 1 : }
185 : :
186 : 1 : void HTTPGateway::send_response(HTTPConnection* conn, HttpStatusCode code,
187 : : std::vector<HttpHeader> headers, StreamBuffer body) {
188 : 1 : conn->send_response(code, std::move(headers), std::move(body));
189 : 1 : }
190 : :
191 : 0 : void HTTPGateway::close_connection(HTTPConnection* conn) {
192 : 0 : std::lock_guard<std::mutex> lock(conn_mutex_);
193 : 0 : auto it = std::find_if(connections_.begin(), connections_.end(),
194 : 0 : [conn](const HTTPConnectionPtr& p) {
195 : 0 : return p.get() == conn;
196 : : });
197 : 0 : if (it != connections_.end()) {
198 : 0 : (*it)->close();
199 : 0 : connections_.erase(it);
200 : : }
201 : 0 : }
202 : :
203 : 1 : void HTTPGateway::set_max_connections(size_t max) {
204 : 1 : max_connections_ = max;
205 : 1 : }
206 : :
207 : 1 : void HTTPGateway::set_max_request_size(size_t max) {
208 : 1 : max_request_size_ = max;
209 : 1 : }
210 : :
211 : 1 : void HTTPGateway::on_accept(int client_fd, EndPoint remote_endpoint) {
212 : : {
213 : 1 : std::lock_guard<std::mutex> lock(conn_mutex_);
214 : 1 : if (connections_.size() >= max_connections_) {
215 : 0 : ::close(client_fd);
216 : 0 : return;
217 : : }
218 : 1 : }
219 : :
220 : : auto conn = HTTPConnection::create(client_fd, LocalEndpoint,
221 : 1 : remote_endpoint, &loop_, HTTPConnectionMode::Server);
222 : :
223 : 1 : conn->set_request_handler([this](HTTPConnection* c, HttpRequest&& req) {
224 : 1 : if (request_handler_) {
225 : 1 : request_handler_(c, std::move(req));
226 : : }
227 : 1 : });
228 : 1 : conn->set_error_handler([this](HTTPConnection* c, const error& err) {
229 : 0 : if (error_handler_) {
230 : 0 : error_handler_(c, err);
231 : : }
232 : 0 : });
233 : :
234 : : {
235 : 1 : std::lock_guard<std::mutex> lock(conn_mutex_);
236 : 1 : connections_.push_back(std::move(conn));
237 : 1 : }
238 : 1 : }
239 : :
240 : : } // namespace net
241 : : } // namespace hpactor
|