Support h2c upgrade

h2c upgrade is a mechanism for supporting http/2 on connections that
might not support alpn [1].  This is done by the client specifying
Connection: upgrade
Upgrade: h2c

This looks very similar to a websocket upgrade, which h2c replacing
websocket.  Because of this, the existing upgrade code needs some
upgrades to avoid parsing twice.

Tested:
```
curl -u root:0penBmc --http2 -k http://192.168.7.2:443/redfish/v1/SessionService/Sessions
```

Succeeds and verbose logging shows that http upgrade succeeded

websocket_test.py in the scripts directory connects and reports events

[1] https://datatracker.ietf.org/doc/html/rfc7540#section-11.8

Change-Id: I8f76e355f99f21337d310ef2f345e6aaa253b48b
Signed-off-by: Ed Tanous <etanous@nvidia.com>
diff --git a/http/http_connection.hpp b/http/http_connection.hpp
index 7069331..a6773bc 100644
--- a/http/http_connection.hpp
+++ b/http/http_connection.hpp
@@ -16,6 +16,7 @@
 #include "mutual_tls.hpp"
 #include "sessions.hpp"
 #include "str_utility.hpp"
+#include "utility.hpp"
 
 #include <boost/asio/error.hpp>
 #include <boost/asio/ip/tcp.hpp>
@@ -31,6 +32,7 @@
 #include <boost/beast/http/message_generator.hpp>
 #include <boost/beast/http/parser.hpp>
 #include <boost/beast/http/read.hpp>
+#include <boost/beast/http/rfc7230.hpp>
 #include <boost/beast/http/status.hpp>
 #include <boost/beast/http/verb.hpp>
 #include <boost/none.hpp>
@@ -254,6 +256,69 @@
         instance.body_limit(boost::none);
     }
 
+    // returns whether connection was upgraded
+    bool doUpgrade(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+    {
+        using boost::beast::http::field;
+        using boost::beast::http::token_list;
+
+        bool isSse =
+            isContentTypeAllowed(req->getHeaderValue("Accept"),
+                                 http_helpers::ContentType::EventStream, false);
+
+        bool isWebsocket = false;
+        bool isH2c = false;
+        // Check connection header is upgrade
+        if (token_list{req->req[field::connection]}.exists("upgrade"))
+        {
+            BMCWEB_LOG_DEBUG("{} Connection: Upgrade header was present",
+                             logPtr(this));
+            // Parse if upgrade is h2c or websocket
+            token_list upgrade{req->req[field::upgrade]};
+            isWebsocket = upgrade.exists("websocket");
+            isH2c = upgrade.exists("h2c");
+            BMCWEB_LOG_DEBUG("{} Upgrade isWebsocket: {} isH2c: {}",
+                             logPtr(this), isWebsocket, isH2c);
+        }
+
+        if (BMCWEB_EXPERIMENTAL_HTTP2 && isH2c)
+        {
+            std::string_view base64settings = req->req[field::http2_settings];
+            if (utility::base64Decode<true>(base64settings, http2settings))
+            {
+                res.result(boost::beast::http::status::switching_protocols);
+                res.addHeader(boost::beast::http::field::connection, "Upgrade");
+                res.addHeader(boost::beast::http::field::upgrade, "h2c");
+            }
+        }
+
+        // websocket and SSE are only allowed on GET
+        if (req->req.method() == boost::beast::http::verb::get)
+        {
+            if (isWebsocket || isSse)
+            {
+                asyncResp->res.setCompleteRequestHandler(
+                    [self(shared_from_this())](crow::Response& thisRes) {
+                        if (thisRes.result() != boost::beast::http::status::ok)
+                        {
+                            // When any error occurs before handle upgradation,
+                            // the result in response will be set to respective
+                            // error. By default the Result will be OK (200),
+                            // which implies successful handle upgrade. Response
+                            // needs to be sent over this connection only on
+                            // failure.
+                            self->completeRequest(thisRes);
+                            return;
+                        }
+                    });
+                BMCWEB_LOG_INFO("{} Upgrading socket", logPtr(this));
+                handler->handleUpgrade(req, asyncResp, std::move(adaptor));
+                return true;
+            }
+        }
+        return false;
+    }
+
     void handle()
     {
         std::error_code reqEc;
@@ -323,30 +388,8 @@
             [self(shared_from_this())](crow::Response& thisRes) {
                 self->completeRequest(thisRes);
             });
-        bool isSse =
-            isContentTypeAllowed(req->getHeaderValue("Accept"),
-                                 http_helpers::ContentType::EventStream, false);
-        std::string_view upgradeType(
-            req->getHeaderValue(boost::beast::http::field::upgrade));
-        if ((req->isUpgrade() &&
-             bmcweb::asciiIEquals(upgradeType, "websocket")) ||
-            isSse)
+        if (doUpgrade(asyncResp))
         {
-            asyncResp->res.setCompleteRequestHandler(
-                [self(shared_from_this())](crow::Response& thisRes) {
-                    if (thisRes.result() != boost::beast::http::status::ok)
-                    {
-                        // When any error occurs before handle upgradation,
-                        // the result in response will be set to respective
-                        // error. By default the Result will be OK (200),
-                        // which implies successful handle upgrade. Response
-                        // needs to be sent over this connection only on
-                        // failure.
-                        self->completeRequest(thisRes);
-                        return;
-                    }
-                });
-            handler->handleUpgrade(req, asyncResp, std::move(adaptor));
             return;
         }
         std::string_view expected =
@@ -680,6 +723,14 @@
             return;
         }
 
+        if (res.result() == boost::beast::http::status::switching_protocols)
+        {
+            auto http2 = std::make_shared<HTTP2Connection<Adaptor, Handler>>(
+                std::move(adaptor), handler, getCachedDateStr);
+            http2->startFromSettings(http2settings);
+            return;
+        }
+
         if (res.result() == boost::beast::http::status::continue_)
         {
             // Reset the result to ok
@@ -797,7 +848,7 @@
 
     std::shared_ptr<crow::Request> req;
     std::string accept;
-
+    std::string http2settings;
     crow::Response res;
 
     std::shared_ptr<persistent_data::UserSession> userSession;