xref: /openbmc/bmcweb/http/http_connection.hpp (revision ebe4c574caac9dfd8b2754bda51c0cf869f1978f)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 #pragma once
4 #include "bmcweb_config.h"
5 
6 #include "async_resp.hpp"
7 #include "authentication.hpp"
8 #include "complete_response_fields.hpp"
9 #include "forward_unauthorized.hpp"
10 #include "http2_connection.hpp"
11 #include "http_body.hpp"
12 #include "http_connect_types.hpp"
13 #include "http_request.hpp"
14 #include "http_response.hpp"
15 #include "http_utility.hpp"
16 #include "logging.hpp"
17 #include "mutual_tls.hpp"
18 #include "sessions.hpp"
19 #include "str_utility.hpp"
20 #include "utility.hpp"
21 
22 #include <boost/asio/error.hpp>
23 #include <boost/asio/ip/tcp.hpp>
24 #include <boost/asio/ssl/stream.hpp>
25 #include <boost/asio/ssl/stream_base.hpp>
26 #include <boost/asio/ssl/verify_context.hpp>
27 #include <boost/asio/steady_timer.hpp>
28 #include <boost/beast/_experimental/test/stream.hpp>
29 #include <boost/beast/core/buffers_generator.hpp>
30 #include <boost/beast/core/detect_ssl.hpp>
31 #include <boost/beast/core/error.hpp>
32 #include <boost/beast/core/flat_static_buffer.hpp>
33 #include <boost/beast/http/error.hpp>
34 #include <boost/beast/http/field.hpp>
35 #include <boost/beast/http/message_generator.hpp>
36 #include <boost/beast/http/parser.hpp>
37 #include <boost/beast/http/read.hpp>
38 #include <boost/beast/http/rfc7230.hpp>
39 #include <boost/beast/http/status.hpp>
40 #include <boost/beast/http/verb.hpp>
41 #include <boost/none.hpp>
42 #include <boost/optional/optional.hpp>
43 
44 #include <bit>
45 #include <chrono>
46 #include <cstddef>
47 #include <cstdint>
48 #include <functional>
49 #include <memory>
50 #include <optional>
51 #include <string>
52 #include <string_view>
53 #include <system_error>
54 #include <type_traits>
55 #include <utility>
56 
57 namespace crow
58 {
59 
60 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
61 static int connectionCount = 0;
62 
63 // request body limit size set by the BMCWEB_HTTP_BODY_LIMIT option
64 constexpr uint64_t httpReqBodyLimit = 1024UL * 1024UL * BMCWEB_HTTP_BODY_LIMIT;
65 
66 constexpr uint64_t loggedOutPostBodyLimit = 4096U;
67 
68 constexpr uint32_t httpHeaderLimit = 8192U;
69 
70 template <typename Adaptor, typename Handler>
71 class Connection :
72     public std::enable_shared_from_this<Connection<Adaptor, Handler>>
73 {
74     using self_type = Connection<Adaptor, Handler>;
75 
76   public:
Connection(Handler * handlerIn,HttpType httpTypeIn,boost::asio::steady_timer && timerIn,std::function<std::string ()> & getCachedDateStrF,boost::asio::ssl::stream<Adaptor> && adaptorIn)77     Connection(Handler* handlerIn, HttpType httpTypeIn,
78                boost::asio::steady_timer&& timerIn,
79                std::function<std::string()>& getCachedDateStrF,
80                boost::asio::ssl::stream<Adaptor>&& adaptorIn) :
81         httpType(httpTypeIn), adaptor(std::move(adaptorIn)), handler(handlerIn),
82         timer(std::move(timerIn)), getCachedDateStr(getCachedDateStrF)
83     {
84         initParser();
85 
86         connectionCount++;
87 
88         BMCWEB_LOG_DEBUG("{} Connection created, total {}", logPtr(this),
89                          connectionCount);
90     }
91 
~Connection()92     ~Connection()
93     {
94         res.releaseCompleteRequestHandler();
95         cancelDeadlineTimer();
96 
97         connectionCount--;
98         BMCWEB_LOG_DEBUG("{} Connection closed, total {}", logPtr(this),
99                          connectionCount);
100     }
101 
102     Connection(const Connection&) = delete;
103     Connection(Connection&&) = delete;
104     Connection& operator=(const Connection&) = delete;
105     Connection& operator=(Connection&&) = delete;
106 
tlsVerifyCallback(bool preverified,boost::asio::ssl::verify_context & ctx)107     bool tlsVerifyCallback(bool preverified,
108                            boost::asio::ssl::verify_context& ctx)
109     {
110         BMCWEB_LOG_DEBUG("{} tlsVerifyCallback called with preverified {}",
111                          logPtr(this), preverified);
112         if (preverified)
113         {
114             mtlsSession = verifyMtlsUser(ip, ctx);
115             if (mtlsSession)
116             {
117                 BMCWEB_LOG_DEBUG("{} Generated TLS session: {}", logPtr(this),
118                                  mtlsSession->uniqueId);
119             }
120         }
121         const persistent_data::AuthConfigMethods& c =
122             persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
123         if (c.tlsStrict)
124         {
125             BMCWEB_LOG_DEBUG(
126                 "{} TLS is in strict mode, returning preverified as is.",
127                 logPtr(this));
128             return preverified;
129         }
130         // If tls strict mode is disabled
131         // We always return true to allow full auth flow for resources that
132         // don't require auth
133         return true;
134     }
135 
prepareMutualTls()136     bool prepareMutualTls()
137     {
138         BMCWEB_LOG_DEBUG("prepareMutualTls");
139 
140         constexpr std::string_view id = "bmcweb";
141 
142         const char* idPtr = id.data();
143         const auto* idCPtr = std::bit_cast<const unsigned char*>(idPtr);
144         auto idLen = static_cast<unsigned int>(id.length());
145         int ret =
146             SSL_set_session_id_context(adaptor.native_handle(), idCPtr, idLen);
147         if (ret == 0)
148         {
149             BMCWEB_LOG_ERROR("{} failed to set SSL id", logPtr(this));
150             return false;
151         }
152 
153         BMCWEB_LOG_DEBUG("set_verify_callback");
154 
155         boost::system::error_code ec;
156         adaptor.set_verify_callback(
157             std::bind_front(&self_type::tlsVerifyCallback, this), ec);
158         if (ec)
159         {
160             BMCWEB_LOG_ERROR("Failed to set verify callback {}", ec);
161             return false;
162         }
163 
164         return true;
165     }
166 
afterDetectSsl(const std::shared_ptr<self_type> &,boost::beast::error_code ec,bool isTls)167     void afterDetectSsl(const std::shared_ptr<self_type>& /*self*/,
168                         boost::beast::error_code ec, bool isTls)
169     {
170         if (ec)
171         {
172             BMCWEB_LOG_ERROR("Couldn't detect ssl ", ec);
173             return;
174         }
175         BMCWEB_LOG_DEBUG("{} TLS was detected as {}", logPtr(this), isTls);
176         if (isTls)
177         {
178             if (httpType != HttpType::HTTPS && httpType != HttpType::BOTH)
179             {
180                 BMCWEB_LOG_WARNING(
181                     "{} Connection closed due to incompatible type",
182                     logPtr(this));
183                 return;
184             }
185             httpType = HttpType::HTTPS;
186             adaptor.async_handshake(
187                 boost::asio::ssl::stream_base::server, buffer.data(),
188                 std::bind_front(&self_type::afterSslHandshake, this,
189                                 shared_from_this()));
190         }
191         else
192         {
193             if (httpType != HttpType::HTTP && httpType != HttpType::BOTH)
194             {
195                 BMCWEB_LOG_WARNING(
196                     "{} Connection closed due to incompatible type",
197                     logPtr(this));
198                 return;
199             }
200 
201             httpType = HttpType::HTTP;
202             BMCWEB_LOG_INFO("Starting non-SSL session");
203             doReadHeaders();
204         }
205     }
206 
start()207     void start()
208     {
209         BMCWEB_LOG_DEBUG("{} Connection started, total {}", logPtr(this),
210                          connectionCount);
211         if (connectionCount >= 200)
212         {
213             BMCWEB_LOG_CRITICAL("{} Max connection count exceeded.",
214                                 logPtr(this));
215             return;
216         }
217 
218         if constexpr (BMCWEB_MUTUAL_TLS_AUTH)
219         {
220             if (!prepareMutualTls())
221             {
222                 BMCWEB_LOG_ERROR("{} Failed to prepare mTLS", logPtr(this));
223                 return;
224             }
225         }
226 
227         startDeadline();
228 
229         readClientIp();
230         boost::beast::async_detect_ssl(
231             adaptor.next_layer(), buffer,
232             std::bind_front(&self_type::afterDetectSsl, this,
233                             shared_from_this()));
234     }
235 
afterSslHandshake(const std::shared_ptr<self_type> &,const boost::system::error_code & ec,size_t bytesParsed)236     void afterSslHandshake(const std::shared_ptr<self_type>& /*self*/,
237                            const boost::system::error_code& ec,
238                            size_t bytesParsed)
239     {
240         buffer.consume(bytesParsed);
241         if (ec)
242         {
243             BMCWEB_LOG_ERROR("{} SSL handshake failed", logPtr(this));
244             return;
245         }
246         BMCWEB_LOG_DEBUG("{} SSL handshake succeeded", logPtr(this));
247         // If http2 is enabled, negotiate the protocol
248         if constexpr (BMCWEB_EXPERIMENTAL_HTTP2)
249         {
250             const unsigned char* alpn = nullptr;
251             unsigned int alpnlen = 0;
252             SSL_get0_alpn_selected(adaptor.native_handle(), &alpn, &alpnlen);
253             if (alpn != nullptr)
254             {
255                 std::string_view selectedProtocol(
256                     std::bit_cast<const char*>(alpn), alpnlen);
257                 BMCWEB_LOG_DEBUG("ALPN selected protocol \"{}\" len: {}",
258                                  selectedProtocol, alpnlen);
259                 if (selectedProtocol == "h2")
260                 {
261                     upgradeToHttp2();
262                     return;
263                 }
264             }
265         }
266 
267         doReadHeaders();
268     }
269 
initParser()270     void initParser()
271     {
272         boost::beast::http::request_parser<bmcweb::HttpBody>& instance =
273             parser.emplace();
274 
275         // reset header limit for newly created parser
276         instance.header_limit(httpHeaderLimit);
277 
278         // Initially set no body limit. We don't yet know if the user is
279         // authenticated.
280         instance.body_limit(boost::none);
281     }
282 
upgradeToHttp2()283     void upgradeToHttp2()
284     {
285         auto http2 = std::make_shared<HTTP2Connection<Adaptor, Handler>>(
286             std::move(adaptor), handler, getCachedDateStr, httpType);
287         if (http2settings.empty())
288         {
289             http2->start();
290         }
291         else
292         {
293             http2->startFromSettings(http2settings);
294         }
295     }
296 
297     // returns whether connection was upgraded
doUpgrade(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)298     bool doUpgrade(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
299     {
300         using boost::beast::http::field;
301         using boost::beast::http::token_list;
302 
303         bool isSse =
304             isContentTypeAllowed(req->getHeaderValue("Accept"),
305                                  http_helpers::ContentType::EventStream, false);
306 
307         bool isWebsocket = false;
308         bool isH2c = false;
309         // Check connection header is upgrade
310         if (token_list{req->req[field::connection]}.exists("upgrade"))
311         {
312             BMCWEB_LOG_DEBUG("{} Connection: Upgrade header was present",
313                              logPtr(this));
314             // Parse if upgrade is h2c or websocket
315             token_list upgrade{req->req[field::upgrade]};
316             isWebsocket = upgrade.exists("websocket");
317             isH2c = upgrade.exists("h2c");
318             BMCWEB_LOG_DEBUG("{} Upgrade isWebsocket: {} isH2c: {}",
319                              logPtr(this), isWebsocket, isH2c);
320         }
321 
322         if (BMCWEB_EXPERIMENTAL_HTTP2 && isH2c)
323         {
324             std::string_view base64settings = req->req[field::http2_settings];
325             if (utility::base64Decode<true>(base64settings, http2settings))
326             {
327                 res.result(boost::beast::http::status::switching_protocols);
328                 res.addHeader(boost::beast::http::field::connection, "Upgrade");
329                 res.addHeader(boost::beast::http::field::upgrade, "h2c");
330             }
331         }
332 
333         // websocket and SSE are only allowed on GET
334         if (req->req.method() == boost::beast::http::verb::get)
335         {
336             if (isWebsocket || isSse)
337             {
338                 asyncResp->res.setCompleteRequestHandler(
339                     [self(shared_from_this())](crow::Response& thisRes) {
340                         if (thisRes.result() != boost::beast::http::status::ok)
341                         {
342                             // When any error occurs before handle upgradation,
343                             // the result in response will be set to respective
344                             // error. By default the Result will be OK (200),
345                             // which implies successful handle upgrade. Response
346                             // needs to be sent over this connection only on
347                             // failure.
348                             self->completeRequest(thisRes);
349                             return;
350                         }
351                     });
352                 BMCWEB_LOG_INFO("{} Upgrading socket", logPtr(this));
353                 if (httpType == HttpType::HTTP)
354                 {
355                     handler->handleUpgrade(req, asyncResp,
356                                            std::move(adaptor.next_layer()));
357                 }
358                 else
359                 {
360                     handler->handleUpgrade(req, asyncResp, std::move(adaptor));
361                 }
362 
363                 return true;
364             }
365         }
366         return false;
367     }
368 
handle()369     void handle()
370     {
371         std::error_code reqEc;
372         if (!parser)
373         {
374             return;
375         }
376         req = std::make_shared<crow::Request>(parser->release(), reqEc);
377         if (reqEc)
378         {
379             BMCWEB_LOG_DEBUG("Request failed to construct{}", reqEc.message());
380             res.result(boost::beast::http::status::bad_request);
381             completeRequest(res);
382             return;
383         }
384         req->session = userSession;
385         accept = req->getHeaderValue("Accept");
386         // Fetch the client IP address
387         req->ipAddress = ip;
388 
389         // Check for HTTP version 1.1.
390         if (req->version() == 11)
391         {
392             if (req->getHeaderValue(boost::beast::http::field::host).empty())
393             {
394                 res.result(boost::beast::http::status::bad_request);
395                 completeRequest(res);
396                 return;
397             }
398         }
399 
400         BMCWEB_LOG_INFO("Request:  {} HTTP/{}.{} {} {} {}", logPtr(this),
401                         req->version() / 10, req->version() % 10,
402                         req->methodString(), req->target(),
403                         req->ipAddress.to_string());
404 
405         if (res.completed)
406         {
407             completeRequest(res);
408             return;
409         }
410         keepAlive = req->keepAlive();
411 
412         if (authenticationEnabled)
413         {
414             if (!crow::authentication::isOnAllowlist(req->url().path(),
415                                                      req->method()) &&
416                 req->session == nullptr)
417             {
418                 BMCWEB_LOG_WARNING("Authentication failed");
419                 forward_unauthorized::sendUnauthorized(
420                     req->url().encoded_path(),
421                     req->getHeaderValue("X-Requested-With"),
422                     req->getHeaderValue("Accept"), res);
423                 completeRequest(res);
424                 return;
425             }
426         }
427 
428         auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
429         BMCWEB_LOG_DEBUG("Setting completion handler");
430         asyncResp->res.setCompleteRequestHandler(
431             [self(shared_from_this())](crow::Response& thisRes) {
432                 self->completeRequest(thisRes);
433             });
434         if (doUpgrade(asyncResp))
435         {
436             return;
437         }
438         std::string_view expected =
439             req->getHeaderValue(boost::beast::http::field::if_none_match);
440         if (!expected.empty())
441         {
442             res.setExpectedHash(expected);
443         }
444         handler->handle(req, asyncResp);
445     }
446 
hardClose()447     void hardClose()
448     {
449         BMCWEB_LOG_DEBUG("{} Closing socket", logPtr(this));
450         adaptor.next_layer().close();
451     }
452 
tlsShutdownComplete(const std::shared_ptr<self_type> & self,const boost::system::error_code & ec)453     void tlsShutdownComplete(const std::shared_ptr<self_type>& self,
454                              const boost::system::error_code& ec)
455     {
456         if (ec)
457         {
458             BMCWEB_LOG_WARNING("{} Failed to shut down TLS cleanly {}",
459                                logPtr(self.get()), ec);
460         }
461         self->hardClose();
462     }
463 
gracefulClose()464     void gracefulClose()
465     {
466         BMCWEB_LOG_DEBUG("{} Socket close requested", logPtr(this));
467 
468         if (httpType == HttpType::HTTPS)
469         {
470             if (mtlsSession != nullptr)
471             {
472                 BMCWEB_LOG_DEBUG("{} Removing TLS session: {}", logPtr(this),
473                                  mtlsSession->uniqueId);
474                 persistent_data::SessionStore::getInstance().removeSession(
475                     mtlsSession);
476             }
477 
478             adaptor.async_shutdown(std::bind_front(
479                 &self_type::tlsShutdownComplete, this, shared_from_this()));
480         }
481         else
482         {
483             hardClose();
484         }
485     }
486 
completeRequest(crow::Response & thisRes)487     void completeRequest(crow::Response& thisRes)
488     {
489         res = std::move(thisRes);
490         res.keepAlive(keepAlive);
491 
492         completeResponseFields(accept, res);
493         res.addHeader(boost::beast::http::field::date, getCachedDateStr());
494 
495         doWrite();
496 
497         // delete lambda with self shared_ptr
498         // to enable connection destruction
499         res.setCompleteRequestHandler(nullptr);
500     }
501 
readClientIp()502     void readClientIp()
503     {
504         boost::system::error_code ec;
505 
506         boost::asio::ip::tcp::endpoint endpoint =
507             boost::beast::get_lowest_layer(adaptor).remote_endpoint(ec);
508 
509         if (ec)
510         {
511             // If remote endpoint fails keep going. "ClientOriginIPAddress"
512             // will be empty.
513             BMCWEB_LOG_ERROR("Failed to get the client's IP Address. ec : {}",
514                              ec);
515             return;
516         }
517         ip = endpoint.address();
518     }
519 
disableAuth()520     void disableAuth()
521     {
522         authenticationEnabled = false;
523     }
524 
525   private:
getContentLengthLimit()526     uint64_t getContentLengthLimit()
527     {
528         if constexpr (!BMCWEB_INSECURE_DISABLE_AUTH)
529         {
530             if (userSession == nullptr)
531             {
532                 return loggedOutPostBodyLimit;
533             }
534         }
535 
536         return httpReqBodyLimit;
537     }
538 
539     // Returns true if content length was within limits
540     // Returns false if content length error has been returned
handleContentLengthError()541     bool handleContentLengthError()
542     {
543         if (!parser)
544         {
545             BMCWEB_LOG_CRITICAL("Parser was null");
546             return false;
547         }
548         const boost::optional<uint64_t> contentLength =
549             parser->content_length();
550         if (!contentLength)
551         {
552             BMCWEB_LOG_DEBUG("{} No content length available", logPtr(this));
553             return true;
554         }
555 
556         uint64_t maxAllowedContentLength = getContentLengthLimit();
557 
558         if (*contentLength > maxAllowedContentLength)
559         {
560             // If the users content limit is between the logged in
561             // and logged out limits They probably just didn't log
562             // in
563             if (*contentLength > loggedOutPostBodyLimit &&
564                 *contentLength < httpReqBodyLimit)
565             {
566                 BMCWEB_LOG_DEBUG(
567                     "{} Content length {} valid, but greater than logged out"
568                     " limit of {}. Setting unauthorized",
569                     logPtr(this), *contentLength, loggedOutPostBodyLimit);
570                 res.result(boost::beast::http::status::unauthorized);
571             }
572             else
573             {
574                 // Otherwise they're over both limits, so inform
575                 // them
576                 BMCWEB_LOG_DEBUG(
577                     "{} Content length {} was greater than global limit {}."
578                     " Setting payload too large",
579                     logPtr(this), *contentLength, httpReqBodyLimit);
580                 res.result(boost::beast::http::status::payload_too_large);
581             }
582 
583             keepAlive = false;
584             doWrite();
585             return false;
586         }
587 
588         return true;
589     }
590 
afterReadHeaders(const std::shared_ptr<self_type> &,const boost::system::error_code & ec,std::size_t bytesTransferred)591     void afterReadHeaders(const std::shared_ptr<self_type>& /*self*/,
592                           const boost::system::error_code& ec,
593                           std::size_t bytesTransferred)
594     {
595         BMCWEB_LOG_DEBUG("{} async_read_header {} Bytes", logPtr(this),
596                          bytesTransferred);
597 
598         if (ec)
599         {
600             cancelDeadlineTimer();
601 
602             if (ec == boost::beast::http::error::header_limit)
603             {
604                 BMCWEB_LOG_ERROR("{} Header field too large, closing",
605                                  logPtr(this), ec.message());
606 
607                 res.result(boost::beast::http::status::
608                                request_header_fields_too_large);
609                 keepAlive = false;
610                 doWrite();
611                 return;
612             }
613             if (ec == boost::beast::http::error::end_of_stream)
614             {
615                 BMCWEB_LOG_WARNING("{} End of stream, closing {}", logPtr(this),
616                                    ec);
617                 hardClose();
618                 return;
619             }
620 
621             BMCWEB_LOG_DEBUG("{} Closing socket due to read error {}",
622                              logPtr(this), ec.message());
623             gracefulClose();
624 
625             return;
626         }
627 
628         if (!parser)
629         {
630             BMCWEB_LOG_ERROR("Parser was unexpectedly null");
631             return;
632         }
633 
634         if (authenticationEnabled)
635         {
636             boost::beast::http::verb method = parser->get().method();
637             userSession = authentication::authenticate(
638                 ip, res, method, parser->get().base(), mtlsSession);
639         }
640 
641         std::string_view expect =
642             parser->get()[boost::beast::http::field::expect];
643         if (bmcweb::asciiIEquals(expect, "100-continue"))
644         {
645             res.result(boost::beast::http::status::continue_);
646             doWrite();
647             return;
648         }
649 
650         if (!handleContentLengthError())
651         {
652             return;
653         }
654 
655         parser->body_limit(getContentLengthLimit());
656 
657         if (parser->is_done())
658         {
659             handle();
660             return;
661         }
662 
663         doRead();
664     }
665 
doReadHeaders()666     void doReadHeaders()
667     {
668         BMCWEB_LOG_DEBUG("{} doReadHeaders", logPtr(this));
669         if (!parser)
670         {
671             BMCWEB_LOG_CRITICAL("Parser was not initialized.");
672             return;
673         }
674 
675         if (httpType == HttpType::HTTP)
676         {
677             boost::beast::http::async_read_header(
678                 adaptor.next_layer(), buffer, *parser,
679                 std::bind_front(&self_type::afterReadHeaders, this,
680                                 shared_from_this()));
681         }
682         else
683         {
684             boost::beast::http::async_read_header(
685                 adaptor, buffer, *parser,
686                 std::bind_front(&self_type::afterReadHeaders, this,
687                                 shared_from_this()));
688         }
689     }
690 
afterRead(const std::shared_ptr<self_type> &,const boost::system::error_code & ec,std::size_t bytesTransferred)691     void afterRead(const std::shared_ptr<self_type>& /*self*/,
692                    const boost::system::error_code& ec,
693                    std::size_t bytesTransferred)
694     {
695         BMCWEB_LOG_DEBUG("{} async_read_some {} Bytes", logPtr(this),
696                          bytesTransferred);
697 
698         if (ec)
699         {
700             BMCWEB_LOG_ERROR("{} Error while reading: {}", logPtr(this),
701                              ec.message());
702             if (ec == boost::beast::http::error::body_limit)
703             {
704                 if (handleContentLengthError())
705                 {
706                     BMCWEB_LOG_CRITICAL("Body length limit reached, "
707                                         "but no content-length "
708                                         "available?  Should never happen");
709                     res.result(
710                         boost::beast::http::status::internal_server_error);
711                     keepAlive = false;
712                     doWrite();
713                 }
714                 return;
715             }
716 
717             gracefulClose();
718             return;
719         }
720 
721         // If the user is logged in, allow them to send files
722         // incrementally one piece at a time. If authentication is
723         // disabled then there is no user session hence always allow to
724         // send one piece at a time.
725         if (userSession != nullptr)
726         {
727             cancelDeadlineTimer();
728         }
729 
730         if (!parser)
731         {
732             BMCWEB_LOG_ERROR("Parser was unexpectedly null");
733             return;
734         }
735         if (!parser->is_done())
736         {
737             doRead();
738             return;
739         }
740 
741         cancelDeadlineTimer();
742         handle();
743     }
744 
doRead()745     void doRead()
746     {
747         BMCWEB_LOG_DEBUG("{} doRead", logPtr(this));
748         if (!parser)
749         {
750             return;
751         }
752         startDeadline();
753         if (httpType == HttpType::HTTP)
754         {
755             boost::beast::http::async_read_some(
756                 adaptor.next_layer(), buffer, *parser,
757                 std::bind_front(&self_type::afterRead, this,
758                                 shared_from_this()));
759         }
760         else
761         {
762             boost::beast::http::async_read_some(
763                 adaptor, buffer, *parser,
764                 std::bind_front(&self_type::afterRead, this,
765                                 shared_from_this()));
766         }
767     }
768 
afterDoWrite(const std::shared_ptr<self_type> &,const boost::system::error_code & ec,std::size_t bytesTransferred)769     void afterDoWrite(const std::shared_ptr<self_type>& /*self*/,
770                       const boost::system::error_code& ec,
771                       std::size_t bytesTransferred)
772     {
773         BMCWEB_LOG_DEBUG("{} async_write wrote {} bytes, ec={}", logPtr(this),
774                          bytesTransferred, ec);
775 
776         cancelDeadlineTimer();
777 
778         if (ec == boost::system::errc::operation_would_block ||
779             ec == boost::system::errc::resource_unavailable_try_again)
780         {
781             doWrite();
782             return;
783         }
784         if (ec)
785         {
786             BMCWEB_LOG_DEBUG("{} from write(2)", logPtr(this));
787             return;
788         }
789 
790         if (res.result() == boost::beast::http::status::switching_protocols)
791         {
792             upgradeToHttp2();
793             return;
794         }
795 
796         if (res.result() == boost::beast::http::status::continue_)
797         {
798             // Reset the result to ok
799             res.result(boost::beast::http::status::ok);
800             doRead();
801             return;
802         }
803 
804         if (!keepAlive)
805         {
806             BMCWEB_LOG_DEBUG("{} keepalive not set.  Closing socket",
807                              logPtr(this));
808 
809             gracefulClose();
810             return;
811         }
812 
813         BMCWEB_LOG_DEBUG("{} Clearing response", logPtr(this));
814         res.clear();
815         initParser();
816 
817         userSession = nullptr;
818 
819         req->clear();
820         doReadHeaders();
821     }
822 
doWrite()823     void doWrite()
824     {
825         BMCWEB_LOG_DEBUG("{} doWrite", logPtr(this));
826         res.preparePayload();
827 
828         startDeadline();
829         if (httpType == HttpType::HTTP)
830         {
831             boost::beast::async_write(
832                 adaptor.next_layer(),
833                 boost::beast::http::message_generator(std::move(res.response)),
834                 std::bind_front(&self_type::afterDoWrite, this,
835                                 shared_from_this()));
836         }
837         else
838         {
839             boost::beast::async_write(
840                 adaptor,
841                 boost::beast::http::message_generator(std::move(res.response)),
842                 std::bind_front(&self_type::afterDoWrite, this,
843                                 shared_from_this()));
844         }
845     }
846 
cancelDeadlineTimer()847     void cancelDeadlineTimer()
848     {
849         timer.cancel();
850     }
851 
afterTimerWait(const std::weak_ptr<self_type> & weakSelf,const boost::system::error_code & ec)852     void afterTimerWait(const std::weak_ptr<self_type>& weakSelf,
853                         const boost::system::error_code& ec)
854     {
855         // Note, we are ignoring other types of errors here;  If the timer
856         // failed for any reason, we should still close the connection
857         std::shared_ptr<Connection<Adaptor, Handler>> self = weakSelf.lock();
858         if (!self)
859         {
860             if (ec == boost::asio::error::operation_aborted)
861             {
862                 BMCWEB_LOG_DEBUG(
863                     "{} Timer canceled on connection being destroyed",
864                     logPtr(self.get()));
865             }
866             else
867             {
868                 BMCWEB_LOG_CRITICAL("{} Failed to capture connection",
869                                     logPtr(self.get()));
870             }
871             return;
872         }
873 
874         self->timerStarted = false;
875 
876         if (ec)
877         {
878             if (ec == boost::asio::error::operation_aborted)
879             {
880                 BMCWEB_LOG_DEBUG("{} Timer canceled", logPtr(self.get()));
881                 return;
882             }
883             BMCWEB_LOG_CRITICAL("{} Timer failed {}", logPtr(self.get()), ec);
884         }
885 
886         BMCWEB_LOG_WARNING("{} Connection timed out, hard closing",
887                            logPtr(self.get()));
888 
889         self->hardClose();
890     }
891 
startDeadline()892     void startDeadline()
893     {
894         // Timer is already started so no further action is required.
895         if (timerStarted)
896         {
897             return;
898         }
899 
900         std::chrono::seconds timeout(15);
901 
902         std::weak_ptr<Connection<Adaptor, Handler>> weakSelf = weak_from_this();
903         timer.expires_after(timeout);
904         timer.async_wait(std::bind_front(&self_type::afterTimerWait, this,
905                                          weak_from_this()));
906 
907         timerStarted = true;
908         BMCWEB_LOG_DEBUG("{} timer started", logPtr(this));
909     }
910 
911     bool authenticationEnabled = !BMCWEB_INSECURE_DISABLE_AUTH;
912     HttpType httpType = HttpType::BOTH;
913 
914     boost::asio::ssl::stream<Adaptor> adaptor;
915     Handler* handler;
916 
917     boost::asio::ip::address ip;
918 
919     // Making this a std::optional allows it to be efficiently destroyed and
920     // re-created on Connection reset
921     std::optional<boost::beast::http::request_parser<bmcweb::HttpBody>> parser;
922 
923     boost::beast::flat_static_buffer<8192> buffer;
924 
925     std::shared_ptr<crow::Request> req;
926     std::string accept;
927     std::string http2settings;
928     crow::Response res;
929 
930     std::shared_ptr<persistent_data::UserSession> userSession;
931     std::shared_ptr<persistent_data::UserSession> mtlsSession;
932 
933     boost::asio::steady_timer timer;
934 
935     bool keepAlive = true;
936 
937     bool timerStarted = false;
938 
939     std::function<std::string()>& getCachedDateStr;
940 
941     using std::enable_shared_from_this<
942         Connection<Adaptor, Handler>>::shared_from_this;
943 
944     using std::enable_shared_from_this<
945         Connection<Adaptor, Handler>>::weak_from_this;
946 };
947 } // namespace crow
948