blob: 31257ed7fb1c9b23f4177d9fb2931aef339c66b6 [file] [log] [blame]
#include "proxy_redfishv1_impl.h"
#include <memory>
#include <string>
#include <utility>
#include "absl/status/statusor.h"
#include "absl/strings/ascii.h"
#include "absl/strings/match.h"
#include "redfish_query_engine/redfish_v1.pb.h"
#include "grpcpp/support/status.h"
#include "grpc/status.h"
#include "grpcpp/security/auth_context.h"
#include "grpcpp/support/status.h"
#include "proxy.h"
#include "proxy_config.pb.h"
#include "redfish_plugin.h"
#include "request_response.h"
#include "voyager/priority_queue.hpp"
namespace milotic {
namespace {
// For Ecclesia lib, contract for redfish v1. Anything out of tree is not parsed
// as JSON. Some implementations may set the OData-Version flag for non-JSON
// items, so we also check content-type if it is present.
bool IsResponseRedfishJson(const redfish::v1::Response &response) {
auto found = response.headers().find("odata-version");
if (found == response.headers().end()) return false;
if (found->second != "4.0") return false;
found = response.headers().find("content-type");
// Default to JSON if there is an odata-version but no content type.
if (found == response.headers().end()) return true;
if (found->second != "application/json") return false;
return true;
}
} // namespace
grpc::Status ProxyRedfishV1Impl::DispatchRequest(
RedfishPlugin::RequestVerb verb, const ::redfish::v1::Request *request,
redfish::v1::Response *response, const grpc::AuthContext &auth_context) {
absl::StatusOr<std::unique_ptr<ProxyRequest>> status_or_http_request =
proxy_.CreateAuthorizedRequest(verb, request->url(), auth_context);
if (!status_or_http_request.ok()) {
return ConvertStatus(status_or_http_request.status());
}
std::unique_ptr<ProxyRequest> http_request =
*std::move(status_or_http_request);
/* Copy http request headers to request */
for (const auto &[key, value] : request->headers()) {
// Never override "Host" header.
if (absl::EqualsIgnoreCase(key, "host")) continue;
http_request->headers[key] = value;
}
if (!request->json_str().empty()) {
http_request->body = request->json_str();
// This is declared inside the if block to ensure empty Content-Type header
// is not added to http_request.
auto &content_type = http_request->headers["Content-Type"];
if (content_type.empty()) content_type = "application/json";
} else if (request->has_octet_stream()) {
http_request->body = request->octet_stream();
auto &content_type = http_request->headers["Content-Type"];
if (content_type.empty()) content_type = "application/octet-stream";
}
absl::StatusOr<std::unique_ptr<Proxy::RequestJob>> job =
proxy_.DispatchRequestToQueue(verb, std::move(http_request));
if (!job.ok()) return ConvertStatus(job.status());
if (job.value()->Wait() != voyager::Job::JobState::kDone) {
return grpc::Status(static_cast<grpc::StatusCode>(GRPC_STATUS_INTERNAL),
"Job error");
}
absl::StatusOr<ProxyResponse> &result = job.value()->response();
if (!result.ok()) return ConvertStatus(result.status());
/* Copy response headers back */
for (const auto &[key, value] : result->headers) {
// HTTP headers names are not case sensitive. Changing consistently to
// lowercase allows for easier comparisons, and is consistent with HTTP2
// headers.
std::string key_lower = key;
absl::AsciiStrToLower(&key_lower);
(*response->mutable_headers())[std::move(key_lower)] = value;
}
/* Copy back response depending on expected response version */
if (IsResponseRedfishJson(*response)) {
response->set_json_str(result->body);
} else {
response->set_octet_stream(result->body);
}
response->set_code(result->code);
return grpc::Status::OK;
}
static Proxy::RegisterService<milotic_grpc_proxy::RedfishV1Options,
ProxyRedfishV1Impl>
redfish_v1_service_reg;
} // namespace milotic