xref: /openbmc/bmcweb/http/complete_response_fields.hpp (revision 08fad5d9dc59323a8916ff97a035221621047d8c)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 #pragma once
4 
5 #include "boost_formatters.hpp"
6 #include "http_body.hpp"
7 #include "http_response.hpp"
8 #include "http_utility.hpp"
9 #include "json_html_serializer.hpp"
10 #include "logging.hpp"
11 #include "security_headers.hpp"
12 
13 #include <boost/beast/http/field.hpp>
14 #include <nlohmann/json.hpp>
15 
16 #include <array>
17 #include <string>
18 #include <string_view>
19 #include <utility>
20 
21 namespace crow
22 {
23 
handleEncoding(std::string_view acceptEncoding,Response & res)24 inline void handleEncoding(std::string_view acceptEncoding, Response& res)
25 {
26     using bmcweb::CompressionType;
27     using enum bmcweb::CompressionType;
28     using http_helpers::Encoding;
29     using enum http_helpers::Encoding;
30     // If the payload is currently compressed, see if we can avoid
31     // decompressing it by sending it to the client directly
32     switch (res.response.body().compressionType)
33     {
34         case Zstd:
35         {
36             std::array<Encoding, 1> allowedEnc{ZSTD};
37             Encoding encoding =
38                 http_helpers::getPreferredEncoding(acceptEncoding, allowedEnc);
39 
40             if (encoding == ZSTD)
41             {
42                 // If the client supports returning zstd directly, allow that.
43                 res.response.body().clientCompressionType = Zstd;
44             }
45         }
46         break;
47         case Gzip:
48         {
49             std::array<Encoding, 1> allowedEnc{GZIP};
50             Encoding encoding =
51                 http_helpers::getPreferredEncoding(acceptEncoding, allowedEnc);
52             if (encoding != GZIP)
53             {
54                 BMCWEB_LOG_WARNING(
55                     "Unimplemented: Returning gzip payload to client that did not explicitly allow it.");
56             }
57         }
58         break;
59         default:
60             break;
61     }
62 }
63 
completeResponseFields(std::string_view accepts,std::string_view acceptEncoding,Response & res)64 inline void completeResponseFields(
65     std::string_view accepts, std::string_view acceptEncoding, Response& res)
66 {
67     BMCWEB_LOG_INFO("Response: {}", res.resultInt());
68     addSecurityHeaders(res);
69 
70     res.setResponseEtagAndHandleNotModified();
71     if (res.jsonValue.is_structured())
72     {
73         using http_helpers::ContentType;
74         std::array<ContentType, 3> allowed{ContentType::CBOR, ContentType::JSON,
75                                            ContentType::HTML};
76         ContentType preferred = getPreferredContentType(accepts, allowed);
77 
78         if (preferred == ContentType::HTML)
79         {
80             json_html_util::prettyPrintJson(res);
81         }
82         else if (preferred == ContentType::CBOR)
83         {
84             res.addHeader(boost::beast::http::field::content_type,
85                           "application/cbor");
86             std::string cbor;
87             nlohmann::json::to_cbor(res.jsonValue, cbor);
88             res.write(std::move(cbor));
89         }
90         else
91         {
92             // Technically preferred could also be NoMatch here, but we'd
93             // like to default to something rather than return 400 for
94             // backward compatibility.
95             res.addHeader(boost::beast::http::field::content_type,
96                           "application/json");
97             res.write(res.jsonValue.dump(
98                 2, ' ', true, nlohmann::json::error_handler_t::replace));
99         }
100     }
101 
102     handleEncoding(acceptEncoding, res);
103 }
104 } // namespace crow
105