| |
| #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 |