blob: 9c36a437d05d4e49bfbaec8a8eea44a0e205dab9 [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
Ed Tanousf0b59af2024-03-20 13:38:04 -07003#include "async_resp.hpp"
Ed Tanous52dd6932024-01-29 08:34:59 -08004#include "http/http2_connection.hpp"
5#include "http/http_request.hpp"
6#include "http/http_response.hpp"
Ed Tanousebe4c572025-02-08 14:29:53 -08007#include "http_connect_types.hpp"
Ed Tanous5b904292024-04-16 11:10:17 -07008#include "nghttp2_adapters.hpp"
Ed Tanous796ba932020-08-02 04:29:21 +00009#include "test_stream.hpp"
Ed Tanous52dd6932024-01-29 08:34:59 -080010
Ed Tanousf0b59af2024-03-20 13:38:04 -070011#include <nghttp2/nghttp2.h>
12#include <unistd.h>
13
14#include <boost/asio/buffer.hpp>
Ed Tanousf0b59af2024-03-20 13:38:04 -070015#include <boost/asio/io_context.hpp>
Ed Tanousebe4c572025-02-08 14:29:53 -080016#include <boost/asio/ssl/context.hpp>
17#include <boost/asio/ssl/stream.hpp>
Ed Tanous52dd6932024-01-29 08:34:59 -080018#include <boost/asio/steady_timer.hpp>
Ed Tanousd7857202025-01-28 15:32:26 -080019#include <boost/asio/write.hpp>
Ed Tanousf0b59af2024-03-20 13:38:04 -070020#include <boost/beast/http/field.hpp>
Ed Tanous52dd6932024-01-29 08:34:59 -080021
Ed Tanous634a5e02025-09-26 12:05:08 -070022#include <array>
Ed Tanous52dd6932024-01-29 08:34:59 -080023#include <bit>
Ed Tanousf0b59af2024-03-20 13:38:04 -070024#include <cstddef>
25#include <cstdint>
Ed Tanous52dd6932024-01-29 08:34:59 -080026#include <functional>
27#include <memory>
28#include <string>
29#include <string_view>
30#include <utility>
31#include <vector>
32
33#include "gmock/gmock.h"
34#include "gtest/gtest.h"
35namespace crow
36{
37
38namespace
39{
40
41using ::testing::Pair;
42using ::testing::UnorderedElementsAre;
43
44struct FakeHandler
45{
46 bool called = false;
Jonathan Doman102a4cd2024-04-15 16:56:23 -070047 void handle(const std::shared_ptr<Request>& req,
Ed Tanous52dd6932024-01-29 08:34:59 -080048 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
49 {
50 called = true;
Jonathan Doman102a4cd2024-04-15 16:56:23 -070051 EXPECT_EQ(req->url().buffer(), "/redfish/v1/");
52 EXPECT_EQ(req->methodString(), "GET");
53 EXPECT_EQ(req->getHeaderValue(boost::beast::http::field::user_agent),
Ed Tanous52dd6932024-01-29 08:34:59 -080054 "curl/8.5.0");
Jonathan Doman102a4cd2024-04-15 16:56:23 -070055 EXPECT_EQ(req->getHeaderValue(boost::beast::http::field::accept),
56 "*/*");
57 EXPECT_EQ(req->getHeaderValue(":authority"), "localhost:18080");
Ed Tanous52dd6932024-01-29 08:34:59 -080058 asyncResp->res.write("StringOutput");
59 }
60};
61
62std::string getDateStr()
63{
64 return "TestTime";
65}
66
67void unpackHeaders(std::string_view dataField,
68 std::vector<std::pair<std::string, std::string>>& headers)
69{
Ed Tanousa93163a2024-03-25 11:20:18 -070070 nghttp2_hd_inflater_ex inflater;
Ed Tanous52dd6932024-01-29 08:34:59 -080071
72 while (!dataField.empty())
73 {
74 nghttp2_nv nv;
75 int inflateFlags = 0;
76 const uint8_t* data = std::bit_cast<const uint8_t*>(dataField.data());
Patrick Williamsbd79bce2024-08-16 15:22:20 -040077 ssize_t parsed =
78 inflater.hd2(&nv, &inflateFlags, data, dataField.size(), 1);
Ed Tanous52dd6932024-01-29 08:34:59 -080079
80 ASSERT_GT(parsed, 0);
81 dataField.remove_prefix(static_cast<size_t>(parsed));
82 if ((inflateFlags & NGHTTP2_HD_INFLATE_EMIT) > 0)
83 {
84 const char* namePtr = std::bit_cast<const char*>(nv.name);
85 std::string key(namePtr, nv.namelen);
86 const char* valPtr = std::bit_cast<const char*>(nv.value);
87 std::string value(valPtr, nv.valuelen);
88 headers.emplace_back(key, value);
89 }
90 if ((inflateFlags & NGHTTP2_HD_INFLATE_FINAL) > 0)
91 {
92 EXPECT_EQ(inflater.endHeaders(), 0);
93 break;
94 }
95 }
96 EXPECT_TRUE(dataField.empty());
97}
98
99TEST(http_connection, RequestPropogates)
100{
101 using namespace std::literals;
102 boost::asio::io_context io;
Ed Tanous796ba932020-08-02 04:29:21 +0000103 TestStream stream(io);
104 TestStream out(io);
Ed Tanous52dd6932024-01-29 08:34:59 -0800105 stream.connect(out);
106 // This is a binary pre-encrypted stream captured from curl for a request to
107 // curl https://localhost:18080/redfish/v1/
108 std::string_view toSend =
109 // Hello
110 "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
111 // 18 byte settings frame
112 "\x00\x00\x12\x04\x00\x00\x00\x00\x00"
113 // Settings
114 "\x00\x03\x00\x00\x00\x64\x00\x04\x00\xa0\x00\x00\x00\x02\x00\x00\x00\x00"
115 // Window update frame
116 "\x00\x00\x04\x08\x00\x00\x00\x00\x00"
117 // Window update
118 "\x3e\x7f\x00\x01"
119 // Header frame END_STREAM, END_HEADERS set
120 "\x00\x00\x29\x01\x05\x00\x00\x00"
121 // Header payload
122 "\x01\x82\x87\x41\x8b\xa0\xe4\x1d\x13\x9d\x09\xb8\x17\x80\xf0\x3f"
123 "\x04\x89\x62\xc2\xc9\x29\x91\x3b\x1d\xc2\xc7\x7a\x88\x25\xb6\x50"
124 "\xc3\xcb\xb6\xb8\x3f\x53\x03\x2a\x2f\x2a"sv;
125
126 boost::asio::write(out, boost::asio::buffer(toSend));
127
128 FakeHandler handler;
129 boost::asio::steady_timer timer(io);
130 std::function<std::string()> date(getDateStr);
Ed Tanousebe4c572025-02-08 14:29:53 -0800131 boost::asio::ssl::context sslCtx(boost::asio::ssl::context::tls_server);
Ed Tanous796ba932020-08-02 04:29:21 +0000132 auto conn = std::make_shared<HTTP2Connection<TestStream, FakeHandler>>(
Ed Tanousebe4c572025-02-08 14:29:53 -0800133 boost::asio::ssl::stream<TestStream>(std::move(stream), sslCtx),
Ed Tanous2e3cdf82025-08-01 09:49:35 -0700134 &handler, date, HttpType::HTTP, nullptr);
Ed Tanous52dd6932024-01-29 08:34:59 -0800135 conn->start();
136
Ed Tanous634a5e02025-09-26 12:05:08 -0700137 std::array<std::string_view, 5> expectedPrefix = {
Ed Tanous52dd6932024-01-29 08:34:59 -0800138 // Settings frame size 13
Ed Tanous634a5e02025-09-26 12:05:08 -0700139 "\x00\x00\x0c\x04\x00\x00\x00\x00\x00"sv,
Ed Tanous52dd6932024-01-29 08:34:59 -0800140 // 4 max concurrent streams
Ed Tanous634a5e02025-09-26 12:05:08 -0700141 "\x00\x03\x00\x00\x00\x04"sv,
Ed Tanous52dd6932024-01-29 08:34:59 -0800142 // Enable push = false
Ed Tanous634a5e02025-09-26 12:05:08 -0700143 "\x00\x02\x00\x00\x00\x00"sv,
Ed Tanous52dd6932024-01-29 08:34:59 -0800144 // Settings ACK from server to client
Ed Tanous634a5e02025-09-26 12:05:08 -0700145 "\x00\x00\x00\x04\x01\x00\x00\x00\x00"sv,
Ed Tanous52dd6932024-01-29 08:34:59 -0800146
Ed Tanousc056aa72024-04-14 09:57:09 -0700147 // Start Headers frame stream 1, size 0x005f
Ed Tanous634a5e02025-09-26 12:05:08 -0700148 "\x00\x00\x5f\x01\x04\x00\x00\x00\x01"sv,
149 };
Ed Tanous52dd6932024-01-29 08:34:59 -0800150
151 std::string_view expectedPostfix =
152 // Data Frame, Length 12, Stream 1, End Stream flag set
153 "\x00\x00\x0c\x00\x01\x00\x00\x00\x01"
154 // The body expected
155 "StringOutput"sv;
156
157 std::string_view outStr;
Ed Tanousc056aa72024-04-14 09:57:09 -0700158 constexpr size_t headerSize = 0x05f;
Ed Tanous52dd6932024-01-29 08:34:59 -0800159
Ed Tanous634a5e02025-09-26 12:05:08 -0700160 size_t expectedPrefixSize = 0;
161 for (const auto prefix : expectedPrefix)
162 {
163 expectedPrefixSize += prefix.size();
164 }
Ed Tanous52dd6932024-01-29 08:34:59 -0800165 // Run until we receive the expected amount of data
166 while (outStr.size() <
Ed Tanous634a5e02025-09-26 12:05:08 -0700167 expectedPrefixSize + headerSize + expectedPostfix.size())
Ed Tanous52dd6932024-01-29 08:34:59 -0800168 {
169 io.run_one();
170 outStr = out.str();
171 }
172 EXPECT_TRUE(handler.called);
173
174 // check the stream output against expected
Ed Tanous634a5e02025-09-26 12:05:08 -0700175 for (const auto& prefix : expectedPrefix)
176 {
177 EXPECT_EQ(outStr.substr(0, prefix.size()), prefix);
178 outStr.remove_prefix(prefix.size());
179 }
180
Ed Tanous52dd6932024-01-29 08:34:59 -0800181 std::vector<std::pair<std::string, std::string>> headers;
182 unpackHeaders(outStr.substr(0, headerSize), headers);
183 outStr.remove_prefix(headerSize);
184
Ed Tanousc056aa72024-04-14 09:57:09 -0700185 EXPECT_THAT(headers,
186 UnorderedElementsAre(
187 Pair(":status", "200"), Pair("content-length", "12"),
188 Pair("strict-transport-security",
189 "max-age=31536000; includeSubdomains"),
190 Pair("cache-control", "no-store, max-age=0"),
191 Pair("x-content-type-options", "nosniff"),
192 Pair("pragma", "no-cache"), Pair("date", "TestTime")));
Ed Tanous52dd6932024-01-29 08:34:59 -0800193
194 EXPECT_EQ(outStr, expectedPostfix);
195}
196
197} // namespace
198} // namespace crow