RedfishPkg: implement Redfish HTTP protocol
implement Redfish HTTP protocol driver. Signed-off-by: Nickle Wang <nicklew@nvidia.com> Co-authored-by: Igor Kulchytskyy <igork@ami.com> Cc: Abner Chang <abner.chang@amd.com> Cc: Igor Kulchytskyy <igork@ami.com> Cc: Nick Ramirez <nramirez@nvidia.com> Reviewed-by: Abner Chang <abner.chang@amd.com> Reviewed-by: Igor Kulchytskyy <igork@ami.com> Reviewed-by: Mike Maslenkin <mike.maslenkin@gmail.com>
This commit is contained in:
committed by
mergify[bot]
parent
1988f2df29
commit
0ce2012c6c
692
RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
Normal file
692
RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
Normal file
@@ -0,0 +1,692 @@
|
||||
/** @file
|
||||
RedfishHttpOperation handles HTTP operations.
|
||||
|
||||
Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
|
||||
**/
|
||||
|
||||
#include "RedfishHttpOperation.h"
|
||||
#include "RedfishHttpData.h"
|
||||
|
||||
/**
|
||||
This function copies all headers in SrcHeaders to DstHeaders.
|
||||
It's call responsibility to release returned DstHeaders.
|
||||
|
||||
@param[in] SrcHeaders Source headers.
|
||||
@param[in] SrcHeaderCount Number of header in source headers.
|
||||
@param[out] DstHeaders Destination headers.
|
||||
@param[out] DstHeaderCount Number of header in designation headers.
|
||||
|
||||
@retval EFI_SUCCESS Headers are copied successfully.
|
||||
@retval Others Errors occur.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
CopyHttpHeaders (
|
||||
IN EFI_HTTP_HEADER *SrcHeaders,
|
||||
IN UINTN SrcHeaderCount,
|
||||
OUT EFI_HTTP_HEADER **DstHeaders,
|
||||
OUT UINTN *DstHeaderCount
|
||||
)
|
||||
{
|
||||
UINTN Index;
|
||||
|
||||
if ((SrcHeaders == NULL) || (SrcHeaderCount == 0) || (DstHeaders == NULL) || (DstHeaderCount == NULL)) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
*DstHeaderCount = 0;
|
||||
*DstHeaders = AllocateZeroPool (sizeof (EFI_HTTP_HEADER) * SrcHeaderCount);
|
||||
if (*DstHeaders == NULL) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
*DstHeaderCount = SrcHeaderCount;
|
||||
for (Index = 0; Index < SrcHeaderCount; Index++) {
|
||||
(*DstHeaders)[Index].FieldName = ASCII_STR_DUPLICATE (SrcHeaders[Index].FieldName);
|
||||
if ((*DstHeaders)[Index].FieldName == NULL) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
(*DstHeaders)[Index].FieldValue = ASCII_STR_DUPLICATE (SrcHeaders[Index].FieldValue);
|
||||
if ((*DstHeaders)[Index].FieldValue == NULL) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
This function free resources in Request. Request is no longer available
|
||||
after this function returns successfully.
|
||||
|
||||
@param[in] Request HTTP request to be released.
|
||||
|
||||
@retval EFI_SUCCESS Resource is released successfully.
|
||||
@retval Others Errors occur.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
ReleaseRedfishRequest (
|
||||
IN REDFISH_REQUEST *Request
|
||||
)
|
||||
{
|
||||
if (Request == NULL) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if ((Request->Headers != NULL) && (Request->HeaderCount > 0)) {
|
||||
HttpFreeHeaderFields (Request->Headers, Request->HeaderCount);
|
||||
Request->Headers = NULL;
|
||||
Request->HeaderCount = 0;
|
||||
}
|
||||
|
||||
if (Request->Content != NULL) {
|
||||
FreePool (Request->Content);
|
||||
Request->Content = NULL;
|
||||
}
|
||||
|
||||
if (Request->ContentType != NULL) {
|
||||
FreePool (Request->ContentType);
|
||||
Request->ContentType = NULL;
|
||||
}
|
||||
|
||||
Request->ContentLength = 0;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
This function free resources in given Response.
|
||||
|
||||
@param[in] Response HTTP response to be released.
|
||||
|
||||
@retval EFI_SUCCESS Resource is released successfully.
|
||||
@retval Others Errors occur.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
ReleaseRedfishResponse (
|
||||
IN REDFISH_RESPONSE *Response
|
||||
)
|
||||
{
|
||||
if (Response == NULL) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if ((Response->Headers != NULL) && (Response->HeaderCount > 0)) {
|
||||
HttpFreeHeaderFields (Response->Headers, Response->HeaderCount);
|
||||
Response->Headers = NULL;
|
||||
Response->HeaderCount = 0;
|
||||
}
|
||||
|
||||
if (Response->Payload != NULL) {
|
||||
ReleaseRedfishPayload (Response->Payload);
|
||||
Response->Payload = NULL;
|
||||
}
|
||||
|
||||
if (Response->StatusCode != NULL) {
|
||||
FreePool (Response->StatusCode);
|
||||
Response->StatusCode = NULL;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
This function free resources in given HTTP message.
|
||||
|
||||
@param[in] HttpMessage HTTP message to be released.
|
||||
@param[in] IsRequest TRUE if this is request type of HTTP message.
|
||||
FALSE if this is response type of HTTP message.
|
||||
|
||||
@retval EFI_SUCCESS Resource is released successfully.
|
||||
@retval Others Errors occur.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
ReleaseHttpMessage (
|
||||
IN EFI_HTTP_MESSAGE *HttpMessage,
|
||||
IN BOOLEAN IsRequest
|
||||
)
|
||||
{
|
||||
if (HttpMessage == NULL) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (IsRequest) {
|
||||
if (HttpMessage->Data.Request != NULL) {
|
||||
if (HttpMessage->Data.Request->Url != NULL) {
|
||||
FreePool (HttpMessage->Data.Request->Url);
|
||||
}
|
||||
|
||||
FreePool (HttpMessage->Data.Request);
|
||||
HttpMessage->Data.Request = NULL;
|
||||
}
|
||||
} else {
|
||||
if (HttpMessage->Data.Response != NULL) {
|
||||
FreePool (HttpMessage->Data.Response);
|
||||
HttpMessage->Data.Response = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (HttpMessage->Body != NULL) {
|
||||
FreePool (HttpMessage->Body);
|
||||
HttpMessage->Body = NULL;
|
||||
}
|
||||
|
||||
if (HttpMessage->Headers != NULL) {
|
||||
HttpFreeHeaderFields (HttpMessage->Headers, HttpMessage->HeaderCount);
|
||||
HttpMessage->Headers = NULL;
|
||||
HttpMessage->HeaderCount = 0;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
This function build Redfish message for sending data to Redfish service.
|
||||
It's call responsibility to properly release returned HTTP message by
|
||||
calling ReleaseHttpMessage.
|
||||
|
||||
@param[in] ServicePrivate Pointer to Redfish service private data.
|
||||
@param[in] Uri Redfish service URI.
|
||||
@param[in] Method HTTP method.
|
||||
@param[in] Request Additional data to send to Redfish service.
|
||||
This is optional.
|
||||
@param[in] ContentEncoding Content encoding method to compress HTTP context.
|
||||
This is optional. When ContentEncoding is NULL,
|
||||
No compress method will be performed.
|
||||
|
||||
@retval EFI_HTTP_MESSAGE * Pointer to newly created HTTP message.
|
||||
@retval NULL Error occurred.
|
||||
|
||||
**/
|
||||
EFI_HTTP_MESSAGE *
|
||||
BuildRequestMessage (
|
||||
IN REDFISH_SERVICE_PRIVATE *ServicePrivate,
|
||||
IN EFI_STRING Uri,
|
||||
IN EFI_HTTP_METHOD Method,
|
||||
IN REDFISH_REQUEST *Request OPTIONAL,
|
||||
IN CHAR8 *ContentEncoding OPTIONAL
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_STRING Url;
|
||||
UINTN UrlSize;
|
||||
UINTN Index;
|
||||
EFI_HTTP_MESSAGE *RequestMsg;
|
||||
EFI_HTTP_REQUEST_DATA *RequestData;
|
||||
UINTN HeaderCount;
|
||||
UINTN HeaderIndex;
|
||||
EFI_HTTP_HEADER *Headers;
|
||||
CHAR8 ContentLengthStr[REDFISH_CONTENT_LENGTH_SIZE];
|
||||
VOID *Content;
|
||||
UINTN ContentLength;
|
||||
BOOLEAN HasContent;
|
||||
BOOLEAN DoContentEncoding;
|
||||
|
||||
RequestMsg = NULL;
|
||||
RequestData = NULL;
|
||||
Url = NULL;
|
||||
UrlSize = 0;
|
||||
Content = NULL;
|
||||
ContentLength = 0;
|
||||
HeaderCount = REDFISH_COMMON_HEADER_SIZE;
|
||||
HeaderIndex = 0;
|
||||
Headers = NULL;
|
||||
HasContent = FALSE;
|
||||
DoContentEncoding = FALSE;
|
||||
|
||||
if ((ServicePrivate == NULL) || (IS_EMPTY_STRING (Uri))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (Method >= HttpMethodMax) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: %s\n", __func__, Uri));
|
||||
|
||||
//
|
||||
// Build full URL for HTTP query.
|
||||
//
|
||||
UrlSize = (AsciiStrLen (ServicePrivate->Host) + StrLen (Uri) + 1) * sizeof (CHAR16);
|
||||
Url = AllocateZeroPool (UrlSize);
|
||||
if (Url == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UnicodeSPrint (Url, UrlSize, L"%a%s", ServicePrivate->Host, Uri);
|
||||
DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: Url: %s\n", __func__, Url));
|
||||
|
||||
//
|
||||
// Step 1: build the HTTP headers.
|
||||
//
|
||||
if (!IS_EMPTY_STRING (ServicePrivate->SessionToken) || !IS_EMPTY_STRING (ServicePrivate->BasicAuth)) {
|
||||
HeaderCount++;
|
||||
}
|
||||
|
||||
if ((Request != NULL) && (Request->HeaderCount > 0)) {
|
||||
HeaderCount += Request->HeaderCount;
|
||||
}
|
||||
|
||||
//
|
||||
// Check and see if we will do content encoding or not
|
||||
//
|
||||
if (!IS_EMPTY_STRING (ContentEncoding)) {
|
||||
if (AsciiStrCmp (ContentEncoding, REDFISH_HTTP_CONTENT_ENCODING_NONE) != 0) {
|
||||
DoContentEncoding = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if ((Request != NULL) && !IS_EMPTY_STRING (Request->Content)) {
|
||||
HeaderCount += 2;
|
||||
HasContent = TRUE;
|
||||
if (DoContentEncoding) {
|
||||
HeaderCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Headers = AllocateZeroPool (HeaderCount * sizeof (EFI_HTTP_HEADER));
|
||||
if (Headers == NULL) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
|
||||
if (!IS_EMPTY_STRING (ServicePrivate->SessionToken)) {
|
||||
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_X_AUTH_TOKEN, ServicePrivate->SessionToken);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
} else if (!IS_EMPTY_STRING (ServicePrivate->BasicAuth)) {
|
||||
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_AUTHORIZATION, ServicePrivate->BasicAuth);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (Request != NULL) {
|
||||
for (Index = 0; Index < Request->HeaderCount; Index++) {
|
||||
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], Request->Headers[Index].FieldName, Request->Headers[Index].FieldValue);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_HOST, ServicePrivate->HostName);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
|
||||
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], REDFISH_HTTP_HEADER_ODATA_VERSION_STR, REDFISH_HTTP_HEADER_ODATA_VERSION_VALUE);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
|
||||
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_ACCEPT, HTTP_CONTENT_TYPE_APP_JSON);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
|
||||
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_USER_AGENT, REDFISH_HTTP_HEADER_USER_AGENT_VALUE);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
|
||||
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], REDFISH_HTTP_HEADER_CONNECTION_STR, REDFISH_HTTP_HEADER_CONNECTION_VALUE);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
|
||||
//
|
||||
// Handle content header
|
||||
//
|
||||
if (HasContent) {
|
||||
if (Request->ContentType == NULL) {
|
||||
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_TYPE, HTTP_CONTENT_TYPE_APP_JSON);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
} else {
|
||||
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_TYPE, Request->ContentType);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (Request->ContentLength == 0) {
|
||||
Request->ContentLength = AsciiStrLen (Request->Content);
|
||||
}
|
||||
|
||||
AsciiSPrint (
|
||||
ContentLengthStr,
|
||||
sizeof (ContentLengthStr),
|
||||
"%lu",
|
||||
(UINT64)Request->ContentLength
|
||||
);
|
||||
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_LENGTH, ContentLengthStr);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
|
||||
//
|
||||
// Encoding
|
||||
//
|
||||
if (DoContentEncoding) {
|
||||
//
|
||||
// We currently only support gzip Content-Encoding.
|
||||
//
|
||||
Status = RedfishContentEncode (
|
||||
ContentEncoding,
|
||||
Request->Content,
|
||||
Request->ContentLength,
|
||||
&Content,
|
||||
&ContentLength
|
||||
);
|
||||
if (Status == EFI_INVALID_PARAMETER) {
|
||||
DEBUG ((DEBUG_ERROR, "%a: Error to encode content.\n", __func__));
|
||||
goto ON_ERROR;
|
||||
} else if (Status == EFI_UNSUPPORTED) {
|
||||
DoContentEncoding = FALSE;
|
||||
DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: No content coding for %a! Use raw data instead.\n", __func__, ContentEncoding));
|
||||
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_ENCODING, HTTP_CONTENT_ENCODING_IDENTITY);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
} else {
|
||||
Status = HttpSetFieldNameAndValue (&Headers[HeaderIndex++], HTTP_HEADER_CONTENT_ENCODING, HTTP_CONTENT_ENCODING_GZIP);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// When the content is from caller, we use our own copy so that we properly release it later.
|
||||
//
|
||||
if (!DoContentEncoding) {
|
||||
Content = AllocateCopyPool (Request->ContentLength, Request->Content);
|
||||
if (Content == NULL) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
|
||||
ContentLength = Request->ContentLength;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Step 2: build the rest of HTTP request info.
|
||||
//
|
||||
RequestData = AllocateZeroPool (sizeof (EFI_HTTP_REQUEST_DATA));
|
||||
if (RequestData == NULL) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
|
||||
RequestData->Method = Method;
|
||||
RequestData->Url = Url;
|
||||
|
||||
//
|
||||
// Step 3: fill in EFI_HTTP_MESSAGE
|
||||
//
|
||||
RequestMsg = AllocateZeroPool (sizeof (EFI_HTTP_MESSAGE));
|
||||
if (RequestMsg == NULL) {
|
||||
goto ON_ERROR;
|
||||
}
|
||||
|
||||
ASSERT (HeaderIndex == HeaderCount);
|
||||
RequestMsg->Data.Request = RequestData;
|
||||
RequestMsg->HeaderCount = HeaderIndex;
|
||||
RequestMsg->Headers = Headers;
|
||||
|
||||
if (HasContent) {
|
||||
RequestMsg->BodyLength = ContentLength;
|
||||
RequestMsg->Body = Content;
|
||||
}
|
||||
|
||||
return RequestMsg;
|
||||
|
||||
ON_ERROR:
|
||||
|
||||
if (Headers != NULL) {
|
||||
HttpFreeHeaderFields (Headers, HeaderIndex);
|
||||
}
|
||||
|
||||
if (RequestData != NULL) {
|
||||
FreePool (RequestData);
|
||||
}
|
||||
|
||||
if (RequestMsg != NULL) {
|
||||
FreePool (RequestMsg);
|
||||
}
|
||||
|
||||
if (Url != NULL) {
|
||||
FreePool (Url);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
This function parse response message from Redfish service, and
|
||||
build Redfish response for caller. It's call responsibility to
|
||||
properly release Redfish response by calling ReleaseRedfishResponse.
|
||||
|
||||
@param[in] ServicePrivate Pointer to Redfish service private data.
|
||||
@param[in] ResponseMsg Response message from Redfish service.
|
||||
@param[out] RedfishResponse Redfish response data.
|
||||
|
||||
@retval EFI_SUCCESS Redfish response is returned successfully.
|
||||
@retval Others Errors occur.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
ParseResponseMessage (
|
||||
IN REDFISH_SERVICE_PRIVATE *ServicePrivate,
|
||||
IN EFI_HTTP_MESSAGE *ResponseMsg,
|
||||
OUT REDFISH_RESPONSE *RedfishResponse
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EDKII_JSON_VALUE JsonData;
|
||||
EFI_HTTP_HEADER *ContentEncodedHeader;
|
||||
VOID *DecodedBody;
|
||||
UINTN DecodedLength;
|
||||
|
||||
if ((ServicePrivate == NULL) || (ResponseMsg == NULL) || (RedfishResponse == NULL)) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a\n", __func__));
|
||||
|
||||
//
|
||||
// Initialization
|
||||
//
|
||||
JsonData = NULL;
|
||||
RedfishResponse->HeaderCount = 0;
|
||||
RedfishResponse->Headers = NULL;
|
||||
RedfishResponse->Payload = NULL;
|
||||
RedfishResponse->StatusCode = NULL;
|
||||
DecodedBody = NULL;
|
||||
DecodedLength = 0;
|
||||
|
||||
//
|
||||
// Return the HTTP StatusCode.
|
||||
//
|
||||
if (ResponseMsg->Data.Response != NULL) {
|
||||
DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: status: %d\n", __func__, ResponseMsg->Data.Response->StatusCode));
|
||||
RedfishResponse->StatusCode = AllocateCopyPool (sizeof (EFI_HTTP_STATUS_CODE), &ResponseMsg->Data.Response->StatusCode);
|
||||
if (RedfishResponse->StatusCode == NULL) {
|
||||
DEBUG ((DEBUG_ERROR, "%a: Failed to create status code.\n", __func__));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Return the HTTP headers.
|
||||
//
|
||||
if (ResponseMsg->Headers != NULL) {
|
||||
DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: header count: %d\n", __func__, ResponseMsg->HeaderCount));
|
||||
Status = CopyHttpHeaders (
|
||||
ResponseMsg->Headers,
|
||||
ResponseMsg->HeaderCount,
|
||||
&RedfishResponse->Headers,
|
||||
&RedfishResponse->HeaderCount
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_ERROR, "%a: Failed to copy HTTP headers: %r\n", __func__, Status));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Return the HTTP body.
|
||||
//
|
||||
if ((ResponseMsg->BodyLength != 0) && (ResponseMsg->Body != NULL)) {
|
||||
DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: body length: %d\n", __func__, ResponseMsg->BodyLength));
|
||||
//
|
||||
// Check if data is encoded.
|
||||
//
|
||||
ContentEncodedHeader = HttpFindHeader (RedfishResponse->HeaderCount, RedfishResponse->Headers, HTTP_HEADER_CONTENT_ENCODING);
|
||||
if (ContentEncodedHeader != NULL) {
|
||||
//
|
||||
// The content is encoded.
|
||||
//
|
||||
Status = RedfishContentDecode (
|
||||
ContentEncodedHeader->FieldValue,
|
||||
ResponseMsg->Body,
|
||||
ResponseMsg->BodyLength,
|
||||
&DecodedBody,
|
||||
&DecodedLength
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_ERROR, "%a: Failed to decompress the response content: %r decoding method: %a\n.", __func__, Status, ContentEncodedHeader->FieldValue));
|
||||
goto ON_ERROR;
|
||||
}
|
||||
|
||||
JsonData = JsonLoadBuffer (DecodedBody, DecodedLength, 0, NULL);
|
||||
FreePool (DecodedBody);
|
||||
} else {
|
||||
JsonData = JsonLoadBuffer (ResponseMsg->Body, ResponseMsg->BodyLength, 0, NULL);
|
||||
}
|
||||
|
||||
if (!JsonValueIsNull (JsonData)) {
|
||||
RedfishResponse->Payload = CreateRedfishPayload (ServicePrivate, JsonData);
|
||||
if (RedfishResponse->Payload == NULL) {
|
||||
DEBUG ((DEBUG_ERROR, "%a: Failed to create payload\n.", __func__));
|
||||
}
|
||||
|
||||
JsonValueFree (JsonData);
|
||||
} else {
|
||||
DEBUG ((DEBUG_ERROR, "%a: No payload available\n", __func__));
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
|
||||
ON_ERROR:
|
||||
|
||||
if (RedfishResponse != NULL) {
|
||||
ReleaseRedfishResponse (RedfishResponse);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
/**
|
||||
This function send Redfish request to Redfish service by calling
|
||||
Rest Ex protocol.
|
||||
|
||||
@param[in] Service Pointer to Redfish service.
|
||||
@param[in] Uri Uri of Redfish service.
|
||||
@param[in] Method HTTP method.
|
||||
@param[in] Request Request data. This is optional.
|
||||
@param[out] Response Redfish response data.
|
||||
|
||||
@retval EFI_SUCCESS Request is sent and received successfully.
|
||||
@retval Others Errors occur.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
HttpSendReceive (
|
||||
IN REDFISH_SERVICE Service,
|
||||
IN EFI_STRING Uri,
|
||||
IN EFI_HTTP_METHOD Method,
|
||||
IN REDFISH_REQUEST *Request OPTIONAL,
|
||||
OUT REDFISH_RESPONSE *Response
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_STATUS RestExStatus;
|
||||
EFI_HTTP_MESSAGE *RequestMsg;
|
||||
EFI_HTTP_MESSAGE ResponseMsg;
|
||||
REDFISH_SERVICE_PRIVATE *ServicePrivate;
|
||||
EFI_HTTP_HEADER *XAuthTokenHeader;
|
||||
CHAR8 *HttpContentEncoding;
|
||||
|
||||
if ((Service == NULL) || IS_EMPTY_STRING (Uri) || (Response == NULL)) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: Method: 0x%x %s\n", __func__, Method, Uri));
|
||||
|
||||
ServicePrivate = (REDFISH_SERVICE_PRIVATE *)Service;
|
||||
if (ServicePrivate->Signature != REDFISH_HTTP_SERVICE_SIGNATURE) {
|
||||
DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
ZeroMem (&ResponseMsg, sizeof (ResponseMsg));
|
||||
HttpContentEncoding = (CHAR8 *)PcdGetPtr (PcdRedfishServiceContentEncoding);
|
||||
|
||||
RequestMsg = BuildRequestMessage (Service, Uri, Method, Request, HttpContentEncoding);
|
||||
if (RequestMsg == NULL) {
|
||||
DEBUG ((DEBUG_ERROR, "%a: cannot build request message for %s\n", __func__, Uri));
|
||||
return EFI_PROTOCOL_ERROR;
|
||||
}
|
||||
|
||||
//
|
||||
// call RESTEx to get response from REST service.
|
||||
//
|
||||
RestExStatus = ServicePrivate->RestEx->SendReceive (ServicePrivate->RestEx, RequestMsg, &ResponseMsg);
|
||||
if (EFI_ERROR (RestExStatus)) {
|
||||
DEBUG ((DEBUG_ERROR, "%a: %s SendReceive failure: %r\n", __func__, Uri, RestExStatus));
|
||||
}
|
||||
|
||||
//
|
||||
// Return status code, headers and payload to caller as much as possible even when RestEx returns failure.
|
||||
//
|
||||
Status = ParseResponseMessage (ServicePrivate, &ResponseMsg, Response);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_ERROR, "%a: %s parse response failure: %r\n", __func__, Uri, Status));
|
||||
} else {
|
||||
//
|
||||
// Capture session token in header
|
||||
//
|
||||
if ((Method == HttpMethodPost) &&
|
||||
(Response->StatusCode != NULL) &&
|
||||
((*Response->StatusCode == HTTP_STATUS_200_OK) || (*Response->StatusCode == HTTP_STATUS_204_NO_CONTENT)))
|
||||
{
|
||||
XAuthTokenHeader = HttpFindHeader (ResponseMsg.HeaderCount, ResponseMsg.Headers, HTTP_HEADER_X_AUTH_TOKEN);
|
||||
if (XAuthTokenHeader != NULL) {
|
||||
Status = UpdateSessionToken (ServicePrivate, XAuthTokenHeader->FieldValue);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_ERROR, "%a: update session token failure: %r\n", __func__, Status));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Release resources
|
||||
//
|
||||
if (RequestMsg != NULL) {
|
||||
ReleaseHttpMessage (RequestMsg, TRUE);
|
||||
FreePool (RequestMsg);
|
||||
}
|
||||
|
||||
ReleaseHttpMessage (&ResponseMsg, FALSE);
|
||||
|
||||
return RestExStatus;
|
||||
}
|
Reference in New Issue
Block a user