blob: 8dcb397d7fa8fb12f93733f7c77f58e180e26fd6 [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 Tanous5b904292024-04-16 11:10:17 -07007#include "nghttp2_adapters.hpp"
Ed Tanous52dd6932024-01-29 08:34:59 -08008
Ed Tanousf0b59af2024-03-20 13:38:04 -07009#include <nghttp2/nghttp2.h>
10#include <unistd.h>
11
12#include <boost/asio/buffer.hpp>
13#include <boost/asio/impl/write.hpp>
14#include <boost/asio/io_context.hpp>
Ed Tanous52dd6932024-01-29 08:34:59 -080015#include <boost/asio/steady_timer.hpp>
16#include <boost/beast/_experimental/test/stream.hpp>
Ed Tanousf0b59af2024-03-20 13:38:04 -070017#include <boost/beast/http/field.hpp>
Ed Tanous52dd6932024-01-29 08:34:59 -080018
19#include <bit>
Ed Tanousf0b59af2024-03-20 13:38:04 -070020#include <cstddef>
21#include <cstdint>
Ed Tanous52dd6932024-01-29 08:34:59 -080022#include <functional>
23#include <memory>
24#include <string>
25#include <string_view>
26#include <utility>
27#include <vector>
28
29#include "gmock/gmock.h"
30#include "gtest/gtest.h"
31namespace crow
32{
33
34namespace
35{
36
37using ::testing::Pair;
38using ::testing::UnorderedElementsAre;
39
40struct FakeHandler
41{
42 bool called = false;
Jonathan Doman102a4cd2024-04-15 16:56:23 -070043 void handle(const std::shared_ptr<Request>& req,
Ed Tanous52dd6932024-01-29 08:34:59 -080044 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
45 {
46 called = true;
Jonathan Doman102a4cd2024-04-15 16:56:23 -070047 EXPECT_EQ(req->url().buffer(), "/redfish/v1/");
48 EXPECT_EQ(req->methodString(), "GET");
49 EXPECT_EQ(req->getHeaderValue(boost::beast::http::field::user_agent),
Ed Tanous52dd6932024-01-29 08:34:59 -080050 "curl/8.5.0");
Jonathan Doman102a4cd2024-04-15 16:56:23 -070051 EXPECT_EQ(req->getHeaderValue(boost::beast::http::field::accept),
52 "*/*");
53 EXPECT_EQ(req->getHeaderValue(":authority"), "localhost:18080");
Ed Tanous52dd6932024-01-29 08:34:59 -080054 asyncResp->res.write("StringOutput");
55 }
56};
57
58std::string getDateStr()
59{
60 return "TestTime";
61}
62
63void unpackHeaders(std::string_view dataField,
64 std::vector<std::pair<std::string, std::string>>& headers)
65{
Ed Tanousa93163a2024-03-25 11:20:18 -070066 nghttp2_hd_inflater_ex inflater;
Ed Tanous52dd6932024-01-29 08:34:59 -080067
68 while (!dataField.empty())
69 {
70 nghttp2_nv nv;
71 int inflateFlags = 0;
72 const uint8_t* data = std::bit_cast<const uint8_t*>(dataField.data());
Patrick Williamsbd79bce2024-08-16 15:22:20 -040073 ssize_t parsed =
74 inflater.hd2(&nv, &inflateFlags, data, dataField.size(), 1);
Ed Tanous52dd6932024-01-29 08:34:59 -080075
76 ASSERT_GT(parsed, 0);
77 dataField.remove_prefix(static_cast<size_t>(parsed));
78 if ((inflateFlags & NGHTTP2_HD_INFLATE_EMIT) > 0)
79 {
80 const char* namePtr = std::bit_cast<const char*>(nv.name);
81 std::string key(namePtr, nv.namelen);
82 const char* valPtr = std::bit_cast<const char*>(nv.value);
83 std::string value(valPtr, nv.valuelen);
84 headers.emplace_back(key, value);
85 }
86 if ((inflateFlags & NGHTTP2_HD_INFLATE_FINAL) > 0)
87 {
88 EXPECT_EQ(inflater.endHeaders(), 0);
89 break;
90 }
91 }
92 EXPECT_TRUE(dataField.empty());
93}
94
95TEST(http_connection, RequestPropogates)
96{
97 using namespace std::literals;
98 boost::asio::io_context io;
99 boost::beast::test::stream stream(io);
100 boost::beast::test::stream out(io);
101 stream.connect(out);
102 // This is a binary pre-encrypted stream captured from curl for a request to
103 // curl https://localhost:18080/redfish/v1/
104 std::string_view toSend =
105 // Hello
106 "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
107 // 18 byte settings frame
108 "\x00\x00\x12\x04\x00\x00\x00\x00\x00"
109 // Settings
110 "\x00\x03\x00\x00\x00\x64\x00\x04\x00\xa0\x00\x00\x00\x02\x00\x00\x00\x00"
111 // Window update frame
112 "\x00\x00\x04\x08\x00\x00\x00\x00\x00"
113 // Window update
114 "\x3e\x7f\x00\x01"
115 // Header frame END_STREAM, END_HEADERS set
116 "\x00\x00\x29\x01\x05\x00\x00\x00"
117 // Header payload
118 "\x01\x82\x87\x41\x8b\xa0\xe4\x1d\x13\x9d\x09\xb8\x17\x80\xf0\x3f"
119 "\x04\x89\x62\xc2\xc9\x29\x91\x3b\x1d\xc2\xc7\x7a\x88\x25\xb6\x50"
120 "\xc3\xcb\xb6\xb8\x3f\x53\x03\x2a\x2f\x2a"sv;
121
122 boost::asio::write(out, boost::asio::buffer(toSend));
123
124 FakeHandler handler;
125 boost::asio::steady_timer timer(io);
126 std::function<std::string()> date(getDateStr);
127 auto conn = std::make_shared<
128 HTTP2Connection<boost::beast::test::stream, FakeHandler>>(
129 std::move(stream), &handler, date);
130 conn->start();
131
132 std::string_view expectedPrefix =
133 // Settings frame size 13
134 "\x00\x00\x0c\x04\x00\x00\x00\x00\x00"
135 // 4 max concurrent streams
136 "\x00\x03\x00\x00\x00\x04"
137 // Enable push = false
138 "\x00\x02\x00\x00\x00\x00"
139 // Settings ACK from server to client
140 "\x00\x00\x00\x04\x01\x00\x00\x00\x00"
141
Ed Tanousc056aa72024-04-14 09:57:09 -0700142 // Start Headers frame stream 1, size 0x005f
143 "\x00\x00\x5f\x01\x04\x00\x00\x00\x01"sv;
Ed Tanous52dd6932024-01-29 08:34:59 -0800144
145 std::string_view expectedPostfix =
146 // Data Frame, Length 12, Stream 1, End Stream flag set
147 "\x00\x00\x0c\x00\x01\x00\x00\x00\x01"
148 // The body expected
149 "StringOutput"sv;
150
151 std::string_view outStr;
Ed Tanousc056aa72024-04-14 09:57:09 -0700152 constexpr size_t headerSize = 0x05f;
Ed Tanous52dd6932024-01-29 08:34:59 -0800153
154 // Run until we receive the expected amount of data
155 while (outStr.size() <
156 expectedPrefix.size() + headerSize + expectedPostfix.size())
157 {
158 io.run_one();
159 outStr = out.str();
160 }
161 EXPECT_TRUE(handler.called);
162
163 // check the stream output against expected
Ed Tanous325310d2024-03-15 09:05:04 -0700164 EXPECT_EQ(outStr.substr(0, expectedPrefix.size()), expectedPrefix);
Ed Tanous52dd6932024-01-29 08:34:59 -0800165 outStr.remove_prefix(expectedPrefix.size());
166 std::vector<std::pair<std::string, std::string>> headers;
167 unpackHeaders(outStr.substr(0, headerSize), headers);
168 outStr.remove_prefix(headerSize);
169
Ed Tanousc056aa72024-04-14 09:57:09 -0700170 EXPECT_THAT(headers,
171 UnorderedElementsAre(
172 Pair(":status", "200"), Pair("content-length", "12"),
173 Pair("strict-transport-security",
174 "max-age=31536000; includeSubdomains"),
175 Pair("cache-control", "no-store, max-age=0"),
176 Pair("x-content-type-options", "nosniff"),
177 Pair("pragma", "no-cache"), Pair("date", "TestTime")));
Ed Tanous52dd6932024-01-29 08:34:59 -0800178
179 EXPECT_EQ(outStr, expectedPostfix);
180}
181
182} // namespace
183} // namespace crow