| #include "tlbmc/redfish/response.h" |
| |
| #include <string_view> |
| |
| #include "absl/status/status.h" |
| #include "absl/strings/str_cat.h" |
| #include "redfish_v1.pb.h" |
| #include "grpcpp/support/server_callback.h" |
| #include "grpcpp/support/status.h" |
| #include "nlohmann/json.hpp" |
| |
| namespace milotic_tlbmc { |
| |
| RedfishResponse::RedfishResponse(grpc::ServerUnaryReactor* reactor, |
| redfish::v1::Response& grpc_response) |
| : reactor_(reactor), grpc_response_(&grpc_response) {} |
| |
| void RedfishResponse::End() { |
| // Don't send response if end has already been called. |
| if (end_called_) { |
| return; |
| } |
| end_called_ = true; |
| // If there is an async resp attached, then we will use it to send the |
| // response back instead of the reactor. |
| if (async_resp_ != nullptr) { |
| for (const auto& element : http_response_.base()) { |
| async_resp_->res.addHeader(element.name_string(), element.value()); |
| } |
| async_resp_->res.result(http_response_.result()); |
| async_resp_->res.body() = http_response_.body(); |
| async_resp_->res.jsonValue = json_body_; |
| return; |
| } |
| |
| // If there is no grpc response, then we don't need to send the response. |
| if (grpc_response_ == nullptr) { |
| return; |
| } |
| |
| auto* headers = grpc_response_->mutable_headers(); |
| for (const auto& element : http_response_.base()) { |
| // Repeated headers are combined into a ',' separated list. See |
| // https://www.rfc-editor.org/rfc/rfc9110.html#section-5.3-3. |
| auto found = headers->find(element.name_string()); |
| if (found == headers->end()) { |
| (*headers)[element.name_string()] = element.value(); |
| } else { |
| found->second += ","; |
| found->second += element.value(); |
| } |
| } |
| |
| if (http_response_["OData-Version"] == "4.0") { |
| *grpc_response_->mutable_json_str() = json_body_.dump( |
| -1, ' ', true, nlohmann::json::error_handler_t::replace); |
| } else { |
| *grpc_response_->mutable_octet_stream() = http_response_.body(); |
| } |
| grpc_response_->set_code(static_cast<unsigned int>(http_response_.result())); |
| |
| if (reactor_ != nullptr) { |
| reactor_->Finish(grpc::Status::OK); |
| } |
| } |
| |
| void RedfishResponse::SetBodyToJson() { |
| http_response_.base().erase("OData-Version"); |
| SetHeader("OData-Version", "4.0"); |
| } |
| |
| void RedfishResponse::SetBodyToOctetStream() { |
| http_response_.base().erase("OData-Version"); |
| } |
| |
| void RedfishResponse::SetHeader(std::string_view key, std::string_view value) { |
| http_response_.base().insert(key, value); |
| } |
| |
| void RedfishResponse::SetBinaryBody(std::string_view body) { |
| http_response_.body() = body; |
| } |
| |
| void RedfishResponse::SetToNotFound(std::string_view error_message) { |
| http_response_.result(boost::beast::http::status::not_found); |
| |
| nlohmann::json error_message_json; |
| error_message_json["@odata.type"] = "#Message.v1_1_1.Message"; |
| error_message_json["MessageId"] = "Base.1.13.0.ResourceNotFound"; |
| error_message_json["Message"] = error_message; |
| |
| SetErrorMessage(error_message_json); |
| } |
| |
| void RedfishResponse::SetToForbidden(std::string_view error_message) { |
| http_response_.result(boost::beast::http::status::forbidden); |
| |
| nlohmann::json error_message_json; |
| error_message_json["@odata.type"] = "#Message.v1_1_1.Message"; |
| error_message_json["MessageId"] = "Base.1.13.0.ResourceAtUriUnauthorized"; |
| error_message_json["Message"] = error_message; |
| |
| SetErrorMessage(error_message_json); |
| } |
| |
| void RedfishResponse::SetToNotReady(std::string_view error_message) { |
| http_response_.result(boost::beast::http::status::service_unavailable); |
| |
| nlohmann::json error_message_json; |
| error_message_json["@odata.type"] = "#Message.v1_1_1.Message"; |
| error_message_json["MessageId"] = "Base.1.13.0.InternalError"; |
| error_message_json["Message"] = error_message; |
| |
| SetErrorMessage(error_message_json); |
| } |
| |
| void RedfishResponse::SetToAbslStatus(const absl::Status& status) { |
| switch (status.code()) { |
| case absl::StatusCode::kNotFound: |
| SetToNotFound(status.message()); |
| break; |
| case absl::StatusCode::kPermissionDenied: |
| SetToForbidden(status.message()); |
| break; |
| case absl::StatusCode::kUnavailable: |
| SetToNotReady(status.message()); |
| break; |
| default: |
| http_response_.result(boost::beast::http::status::internal_server_error); |
| break; |
| } |
| } |
| |
| void RedfishResponse::SetErrorMessage( |
| const nlohmann::json& error_message_json) { |
| if (!json_body_.contains("/error")) { |
| SetKeyInJsonBody(/*key=*/"/error/code", /*value=*/"Base.1.8.GeneralError"); |
| SetKeyInJsonBody(/*key=*/"/error/message", |
| /*value=*/ |
| "A general error has occurred. See Resolution for " |
| "information on how to " |
| "resolve the error."); |
| } |
| |
| AppendToKeyInJsonBody(/*key=*/"/error/@Message.ExtendedInfo", |
| /*value=*/error_message_json); |
| } |
| |
| } // namespace milotic_tlbmc |