GCC Code Coverage Report


Directory: libs/beast2/
File: include/boost/beast2/server/http_session.hpp
Date: 2025-11-13 15:50:44
Exec Total Coverage
Lines: 0 123 0.0%
Functions: 0 11 0.0%
Branches: 0 136 0.0%

Line Branch Exec Source
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/beast2
8 //
9
10 #ifndef BOOST_BEAST2_SERVER_HTTP_SESSION_HPP
11 #define BOOST_BEAST2_SERVER_HTTP_SESSION_HPP
12
13 #include <boost/beast2/detail/config.hpp>
14 #include <boost/beast2/application.hpp>
15 #include <boost/beast2/log_service.hpp>
16 #include <boost/beast2/format.hpp>
17 #include <boost/beast2/read.hpp>
18 #include <boost/beast2/write.hpp>
19 #include <boost/beast2/server/any_lambda.hpp>
20 #include <boost/beast2/server/basic_router.hpp>
21 #include <boost/beast2/server/route_handler_asio.hpp>
22 #include <boost/beast2/server/router_asio.hpp>
23 #include <boost/beast2/error.hpp>
24 #include <boost/beast2/detail/except.hpp>
25 #include <boost/http_proto/request_parser.hpp>
26 #include <boost/http_proto/response.hpp>
27 #include <boost/http_proto/serializer.hpp>
28 #include <boost/http_proto/string_body.hpp>
29 #include <boost/url/parse.hpp>
30 #include <boost/asio/prepend.hpp>
31
32 namespace boost {
33 namespace beast2 {
34
35 //------------------------------------------------
36 /*
37
38 */
39 /** Mixin for delivering responses to HTTP requests
40 */
41 template<class Stream>
42 class http_session
43 : private detacher::owner
44 {
45 public:
46 http_session(
47 application& app,
48 Stream& stream,
49 router_asio<Stream> rr,
50 any_lambda<void(system::error_code)> close_fn);
51
52 /** Called to start a new HTTP session
53
54 The stream must be in a connected,
55 correct state for a new session.
56 */
57 void do_session(acceptor_config const& config);
58
59 private:
60 void do_read();
61
62 void on_read(
63 system::error_code ec,
64 std::size_t bytes_transferred);
65
66 void on_write(
67 system::error_code const& ec,
68 std::size_t bytes_transferred);
69
70 void do_fail(core::string_view s,
71 system::error_code const& ec);
72
73 resumer do_detach() override;
74
75 void do_resume(system::error_code const& ec) override;
76
77 void do_resume2(system::error_code ec);
78
79 protected:
80 std::string id() const
81 {
82 return std::string("[") + std::to_string(id_) + "] ";
83 }
84
85 protected:
86 section sect_;
87 std::size_t id_ = 0;
88 Stream& stream_;
89 router_asio<Stream> rr_;
90 any_lambda<void(system::error_code)> close_;
91 route_state route_state_;
92 http_proto::request_parser pr_;
93 http_proto::serializer sr_;
94 http_proto::response res_;
95 acceptor_config const* pconfig_ = nullptr;
96
97 using work_guard = asio::executor_work_guard<decltype(
98 std::declval<Stream&>().get_executor())>;
99 std::unique_ptr<work_guard> pwg_;
100 std::unique_ptr<Request> preq_;
101 std::unique_ptr<ResponseAsio<Stream>> pres_;
102 };
103
104 //------------------------------------------------
105
106 template<class Stream>
107 http_session<Stream>::
108 http_session(
109 application& app,
110 Stream& stream,
111 router_asio<Stream> rr,
112 any_lambda<void(system::error_code)> close)
113 : sect_(use_log_service(app).get_section("http_session"))
114 , id_(
115 []() noexcept
116 {
117 static std::size_t n = 0;
118 return ++n;
119 }())
120 , stream_(stream)
121 , rr_(std::move(rr))
122 , close_(close)
123 , pr_(app.services())
124 , sr_(app.services())
125 {
126 }
127
128 /** Called to start a new HTTP session
129
130 The stream must be in a connected,
131 correct state for a new session.
132 */
133 template<class Stream>
134 void
135 http_session<Stream>::
136 do_session(
137 acceptor_config const& config)
138 {
139 pconfig_ = &config;
140 pr_.reset();
141 do_read();
142 }
143
144 template<class Stream>
145 void
146 http_session<Stream>::
147 do_read()
148 {
149 pr_.start();
150 sr_.reset();
151 beast2::async_read(stream_, pr_,
152 call_mf(&http_session::on_read, this));
153 }
154
155 template<class Stream>
156 void
157 http_session<Stream>::
158 on_read(
159 system::error_code ec,
160 std::size_t bytes_transferred)
161 {
162 (void)bytes_transferred;
163
164 if(ec.failed())
165 return do_fail("http_session::on_read", ec);
166
167 LOG_TRC(this->sect_)(
168 "{} http_session::on_read bytes={}",
169 this->id(), bytes_transferred);
170
171 BOOST_ASSERT(pr_.is_complete());
172
173 //----------------------------------------
174 //
175 // set up Request and Response objects
176 //
177
178 preq_.reset(new Request(
179 *this->pconfig_,
180 pr_.get(),
181 pr_));
182
183 pres_.reset(new ResponseAsio<Stream>(
184 stream_,
185 res_,
186 sr_));
187 pres_->detach = detacher(*this);
188
189 // copy version
190 pres_->m.set_version(preq_->m.version());
191
192 // copy keep-alive setting
193 pres_->m.set_start_line(
194 http_proto::status::ok, pr_.get().version());
195 pres_->m.set_keep_alive(pr_.get().keep_alive());
196
197 // parse the URL
198 {
199 auto rv = urls::parse_uri_reference(pr_.get().target());
200 if(rv.has_value())
201 {
202 preq_->url = rv.value();
203 preq_->base_path = "";
204 preq_->path = std::string(rv->encoded_path());
205 }
206 else
207 {
208 // error parsing URL
209 pres_->status(
210 http_proto::status::bad_request);
211 pres_->set_body(
212 "Bad Request: " + rv.error().message());
213 goto do_write;
214 }
215 }
216
217 // invoke handlers for the route
218 BOOST_ASSERT(! pwg_);
219 ec = rr_.dispatch(
220 preq_->m.method(),
221 preq_->url,
222 *preq_, *pres_, route_state_);
223 if(ec == route::send)
224 goto do_write;
225
226 if(ec == route::next)
227 {
228 // unhandled
229 pres_->status(http_proto::status::not_found);
230 std::string s;
231 format_to(s, "The requested URL {} was not found on this server.", preq_->url);
232 //pres_->m.set_keep_alive(false); // VFALCO?
233 pres_->set_body(s);
234 goto do_write;
235 }
236
237 if(ec == route::detach)
238 {
239 // make sure they called detach()
240 BOOST_ASSERT(pwg_);
241 return;
242 }
243
244 // error message of last resort
245 {
246 BOOST_ASSERT(ec.failed());
247 pres_->status(http_proto::status::internal_server_error);
248 std::string s;
249 format_to(s, "An internal server error occurred: {}", ec.message());
250 //pres_->m.set_keep_alive(false); // VFALCO?
251 pres_->set_body(s);
252 }
253
254 do_write:
255 if(sr_.is_done())
256 {
257 // happens when the handler sends the response
258 return on_write(system::error_code(), 0);
259 }
260
261 beast2::async_write(stream_, sr_,
262 call_mf(&http_session::on_write, this));
263 }
264
265 template<class Stream>
266 void
267 http_session<Stream>::
268 on_write(
269 system::error_code const& ec,
270 std::size_t bytes_transferred)
271 {
272 (void)bytes_transferred;
273
274 if(ec.failed())
275 return do_fail("http_session::on_write", ec);
276
277 BOOST_ASSERT(sr_.is_done());
278
279 LOG_TRC(this->sect_)(
280 "{} http_session::on_write bytes={}",
281 this->id(), bytes_transferred);
282
283 if(res_.keep_alive())
284 return do_read();
285
286 // tidy up lingering objects
287 pr_.reset();
288 sr_.reset();
289 res_.clear();
290 preq_.reset();
291 pres_.reset();
292
293 close_({});
294 }
295
296 template<class Stream>
297 void
298 http_session<Stream>::
299 do_fail(
300 core::string_view s, system::error_code const& ec)
301 {
302 LOG_TRC(this->sect_)("{}: {}", s, ec.message());
303
304 // tidy up lingering objects
305 pr_.reset();
306 sr_.reset();
307 res_.clear();
308 preq_.reset();
309 pres_.reset();
310
311 close_(ec);
312 }
313
314 template<class Stream>
315 auto
316 http_session<Stream>::
317 do_detach() ->
318 resumer
319 {
320 BOOST_ASSERT(stream_.get_executor().running_in_this_thread());
321
322 // can't call twice
323 BOOST_ASSERT(! pwg_);
324 pwg_.reset(new work_guard(stream_.get_executor()));
325
326 // VFALCO cancel timer
327
328 return resumer(*this);
329 }
330
331 template<class Stream>
332 void
333 http_session<Stream>::
334 do_resume(system::error_code const& ec)
335 {
336 asio::dispatch(
337 stream_.get_executor(),
338 asio::prepend(call_mf(
339 &http_session::do_resume2, this), ec));
340 }
341
342 template<class Stream>
343 void
344 http_session<Stream>::
345 do_resume2(system::error_code ec)
346 {
347 BOOST_ASSERT(stream_.get_executor().running_in_this_thread());
348
349 BOOST_ASSERT(pwg_.get() != nullptr);
350 pwg_.reset();
351
352 // invoke handlers for the route
353 BOOST_ASSERT(! pwg_);
354 ec = rr_.resume(*preq_, *pres_, ec, route_state_);
355
356 if(ec == route::detach)
357 {
358 // make sure they called detach()
359 BOOST_ASSERT(pwg_);
360 return;
361 }
362
363 if(ec.failed())
364 {
365 // give a default error response?
366 }
367 beast2::async_write(stream_, sr_,
368 call_mf(&http_session::on_write, this));
369 }
370
371 } // beast2
372 } // boost
373
374 #endif
375