* [edk2-devel] [PATCH 2/6] RedfishPkg: implement Redfish HTTP protocol
@ 2024-02-20 6:41 Nickle Wang via groups.io
2024-02-21 1:51 ` Chang, Abner via groups.io
0 siblings, 1 reply; 3+ messages in thread
From: Nickle Wang via groups.io @ 2024-02-20 6:41 UTC (permalink / raw)
To: devel; +Cc: Igor Kulchytskyy, Abner Chang, Nick Ramirez
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>
---
RedfishPkg/RedfishPkg.dec | 7 +-
RedfishPkg/RedfishComponents.dsc.inc | 3 +-
RedfishPkg/RedfishPkg.dsc | 2 +
RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf | 73 +
RedfishPkg/RedfishHttpDxe/RedfishHttpData.h | 256 ++++
RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h | 44 +
.../RedfishHttpDxe/RedfishHttpOperation.h | 76 +
RedfishPkg/RedfishHttpDxe/RedfishHttpData.c | 667 ++++++++
RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c | 1344 +++++++++++++++++
.../RedfishHttpDxe/RedfishHttpOperation.c | 693 +++++++++
RedfishPkg/Redfish.fdf.inc | 3 +-
11 files changed, 3164 insertions(+), 4 deletions(-)
create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
diff --git a/RedfishPkg/RedfishPkg.dec b/RedfishPkg/RedfishPkg.dec
index 9b424efdf3..114f8d2ad8 100644
--- a/RedfishPkg/RedfishPkg.dec
+++ b/RedfishPkg/RedfishPkg.dec
@@ -157,8 +157,11 @@
# set to EFI_REST_EX_PROTOCOL.
#
gEfiRedfishPkgTokenSpaceGuid.PcdRedfishSendReceiveTimeout|5000|UINT32|0x00001009
- ## This is used to enable HTTP content encoding on Redfish communication.
- gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|TRUE|BOOLEAN|0x0000100A
+ #
+ # This PCD string is introduced for platform developer to set the encoding method supported by BMC Redfish.
+ # Currently only "None" and "gzip" are supported.
+ #
+ gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|"None"|VOID*|0x0000100A
#
# Use below PCDs to control Redfhs HTTP protocol.
#
diff --git a/RedfishPkg/RedfishComponents.dsc.inc b/RedfishPkg/RedfishComponents.dsc.inc
index 464ffc8606..d6c5b73d7f 100644
--- a/RedfishPkg/RedfishComponents.dsc.inc
+++ b/RedfishPkg/RedfishComponents.dsc.inc
@@ -7,7 +7,7 @@
# "RedfishDefines.dsc.inc".
#
# (C) Copyright 2020-2021 Hewlett Packard Enterprise Development LP<BR>
-# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
@@ -28,4 +28,5 @@
RedfishPkg/RedfishConfigHandler/RedfishConfigHandlerDriver.inf
RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf
MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf
+ RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
!endif
diff --git a/RedfishPkg/RedfishPkg.dsc b/RedfishPkg/RedfishPkg.dsc
index 25ed193182..5849e7cf9e 100644
--- a/RedfishPkg/RedfishPkg.dsc
+++ b/RedfishPkg/RedfishPkg.dsc
@@ -45,6 +45,8 @@
UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf
RedfishPlatformCredentialLib|RedfishPkg/Library/PlatformCredentialLibNull/PlatformCredentialLibNull.inf
RedfishContentCodingLib|RedfishPkg/Library/RedfishContentCodingLibNull/RedfishContentCodingLibNull.inf
+ ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
+ SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf
# NULL instance of IPMI related library.
IpmiLib|MdeModulePkg/Library/BaseIpmiLibNull/BaseIpmiLibNull.inf
diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
new file mode 100644
index 0000000000..c7dfdffacf
--- /dev/null
+++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
@@ -0,0 +1,73 @@
+## @file
+# RedfishHttpDxe is the DXE driver which provides
+# EdkIIRedfishHttpProtocol to EDK2 Redfish Feature
+# drivers for HTTP operation.
+#
+# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x0001000b
+ BASE_NAME = RedfishHttpDxe
+ FILE_GUID = 85ADB2F1-DA93-47D4-AF4F-3D920D9BD2C0
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = RedfishHttpEntryPoint
+ UNLOAD_IMAGE = RedfishHttpDriverUnload
+
+#
+# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 RISCV64
+#
+
+[Sources]
+ RedfishHttpData.c
+ RedfishHttpData.h
+ RedfishHttpDxe.c
+ RedfishHttpDxe.h
+ RedfishHttpOperation.c
+ RedfishHttpOperation.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ NetworkPkg/NetworkPkg.dec
+ RedfishPkg/RedfishPkg.dec
+
+[LibraryClasses.ARM]
+ ArmSoftFloatLib
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ RedfishContentCodingLib
+ DebugLib
+ HttpLib
+ JsonLib
+ MemoryAllocationLib
+ PrintLib
+ RedfishDebugLib
+ ReportStatusCodeLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiLib
+
+[Protocols]
+ gEdkIIRedfishHttpProtocolGuid ## PRODUCED
+ gEdkIIRedfishCredentialProtocolGuid ## CONSUMES
+ gEfiRestExProtocolGuid ## CONSUEMS
+
+[Pcd]
+ gEfiRedfishPkgTokenSpaceGuid.PcdHttpGetRetry
+ gEfiRedfishPkgTokenSpaceGuid.PcdHttpPutRetry
+ gEfiRedfishPkgTokenSpaceGuid.PcdHttpPatchRetry
+ gEfiRedfishPkgTokenSpaceGuid.PcdHttpPostRetry
+ gEfiRedfishPkgTokenSpaceGuid.PcdHttpDeleteRetry
+ gEfiRedfishPkgTokenSpaceGuid.PcdHttpRetryWaitInSecond
+ gEfiRedfishPkgTokenSpaceGuid.PcdHttpCacheDisabled
+ gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding
+
+[Depex]
+ TRUE
diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpData.h b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
new file mode 100644
index 0000000000..6be610142e
--- /dev/null
+++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
@@ -0,0 +1,256 @@
+/** @file
+ Definitions of RedfishHttpData
+
+ Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef EDKII_REDFISH_HTTP_DATA_H_
+#define EDKII_REDFISH_HTTP_DATA_H_
+
+#include "RedfishHttpDxe.h"
+
+#define REDFISH_HTTP_DRIVER_SIGNATURE SIGNATURE_32 ('r', 'f', 'h', 'p')
+#define REDFISH_HTTP_CACHE_SIGNATURE SIGNATURE_32 ('r', 'f', 'c', 'h')
+#define REDFISH_HTTP_SERVICE_SIGNATURE SIGNATURE_32 ('r', 'f', 's', 'v')
+#define REDFISH_HTTP_PAYLOAD_SIGNATURE SIGNATURE_32 ('r', 'f', 'p', 'l')
+#define REDFISH_HTTP_BASIC_AUTH_STR "Basic "
+
+///
+/// REDFISH_SERVICE_PRIVATE definition.
+///
+typedef struct {
+ UINT32 Signature;
+ CHAR8 *Host;
+ CHAR8 *HostName;
+ CHAR8 *BasicAuth;
+ CHAR8 *SessionToken;
+ EFI_REST_EX_PROTOCOL *RestEx;
+} REDFISH_SERVICE_PRIVATE;
+
+///
+/// REDFISH_PAYLOAD_PRIVATE definition.
+///
+typedef struct {
+ UINT32 Signature;
+ REDFISH_SERVICE_PRIVATE *Service;
+ EDKII_JSON_VALUE JsonValue;
+} REDFISH_PAYLOAD_PRIVATE;
+
+///
+/// Definition of REDFISH_HTTP_CACHE_DATA
+///
+typedef struct {
+ UINT32 Signature;
+ LIST_ENTRY List;
+ EFI_STRING Uri;
+ UINTN HitCount;
+ REDFISH_RESPONSE *Response;
+} REDFISH_HTTP_CACHE_DATA;
+
+#define REDFISH_HTTP_CACHE_FROM_LIST(a) CR (a, REDFISH_HTTP_CACHE_DATA, List, REDFISH_HTTP_CACHE_SIGNATURE)
+
+///
+/// Definition of REDFISH_HTTP_CACHE_LIST
+///
+typedef struct {
+ LIST_ENTRY Head;
+ UINTN Count;
+ UINTN Capacity;
+} REDFISH_HTTP_CACHE_LIST;
+
+///
+/// Definition of REDFISH_HTTP_RETRY_SETTING
+///
+typedef struct {
+ UINT16 MaximumRetryGet;
+ UINT16 MaximumRetryPut;
+ UINT16 MaximumRetryPost;
+ UINT16 MaximumRetryPatch;
+ UINT16 MaximumRetryDelete;
+ UINTN RetryWait;
+} REDFISH_HTTP_RETRY_SETTING;
+
+///
+/// Definition of REDFISH_HTTP_CACHE_PRIVATE
+///
+typedef struct {
+ UINT32 Signature;
+ EFI_HANDLE ImageHandle;
+ BOOLEAN CacheDisabled;
+ EFI_EVENT NotifyEvent;
+ REDFISH_HTTP_CACHE_LIST CacheList;
+ EDKII_REDFISH_HTTP_PROTOCOL Protocol;
+ EDKII_REDFISH_CREDENTIAL_PROTOCOL *CredentialProtocol;
+ REDFISH_HTTP_RETRY_SETTING RetrySetting;
+} REDFISH_HTTP_CACHE_PRIVATE;
+
+#define REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS(a) CR (a, REDFISH_HTTP_CACHE_PRIVATE, Protocol, REDFISH_HTTP_DRIVER_SIGNATURE)
+
+/**
+ Search on given ListHeader for given URI string.
+
+ @param[in] ListHeader Target list to search.
+ @param[in] Uri Target URI to search.
+
+ @retval REDFISH_HTTP_CACHE_DATA Target cache data is found.
+ @retval NULL No cache data with given URI is found.
+
+**/
+REDFISH_HTTP_CACHE_DATA *
+FindHttpCacheData (
+ IN LIST_ENTRY *ListHeader,
+ IN EFI_STRING Uri
+ );
+
+/**
+ This function copy the data in SrcResponse to DstResponse.
+
+ @param[in] SrcResponse Source Response to copy.
+ @param[out] DstResponse Destination Response.
+
+ @retval EFI_SUCCESS Response is copied successfully.
+ @retval Others Error occurs.
+
+**/
+EFI_STATUS
+CopyRedfishResponse (
+ IN REDFISH_RESPONSE *SrcResponse,
+ OUT REDFISH_RESPONSE *DstResponse
+ );
+
+/**
+ Release all cache from list.
+
+ @param[in] CacheList The list to be released.
+
+ @retval EFI_SUCCESS All cache data are released.
+ @retval EFI_INVALID_PARAMETER CacheList is NULL.
+
+**/
+EFI_STATUS
+ReleaseCacheList (
+ IN REDFISH_HTTP_CACHE_LIST *CacheList
+ );
+
+/**
+ Add new cache by given URI and HTTP response to specify List.
+
+ @param[in] List Target cache list to add.
+ @param[in] Uri The URI string matching to this cache data.
+ @param[in] Response HTTP response.
+
+ @retval EFI_SUCCESS Cache data is added.
+ @retval Others Fail to add cache data.
+
+**/
+EFI_STATUS
+AddHttpCacheData (
+ IN REDFISH_HTTP_CACHE_LIST *List,
+ IN EFI_STRING Uri,
+ IN REDFISH_RESPONSE *Response
+ );
+
+/**
+ Delete a cache data by given cache instance.
+
+ @param[in] List Target cache list to be removed.
+ @param[in] Data Pointer to the instance to be deleted.
+
+ @retval EFI_SUCCESS Cache data is removed.
+ @retval Others Fail to remove cache data.
+
+**/
+EFI_STATUS
+DeleteHttpCacheData (
+ IN REDFISH_HTTP_CACHE_LIST *List,
+ IN REDFISH_HTTP_CACHE_DATA *Data
+ );
+
+/**
+ This function release Redfish Payload.
+
+ @param[in] Payload Pointer to payload instance.
+
+ @retval EFI_SUCCESS Payload is released.
+ @retval Others Error occurs.
+
+**/
+EFI_STATUS
+ReleaseRedfishPayload (
+ IN REDFISH_PAYLOAD_PRIVATE *Payload
+ );
+
+/**
+ This function creat new payload. Server and JsonObj are
+ copied to newly created payload.
+
+ @param[in] Service Pointer to Service instance.
+ @param[in] JsonObj Pointer to JSON object.
+
+ @retval REDFISH_PAYLOAD_PRIVATE Newly created payload.
+ @retval NULL Error occurs.
+
+**/
+REDFISH_PAYLOAD_PRIVATE *
+CreateRedfishPayload (
+ IN REDFISH_SERVICE_PRIVATE *Service,
+ IN EDKII_JSON_VALUE JsonValue
+ );
+
+/**
+ This function release Redfish Service.
+
+ @param[in] Service Pointer to service instance.
+
+ @retval EFI_SUCCESS Service is released.
+ @retval Others Error occurs.
+
+**/
+EFI_STATUS
+ReleaseRedfishService (
+ IN REDFISH_SERVICE_PRIVATE *Service
+ );
+
+/**
+ This function creat new service. Host and HostName are copied to
+ newly created service instance.
+
+ @param[in] Host Host string.
+ @param[in] HostName Hostname string.
+ @param[in] BasicAuth Basic Authorization string.
+ @param[in] SessionToken Session token string.
+ @param[in] RestEx Rest EX protocol instance.
+
+ @retval REDFISH_PAYLOAD_PRIVATE Newly created service.
+ @retval NULL Error occurs.
+
+**/
+REDFISH_SERVICE_PRIVATE *
+CreateRedfishService (
+ IN CHAR8 *Host,
+ IN CHAR8 *HostName,
+ IN CHAR8 *BasicAuth OPTIONAL,
+ IN CHAR8 *SessionToken OPTIONAL,
+ IN EFI_REST_EX_PROTOCOL *RestEx
+ );
+
+/**
+ This function update session token in Redfish Service.
+
+ @param[in] Service Pointer to service instance.
+ @param[in] Token Session token.
+
+ @retval EFI_SUCCESS Session token is updated.
+ @retval Others Error occurs.
+
+**/
+EFI_STATUS
+UpdateSessionToken (
+ IN REDFISH_SERVICE_PRIVATE *Service,
+ IN CHAR8 *Token
+ );
+
+#endif
diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
new file mode 100644
index 0000000000..cf6ba9cb47
--- /dev/null
+++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
@@ -0,0 +1,44 @@
+/** @file
+ Definitions of RedfishHttpDxe
+
+ Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef EDKII_REDFISH_HTTP_DXE_H_
+#define EDKII_REDFISH_HTTP_DXE_H_
+
+#include <Uefi.h>
+#include <IndustryStandard/Http11.h>
+
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/RedfishContentCodingLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HttpLib.h>
+#include <Library/JsonLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/RedfishDebugLib.h>
+#include <Library/ReportStatusCodeLib.h>
+#include <Library/PrintLib.h>
+
+#include <Protocol/Http.h>
+#include <Protocol/EdkIIRedfishHttpProtocol.h>
+#include <Protocol/EdkIIRedfishCredential.h>
+#include <Protocol/RestEx.h>
+
+#define IS_EMPTY_STRING(a) ((a) == NULL || (a)[0] == '\0')
+#define REDFISH_HTTP_CACHE_LIST_SIZE 0x80
+#define REDFISH_ERROR_MSG_MAX 128
+#define REDFISH_DEBUG_STRING_LENGTH 200
+#define REDFISH_HOST_NAME_MAX 64 // IPv6 maximum length (39) + "https://" (8) + port number (maximum 5)
+#define REDFISH_HTTP_ERROR_REPORT "Redfish HTTP %a failure(0x%x): %s"
+#define REDFISH_HTTP_CACHE_DEBUG DEBUG_MANAGEABILITY
+#define REDFISH_HTTP_CACHE_DEBUG_DUMP DEBUG_MANAGEABILITY
+#define REDFISH_HTTP_CACHE_DEBUG_REQUEST DEBUG_MANAGEABILITY
+
+#endif
diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
new file mode 100644
index 0000000000..e4fc053e96
--- /dev/null
+++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
@@ -0,0 +1,76 @@
+/** @file
+ Definitions of RedfishHttpOperation
+
+ Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef EDKII_REDFISH_HTTP_OP_H_
+#define EDKII_REDFISH_HTTP_OP_H_
+
+#include "RedfishHttpDxe.h"
+
+#define REDFISH_CONTENT_LENGTH_SIZE 80
+#define REDFISH_COMMON_HEADER_SIZE 5
+#define REDFISH_HTTP_HEADER_ODATA_VERSION_STR "OData-Version"
+#define REDFISH_HTTP_HEADER_ODATA_VERSION_VALUE "4.0"
+#define REDFISH_HTTP_HEADER_USER_AGENT_VALUE "edk2redfish"
+#define REDFISH_HTTP_HEADER_CONNECTION_STR "Connection"
+#define REDFISH_HTTP_HEADER_CONNECTION_VALUE "Keep-Alive"
+#define REDFISH_HTTP_CONTENT_ENCODING_NONE "None"
+
+/**
+ 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 Resrouce is released successfully.
+ @retval Others Errors occur.
+
+**/
+EFI_STATUS
+ReleaseRedfishRequest (
+ IN REDFISH_REQUEST *Request
+ );
+
+/**
+ This function free resources in given Response.
+
+ @param[in] Response HTTP response to be released.
+
+ @retval EFI_SUCCESS Resrouce is released successfully.
+ @retval Others Errors occur.
+
+**/
+EFI_STATUS
+ReleaseRedfishResponse (
+ IN REDFISH_RESPONSE *Response
+ );
+
+/**
+ 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
+ );
+
+#endif
diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpData.c b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
new file mode 100644
index 0000000000..bf95e9f8d4
--- /dev/null
+++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
@@ -0,0 +1,667 @@
+/** @file
+ RedfishHttpData handles internal data to support Redfish HTTP protocol.
+
+ Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "RedfishHttpData.h"
+#include "RedfishHttpOperation.h"
+
+/**
+ This function update session token in Redfish Service.
+
+ @param[in] Service Pointer to service instance.
+ @param[in] Token Session token.
+
+ @retval EFI_SUCCESS Session token is updated.
+ @retval Others Error occurs.
+
+**/
+EFI_STATUS
+UpdateSessionToken (
+ IN REDFISH_SERVICE_PRIVATE *Service,
+ IN CHAR8 *Token
+ )
+{
+ if ((Service == NULL) || IS_EMPTY_STRING (Token)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Service->SessionToken != NULL) {
+ FreePool (Service->SessionToken);
+ }
+
+ Service->SessionToken = AllocateCopyPool (AsciiStrSize (Token), Token);
+ if (Service->SessionToken == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function release Redfish Service.
+
+ @param[in] Service Pointer to service instance.
+
+ @retval EFI_SUCCESS Service is released.
+ @retval Others Error occurs.
+
+**/
+EFI_STATUS
+ReleaseRedfishService (
+ IN REDFISH_SERVICE_PRIVATE *Service
+ )
+{
+ if (Service == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Service->Host != NULL) {
+ FreePool (Service->Host);
+ }
+
+ if (Service->HostName != NULL) {
+ FreePool (Service->HostName);
+ }
+
+ if (Service->BasicAuth != NULL) {
+ ZeroMem (Service->BasicAuth, AsciiStrSize (Service->BasicAuth));
+ FreePool (Service->BasicAuth);
+ }
+
+ if (Service->SessionToken != NULL) {
+ ZeroMem (Service->SessionToken, AsciiStrSize (Service->SessionToken));
+ FreePool (Service->SessionToken);
+ }
+
+ FreePool (Service);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function creat new service. Host and HostName are copied to
+ newly created service instance.
+
+ @param[in] Host Host string.
+ @param[in] HostName Hostname string.
+ @param[in] BasicAuth Basic Authorization string.
+ @param[in] SessionToken Session token string.
+ @param[in] RestEx Rest EX protocol instance.
+
+ @retval REDFISH_PAYLOAD_PRIVATE Newly created service.
+ @retval NULL Error occurs.
+
+**/
+REDFISH_SERVICE_PRIVATE *
+CreateRedfishService (
+ IN CHAR8 *Host,
+ IN CHAR8 *HostName,
+ IN CHAR8 *BasicAuth OPTIONAL,
+ IN CHAR8 *SessionToken OPTIONAL,
+ IN EFI_REST_EX_PROTOCOL *RestEx
+ )
+{
+ REDFISH_SERVICE_PRIVATE *NewService;
+ UINTN AuthStrSize;
+
+ if (IS_EMPTY_STRING (Host) || IS_EMPTY_STRING (HostName) || (RestEx == NULL)) {
+ return NULL;
+ }
+
+ NewService = AllocateZeroPool (sizeof (REDFISH_SERVICE_PRIVATE));
+ if (NewService == NULL) {
+ return NULL;
+ }
+
+ NewService->Signature = REDFISH_HTTP_SERVICE_SIGNATURE;
+ NewService->Host = AllocateCopyPool (AsciiStrSize (Host), Host);
+ if (NewService->Host == NULL) {
+ goto ON_ERROR;
+ }
+
+ NewService->HostName = AllocateCopyPool (AsciiStrSize (HostName), HostName);
+ if (NewService->HostName == NULL) {
+ goto ON_ERROR;
+ }
+
+ if (!IS_EMPTY_STRING (BasicAuth)) {
+ AuthStrSize = AsciiStrSize (BasicAuth) + AsciiStrLen (REDFISH_HTTP_BASIC_AUTH_STR);
+ NewService->BasicAuth = AllocateZeroPool (AuthStrSize);
+ if (NewService->BasicAuth == NULL) {
+ goto ON_ERROR;
+ }
+
+ AsciiSPrint (NewService->BasicAuth, AuthStrSize, "%a%a", REDFISH_HTTP_BASIC_AUTH_STR, BasicAuth);
+ }
+
+ if (!IS_EMPTY_STRING (SessionToken)) {
+ NewService->SessionToken = AllocateCopyPool (AsciiStrSize (SessionToken), SessionToken);
+ if (NewService->SessionToken == NULL) {
+ goto ON_ERROR;
+ }
+ }
+
+ NewService->RestEx = RestEx;
+
+ return NewService;
+
+ON_ERROR:
+
+ ReleaseRedfishService (NewService);
+
+ return NULL;
+}
+
+/**
+ This function release Redfish Payload.
+
+ @param[in] Payload Pointer to payload instance.
+
+ @retval EFI_SUCCESS Payload is released.
+ @retval Others Error occurs.
+
+**/
+EFI_STATUS
+ReleaseRedfishPayload (
+ IN REDFISH_PAYLOAD_PRIVATE *Payload
+ )
+{
+ if (Payload == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Payload->Service != NULL) {
+ ReleaseRedfishService (Payload->Service);
+ }
+
+ if (Payload->JsonValue != NULL) {
+ JsonValueFree (Payload->JsonValue);
+ }
+
+ FreePool (Payload);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function creat new payload. Server and JsonObj are
+ copied to newly created payload.
+
+ @param[in] Service Pointer to Service instance.
+ @param[in] JsonValue Pointer to JSON value.
+
+ @retval REDFISH_PAYLOAD_PRIVATE Newly created payload.
+ @retval NULL Error occurs.
+
+**/
+REDFISH_PAYLOAD_PRIVATE *
+CreateRedfishPayload (
+ IN REDFISH_SERVICE_PRIVATE *Service,
+ IN EDKII_JSON_VALUE JsonValue
+ )
+{
+ REDFISH_PAYLOAD_PRIVATE *NewPayload;
+
+ if ((Service == NULL) || (JsonValue == NULL)) {
+ return NULL;
+ }
+
+ NewPayload = AllocateZeroPool (sizeof (REDFISH_PAYLOAD_PRIVATE));
+ if (NewPayload == NULL) {
+ return NULL;
+ }
+
+ NewPayload->Signature = REDFISH_HTTP_PAYLOAD_SIGNATURE;
+ NewPayload->Service = CreateRedfishService (Service->Host, Service->HostName, Service->BasicAuth, Service->SessionToken, Service->RestEx);
+ if (NewPayload->Service == NULL) {
+ goto ON_ERROR;
+ }
+
+ NewPayload->JsonValue = JsonValueClone (JsonValue);
+ if (NewPayload->JsonValue == NULL) {
+ goto ON_ERROR;
+ }
+
+ return NewPayload;
+
+ON_ERROR:
+
+ ReleaseRedfishPayload (NewPayload);
+
+ return NULL;
+}
+
+/**
+ This function copy the data in SrcResponse to DstResponse.
+
+ @param[in] SrcResponse Source Response to copy.
+ @param[out] DstResponse Destination Response.
+
+ @retval EFI_SUCCESS Response is copied successfully.
+ @retval Others Error occurs.
+
+**/
+EFI_STATUS
+CopyRedfishResponse (
+ IN REDFISH_RESPONSE *SrcResponse,
+ OUT REDFISH_RESPONSE *DstResponse
+ )
+{
+ REDFISH_PAYLOAD_PRIVATE *Payload;
+ UINTN Index;
+
+ if ((SrcResponse == NULL) || (DstResponse == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (SrcResponse == DstResponse) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Status code
+ //
+ if (SrcResponse->StatusCode != NULL) {
+ DstResponse->StatusCode = AllocateCopyPool (sizeof (EFI_HTTP_STATUS_CODE), SrcResponse->StatusCode);
+ if (DstResponse->StatusCode == NULL) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // Header
+ //
+ if ((SrcResponse->HeaderCount > 0) && (SrcResponse->Headers != NULL)) {
+ DstResponse->HeaderCount = 0;
+ DstResponse->Headers = AllocateZeroPool (sizeof (EFI_HTTP_HEADER) * SrcResponse->HeaderCount);
+ if (DstResponse->Headers == NULL) {
+ goto ON_ERROR;
+ }
+
+ for (Index = 0; Index < SrcResponse->HeaderCount; Index++) {
+ DstResponse->Headers[Index].FieldName = AllocateCopyPool (AsciiStrSize (SrcResponse->Headers[Index].FieldName), SrcResponse->Headers[Index].FieldName);
+ if (DstResponse->Headers[Index].FieldName == NULL) {
+ goto ON_ERROR;
+ }
+
+ DstResponse->Headers[Index].FieldValue = AllocateCopyPool (AsciiStrSize (SrcResponse->Headers[Index].FieldValue), SrcResponse->Headers[Index].FieldValue);
+ if (DstResponse->Headers[Index].FieldValue == NULL) {
+ goto ON_ERROR;
+ }
+
+ DstResponse->HeaderCount += 1;
+ }
+ }
+
+ //
+ // Payload
+ //
+ if (SrcResponse->Payload != NULL) {
+ Payload = (REDFISH_PAYLOAD_PRIVATE *)SrcResponse->Payload;
+ if (Payload->Signature != REDFISH_HTTP_PAYLOAD_SIGNATURE) {
+ DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
+ goto ON_ERROR;
+ }
+
+ DstResponse->Payload = CreateRedfishPayload (Payload->Service, Payload->JsonValue);
+ if (DstResponse->Payload == NULL) {
+ goto ON_ERROR;
+ }
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ ReleaseRedfishResponse (DstResponse);
+
+ return EFI_OUT_OF_RESOURCES;
+}
+
+/**
+ This function clone input response and return to caller
+
+ @param[in] Response Response to clone.
+
+ @retval REDFISH_RESPONSE * Response is cloned.
+ @retval NULL Errors occur.
+
+**/
+REDFISH_RESPONSE *
+CloneRedfishResponse (
+ IN REDFISH_RESPONSE *Response
+ )
+{
+ EFI_STATUS Status;
+ REDFISH_RESPONSE *NewResponse;
+
+ if (Response == NULL) {
+ return NULL;
+ }
+
+ NewResponse = AllocateZeroPool (sizeof (REDFISH_RESPONSE));
+ if (NewResponse == NULL) {
+ return NULL;
+ }
+
+ Status = CopyRedfishResponse (Response, NewResponse);
+ if (EFI_ERROR (Status)) {
+ FreePool (NewResponse);
+ return NULL;
+ }
+
+ return NewResponse;
+}
+
+/**
+ Release REDFISH_HTTP_CACHE_DATA resource
+
+ @param[in] Data Pointer to REDFISH_HTTP_CACHE_DATA instance
+
+ @retval EFI_SUCCESS REDFISH_HTTP_CACHE_DATA is released successfully.
+ @retval EFI_INVALID_PARAMETER Data is NULL
+
+**/
+EFI_STATUS
+ReleaseHttpCacheData (
+ IN REDFISH_HTTP_CACHE_DATA *Data
+ )
+{
+ if (Data == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Data->Uri != NULL) {
+ FreePool (Data->Uri);
+ }
+
+ if (Data->Response != NULL) {
+ ReleaseRedfishResponse (Data->Response);
+ FreePool (Data->Response);
+ }
+
+ FreePool (Data);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Create new cache data.
+
+ @param[in] Uri The URI string matching to this cache data.
+ @param[in] Response HTTP response.
+
+ @retval REDFISH_HTTP_CACHE_DATA * Pointer to newly created cache data.
+ @retval NULL No memory available.
+
+**/
+REDFISH_HTTP_CACHE_DATA *
+NewHttpCacheData (
+ IN EFI_STRING Uri,
+ IN REDFISH_RESPONSE *Response
+ )
+{
+ REDFISH_HTTP_CACHE_DATA *NewData;
+ UINTN Size;
+
+ if (IS_EMPTY_STRING (Uri) || (Response == NULL)) {
+ return NULL;
+ }
+
+ NewData = AllocateZeroPool (sizeof (REDFISH_HTTP_CACHE_DATA));
+ if (NewData == NULL) {
+ return NULL;
+ }
+
+ NewData->Signature = REDFISH_HTTP_CACHE_SIGNATURE;
+ Size = StrSize (Uri);
+ NewData->Uri = AllocateCopyPool (Size, Uri);
+ if (NewData->Uri == NULL) {
+ goto ON_ERROR;
+ }
+
+ NewData->Response = Response;
+ NewData->HitCount = 1;
+
+ return NewData;
+
+ON_ERROR:
+
+ if (NewData != NULL) {
+ ReleaseHttpCacheData (NewData);
+ }
+
+ return NULL;
+}
+
+/**
+ Search on given ListHeader for given URI string.
+
+ @param[in] ListHeader Target list to search.
+ @param[in] Uri Target URI to search.
+
+ @retval REDFISH_HTTP_CACHE_DATA Target cache data is found.
+ @retval NULL No cache data with given URI is found.
+
+**/
+REDFISH_HTTP_CACHE_DATA *
+FindHttpCacheData (
+ IN LIST_ENTRY *ListHeader,
+ IN EFI_STRING Uri
+ )
+{
+ LIST_ENTRY *List;
+ REDFISH_HTTP_CACHE_DATA *Data;
+
+ if (IS_EMPTY_STRING (Uri)) {
+ return NULL;
+ }
+
+ if (IsListEmpty (ListHeader)) {
+ return NULL;
+ }
+
+ Data = NULL;
+ List = GetFirstNode (ListHeader);
+ while (!IsNull (ListHeader, List)) {
+ Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
+
+ if (StrCmp (Data->Uri, Uri) == 0) {
+ return Data;
+ }
+
+ List = GetNextNode (ListHeader, List);
+ }
+
+ return NULL;
+}
+
+/**
+ Search on given ListHeader and return cache data with minimum hit count.
+
+ @param[in] ListHeader Target list to search.
+
+ @retval REDFISH_HTTP_CACHE_DATA Target cache data is returned.
+ @retval NULL No cache data is found.
+
+**/
+REDFISH_HTTP_CACHE_DATA *
+FindUnusedHttpCacheData (
+ IN LIST_ENTRY *ListHeader
+ )
+{
+ LIST_ENTRY *List;
+ REDFISH_HTTP_CACHE_DATA *Data;
+ REDFISH_HTTP_CACHE_DATA *UnusedData;
+ UINTN HitCount;
+
+ if (IsListEmpty (ListHeader)) {
+ return NULL;
+ }
+
+ Data = NULL;
+ UnusedData = NULL;
+ HitCount = 0;
+
+ List = GetFirstNode (ListHeader);
+ Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
+ UnusedData = Data;
+ HitCount = Data->HitCount;
+ List = GetNextNode (ListHeader, List);
+
+ while (!IsNull (ListHeader, List)) {
+ Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
+
+ if (Data->HitCount < HitCount) {
+ HitCount = Data->HitCount;
+ UnusedData = Data;
+ }
+
+ List = GetNextNode (ListHeader, List);
+ }
+
+ return UnusedData;
+}
+
+/**
+ Delete a cache data by given cache instance.
+
+ @param[in] List Target cache list to be removed.
+ @param[in] Data Pointer to the instance to be deleted.
+
+ @retval EFI_SUCCESS Cache data is removed.
+ @retval Others Fail to remove cache data.
+
+**/
+EFI_STATUS
+DeleteHttpCacheData (
+ IN REDFISH_HTTP_CACHE_LIST *List,
+ IN REDFISH_HTTP_CACHE_DATA *Data
+ )
+{
+ if ((List == NULL) || (Data == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: delete: %s\n", __func__, Data->Uri));
+
+ RemoveEntryList (&Data->List);
+ --List->Count;
+
+ return ReleaseHttpCacheData (Data);
+}
+
+/**
+ Add new cache by given URI and HTTP response to specify List.
+
+ @param[in] List Target cache list to add.
+ @param[in] Uri The URI string matching to this cache data.
+ @param[in] Response HTTP response.
+
+ @retval EFI_SUCCESS Cache data is added.
+ @retval Others Fail to add cache data.
+
+**/
+EFI_STATUS
+AddHttpCacheData (
+ IN REDFISH_HTTP_CACHE_LIST *List,
+ IN EFI_STRING Uri,
+ IN REDFISH_RESPONSE *Response
+ )
+{
+ REDFISH_HTTP_CACHE_DATA *NewData;
+ REDFISH_HTTP_CACHE_DATA *OldData;
+ REDFISH_HTTP_CACHE_DATA *UnusedData;
+ REDFISH_RESPONSE *NewResponse;
+
+ if ((List == NULL) || IS_EMPTY_STRING (Uri) || (Response == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // If same cache data exist, replace it with latest one.
+ //
+ OldData = FindHttpCacheData (&List->Head, Uri);
+ if (OldData != NULL) {
+ DeleteHttpCacheData (List, OldData);
+ }
+
+ //
+ // Check capacity
+ //
+ if (List->Count >= List->Capacity) {
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: list is full and retire unused cache\n", __func__));
+ UnusedData = FindUnusedHttpCacheData (&List->Head);
+ if (UnusedData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DeleteHttpCacheData (List, UnusedData);
+ }
+
+ //
+ // Clone a local copy
+ //
+ NewResponse = CloneRedfishResponse (Response);
+ if (NewResponse == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NewData = NewHttpCacheData (Uri, NewResponse);
+ if (NewData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InsertTailList (&List->Head, &NewData->List);
+ ++List->Count;
+
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache(%d/%d) %s\n", __func__, List->Count, List->Capacity, NewData->Uri));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Release all cache from list.
+
+ @param[in] CacheList The list to be released.
+
+ @retval EFI_SUCCESS All cache data are released.
+ @retval EFI_INVALID_PARAMETER CacheList is NULL.
+
+**/
+EFI_STATUS
+ReleaseCacheList (
+ IN REDFISH_HTTP_CACHE_LIST *CacheList
+ )
+{
+ LIST_ENTRY *List;
+ LIST_ENTRY *Next;
+ REDFISH_HTTP_CACHE_DATA *Data;
+
+ if (CacheList == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (IsListEmpty (&CacheList->Head)) {
+ return EFI_SUCCESS;
+ }
+
+ Data = NULL;
+ Next = NULL;
+ List = GetFirstNode (&CacheList->Head);
+ while (!IsNull (&CacheList->Head, List)) {
+ Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
+ Next = GetNextNode (&CacheList->Head, List);
+
+ DeleteHttpCacheData (CacheList, Data);
+
+ List = Next;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
new file mode 100644
index 0000000000..39958d4865
--- /dev/null
+++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
@@ -0,0 +1,1344 @@
+/** @file
+ RedfishHttpDxe produces EdkIIRedfishHttpProtocol
+ for EDK2 Redfish Feature driver to do HTTP operations.
+
+ Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "RedfishHttpDxe.h"
+#include "RedfishHttpData.h"
+#include "RedfishHttpOperation.h"
+
+REDFISH_HTTP_CACHE_PRIVATE *mRedfishHttpCachePrivate = NULL;
+
+/**
+ Debug output the cache list.
+
+ @param[in] Msg Debug message string.
+ @param[in] ErrorLevel Output error level.
+ @param[in] CacheList Target list to dump.
+
+ @retval EFI_SUCCESS Debug dump finished.
+ @retval EFI_INVALID_PARAMETER HttpCacheList is NULL.
+
+**/
+EFI_STATUS
+DebugPrintHttpCacheList (
+ IN CONST CHAR8 *Msg,
+ IN UINTN ErrorLevel,
+ IN REDFISH_HTTP_CACHE_LIST *CacheList
+ )
+{
+ LIST_ENTRY *List;
+ REDFISH_HTTP_CACHE_DATA *Data;
+ UINTN Index;
+
+ if (CacheList == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IS_EMPTY_STRING (Msg)) {
+ DEBUG ((ErrorLevel, "%a\n", Msg));
+ }
+
+ if (IsListEmpty (&CacheList->Head)) {
+ DEBUG ((ErrorLevel, "list is empty\n"));
+ return EFI_NOT_FOUND;
+ }
+
+ DEBUG ((ErrorLevel, "list count: %d capacity: %d\n", CacheList->Count, CacheList->Capacity));
+ Data = NULL;
+ Index = 0;
+ List = GetFirstNode (&CacheList->Head);
+ while (!IsNull (&CacheList->Head, List)) {
+ Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
+
+ DEBUG ((ErrorLevel, "%d) Uri: %s Hit: %d\n", ++Index, Data->Uri, Data->HitCount));
+
+ List = GetNextNode (&CacheList->Head, List);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+
+ Check HTTP status code to see if we like to retry HTTP request or not.
+
+ @param[in] StatusCode HTTP status code.
+
+ @retval BOOLEAN Return true when we like to retry request.
+ Return false when we don't want to retry request.
+
+**/
+BOOLEAN
+RedfishRetryRequired (
+ IN EFI_HTTP_STATUS_CODE *StatusCode
+ )
+{
+ if (StatusCode == NULL) {
+ return TRUE;
+ }
+
+ if ((*StatusCode == HTTP_STATUS_500_INTERNAL_SERVER_ERROR) ||
+ (*StatusCode == HTTP_STATUS_UNSUPPORTED_STATUS))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+
+ Convert Unicode string to ASCII string. It's call responsibility to release returned buffer.
+
+ @param[in] UnicodeStr Unicode string to convert.
+
+ @retval CHAR8 * ASCII string returned.
+ @retval NULL Errors occur.
+
+**/
+CHAR8 *
+StringUnicodeToAscii (
+ IN EFI_STRING UnicodeStr
+ )
+{
+ CHAR8 *AsciiStr;
+ UINTN AsciiStrSize;
+ EFI_STATUS Status;
+
+ if (IS_EMPTY_STRING (UnicodeStr)) {
+ return NULL;
+ }
+
+ AsciiStrSize = StrLen (UnicodeStr) + 1;
+ AsciiStr = AllocateZeroPool (AsciiStrSize);
+ if (AsciiStr == NULL) {
+ return NULL;
+ }
+
+ Status = UnicodeStrToAsciiStrS (UnicodeStr, AsciiStr, AsciiStrSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "UnicodeStrToAsciiStrS failed: %r\n", Status));
+ FreePool (AsciiStr);
+ return NULL;
+ }
+
+ return AsciiStr;
+}
+
+/**
+ Return HTTP method in ASCII string. Caller does not need
+ to free returned string buffer.
+
+ @param[in] Method HTTP method.
+
+ @retval CHAR8 * Method in string.
+**/
+CHAR8 *
+HttpMethodToString (
+ IN EFI_HTTP_METHOD Method
+ )
+{
+ switch (Method) {
+ case HttpMethodGet:
+ return HTTP_METHOD_GET;
+ break;
+ case HttpMethodPost:
+ return HTTP_METHOD_POST;
+ break;
+ case HttpMethodPatch:
+ return HTTP_METHOD_PATCH;
+ break;
+ case HttpMethodPut:
+ return HTTP_METHOD_PUT;
+ break;
+ case HttpMethodDelete:
+ return HTTP_METHOD_DELETE;
+ break;
+ default:
+ break;
+ }
+
+ return "Unknown";
+}
+
+/**
+ Report HTTP communication error via report status code.
+
+ @param[in] Method HTTP method.
+ @param[in] Uri The URI which has failure.
+ @param[in] HttpStatusCode HTTP status code.
+
+**/
+VOID
+ReportHttpError (
+ IN EFI_HTTP_METHOD Method,
+ IN EFI_STRING Uri,
+ IN EFI_HTTP_STATUS_CODE *HttpStatusCode OPTIONAL
+ )
+{
+ CHAR8 ErrorMsg[REDFISH_ERROR_MSG_MAX];
+
+ if (IS_EMPTY_STRING (Uri)) {
+ DEBUG ((DEBUG_ERROR, "%a: no URI to report error status\n", __func__));
+ return;
+ }
+
+ //
+ // Report failure of URI and HTTP status code.
+ //
+ AsciiSPrint (ErrorMsg, sizeof (ErrorMsg), REDFISH_HTTP_ERROR_REPORT, HttpMethodToString (Method), (HttpStatusCode == NULL ? HTTP_STATUS_UNSUPPORTED_STATUS : *HttpStatusCode), Uri);
+ DEBUG ((DEBUG_ERROR, "%a\n", ErrorMsg));
+ //
+ // TODO:
+ // Below PI status code is approved by PIWG and wait for specification published.
+ // We will uncomment below report status code after PI status code get published.
+ // REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4483
+ //
+ // REPORT_STATUS_CODE_WITH_EXTENDED_DATA (
+ // EFI_ERROR_CODE | EFI_ERROR_MAJOR,
+ // EFI_COMPUTING_UNIT_MANAGEABILITY | EFI_MANAGEABILITY_EC_REDFISH_COMMUNICATION_ERROR,
+ // ErrorMsg,
+ // AsciiStrSize (ErrorMsg)
+ // );
+}
+
+/**
+ This function create Redfish service. It's caller's responsibility to free returned
+ Redfish service by calling FreeService ().
+
+ @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+ @param[in] RedfishConfigServiceInfo Redfish config service information.
+
+ @retval REDFISH_SERVICE Redfish service is created.
+ @retval NULL Errors occur.
+
+**/
+REDFISH_SERVICE
+EFIAPI
+RedfishCreateRedfishService (
+ IN EDKII_REDFISH_HTTP_PROTOCOL *This,
+ IN REDFISH_CONFIG_SERVICE_INFORMATION *RedfishConfigServiceInfo
+ )
+{
+ EFI_STATUS Status;
+ REDFISH_HTTP_CACHE_PRIVATE *Private;
+ REDFISH_SERVICE_PRIVATE *NewService;
+ CHAR8 *AsciiLocation;
+ CHAR8 *Host;
+ CHAR8 *BasicAuthString;
+ UINTN BasicAuthStrSize;
+ CHAR8 *EncodedAuthString;
+ UINTN EncodedAuthStrSize;
+ EDKII_REDFISH_AUTH_METHOD AuthMethod;
+ CHAR8 *Username;
+ CHAR8 *Password;
+ UINTN UsernameSize;
+ UINTN PasswordSize;
+ EFI_REST_EX_PROTOCOL *RestEx;
+
+ if ((This == NULL) || (RedfishConfigServiceInfo == NULL)) {
+ return NULL;
+ }
+
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: service location: %s\n", __func__, RedfishConfigServiceInfo->RedfishServiceLocation));
+
+ Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
+ BasicAuthString = NULL;
+ EncodedAuthString = NULL;
+ Username = NULL;
+ Password = NULL;
+ NewService = NULL;
+ AsciiLocation = NULL;
+ Host = NULL;
+ BasicAuthStrSize = 0;
+ EncodedAuthStrSize = 0;
+ UsernameSize = 0;
+ PasswordSize = 0;
+
+ //
+ // Build host and host name from service location
+ //
+ if (!IS_EMPTY_STRING (RedfishConfigServiceInfo->RedfishServiceLocation)) {
+ AsciiLocation = StringUnicodeToAscii (RedfishConfigServiceInfo->RedfishServiceLocation);
+ if (AsciiLocation == NULL) {
+ goto ON_RELEASE;
+ }
+
+ Host = AllocateZeroPool (REDFISH_HOST_NAME_MAX);
+ if (AsciiLocation == NULL) {
+ goto ON_RELEASE;
+ }
+
+ if (RedfishConfigServiceInfo->RedfishServiceUseHttps) {
+ AsciiSPrint (Host, REDFISH_HOST_NAME_MAX, "https://%a", AsciiLocation);
+ } else {
+ AsciiSPrint (Host, REDFISH_HOST_NAME_MAX, "http://%a", AsciiLocation);
+ }
+
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Host: %a\n", __func__, Host));
+ }
+
+ //
+ // Find Rest Ex protocol
+ //
+ if (RedfishConfigServiceInfo->RedfishServiceRestExHandle != NULL) {
+ Status = gBS->HandleProtocol (
+ RedfishConfigServiceInfo->RedfishServiceRestExHandle,
+ &gEfiRestExProtocolGuid,
+ (VOID **)&RestEx
+ );
+ } else {
+ DEBUG ((DEBUG_ERROR, "%a: Rest Ex protocol is not available\n", __func__));
+ goto ON_RELEASE;
+ }
+
+ //
+ // Get credential
+ //
+ if (Private->CredentialProtocol == NULL) {
+ //
+ // No credential available on this system.
+ //
+ DEBUG ((DEBUG_WARN, "%a: no credential protocol available\n", __func__));
+ } else {
+ Status = Private->CredentialProtocol->GetAuthInfo (
+ Private->CredentialProtocol,
+ &AuthMethod,
+ &Username,
+ &Password
+ );
+ if (EFI_ERROR (Status) || IS_EMPTY_STRING (Username) || IS_EMPTY_STRING (Password)) {
+ DEBUG ((DEBUG_ERROR, "%a: cannot get authentication information: %r\n", __func__, Status));
+ goto ON_RELEASE;
+ } else {
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Auth method: 0x%x username: %a password: %a\n", __func__, AuthMethod, Username, Password));
+
+ //
+ // Perform base64 encoding (RFC 7617)
+ //
+ UsernameSize = AsciiStrSize (Username);
+ PasswordSize = AsciiStrSize (Password);
+ BasicAuthStrSize = UsernameSize + PasswordSize; // one byte taken from null-terminator for ':'
+ BasicAuthString = AllocateZeroPool (BasicAuthStrSize);
+ if (BasicAuthString == NULL) {
+ goto ON_RELEASE;
+ }
+
+ AsciiSPrint (
+ BasicAuthString,
+ BasicAuthStrSize,
+ "%a:%a",
+ Username,
+ Password
+ );
+
+ Status = Base64Encode (
+ (CONST UINT8 *)BasicAuthString,
+ BasicAuthStrSize,
+ EncodedAuthString,
+ &EncodedAuthStrSize
+ );
+ if ((Status == EFI_BUFFER_TOO_SMALL) && (EncodedAuthStrSize > 0)) {
+ EncodedAuthString = AllocateZeroPool (EncodedAuthStrSize);
+ if (EncodedAuthString == NULL) {
+ goto ON_RELEASE;
+ }
+
+ Status = Base64Encode (
+ (CONST UINT8 *)BasicAuthString,
+ BasicAuthStrSize,
+ EncodedAuthString,
+ &EncodedAuthStrSize
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Base64Encode failure: %r\n", __func__, Status));
+ }
+
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Basic authorization: %a\n", __func__, EncodedAuthString));
+ } else {
+ DEBUG ((DEBUG_ERROR, "%a: Base64Encode failure: %r\n", __func__, Status));
+ goto ON_RELEASE;
+ }
+ }
+ }
+
+ NewService = CreateRedfishService (Host, AsciiLocation, EncodedAuthString, NULL, RestEx);
+ if (NewService == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: CreateRedfishService\n", __func__));
+ }
+
+ON_RELEASE:
+
+ if (BasicAuthString != NULL) {
+ ZeroMem (BasicAuthString, BasicAuthStrSize);
+ FreePool (BasicAuthString);
+ }
+
+ if (EncodedAuthString != NULL) {
+ ZeroMem (BasicAuthString, EncodedAuthStrSize);
+ FreePool (EncodedAuthString);
+ }
+
+ if (Username != NULL) {
+ ZeroMem (Username, UsernameSize);
+ FreePool (Username);
+ }
+
+ if (Password != NULL) {
+ ZeroMem (Password, PasswordSize);
+ FreePool (Password);
+ }
+
+ if (AsciiLocation != NULL) {
+ FreePool (AsciiLocation);
+ }
+
+ if (Host != NULL) {
+ FreePool (Host);
+ }
+
+ return NewService;
+}
+
+/**
+ This function free resources in Redfish service. RedfishService is no longer available
+ after this function returns successfully.
+
+ @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+ @param[in] RedfishService Pointer to Redfish service to be released.
+
+ @retval EFI_SUCCESS Resrouce is released successfully.
+ @retval Others Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishFreeRedfishService (
+ IN EDKII_REDFISH_HTTP_PROTOCOL *This,
+ IN REDFISH_SERVICE RedfishService
+ )
+{
+ REDFISH_SERVICE_PRIVATE *Service;
+
+ if ((This == NULL) || (RedfishService == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Service = (REDFISH_SERVICE_PRIVATE *)RedfishService;
+ if (Service->Signature != REDFISH_HTTP_SERVICE_SIGNATURE) {
+ DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
+ }
+
+ return ReleaseRedfishService (Service);
+}
+
+/**
+ This function returns JSON value in given RedfishPayload. Returned JSON value
+ is a reference to the JSON value in RedfishPayload. Any modification to returned
+ JSON value will change JSON value in RedfishPayload.
+
+ @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+ @param[in] RedfishPayload Pointer to Redfish payload.
+
+ @retval EDKII_JSON_VALUE JSON value is returned.
+ @retval NULL Errors occur.
+
+**/
+EDKII_JSON_VALUE
+EFIAPI
+RedfishJsonInRedfishPayload (
+ IN EDKII_REDFISH_HTTP_PROTOCOL *This,
+ IN REDFISH_PAYLOAD RedfishPayload
+ )
+{
+ REDFISH_PAYLOAD_PRIVATE *Payload;
+
+ if ((This == NULL) || (RedfishPayload == NULL)) {
+ return NULL;
+ }
+
+ Payload = (REDFISH_PAYLOAD_PRIVATE *)RedfishPayload;
+ if (Payload->Signature != REDFISH_HTTP_PAYLOAD_SIGNATURE) {
+ DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
+ }
+
+ return Payload->JsonValue;
+}
+
+/**
+ Perform HTTP GET to Get redfish resource from given resource URI with
+ cache mechanism supported. It's caller's responsibility to free Response
+ by calling FreeResponse ().
+
+ @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+ @param[in] Service Redfish service instance to perform HTTP GET.
+ @param[in] Uri Target resource URI.
+ @param[in] Request Additional request context. This is optional.
+ @param[out] Response HTTP response from redfish service.
+ @param[in] UseCache If it is TRUE, this function will search for
+ cache first. If it is FALSE, this function
+ will query Redfish URI directly.
+
+ @retval EFI_SUCCESS Resrouce is returned successfully.
+ @retval Others Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishGetResource (
+ IN EDKII_REDFISH_HTTP_PROTOCOL *This,
+ IN REDFISH_SERVICE Service,
+ IN EFI_STRING Uri,
+ IN REDFISH_REQUEST *Request OPTIONAL,
+ OUT REDFISH_RESPONSE *Response,
+ IN BOOLEAN UseCache
+ )
+{
+ EFI_STATUS Status;
+ REDFISH_HTTP_CACHE_DATA *CacheData;
+ UINTN RetryCount;
+ REDFISH_HTTP_CACHE_PRIVATE *Private;
+
+ if ((This == NULL) || (Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Get URI: %s cache: %a\n", __func__, Uri, (UseCache ? "true" : "false")));
+
+ Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
+ CacheData = NULL;
+ RetryCount = 0;
+ ZeroMem (Response, sizeof (REDFISH_RESPONSE));
+
+ if (Private->CacheDisabled) {
+ UseCache = FALSE;
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache is disabled by PCD!\n", __func__));
+ }
+
+ //
+ // Search for cache list.
+ //
+ if (UseCache) {
+ CacheData = FindHttpCacheData (&Private->CacheList.Head, Uri);
+ if (CacheData != NULL) {
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: cache hit! %s\n", __func__, Uri));
+
+ //
+ // Copy cached response to caller's buffer.
+ //
+ Status = CopyRedfishResponse (CacheData->Response, Response);
+ CacheData->HitCount += 1;
+ return Status;
+ }
+ }
+
+ //
+ // Get resource from redfish service.
+ //
+ do {
+ RetryCount += 1;
+ Status = HttpSendReceive (
+ Service,
+ Uri,
+ HttpMethodGet,
+ Request,
+ Response
+ );
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request: %s :%r\n", __func__, Uri, Status));
+ if (!EFI_ERROR (Status) || (RetryCount >= Private->RetrySetting.MaximumRetryGet)) {
+ break;
+ }
+
+ //
+ // Retry when BMC is not ready.
+ //
+ if ((Response->StatusCode != NULL)) {
+ DEBUG_CODE (
+ DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+ );
+
+ if (!RedfishRetryRequired (Response->StatusCode)) {
+ break;
+ }
+
+ //
+ // Release response for next round of request.
+ //
+ This->FreeResponse (This, Response);
+ }
+
+ DEBUG ((DEBUG_WARN, "%a: RedfishGetByUriEx failed, retry (%d/%d)\n", __func__, RetryCount, Private->RetrySetting.MaximumRetryGet));
+ if (Private->RetrySetting.RetryWait > 0) {
+ gBS->Stall (Private->RetrySetting.RetryWait);
+ }
+ } while (TRUE);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG_CODE (
+ DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+ );
+ //
+ // Report status code for Redfish failure
+ //
+ ReportHttpError (HttpMethodGet, Uri, Response->StatusCode);
+ DEBUG ((DEBUG_ERROR, "%a: get %s failed (%d/%d): %r\n", __func__, Uri, RetryCount, Private->RetrySetting.MaximumRetryGet, Status));
+ goto ON_RELEASE;
+ }
+
+ if (!Private->CacheDisabled) {
+ //
+ // Keep response in cache list
+ //
+ Status = AddHttpCacheData (&Private->CacheList, Uri, Response);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: failed to cache %s: %r\n", __func__, Uri, Status));
+ goto ON_RELEASE;
+ }
+
+ DEBUG_CODE (
+ DebugPrintHttpCacheList (__func__, REDFISH_HTTP_CACHE_DEBUG_DUMP, &Private->CacheList);
+ );
+ }
+
+ON_RELEASE:
+
+ return Status;
+}
+
+/**
+ This function free resources in Request. Request is no longer available
+ after this function returns successfully.
+
+ @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+ @param[in] Request HTTP request to be released.
+
+ @retval EFI_SUCCESS Resrouce is released successfully.
+ @retval Others Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishFreeRequest (
+ IN EDKII_REDFISH_HTTP_PROTOCOL *This,
+ IN REDFISH_REQUEST *Request
+ )
+{
+ if ((This == NULL) || (Request == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: entry\n", __func__));
+
+ return ReleaseRedfishRequest (Request);
+}
+
+/**
+ This function free resources in given Response.
+
+ @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+ @param[in] Response HTTP response to be released.
+
+ @retval EFI_SUCCESS Resrouce is released successfully.
+ @retval Others Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishFreeResponse (
+ IN EDKII_REDFISH_HTTP_PROTOCOL *This,
+ IN REDFISH_RESPONSE *Response
+ )
+{
+ if ((This == NULL) || (Response == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: entry\n", __func__));
+
+ return ReleaseRedfishResponse (Response);
+}
+
+/**
+ This function expire the cached response of given URI.
+
+ @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+ @param[in] Uri Target response of URI.
+
+ @retval EFI_SUCCESS Target response is expired successfully.
+ @retval Others Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishExpireResponse (
+ IN EDKII_REDFISH_HTTP_PROTOCOL *This,
+ IN EFI_STRING Uri
+ )
+{
+ REDFISH_HTTP_CACHE_PRIVATE *Private;
+ REDFISH_HTTP_CACHE_DATA *CacheData;
+
+ if ((This == NULL) || IS_EMPTY_STRING (Uri)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: expire URI: %s\n", __func__, Uri));
+
+ Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
+
+ CacheData = FindHttpCacheData (&Private->CacheList.Head, Uri);
+ if (CacheData == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ return DeleteHttpCacheData (&Private->CacheList, CacheData);
+}
+
+/**
+ Perform HTTP PATCH to send redfish resource to given resource URI.
+ It's caller's responsibility to free Response by calling FreeResponse ().
+
+ @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+ @param[in] Service Redfish service instance to perform HTTP PATCH.
+ @param[in] Uri Target resource URI.
+ @param[in] Content Data to patch.
+ @param[in] ContentSize Size of the Content to be send to Redfish service.
+ This is optional. When ContentSize is 0, ContentSize
+ is the size of Content.
+ @param[in] ContentType Type of the Content to be send to Redfish service.
+ This is optional. When ContentType is NULL, content
+ type HTTP_CONTENT_TYPE_APP_JSON will be used.
+ @param[out] Response HTTP response from redfish service.
+
+ @retval EFI_SUCCESS Resrouce is returned successfully.
+ @retval Others Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishPatchResource (
+ IN EDKII_REDFISH_HTTP_PROTOCOL *This,
+ IN REDFISH_SERVICE Service,
+ IN EFI_STRING Uri,
+ IN CHAR8 *Content,
+ IN UINTN ContentSize OPTIONAL,
+ IN CHAR8 *ContentType OPTIONAL,
+ OUT REDFISH_RESPONSE *Response
+ )
+{
+ EFI_STATUS Status;
+ UINTN RetryCount;
+ REDFISH_REQUEST Request;
+ REDFISH_HTTP_CACHE_PRIVATE *Private;
+
+ if ((This == NULL) || (Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Patch URI: %s\n", __func__, Uri));
+
+ Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
+ RetryCount = 0;
+ ZeroMem (Response, sizeof (REDFISH_RESPONSE));
+ ZeroMem (&Request, sizeof (REDFISH_REQUEST));
+
+ Request.Content = Content;
+ Request.ContentLength = ContentSize;
+ Request.ContentType = ContentType;
+
+ //
+ // Patch resource to redfish service.
+ //
+ do {
+ RetryCount += 1;
+ Status = HttpSendReceive (
+ Service,
+ Uri,
+ HttpMethodPatch,
+ &Request,
+ Response
+ );
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request: %s :%r\n", __func__, Uri, Status));
+ if (!EFI_ERROR (Status) || (RetryCount >= Private->RetrySetting.MaximumRetryPatch)) {
+ break;
+ }
+
+ //
+ // Retry when BMC is not ready.
+ //
+ if ((Response->StatusCode != NULL)) {
+ DEBUG_CODE (
+ DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+ );
+
+ if (!RedfishRetryRequired (Response->StatusCode)) {
+ break;
+ }
+
+ //
+ // Release response for next round of request.
+ //
+ This->FreeResponse (This, Response);
+ }
+
+ DEBUG ((DEBUG_WARN, "%a: RedfishPatchToUriEx failed, retry (%d/%d)\n", __func__, RetryCount, Private->RetrySetting.MaximumRetryPatch));
+ if (Private->RetrySetting.RetryWait > 0) {
+ gBS->Stall (Private->RetrySetting.RetryWait);
+ }
+ } while (TRUE);
+
+ //
+ // Redfish resource is updated. Automatically expire the cached response
+ // so application can directly get resource from Redfish service again.
+ //
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire URI: %s\n", __func__, Uri));
+ RedfishExpireResponse (This, Uri);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG_CODE (
+ DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+ );
+ //
+ // Report status code for Redfish failure
+ //
+ ReportHttpError (HttpMethodPatch, Uri, Response->StatusCode);
+ DEBUG ((DEBUG_ERROR, "%a: patch %s failed (%d/%d): %r\n", __func__, Uri, RetryCount, Private->RetrySetting.MaximumRetryPatch, Status));
+ goto ON_RELEASE;
+ }
+
+ON_RELEASE:
+
+ return Status;
+}
+
+/**
+ Perform HTTP PUT to send redfish resource to given resource URI.
+ It's caller's responsibility to free Response by calling FreeResponse ().
+
+ @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+ @param[in] Service Redfish service instance to perform HTTP PUT.
+ @param[in] Uri Target resource URI.
+ @param[in] Content Data to put.
+ @param[in] ContentSize Size of the Content to be send to Redfish service.
+ This is optional. When ContentSize is 0, ContentSize
+ is the size of Content.
+ @param[in] ContentType Type of the Content to be send to Redfish service.
+ This is optional. When ContentType is NULL, content
+ type HTTP_CONTENT_TYPE_APP_JSON will be used.
+ @param[out] Response HTTP response from redfish service.
+
+ @retval EFI_SUCCESS Resrouce is returned successfully.
+ @retval Others Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishPutResource (
+ IN EDKII_REDFISH_HTTP_PROTOCOL *This,
+ IN REDFISH_SERVICE Service,
+ IN EFI_STRING Uri,
+ IN CHAR8 *Content,
+ IN UINTN ContentSize OPTIONAL,
+ IN CHAR8 *ContentType OPTIONAL,
+ OUT REDFISH_RESPONSE *Response
+ )
+{
+ EFI_STATUS Status;
+ UINTN RetryCount;
+ REDFISH_REQUEST Request;
+ REDFISH_HTTP_CACHE_PRIVATE *Private;
+
+ if ((This == NULL) || (Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Put URI: %s\n", __func__, Uri));
+
+ Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
+ RetryCount = 0;
+ ZeroMem (Response, sizeof (REDFISH_RESPONSE));
+ ZeroMem (&Request, sizeof (REDFISH_REQUEST));
+
+ Request.Content = Content;
+ Request.ContentLength = ContentSize;
+ Request.ContentType = ContentType;
+
+ //
+ // Patch resource to redfish service.
+ //
+ do {
+ RetryCount += 1;
+ Status = HttpSendReceive (
+ Service,
+ Uri,
+ HttpMethodPut,
+ &Request,
+ Response
+ );
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request: %s :%r\n", __func__, Uri, Status));
+ if (!EFI_ERROR (Status) || (RetryCount >= Private->RetrySetting.MaximumRetryPut)) {
+ break;
+ }
+
+ //
+ // Retry when BMC is not ready.
+ //
+ if ((Response->StatusCode != NULL)) {
+ DEBUG_CODE (
+ DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+ );
+
+ if (!RedfishRetryRequired (Response->StatusCode)) {
+ break;
+ }
+
+ //
+ // Release response for next round of request.
+ //
+ This->FreeResponse (This, Response);
+ }
+
+ DEBUG ((DEBUG_WARN, "%a: RedfishPutToUri failed, retry (%d/%d)\n", __func__, RetryCount, Private->RetrySetting.MaximumRetryPut));
+ if (Private->RetrySetting.RetryWait > 0) {
+ gBS->Stall (Private->RetrySetting.RetryWait);
+ }
+ } while (TRUE);
+
+ //
+ // Redfish resource is updated. Automatically expire the cached response
+ // so application can directly get resource from Redfish service again.
+ //
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire URI: %s\n", __func__, Uri));
+ RedfishExpireResponse (This, Uri);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG_CODE (
+ DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+ );
+ //
+ // Report status code for Redfish failure
+ //
+ ReportHttpError (HttpMethodPut, Uri, Response->StatusCode);
+ DEBUG ((DEBUG_ERROR, "%a: put %s failed (%d/%d): %r\n", __func__, Uri, RetryCount, Private->RetrySetting.MaximumRetryPut, Status));
+ goto ON_RELEASE;
+ }
+
+ON_RELEASE:
+
+ return Status;
+}
+
+/**
+ Perform HTTP POST to send redfish resource to given resource URI.
+ It's caller's responsibility to free Response by calling FreeResponse ().
+
+ @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+ @param[in] Service Redfish service instance to perform HTTP POST.
+ @param[in] Uri Target resource URI.
+ @param[in] Content Data to post.
+ @param[in] ContentSize Size of the Content to be send to Redfish service.
+ This is optional. When ContentSize is 0, ContentSize
+ is the size of Content.
+ @param[in] ContentType Type of the Content to be send to Redfish service.
+ This is optional. When ContentType is NULL, content
+ type HTTP_CONTENT_TYPE_APP_JSON will be used.
+ @param[out] Response HTTP response from redfish service.
+
+ @retval EFI_SUCCESS Resrouce is returned successfully.
+ @retval Others Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishPostResource (
+ IN EDKII_REDFISH_HTTP_PROTOCOL *This,
+ IN REDFISH_SERVICE Service,
+ IN EFI_STRING Uri,
+ IN CHAR8 *Content,
+ IN UINTN ContentSize OPTIONAL,
+ IN CHAR8 *ContentType OPTIONAL,
+ OUT REDFISH_RESPONSE *Response
+ )
+{
+ EFI_STATUS Status;
+ UINTN RetryCount;
+ REDFISH_REQUEST Request;
+ REDFISH_HTTP_CACHE_PRIVATE *Private;
+
+ if ((This == NULL) || (Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Post URI: %s\n", __func__, Uri));
+
+ Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
+ RetryCount = 0;
+ ZeroMem (Response, sizeof (REDFISH_RESPONSE));
+ ZeroMem (&Request, sizeof (REDFISH_REQUEST));
+
+ Request.Content = Content;
+ Request.ContentLength = ContentSize;
+ Request.ContentType = ContentType;
+
+ //
+ // Patch resource to redfish service.
+ //
+ do {
+ RetryCount += 1;
+ Status = HttpSendReceive (
+ Service,
+ Uri,
+ HttpMethodPost,
+ &Request,
+ Response
+ );
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request: %s :%r\n", __func__, Uri, Status));
+ if (!EFI_ERROR (Status) || (RetryCount >= Private->RetrySetting.MaximumRetryPost)) {
+ break;
+ }
+
+ //
+ // Retry when BMC is not ready.
+ //
+ if ((Response->StatusCode != NULL)) {
+ DEBUG_CODE (
+ DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+ );
+
+ if (!RedfishRetryRequired (Response->StatusCode)) {
+ break;
+ }
+
+ //
+ // Release response for next round of request.
+ //
+ This->FreeResponse (This, Response);
+ }
+
+ DEBUG ((DEBUG_WARN, "%a: RedfishPostToUri failed, retry (%d/%d)\n", __func__, RetryCount, Private->RetrySetting.MaximumRetryPost));
+ if (Private->RetrySetting.RetryWait > 0) {
+ gBS->Stall (Private->RetrySetting.RetryWait);
+ }
+ } while (TRUE);
+
+ //
+ // Redfish resource is updated. Automatically expire the cached response
+ // so application can directly get resource from Redfish service again.
+ //
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire URI: %s\n", __func__, Uri));
+ RedfishExpireResponse (This, Uri);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG_CODE (
+ DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+ );
+ //
+ // Report status code for Redfish failure
+ //
+ ReportHttpError (HttpMethodPost, Uri, Response->StatusCode);
+ DEBUG ((DEBUG_ERROR, "%a: post %s failed (%d/%d): %r\n", __func__, Uri, RetryCount, Private->RetrySetting.MaximumRetryPost, Status));
+ goto ON_RELEASE;
+ }
+
+ON_RELEASE:
+
+ return Status;
+}
+
+/**
+ Perform HTTP DELETE to delete redfish resource on given resource URI.
+ It's caller's responsibility to free Response by calling FreeResponse ().
+
+ @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
+ @param[in] Service Redfish service instance to perform HTTP DELETE.
+ @param[in] Uri Target resource URI.
+ @param[in] Content JSON represented properties to be deleted. This is
+ optional.
+ @param[in] ContentSize Size of the Content to be send to Redfish service.
+ This is optional. When ContentSize is 0, ContentSize
+ is the size of Content if Content is not NULL.
+ @param[in] ContentType Type of the Content to be send to Redfish service.
+ This is optional. When Content is not NULL and
+ ContentType is NULL, content type HTTP_CONTENT_TYPE_APP_JSON
+ will be used.
+ @param[out] Response HTTP response from redfish service.
+
+ @retval EFI_SUCCESS Resrouce is returned successfully.
+ @retval Others Errors occur.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishDeleteResource (
+ IN EDKII_REDFISH_HTTP_PROTOCOL *This,
+ IN REDFISH_SERVICE Service,
+ IN EFI_STRING Uri,
+ IN CHAR8 *Content OPTIONAL,
+ IN UINTN ContentSize OPTIONAL,
+ IN CHAR8 *ContentType OPTIONAL,
+ OUT REDFISH_RESPONSE *Response
+ )
+{
+ EFI_STATUS Status;
+ UINTN RetryCount;
+ REDFISH_REQUEST Request;
+ REDFISH_HTTP_CACHE_PRIVATE *Private;
+
+ if ((This == NULL) || (Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Delete URI: %s\n", __func__, Uri));
+
+ Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
+ RetryCount = 0;
+ ZeroMem (Response, sizeof (REDFISH_RESPONSE));
+ ZeroMem (&Request, sizeof (REDFISH_REQUEST));
+
+ Request.Content = Content;
+ Request.ContentLength = ContentSize;
+ Request.ContentType = ContentType;
+
+ //
+ // Patch resource to redfish service.
+ //
+ do {
+ RetryCount += 1;
+ Status = HttpSendReceive (
+ Service,
+ Uri,
+ HttpMethodDelete,
+ &Request,
+ Response
+ );
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request: %s :%r\n", __func__, Uri, Status));
+ if (!EFI_ERROR (Status) || (RetryCount >= Private->RetrySetting.MaximumRetryDelete)) {
+ break;
+ }
+
+ //
+ // Retry when BMC is not ready.
+ //
+ if ((Response->StatusCode != NULL)) {
+ DEBUG_CODE (
+ DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+ );
+
+ if (!RedfishRetryRequired (Response->StatusCode)) {
+ break;
+ }
+
+ //
+ // Release response for next round of request.
+ //
+ This->FreeResponse (This, Response);
+ }
+
+ DEBUG ((DEBUG_WARN, "%a: RedfishDeleteByUri failed, retry (%d/%d)\n", __func__, RetryCount, Private->RetrySetting.MaximumRetryDelete));
+ if (Private->RetrySetting.RetryWait > 0) {
+ gBS->Stall (Private->RetrySetting.RetryWait);
+ }
+ } while (TRUE);
+
+ //
+ // Redfish resource is updated. Automatically expire the cached response
+ // so application can directly get resource from Redfish service again.
+ //
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire URI: %s\n", __func__, Uri));
+ RedfishExpireResponse (This, Uri);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG_CODE (
+ DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+ );
+ //
+ // Report status code for Redfish failure
+ //
+ ReportHttpError (HttpMethodDelete, Uri, Response->StatusCode);
+ DEBUG ((DEBUG_ERROR, "%a: delete %s failed (%d/%d): %r\n", __func__, Uri, RetryCount, Private->RetrySetting.MaximumRetryDelete, Status));
+ goto ON_RELEASE;
+ }
+
+ON_RELEASE:
+
+ return Status;
+}
+
+EDKII_REDFISH_HTTP_PROTOCOL mEdkIIRedfishHttpProtocol = {
+ EDKII_REDFISH_HTTP_PROTOCOL_REVISION,
+ RedfishCreateRedfishService,
+ RedfishFreeRedfishService,
+ RedfishJsonInRedfishPayload,
+ RedfishGetResource,
+ RedfishPatchResource,
+ RedfishPutResource,
+ RedfishPostResource,
+ RedfishDeleteResource,
+ RedfishFreeRequest,
+ RedfishFreeResponse,
+ RedfishExpireResponse
+};
+
+/**
+ Unloads an image.
+
+ @param[in] ImageHandle Handle that identifies the image to be unloaded.
+
+ @retval EFI_SUCCESS The image has been unloaded.
+ @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishHttpDriverUnload (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ if (mRedfishHttpCachePrivate == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ if (!IsListEmpty (&mRedfishHttpCachePrivate->CacheList.Head)) {
+ ReleaseCacheList (&mRedfishHttpCachePrivate->CacheList);
+ }
+
+ gBS->UninstallMultipleProtocolInterfaces (
+ ImageHandle,
+ &gEdkIIRedfishHttpProtocolGuid,
+ &mRedfishHttpCachePrivate->Protocol,
+ NULL
+ );
+
+ FreePool (mRedfishHttpCachePrivate);
+ mRedfishHttpCachePrivate = NULL;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This is a EDKII_REDFISH_CREDENTIAL_PROTOCOL notification event handler.
+
+ @param[in] Event Event whose notification function is being invoked.
+ @param[in] Context Pointer to the notification function's context.
+
+**/
+VOID
+EFIAPI
+CredentialProtocolInstalled (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ REDFISH_HTTP_CACHE_PRIVATE *Private;
+
+ Private = (REDFISH_HTTP_CACHE_PRIVATE *)Context;
+ if (Private->Signature != REDFISH_HTTP_DRIVER_SIGNATURE) {
+ DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
+ return;
+ }
+
+ //
+ // Locate HII database protocol.
+ //
+ Status = gBS->LocateProtocol (
+ &gEdkIIRedfishCredentialProtocolGuid,
+ NULL,
+ (VOID **)&Private->CredentialProtocol
+ );
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ gBS->CloseEvent (Event);
+}
+
+/**
+ Main entry for this driver.
+
+ @param[in] ImageHandle Image handle this driver.
+ @param[in] SystemTable Pointer to SystemTable.
+
+ @retval EFI_SUCCESS This function always complete successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishHttpEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ VOID *Registration;
+
+ if (mRedfishHttpCachePrivate != NULL) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ mRedfishHttpCachePrivate = AllocateZeroPool (sizeof (REDFISH_HTTP_CACHE_PRIVATE));
+ if (mRedfishHttpCachePrivate == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Initial cache list and protocol instance.
+ //
+ mRedfishHttpCachePrivate->Signature = REDFISH_HTTP_DRIVER_SIGNATURE;
+ mRedfishHttpCachePrivate->ImageHandle = ImageHandle;
+ CopyMem (&mRedfishHttpCachePrivate->Protocol, &mEdkIIRedfishHttpProtocol, sizeof (EDKII_REDFISH_HTTP_PROTOCOL));
+ mRedfishHttpCachePrivate->CacheList.Capacity = REDFISH_HTTP_CACHE_LIST_SIZE;
+ mRedfishHttpCachePrivate->CacheList.Count = 0x00;
+ mRedfishHttpCachePrivate->CacheDisabled = PcdGetBool (PcdHttpCacheDisabled);
+ InitializeListHead (&mRedfishHttpCachePrivate->CacheList.Head);
+
+ //
+ // Get retry settings
+ //
+ mRedfishHttpCachePrivate->RetrySetting.MaximumRetryGet = PcdGet16 (PcdHttpGetRetry);
+ mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPut = PcdGet16 (PcdHttpPutRetry);
+ mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPatch = PcdGet16 (PcdHttpPatchRetry);
+ mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPost = PcdGet16 (PcdHttpPostRetry);
+ mRedfishHttpCachePrivate->RetrySetting.MaximumRetryDelete = PcdGet16 (PcdHttpDeleteRetry);
+ mRedfishHttpCachePrivate->RetrySetting.RetryWait = PcdGet16 (PcdHttpRetryWaitInSecond) * 1000000U;
+
+ //
+ // Install the gEdkIIRedfishHttpProtocolGuid onto Handle.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &mRedfishHttpCachePrivate->ImageHandle,
+ &gEdkIIRedfishHttpProtocolGuid,
+ &mRedfishHttpCachePrivate->Protocol,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: cannot install Redfish http protocol: %r\n", __func__, Status));
+ RedfishHttpDriverUnload (ImageHandle);
+ return Status;
+ }
+
+ //
+ // Install protocol notification if credential protocol is installed.
+ //
+ mRedfishHttpCachePrivate->NotifyEvent = EfiCreateProtocolNotifyEvent (
+ &gEdkIIRedfishCredentialProtocolGuid,
+ TPL_CALLBACK,
+ CredentialProtocolInstalled,
+ mRedfishHttpCachePrivate,
+ &Registration
+ );
+ if (mRedfishHttpCachePrivate->NotifyEvent == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: failed to create protocol notification for gEdkIIRedfishCredentialProtocolGuid\n", __func__));
+ ASSERT (FALSE);
+ RedfishHttpDriverUnload (ImageHandle);
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
new file mode 100644
index 0000000000..5652818d16
--- /dev/null
+++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
@@ -0,0 +1,693 @@
+/** @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;
+ }
+
+ for (Index = 0; Index < SrcHeaderCount; Index++) {
+ (*DstHeaders)[Index].FieldName = AllocateCopyPool (AsciiStrSize (SrcHeaders[Index].FieldName), SrcHeaders[Index].FieldName);
+ if ((*DstHeaders)[Index].FieldName == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ (*DstHeaders)[Index].FieldValue = AllocateCopyPool (AsciiStrSize (SrcHeaders[Index].FieldValue), SrcHeaders[Index].FieldValue);
+ if ((*DstHeaders)[Index].FieldValue == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *DstHeaderCount += 1;
+ }
+
+ 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 Resrouce 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 Resrouce 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 Resrouce 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;
+}
diff --git a/RedfishPkg/Redfish.fdf.inc b/RedfishPkg/Redfish.fdf.inc
index 3e5a77766e..5cbe3592fd 100644
--- a/RedfishPkg/Redfish.fdf.inc
+++ b/RedfishPkg/Redfish.fdf.inc
@@ -6,7 +6,7 @@
# to be built in the firmware volume.
#
# (C) Copyright 2020-2021 Hewlett Packard Enterprise Development LP<BR>
-# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
@@ -20,4 +20,5 @@
INF RedfishPkg/RedfishConfigHandler/RedfishConfigHandlerDriver.inf
INF RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf
INF MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf
+ INF RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
!endif
--
2.34.1
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115627): https://edk2.groups.io/g/devel/message/115627
Mute This Topic: https://groups.io/mt/104463404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [edk2-devel] [PATCH 2/6] RedfishPkg: implement Redfish HTTP protocol
2024-02-20 6:41 [edk2-devel] [PATCH 2/6] RedfishPkg: implement Redfish HTTP protocol Nickle Wang via groups.io
@ 2024-02-21 1:51 ` Chang, Abner via groups.io
2024-02-22 9:16 ` Nickle Wang via groups.io
0 siblings, 1 reply; 3+ messages in thread
From: Chang, Abner via groups.io @ 2024-02-21 1:51 UTC (permalink / raw)
To: Nickle Wang, devel@edk2.groups.io; +Cc: Igor Kulchytskyy, Nick Ramirez
[AMD Official Use Only - General]
One comment below.
> -----Original Message-----
> From: Nickle Wang <nicklew@nvidia.com>
> Sent: Tuesday, February 20, 2024 2:41 PM
> To: devel@edk2.groups.io
> Cc: Igor Kulchytskyy <igork@ami.com>; Chang, Abner
> <Abner.Chang@amd.com>; Nick Ramirez <nramirez@nvidia.com>
> Subject: [PATCH 2/6] RedfishPkg: implement Redfish HTTP protocol
>
> Caution: This message originated from an External Source. Use proper caution
> when opening attachments, clicking links, or responding.
>
>
> 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>
> ---
> RedfishPkg/RedfishPkg.dec | 7 +-
> RedfishPkg/RedfishComponents.dsc.inc | 3 +-
> RedfishPkg/RedfishPkg.dsc | 2 +
> RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf | 73 +
> RedfishPkg/RedfishHttpDxe/RedfishHttpData.h | 256 ++++
> RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h | 44 +
> .../RedfishHttpDxe/RedfishHttpOperation.h | 76 +
> RedfishPkg/RedfishHttpDxe/RedfishHttpData.c | 667 ++++++++
> RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c | 1344
> +++++++++++++++++
> .../RedfishHttpDxe/RedfishHttpOperation.c | 693 +++++++++
> RedfishPkg/Redfish.fdf.inc | 3 +-
> 11 files changed, 3164 insertions(+), 4 deletions(-)
> create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
> create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
> create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
> create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
> create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
> create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
> create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
>
> diff --git a/RedfishPkg/RedfishPkg.dec b/RedfishPkg/RedfishPkg.dec
> index 9b424efdf3..114f8d2ad8 100644
> --- a/RedfishPkg/RedfishPkg.dec
> +++ b/RedfishPkg/RedfishPkg.dec
> @@ -157,8 +157,11 @@
> # set to EFI_REST_EX_PROTOCOL.
> #
>
> gEfiRedfishPkgTokenSpaceGuid.PcdRedfishSendReceiveTimeout|5000|UINT3
> 2|0x00001009
> - ## This is used to enable HTTP content encoding on Redfish communication.
> -
> gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|TRUE|BO
> OLEAN|0x0000100A
> + #
> + # This PCD string is introduced for platform developer to set the encoding
> method supported by BMC Redfish.
> + # Currently only "None" and "gzip" are supported.
> + #
> +
> gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|"None"|V
> OID*|0x0000100A
> #
> # Use below PCDs to control Redfhs HTTP protocol.
> #
> diff --git a/RedfishPkg/RedfishComponents.dsc.inc
> b/RedfishPkg/RedfishComponents.dsc.inc
> index 464ffc8606..d6c5b73d7f 100644
> --- a/RedfishPkg/RedfishComponents.dsc.inc
> +++ b/RedfishPkg/RedfishComponents.dsc.inc
> @@ -7,7 +7,7 @@
> # "RedfishDefines.dsc.inc".
> #
> # (C) Copyright 2020-2021 Hewlett Packard Enterprise Development LP<BR>
> -# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> #
> # SPDX-License-Identifier: BSD-2-Clause-Patent
> #
> @@ -28,4 +28,5 @@
> RedfishPkg/RedfishConfigHandler/RedfishConfigHandlerDriver.inf
> RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf
> MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf
> + RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
> !endif
> diff --git a/RedfishPkg/RedfishPkg.dsc b/RedfishPkg/RedfishPkg.dsc
> index 25ed193182..5849e7cf9e 100644
> --- a/RedfishPkg/RedfishPkg.dsc
> +++ b/RedfishPkg/RedfishPkg.dsc
> @@ -45,6 +45,8 @@
>
> UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesL
> ib.inf
>
> RedfishPlatformCredentialLib|RedfishPkg/Library/PlatformCredentialLibNull/
> PlatformCredentialLibNull.inf
>
> RedfishContentCodingLib|RedfishPkg/Library/RedfishContentCodingLibNull/
> RedfishContentCodingLibNull.inf
> +
> ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeR
> eportStatusCodeLib.inf
> + SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf
>
> # NULL instance of IPMI related library.
> IpmiLib|MdeModulePkg/Library/BaseIpmiLibNull/BaseIpmiLibNull.inf
> diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
> b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
> new file mode 100644
> index 0000000000..c7dfdffacf
> --- /dev/null
> +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
> @@ -0,0 +1,73 @@
> +## @file
> +# RedfishHttpDxe is the DXE driver which provides
> +# EdkIIRedfishHttpProtocol to EDK2 Redfish Feature
> +# drivers for HTTP operation.
> +#
> +# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> + INF_VERSION = 0x0001000b
> + BASE_NAME = RedfishHttpDxe
> + FILE_GUID = 85ADB2F1-DA93-47D4-AF4F-3D920D9BD2C0
> + MODULE_TYPE = DXE_DRIVER
> + VERSION_STRING = 1.0
> + ENTRY_POINT = RedfishHttpEntryPoint
> + UNLOAD_IMAGE = RedfishHttpDriverUnload
> +
> +#
> +# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 RISCV64
> +#
> +
> +[Sources]
> + RedfishHttpData.c
> + RedfishHttpData.h
> + RedfishHttpDxe.c
> + RedfishHttpDxe.h
> + RedfishHttpOperation.c
> + RedfishHttpOperation.h
> +
> +[Packages]
> + MdePkg/MdePkg.dec
> + MdeModulePkg/MdeModulePkg.dec
> + NetworkPkg/NetworkPkg.dec
> + RedfishPkg/RedfishPkg.dec
> +
> +[LibraryClasses.ARM]
> + ArmSoftFloatLib
> +
> +[LibraryClasses]
> + BaseLib
> + BaseMemoryLib
> + RedfishContentCodingLib
> + DebugLib
> + HttpLib
> + JsonLib
> + MemoryAllocationLib
> + PrintLib
> + RedfishDebugLib
> + ReportStatusCodeLib
> + UefiBootServicesTableLib
> + UefiDriverEntryPoint
> + UefiLib
> +
> +[Protocols]
> + gEdkIIRedfishHttpProtocolGuid ## PRODUCED
> + gEdkIIRedfishCredentialProtocolGuid ## CONSUMES
> + gEfiRestExProtocolGuid ## CONSUEMS
> +
> +[Pcd]
> + gEfiRedfishPkgTokenSpaceGuid.PcdHttpGetRetry
> + gEfiRedfishPkgTokenSpaceGuid.PcdHttpPutRetry
> + gEfiRedfishPkgTokenSpaceGuid.PcdHttpPatchRetry
> + gEfiRedfishPkgTokenSpaceGuid.PcdHttpPostRetry
> + gEfiRedfishPkgTokenSpaceGuid.PcdHttpDeleteRetry
> + gEfiRedfishPkgTokenSpaceGuid.PcdHttpRetryWaitInSecond
> + gEfiRedfishPkgTokenSpaceGuid.PcdHttpCacheDisabled
> + gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding
> +
> +[Depex]
> + TRUE
> diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
> b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
> new file mode 100644
> index 0000000000..6be610142e
> --- /dev/null
> +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
> @@ -0,0 +1,256 @@
> +/** @file
> + Definitions of RedfishHttpData
> +
> + Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef EDKII_REDFISH_HTTP_DATA_H_
> +#define EDKII_REDFISH_HTTP_DATA_H_
> +
> +#include "RedfishHttpDxe.h"
> +
> +#define REDFISH_HTTP_DRIVER_SIGNATURE SIGNATURE_32 ('r', 'f', 'h', 'p')
> +#define REDFISH_HTTP_CACHE_SIGNATURE SIGNATURE_32 ('r', 'f', 'c', 'h')
> +#define REDFISH_HTTP_SERVICE_SIGNATURE SIGNATURE_32 ('r', 'f', 's', 'v')
> +#define REDFISH_HTTP_PAYLOAD_SIGNATURE SIGNATURE_32 ('r', 'f', 'p', 'l')
> +#define REDFISH_HTTP_BASIC_AUTH_STR "Basic "
> +
> +///
> +/// REDFISH_SERVICE_PRIVATE definition.
> +///
> +typedef struct {
> + UINT32 Signature;
> + CHAR8 *Host;
> + CHAR8 *HostName;
> + CHAR8 *BasicAuth;
> + CHAR8 *SessionToken;
> + EFI_REST_EX_PROTOCOL *RestEx;
> +} REDFISH_SERVICE_PRIVATE;
> +
> +///
> +/// REDFISH_PAYLOAD_PRIVATE definition.
> +///
> +typedef struct {
> + UINT32 Signature;
> + REDFISH_SERVICE_PRIVATE *Service;
> + EDKII_JSON_VALUE JsonValue;
> +} REDFISH_PAYLOAD_PRIVATE;
> +
> +///
> +/// Definition of REDFISH_HTTP_CACHE_DATA
> +///
> +typedef struct {
> + UINT32 Signature;
> + LIST_ENTRY List;
> + EFI_STRING Uri;
> + UINTN HitCount;
> + REDFISH_RESPONSE *Response;
> +} REDFISH_HTTP_CACHE_DATA;
> +
> +#define REDFISH_HTTP_CACHE_FROM_LIST(a) CR (a,
> REDFISH_HTTP_CACHE_DATA, List, REDFISH_HTTP_CACHE_SIGNATURE)
> +
> +///
> +/// Definition of REDFISH_HTTP_CACHE_LIST
> +///
> +typedef struct {
> + LIST_ENTRY Head;
> + UINTN Count;
> + UINTN Capacity;
> +} REDFISH_HTTP_CACHE_LIST;
> +
> +///
> +/// Definition of REDFISH_HTTP_RETRY_SETTING
> +///
> +typedef struct {
> + UINT16 MaximumRetryGet;
> + UINT16 MaximumRetryPut;
> + UINT16 MaximumRetryPost;
> + UINT16 MaximumRetryPatch;
> + UINT16 MaximumRetryDelete;
> + UINTN RetryWait;
> +} REDFISH_HTTP_RETRY_SETTING;
> +
> +///
> +/// Definition of REDFISH_HTTP_CACHE_PRIVATE
> +///
> +typedef struct {
> + UINT32 Signature;
> + EFI_HANDLE ImageHandle;
> + BOOLEAN CacheDisabled;
> + EFI_EVENT NotifyEvent;
> + REDFISH_HTTP_CACHE_LIST CacheList;
> + EDKII_REDFISH_HTTP_PROTOCOL Protocol;
> + EDKII_REDFISH_CREDENTIAL_PROTOCOL *CredentialProtocol;
> + REDFISH_HTTP_RETRY_SETTING RetrySetting;
> +} REDFISH_HTTP_CACHE_PRIVATE;
> +
> +#define REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS(a) CR (a,
> REDFISH_HTTP_CACHE_PRIVATE, Protocol,
> REDFISH_HTTP_DRIVER_SIGNATURE)
> +
> +/**
> + Search on given ListHeader for given URI string.
> +
> + @param[in] ListHeader Target list to search.
> + @param[in] Uri Target URI to search.
> +
> + @retval REDFISH_HTTP_CACHE_DATA Target cache data is found.
> + @retval NULL No cache data with given URI is found.
> +
> +**/
> +REDFISH_HTTP_CACHE_DATA *
> +FindHttpCacheData (
> + IN LIST_ENTRY *ListHeader,
> + IN EFI_STRING Uri
> + );
> +
> +/**
> + This function copy the data in SrcResponse to DstResponse.
> +
> + @param[in] SrcResponse Source Response to copy.
> + @param[out] DstResponse Destination Response.
> +
> + @retval EFI_SUCCESS Response is copied successfully.
> + @retval Others Error occurs.
> +
> +**/
> +EFI_STATUS
> +CopyRedfishResponse (
> + IN REDFISH_RESPONSE *SrcResponse,
> + OUT REDFISH_RESPONSE *DstResponse
> + );
> +
> +/**
> + Release all cache from list.
> +
> + @param[in] CacheList The list to be released.
> +
> + @retval EFI_SUCCESS All cache data are released.
> + @retval EFI_INVALID_PARAMETER CacheList is NULL.
> +
> +**/
> +EFI_STATUS
> +ReleaseCacheList (
> + IN REDFISH_HTTP_CACHE_LIST *CacheList
> + );
> +
> +/**
> + Add new cache by given URI and HTTP response to specify List.
> +
> + @param[in] List Target cache list to add.
> + @param[in] Uri The URI string matching to this cache data.
> + @param[in] Response HTTP response.
> +
> + @retval EFI_SUCCESS Cache data is added.
> + @retval Others Fail to add cache data.
> +
> +**/
> +EFI_STATUS
> +AddHttpCacheData (
> + IN REDFISH_HTTP_CACHE_LIST *List,
> + IN EFI_STRING Uri,
> + IN REDFISH_RESPONSE *Response
> + );
> +
> +/**
> + Delete a cache data by given cache instance.
> +
> + @param[in] List Target cache list to be removed.
> + @param[in] Data Pointer to the instance to be deleted.
> +
> + @retval EFI_SUCCESS Cache data is removed.
> + @retval Others Fail to remove cache data.
> +
> +**/
> +EFI_STATUS
> +DeleteHttpCacheData (
> + IN REDFISH_HTTP_CACHE_LIST *List,
> + IN REDFISH_HTTP_CACHE_DATA *Data
> + );
> +
> +/**
> + This function release Redfish Payload.
> +
> + @param[in] Payload Pointer to payload instance.
> +
> + @retval EFI_SUCCESS Payload is released.
> + @retval Others Error occurs.
> +
> +**/
> +EFI_STATUS
> +ReleaseRedfishPayload (
> + IN REDFISH_PAYLOAD_PRIVATE *Payload
> + );
> +
> +/**
> + This function creat new payload. Server and JsonObj are
> + copied to newly created payload.
> +
> + @param[in] Service Pointer to Service instance.
> + @param[in] JsonObj Pointer to JSON object.
> +
> + @retval REDFISH_PAYLOAD_PRIVATE Newly created payload.
> + @retval NULL Error occurs.
> +
> +**/
> +REDFISH_PAYLOAD_PRIVATE *
> +CreateRedfishPayload (
> + IN REDFISH_SERVICE_PRIVATE *Service,
> + IN EDKII_JSON_VALUE JsonValue
> + );
> +
> +/**
> + This function release Redfish Service.
> +
> + @param[in] Service Pointer to service instance.
> +
> + @retval EFI_SUCCESS Service is released.
> + @retval Others Error occurs.
> +
> +**/
> +EFI_STATUS
> +ReleaseRedfishService (
> + IN REDFISH_SERVICE_PRIVATE *Service
> + );
> +
> +/**
> + This function creat new service. Host and HostName are copied to
> + newly created service instance.
> +
> + @param[in] Host Host string.
> + @param[in] HostName Hostname string.
> + @param[in] BasicAuth Basic Authorization string.
> + @param[in] SessionToken Session token string.
> + @param[in] RestEx Rest EX protocol instance.
> +
> + @retval REDFISH_PAYLOAD_PRIVATE Newly created service.
> + @retval NULL Error occurs.
> +
> +**/
> +REDFISH_SERVICE_PRIVATE *
> +CreateRedfishService (
> + IN CHAR8 *Host,
> + IN CHAR8 *HostName,
> + IN CHAR8 *BasicAuth OPTIONAL,
> + IN CHAR8 *SessionToken OPTIONAL,
> + IN EFI_REST_EX_PROTOCOL *RestEx
> + );
> +
> +/**
> + This function update session token in Redfish Service.
> +
> + @param[in] Service Pointer to service instance.
> + @param[in] Token Session token.
> +
> + @retval EFI_SUCCESS Session token is updated.
> + @retval Others Error occurs.
> +
> +**/
> +EFI_STATUS
> +UpdateSessionToken (
> + IN REDFISH_SERVICE_PRIVATE *Service,
> + IN CHAR8 *Token
> + );
> +
> +#endif
> diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
> b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
> new file mode 100644
> index 0000000000..cf6ba9cb47
> --- /dev/null
> +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
> @@ -0,0 +1,44 @@
> +/** @file
> + Definitions of RedfishHttpDxe
> +
> + Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef EDKII_REDFISH_HTTP_DXE_H_
> +#define EDKII_REDFISH_HTTP_DXE_H_
> +
> +#include <Uefi.h>
> +#include <IndustryStandard/Http11.h>
> +
> +#include <Library/UefiLib.h>
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/RedfishContentCodingLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/HttpLib.h>
> +#include <Library/JsonLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/RedfishDebugLib.h>
> +#include <Library/ReportStatusCodeLib.h>
> +#include <Library/PrintLib.h>
> +
> +#include <Protocol/Http.h>
> +#include <Protocol/EdkIIRedfishHttpProtocol.h>
> +#include <Protocol/EdkIIRedfishCredential.h>
> +#include <Protocol/RestEx.h>
> +
> +#define IS_EMPTY_STRING(a) ((a) == NULL || (a)[0] == '\0')
> +#define REDFISH_HTTP_CACHE_LIST_SIZE 0x80
> +#define REDFISH_ERROR_MSG_MAX 128
> +#define REDFISH_DEBUG_STRING_LENGTH 200
> +#define REDFISH_HOST_NAME_MAX 64 // IPv6 maximum length (39)
> + "https://" (8) + port number (maximum 5)
> +#define REDFISH_HTTP_ERROR_REPORT "Redfish HTTP %a failure(0x%x):
> %s"
> +#define REDFISH_HTTP_CACHE_DEBUG DEBUG_MANAGEABILITY
> +#define REDFISH_HTTP_CACHE_DEBUG_DUMP DEBUG_MANAGEABILITY
> +#define REDFISH_HTTP_CACHE_DEBUG_REQUEST DEBUG_MANAGEABILITY
> +
> +#endif
> diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
> b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
> new file mode 100644
> index 0000000000..e4fc053e96
> --- /dev/null
> +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
> @@ -0,0 +1,76 @@
> +/** @file
> + Definitions of RedfishHttpOperation
> +
> + Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef EDKII_REDFISH_HTTP_OP_H_
> +#define EDKII_REDFISH_HTTP_OP_H_
Nickle, I missed this when I reviewed it offline.
Could you please update the macro to align with header file name, EDKII_REDFISH_HTTP_OPERATION_H_
Thanks
Abner
> +
> +#include "RedfishHttpDxe.h"
> +
> +#define REDFISH_CONTENT_LENGTH_SIZE 80
> +#define REDFISH_COMMON_HEADER_SIZE 5
> +#define REDFISH_HTTP_HEADER_ODATA_VERSION_STR "OData-Version"
> +#define REDFISH_HTTP_HEADER_ODATA_VERSION_VALUE "4.0"
> +#define REDFISH_HTTP_HEADER_USER_AGENT_VALUE "edk2redfish"
> +#define REDFISH_HTTP_HEADER_CONNECTION_STR "Connection"
> +#define REDFISH_HTTP_HEADER_CONNECTION_VALUE "Keep-Alive"
> +#define REDFISH_HTTP_CONTENT_ENCODING_NONE "None"
> +
> +/**
> + 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 Resrouce is released successfully.
> + @retval Others Errors occur.
> +
> +**/
> +EFI_STATUS
> +ReleaseRedfishRequest (
> + IN REDFISH_REQUEST *Request
> + );
> +
> +/**
> + This function free resources in given Response.
> +
> + @param[in] Response HTTP response to be released.
> +
> + @retval EFI_SUCCESS Resrouce is released successfully.
> + @retval Others Errors occur.
> +
> +**/
> +EFI_STATUS
> +ReleaseRedfishResponse (
> + IN REDFISH_RESPONSE *Response
> + );
> +
> +/**
> + 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
> + );
> +
> +#endif
> diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
> b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
> new file mode 100644
> index 0000000000..bf95e9f8d4
> --- /dev/null
> +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
> @@ -0,0 +1,667 @@
> +/** @file
> + RedfishHttpData handles internal data to support Redfish HTTP protocol.
> +
> + Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include "RedfishHttpData.h"
> +#include "RedfishHttpOperation.h"
> +
> +/**
> + This function update session token in Redfish Service.
> +
> + @param[in] Service Pointer to service instance.
> + @param[in] Token Session token.
> +
> + @retval EFI_SUCCESS Session token is updated.
> + @retval Others Error occurs.
> +
> +**/
> +EFI_STATUS
> +UpdateSessionToken (
> + IN REDFISH_SERVICE_PRIVATE *Service,
> + IN CHAR8 *Token
> + )
> +{
> + if ((Service == NULL) || IS_EMPTY_STRING (Token)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if (Service->SessionToken != NULL) {
> + FreePool (Service->SessionToken);
> + }
> +
> + Service->SessionToken = AllocateCopyPool (AsciiStrSize (Token), Token);
> + if (Service->SessionToken == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function release Redfish Service.
> +
> + @param[in] Service Pointer to service instance.
> +
> + @retval EFI_SUCCESS Service is released.
> + @retval Others Error occurs.
> +
> +**/
> +EFI_STATUS
> +ReleaseRedfishService (
> + IN REDFISH_SERVICE_PRIVATE *Service
> + )
> +{
> + if (Service == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if (Service->Host != NULL) {
> + FreePool (Service->Host);
> + }
> +
> + if (Service->HostName != NULL) {
> + FreePool (Service->HostName);
> + }
> +
> + if (Service->BasicAuth != NULL) {
> + ZeroMem (Service->BasicAuth, AsciiStrSize (Service->BasicAuth));
> + FreePool (Service->BasicAuth);
> + }
> +
> + if (Service->SessionToken != NULL) {
> + ZeroMem (Service->SessionToken, AsciiStrSize (Service->SessionToken));
> + FreePool (Service->SessionToken);
> + }
> +
> + FreePool (Service);
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function creat new service. Host and HostName are copied to
> + newly created service instance.
> +
> + @param[in] Host Host string.
> + @param[in] HostName Hostname string.
> + @param[in] BasicAuth Basic Authorization string.
> + @param[in] SessionToken Session token string.
> + @param[in] RestEx Rest EX protocol instance.
> +
> + @retval REDFISH_PAYLOAD_PRIVATE Newly created service.
> + @retval NULL Error occurs.
> +
> +**/
> +REDFISH_SERVICE_PRIVATE *
> +CreateRedfishService (
> + IN CHAR8 *Host,
> + IN CHAR8 *HostName,
> + IN CHAR8 *BasicAuth OPTIONAL,
> + IN CHAR8 *SessionToken OPTIONAL,
> + IN EFI_REST_EX_PROTOCOL *RestEx
> + )
> +{
> + REDFISH_SERVICE_PRIVATE *NewService;
> + UINTN AuthStrSize;
> +
> + if (IS_EMPTY_STRING (Host) || IS_EMPTY_STRING (HostName) || (RestEx
> == NULL)) {
> + return NULL;
> + }
> +
> + NewService = AllocateZeroPool (sizeof (REDFISH_SERVICE_PRIVATE));
> + if (NewService == NULL) {
> + return NULL;
> + }
> +
> + NewService->Signature = REDFISH_HTTP_SERVICE_SIGNATURE;
> + NewService->Host = AllocateCopyPool (AsciiStrSize (Host), Host);
> + if (NewService->Host == NULL) {
> + goto ON_ERROR;
> + }
> +
> + NewService->HostName = AllocateCopyPool (AsciiStrSize (HostName),
> HostName);
> + if (NewService->HostName == NULL) {
> + goto ON_ERROR;
> + }
> +
> + if (!IS_EMPTY_STRING (BasicAuth)) {
> + AuthStrSize = AsciiStrSize (BasicAuth) + AsciiStrLen
> (REDFISH_HTTP_BASIC_AUTH_STR);
> + NewService->BasicAuth = AllocateZeroPool (AuthStrSize);
> + if (NewService->BasicAuth == NULL) {
> + goto ON_ERROR;
> + }
> +
> + AsciiSPrint (NewService->BasicAuth, AuthStrSize, "%a%a",
> REDFISH_HTTP_BASIC_AUTH_STR, BasicAuth);
> + }
> +
> + if (!IS_EMPTY_STRING (SessionToken)) {
> + NewService->SessionToken = AllocateCopyPool (AsciiStrSize
> (SessionToken), SessionToken);
> + if (NewService->SessionToken == NULL) {
> + goto ON_ERROR;
> + }
> + }
> +
> + NewService->RestEx = RestEx;
> +
> + return NewService;
> +
> +ON_ERROR:
> +
> + ReleaseRedfishService (NewService);
> +
> + return NULL;
> +}
> +
> +/**
> + This function release Redfish Payload.
> +
> + @param[in] Payload Pointer to payload instance.
> +
> + @retval EFI_SUCCESS Payload is released.
> + @retval Others Error occurs.
> +
> +**/
> +EFI_STATUS
> +ReleaseRedfishPayload (
> + IN REDFISH_PAYLOAD_PRIVATE *Payload
> + )
> +{
> + if (Payload == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if (Payload->Service != NULL) {
> + ReleaseRedfishService (Payload->Service);
> + }
> +
> + if (Payload->JsonValue != NULL) {
> + JsonValueFree (Payload->JsonValue);
> + }
> +
> + FreePool (Payload);
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This function creat new payload. Server and JsonObj are
> + copied to newly created payload.
> +
> + @param[in] Service Pointer to Service instance.
> + @param[in] JsonValue Pointer to JSON value.
> +
> + @retval REDFISH_PAYLOAD_PRIVATE Newly created payload.
> + @retval NULL Error occurs.
> +
> +**/
> +REDFISH_PAYLOAD_PRIVATE *
> +CreateRedfishPayload (
> + IN REDFISH_SERVICE_PRIVATE *Service,
> + IN EDKII_JSON_VALUE JsonValue
> + )
> +{
> + REDFISH_PAYLOAD_PRIVATE *NewPayload;
> +
> + if ((Service == NULL) || (JsonValue == NULL)) {
> + return NULL;
> + }
> +
> + NewPayload = AllocateZeroPool (sizeof (REDFISH_PAYLOAD_PRIVATE));
> + if (NewPayload == NULL) {
> + return NULL;
> + }
> +
> + NewPayload->Signature = REDFISH_HTTP_PAYLOAD_SIGNATURE;
> + NewPayload->Service = CreateRedfishService (Service->Host, Service-
> >HostName, Service->BasicAuth, Service->SessionToken, Service->RestEx);
> + if (NewPayload->Service == NULL) {
> + goto ON_ERROR;
> + }
> +
> + NewPayload->JsonValue = JsonValueClone (JsonValue);
> + if (NewPayload->JsonValue == NULL) {
> + goto ON_ERROR;
> + }
> +
> + return NewPayload;
> +
> +ON_ERROR:
> +
> + ReleaseRedfishPayload (NewPayload);
> +
> + return NULL;
> +}
> +
> +/**
> + This function copy the data in SrcResponse to DstResponse.
> +
> + @param[in] SrcResponse Source Response to copy.
> + @param[out] DstResponse Destination Response.
> +
> + @retval EFI_SUCCESS Response is copied successfully.
> + @retval Others Error occurs.
> +
> +**/
> +EFI_STATUS
> +CopyRedfishResponse (
> + IN REDFISH_RESPONSE *SrcResponse,
> + OUT REDFISH_RESPONSE *DstResponse
> + )
> +{
> + REDFISH_PAYLOAD_PRIVATE *Payload;
> + UINTN Index;
> +
> + if ((SrcResponse == NULL) || (DstResponse == NULL)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if (SrcResponse == DstResponse) {
> + return EFI_SUCCESS;
> + }
> +
> + //
> + // Status code
> + //
> + if (SrcResponse->StatusCode != NULL) {
> + DstResponse->StatusCode = AllocateCopyPool (sizeof
> (EFI_HTTP_STATUS_CODE), SrcResponse->StatusCode);
> + if (DstResponse->StatusCode == NULL) {
> + goto ON_ERROR;
> + }
> + }
> +
> + //
> + // Header
> + //
> + if ((SrcResponse->HeaderCount > 0) && (SrcResponse->Headers != NULL)) {
> + DstResponse->HeaderCount = 0;
> + DstResponse->Headers = AllocateZeroPool (sizeof (EFI_HTTP_HEADER) *
> SrcResponse->HeaderCount);
> + if (DstResponse->Headers == NULL) {
> + goto ON_ERROR;
> + }
> +
> + for (Index = 0; Index < SrcResponse->HeaderCount; Index++) {
> + DstResponse->Headers[Index].FieldName = AllocateCopyPool (AsciiStrSize
> (SrcResponse->Headers[Index].FieldName), SrcResponse-
> >Headers[Index].FieldName);
> + if (DstResponse->Headers[Index].FieldName == NULL) {
> + goto ON_ERROR;
> + }
> +
> + DstResponse->Headers[Index].FieldValue = AllocateCopyPool (AsciiStrSize
> (SrcResponse->Headers[Index].FieldValue), SrcResponse-
> >Headers[Index].FieldValue);
> + if (DstResponse->Headers[Index].FieldValue == NULL) {
> + goto ON_ERROR;
> + }
> +
> + DstResponse->HeaderCount += 1;
> + }
> + }
> +
> + //
> + // Payload
> + //
> + if (SrcResponse->Payload != NULL) {
> + Payload = (REDFISH_PAYLOAD_PRIVATE *)SrcResponse->Payload;
> + if (Payload->Signature != REDFISH_HTTP_PAYLOAD_SIGNATURE) {
> + DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
> + goto ON_ERROR;
> + }
> +
> + DstResponse->Payload = CreateRedfishPayload (Payload->Service, Payload-
> >JsonValue);
> + if (DstResponse->Payload == NULL) {
> + goto ON_ERROR;
> + }
> + }
> +
> + return EFI_SUCCESS;
> +
> +ON_ERROR:
> +
> + ReleaseRedfishResponse (DstResponse);
> +
> + return EFI_OUT_OF_RESOURCES;
> +}
> +
> +/**
> + This function clone input response and return to caller
> +
> + @param[in] Response Response to clone.
> +
> + @retval REDFISH_RESPONSE * Response is cloned.
> + @retval NULL Errors occur.
> +
> +**/
> +REDFISH_RESPONSE *
> +CloneRedfishResponse (
> + IN REDFISH_RESPONSE *Response
> + )
> +{
> + EFI_STATUS Status;
> + REDFISH_RESPONSE *NewResponse;
> +
> + if (Response == NULL) {
> + return NULL;
> + }
> +
> + NewResponse = AllocateZeroPool (sizeof (REDFISH_RESPONSE));
> + if (NewResponse == NULL) {
> + return NULL;
> + }
> +
> + Status = CopyRedfishResponse (Response, NewResponse);
> + if (EFI_ERROR (Status)) {
> + FreePool (NewResponse);
> + return NULL;
> + }
> +
> + return NewResponse;
> +}
> +
> +/**
> + Release REDFISH_HTTP_CACHE_DATA resource
> +
> + @param[in] Data Pointer to REDFISH_HTTP_CACHE_DATA instance
> +
> + @retval EFI_SUCCESS REDFISH_HTTP_CACHE_DATA is released
> successfully.
> + @retval EFI_INVALID_PARAMETER Data is NULL
> +
> +**/
> +EFI_STATUS
> +ReleaseHttpCacheData (
> + IN REDFISH_HTTP_CACHE_DATA *Data
> + )
> +{
> + if (Data == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if (Data->Uri != NULL) {
> + FreePool (Data->Uri);
> + }
> +
> + if (Data->Response != NULL) {
> + ReleaseRedfishResponse (Data->Response);
> + FreePool (Data->Response);
> + }
> +
> + FreePool (Data);
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + Create new cache data.
> +
> + @param[in] Uri The URI string matching to this cache data.
> + @param[in] Response HTTP response.
> +
> + @retval REDFISH_HTTP_CACHE_DATA * Pointer to newly created cache
> data.
> + @retval NULL No memory available.
> +
> +**/
> +REDFISH_HTTP_CACHE_DATA *
> +NewHttpCacheData (
> + IN EFI_STRING Uri,
> + IN REDFISH_RESPONSE *Response
> + )
> +{
> + REDFISH_HTTP_CACHE_DATA *NewData;
> + UINTN Size;
> +
> + if (IS_EMPTY_STRING (Uri) || (Response == NULL)) {
> + return NULL;
> + }
> +
> + NewData = AllocateZeroPool (sizeof (REDFISH_HTTP_CACHE_DATA));
> + if (NewData == NULL) {
> + return NULL;
> + }
> +
> + NewData->Signature = REDFISH_HTTP_CACHE_SIGNATURE;
> + Size = StrSize (Uri);
> + NewData->Uri = AllocateCopyPool (Size, Uri);
> + if (NewData->Uri == NULL) {
> + goto ON_ERROR;
> + }
> +
> + NewData->Response = Response;
> + NewData->HitCount = 1;
> +
> + return NewData;
> +
> +ON_ERROR:
> +
> + if (NewData != NULL) {
> + ReleaseHttpCacheData (NewData);
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + Search on given ListHeader for given URI string.
> +
> + @param[in] ListHeader Target list to search.
> + @param[in] Uri Target URI to search.
> +
> + @retval REDFISH_HTTP_CACHE_DATA Target cache data is found.
> + @retval NULL No cache data with given URI is found.
> +
> +**/
> +REDFISH_HTTP_CACHE_DATA *
> +FindHttpCacheData (
> + IN LIST_ENTRY *ListHeader,
> + IN EFI_STRING Uri
> + )
> +{
> + LIST_ENTRY *List;
> + REDFISH_HTTP_CACHE_DATA *Data;
> +
> + if (IS_EMPTY_STRING (Uri)) {
> + return NULL;
> + }
> +
> + if (IsListEmpty (ListHeader)) {
> + return NULL;
> + }
> +
> + Data = NULL;
> + List = GetFirstNode (ListHeader);
> + while (!IsNull (ListHeader, List)) {
> + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> +
> + if (StrCmp (Data->Uri, Uri) == 0) {
> + return Data;
> + }
> +
> + List = GetNextNode (ListHeader, List);
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + Search on given ListHeader and return cache data with minimum hit count.
> +
> + @param[in] ListHeader Target list to search.
> +
> + @retval REDFISH_HTTP_CACHE_DATA Target cache data is returned.
> + @retval NULL No cache data is found.
> +
> +**/
> +REDFISH_HTTP_CACHE_DATA *
> +FindUnusedHttpCacheData (
> + IN LIST_ENTRY *ListHeader
> + )
> +{
> + LIST_ENTRY *List;
> + REDFISH_HTTP_CACHE_DATA *Data;
> + REDFISH_HTTP_CACHE_DATA *UnusedData;
> + UINTN HitCount;
> +
> + if (IsListEmpty (ListHeader)) {
> + return NULL;
> + }
> +
> + Data = NULL;
> + UnusedData = NULL;
> + HitCount = 0;
> +
> + List = GetFirstNode (ListHeader);
> + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> + UnusedData = Data;
> + HitCount = Data->HitCount;
> + List = GetNextNode (ListHeader, List);
> +
> + while (!IsNull (ListHeader, List)) {
> + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> +
> + if (Data->HitCount < HitCount) {
> + HitCount = Data->HitCount;
> + UnusedData = Data;
> + }
> +
> + List = GetNextNode (ListHeader, List);
> + }
> +
> + return UnusedData;
> +}
> +
> +/**
> + Delete a cache data by given cache instance.
> +
> + @param[in] List Target cache list to be removed.
> + @param[in] Data Pointer to the instance to be deleted.
> +
> + @retval EFI_SUCCESS Cache data is removed.
> + @retval Others Fail to remove cache data.
> +
> +**/
> +EFI_STATUS
> +DeleteHttpCacheData (
> + IN REDFISH_HTTP_CACHE_LIST *List,
> + IN REDFISH_HTTP_CACHE_DATA *Data
> + )
> +{
> + if ((List == NULL) || (Data == NULL)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: delete: %s\n", __func__,
> Data->Uri));
> +
> + RemoveEntryList (&Data->List);
> + --List->Count;
> +
> + return ReleaseHttpCacheData (Data);
> +}
> +
> +/**
> + Add new cache by given URI and HTTP response to specify List.
> +
> + @param[in] List Target cache list to add.
> + @param[in] Uri The URI string matching to this cache data.
> + @param[in] Response HTTP response.
> +
> + @retval EFI_SUCCESS Cache data is added.
> + @retval Others Fail to add cache data.
> +
> +**/
> +EFI_STATUS
> +AddHttpCacheData (
> + IN REDFISH_HTTP_CACHE_LIST *List,
> + IN EFI_STRING Uri,
> + IN REDFISH_RESPONSE *Response
> + )
> +{
> + REDFISH_HTTP_CACHE_DATA *NewData;
> + REDFISH_HTTP_CACHE_DATA *OldData;
> + REDFISH_HTTP_CACHE_DATA *UnusedData;
> + REDFISH_RESPONSE *NewResponse;
> +
> + if ((List == NULL) || IS_EMPTY_STRING (Uri) || (Response == NULL)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + //
> + // If same cache data exist, replace it with latest one.
> + //
> + OldData = FindHttpCacheData (&List->Head, Uri);
> + if (OldData != NULL) {
> + DeleteHttpCacheData (List, OldData);
> + }
> +
> + //
> + // Check capacity
> + //
> + if (List->Count >= List->Capacity) {
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: list is full and retire unused
> cache\n", __func__));
> + UnusedData = FindUnusedHttpCacheData (&List->Head);
> + if (UnusedData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + DeleteHttpCacheData (List, UnusedData);
> + }
> +
> + //
> + // Clone a local copy
> + //
> + NewResponse = CloneRedfishResponse (Response);
> + if (NewResponse == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + NewData = NewHttpCacheData (Uri, NewResponse);
> + if (NewData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + InsertTailList (&List->Head, &NewData->List);
> + ++List->Count;
> +
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache(%d/%d) %s\n",
> __func__, List->Count, List->Capacity, NewData->Uri));
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + Release all cache from list.
> +
> + @param[in] CacheList The list to be released.
> +
> + @retval EFI_SUCCESS All cache data are released.
> + @retval EFI_INVALID_PARAMETER CacheList is NULL.
> +
> +**/
> +EFI_STATUS
> +ReleaseCacheList (
> + IN REDFISH_HTTP_CACHE_LIST *CacheList
> + )
> +{
> + LIST_ENTRY *List;
> + LIST_ENTRY *Next;
> + REDFISH_HTTP_CACHE_DATA *Data;
> +
> + if (CacheList == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if (IsListEmpty (&CacheList->Head)) {
> + return EFI_SUCCESS;
> + }
> +
> + Data = NULL;
> + Next = NULL;
> + List = GetFirstNode (&CacheList->Head);
> + while (!IsNull (&CacheList->Head, List)) {
> + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> + Next = GetNextNode (&CacheList->Head, List);
> +
> + DeleteHttpCacheData (CacheList, Data);
> +
> + List = Next;
> + }
> +
> + return EFI_SUCCESS;
> +}
> diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
> b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
> new file mode 100644
> index 0000000000..39958d4865
> --- /dev/null
> +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
> @@ -0,0 +1,1344 @@
> +/** @file
> + RedfishHttpDxe produces EdkIIRedfishHttpProtocol
> + for EDK2 Redfish Feature driver to do HTTP operations.
> +
> + Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include "RedfishHttpDxe.h"
> +#include "RedfishHttpData.h"
> +#include "RedfishHttpOperation.h"
> +
> +REDFISH_HTTP_CACHE_PRIVATE *mRedfishHttpCachePrivate = NULL;
> +
> +/**
> + Debug output the cache list.
> +
> + @param[in] Msg Debug message string.
> + @param[in] ErrorLevel Output error level.
> + @param[in] CacheList Target list to dump.
> +
> + @retval EFI_SUCCESS Debug dump finished.
> + @retval EFI_INVALID_PARAMETER HttpCacheList is NULL.
> +
> +**/
> +EFI_STATUS
> +DebugPrintHttpCacheList (
> + IN CONST CHAR8 *Msg,
> + IN UINTN ErrorLevel,
> + IN REDFISH_HTTP_CACHE_LIST *CacheList
> + )
> +{
> + LIST_ENTRY *List;
> + REDFISH_HTTP_CACHE_DATA *Data;
> + UINTN Index;
> +
> + if (CacheList == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if (!IS_EMPTY_STRING (Msg)) {
> + DEBUG ((ErrorLevel, "%a\n", Msg));
> + }
> +
> + if (IsListEmpty (&CacheList->Head)) {
> + DEBUG ((ErrorLevel, "list is empty\n"));
> + return EFI_NOT_FOUND;
> + }
> +
> + DEBUG ((ErrorLevel, "list count: %d capacity: %d\n", CacheList->Count,
> CacheList->Capacity));
> + Data = NULL;
> + Index = 0;
> + List = GetFirstNode (&CacheList->Head);
> + while (!IsNull (&CacheList->Head, List)) {
> + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> +
> + DEBUG ((ErrorLevel, "%d) Uri: %s Hit: %d\n", ++Index, Data->Uri, Data-
> >HitCount));
> +
> + List = GetNextNode (&CacheList->Head, List);
> + }
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> +
> + Check HTTP status code to see if we like to retry HTTP request or not.
> +
> + @param[in] StatusCode HTTP status code.
> +
> + @retval BOOLEAN Return true when we like to retry request.
> + Return false when we don't want to retry request.
> +
> +**/
> +BOOLEAN
> +RedfishRetryRequired (
> + IN EFI_HTTP_STATUS_CODE *StatusCode
> + )
> +{
> + if (StatusCode == NULL) {
> + return TRUE;
> + }
> +
> + if ((*StatusCode == HTTP_STATUS_500_INTERNAL_SERVER_ERROR) ||
> + (*StatusCode == HTTP_STATUS_UNSUPPORTED_STATUS))
> + {
> + return TRUE;
> + }
> +
> + return FALSE;
> +}
> +
> +/**
> +
> + Convert Unicode string to ASCII string. It's call responsibility to release
> returned buffer.
> +
> + @param[in] UnicodeStr Unicode string to convert.
> +
> + @retval CHAR8 * ASCII string returned.
> + @retval NULL Errors occur.
> +
> +**/
> +CHAR8 *
> +StringUnicodeToAscii (
> + IN EFI_STRING UnicodeStr
> + )
> +{
> + CHAR8 *AsciiStr;
> + UINTN AsciiStrSize;
> + EFI_STATUS Status;
> +
> + if (IS_EMPTY_STRING (UnicodeStr)) {
> + return NULL;
> + }
> +
> + AsciiStrSize = StrLen (UnicodeStr) + 1;
> + AsciiStr = AllocateZeroPool (AsciiStrSize);
> + if (AsciiStr == NULL) {
> + return NULL;
> + }
> +
> + Status = UnicodeStrToAsciiStrS (UnicodeStr, AsciiStr, AsciiStrSize);
> + if (EFI_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "UnicodeStrToAsciiStrS failed: %r\n", Status));
> + FreePool (AsciiStr);
> + return NULL;
> + }
> +
> + return AsciiStr;
> +}
> +
> +/**
> + Return HTTP method in ASCII string. Caller does not need
> + to free returned string buffer.
> +
> + @param[in] Method HTTP method.
> +
> + @retval CHAR8 * Method in string.
> +**/
> +CHAR8 *
> +HttpMethodToString (
> + IN EFI_HTTP_METHOD Method
> + )
> +{
> + switch (Method) {
> + case HttpMethodGet:
> + return HTTP_METHOD_GET;
> + break;
> + case HttpMethodPost:
> + return HTTP_METHOD_POST;
> + break;
> + case HttpMethodPatch:
> + return HTTP_METHOD_PATCH;
> + break;
> + case HttpMethodPut:
> + return HTTP_METHOD_PUT;
> + break;
> + case HttpMethodDelete:
> + return HTTP_METHOD_DELETE;
> + break;
> + default:
> + break;
> + }
> +
> + return "Unknown";
> +}
> +
> +/**
> + Report HTTP communication error via report status code.
> +
> + @param[in] Method HTTP method.
> + @param[in] Uri The URI which has failure.
> + @param[in] HttpStatusCode HTTP status code.
> +
> +**/
> +VOID
> +ReportHttpError (
> + IN EFI_HTTP_METHOD Method,
> + IN EFI_STRING Uri,
> + IN EFI_HTTP_STATUS_CODE *HttpStatusCode OPTIONAL
> + )
> +{
> + CHAR8 ErrorMsg[REDFISH_ERROR_MSG_MAX];
> +
> + if (IS_EMPTY_STRING (Uri)) {
> + DEBUG ((DEBUG_ERROR, "%a: no URI to report error status\n",
> __func__));
> + return;
> + }
> +
> + //
> + // Report failure of URI and HTTP status code.
> + //
> + AsciiSPrint (ErrorMsg, sizeof (ErrorMsg), REDFISH_HTTP_ERROR_REPORT,
> HttpMethodToString (Method), (HttpStatusCode == NULL ?
> HTTP_STATUS_UNSUPPORTED_STATUS : *HttpStatusCode), Uri);
> + DEBUG ((DEBUG_ERROR, "%a\n", ErrorMsg));
> + //
> + // TODO:
> + // Below PI status code is approved by PIWG and wait for specification
> published.
> + // We will uncomment below report status code after PI status code get
> published.
> + // REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4483
> + //
> + // REPORT_STATUS_CODE_WITH_EXTENDED_DATA (
> + // EFI_ERROR_CODE | EFI_ERROR_MAJOR,
> + // EFI_COMPUTING_UNIT_MANAGEABILITY |
> EFI_MANAGEABILITY_EC_REDFISH_COMMUNICATION_ERROR,
> + // ErrorMsg,
> + // AsciiStrSize (ErrorMsg)
> + // );
> +}
> +
> +/**
> + This function create Redfish service. It's caller's responsibility to free
> returned
> + Redfish service by calling FreeService ().
> +
> + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL
> instance.
> + @param[in] RedfishConfigServiceInfo Redfish config service information.
> +
> + @retval REDFISH_SERVICE Redfish service is created.
> + @retval NULL Errors occur.
> +
> +**/
> +REDFISH_SERVICE
> +EFIAPI
> +RedfishCreateRedfishService (
> + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> + IN REDFISH_CONFIG_SERVICE_INFORMATION *RedfishConfigServiceInfo
> + )
> +{
> + EFI_STATUS Status;
> + REDFISH_HTTP_CACHE_PRIVATE *Private;
> + REDFISH_SERVICE_PRIVATE *NewService;
> + CHAR8 *AsciiLocation;
> + CHAR8 *Host;
> + CHAR8 *BasicAuthString;
> + UINTN BasicAuthStrSize;
> + CHAR8 *EncodedAuthString;
> + UINTN EncodedAuthStrSize;
> + EDKII_REDFISH_AUTH_METHOD AuthMethod;
> + CHAR8 *Username;
> + CHAR8 *Password;
> + UINTN UsernameSize;
> + UINTN PasswordSize;
> + EFI_REST_EX_PROTOCOL *RestEx;
> +
> + if ((This == NULL) || (RedfishConfigServiceInfo == NULL)) {
> + return NULL;
> + }
> +
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: service location: %s\n",
> __func__, RedfishConfigServiceInfo->RedfishServiceLocation));
> +
> + Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> + BasicAuthString = NULL;
> + EncodedAuthString = NULL;
> + Username = NULL;
> + Password = NULL;
> + NewService = NULL;
> + AsciiLocation = NULL;
> + Host = NULL;
> + BasicAuthStrSize = 0;
> + EncodedAuthStrSize = 0;
> + UsernameSize = 0;
> + PasswordSize = 0;
> +
> + //
> + // Build host and host name from service location
> + //
> + if (!IS_EMPTY_STRING (RedfishConfigServiceInfo->RedfishServiceLocation))
> {
> + AsciiLocation = StringUnicodeToAscii (RedfishConfigServiceInfo-
> >RedfishServiceLocation);
> + if (AsciiLocation == NULL) {
> + goto ON_RELEASE;
> + }
> +
> + Host = AllocateZeroPool (REDFISH_HOST_NAME_MAX);
> + if (AsciiLocation == NULL) {
> + goto ON_RELEASE;
> + }
> +
> + if (RedfishConfigServiceInfo->RedfishServiceUseHttps) {
> + AsciiSPrint (Host, REDFISH_HOST_NAME_MAX, "https://%a",
> AsciiLocation);
> + } else {
> + AsciiSPrint (Host, REDFISH_HOST_NAME_MAX, "http://%a",
> AsciiLocation);
> + }
> +
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Host: %a\n", __func__,
> Host));
> + }
> +
> + //
> + // Find Rest Ex protocol
> + //
> + if (RedfishConfigServiceInfo->RedfishServiceRestExHandle != NULL) {
> + Status = gBS->HandleProtocol (
> + RedfishConfigServiceInfo->RedfishServiceRestExHandle,
> + &gEfiRestExProtocolGuid,
> + (VOID **)&RestEx
> + );
> + } else {
> + DEBUG ((DEBUG_ERROR, "%a: Rest Ex protocol is not available\n",
> __func__));
> + goto ON_RELEASE;
> + }
> +
> + //
> + // Get credential
> + //
> + if (Private->CredentialProtocol == NULL) {
> + //
> + // No credential available on this system.
> + //
> + DEBUG ((DEBUG_WARN, "%a: no credential protocol available\n",
> __func__));
> + } else {
> + Status = Private->CredentialProtocol->GetAuthInfo (
> + Private->CredentialProtocol,
> + &AuthMethod,
> + &Username,
> + &Password
> + );
> + if (EFI_ERROR (Status) || IS_EMPTY_STRING (Username) ||
> IS_EMPTY_STRING (Password)) {
> + DEBUG ((DEBUG_ERROR, "%a: cannot get authentication information:
> %r\n", __func__, Status));
> + goto ON_RELEASE;
> + } else {
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Auth method: 0x%x
> username: %a password: %a\n", __func__, AuthMethod, Username,
> Password));
> +
> + //
> + // Perform base64 encoding (RFC 7617)
> + //
> + UsernameSize = AsciiStrSize (Username);
> + PasswordSize = AsciiStrSize (Password);
> + BasicAuthStrSize = UsernameSize + PasswordSize; // one byte taken from
> null-terminator for ':'
> + BasicAuthString = AllocateZeroPool (BasicAuthStrSize);
> + if (BasicAuthString == NULL) {
> + goto ON_RELEASE;
> + }
> +
> + AsciiSPrint (
> + BasicAuthString,
> + BasicAuthStrSize,
> + "%a:%a",
> + Username,
> + Password
> + );
> +
> + Status = Base64Encode (
> + (CONST UINT8 *)BasicAuthString,
> + BasicAuthStrSize,
> + EncodedAuthString,
> + &EncodedAuthStrSize
> + );
> + if ((Status == EFI_BUFFER_TOO_SMALL) && (EncodedAuthStrSize > 0)) {
> + EncodedAuthString = AllocateZeroPool (EncodedAuthStrSize);
> + if (EncodedAuthString == NULL) {
> + goto ON_RELEASE;
> + }
> +
> + Status = Base64Encode (
> + (CONST UINT8 *)BasicAuthString,
> + BasicAuthStrSize,
> + EncodedAuthString,
> + &EncodedAuthStrSize
> + );
> + if (EFI_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "%a: Base64Encode failure: %r\n", __func__,
> Status));
> + }
> +
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Basic authorization:
> %a\n", __func__, EncodedAuthString));
> + } else {
> + DEBUG ((DEBUG_ERROR, "%a: Base64Encode failure: %r\n", __func__,
> Status));
> + goto ON_RELEASE;
> + }
> + }
> + }
> +
> + NewService = CreateRedfishService (Host, AsciiLocation,
> EncodedAuthString, NULL, RestEx);
> + if (NewService == NULL) {
> + DEBUG ((DEBUG_ERROR, "%a: CreateRedfishService\n", __func__));
> + }
> +
> +ON_RELEASE:
> +
> + if (BasicAuthString != NULL) {
> + ZeroMem (BasicAuthString, BasicAuthStrSize);
> + FreePool (BasicAuthString);
> + }
> +
> + if (EncodedAuthString != NULL) {
> + ZeroMem (BasicAuthString, EncodedAuthStrSize);
> + FreePool (EncodedAuthString);
> + }
> +
> + if (Username != NULL) {
> + ZeroMem (Username, UsernameSize);
> + FreePool (Username);
> + }
> +
> + if (Password != NULL) {
> + ZeroMem (Password, PasswordSize);
> + FreePool (Password);
> + }
> +
> + if (AsciiLocation != NULL) {
> + FreePool (AsciiLocation);
> + }
> +
> + if (Host != NULL) {
> + FreePool (Host);
> + }
> +
> + return NewService;
> +}
> +
> +/**
> + This function free resources in Redfish service. RedfishService is no longer
> available
> + after this function returns successfully.
> +
> + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL
> instance.
> + @param[in] RedfishService Pointer to Redfish service to be released.
> +
> + @retval EFI_SUCCESS Resrouce is released successfully.
> + @retval Others Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishFreeRedfishService (
> + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> + IN REDFISH_SERVICE RedfishService
> + )
> +{
> + REDFISH_SERVICE_PRIVATE *Service;
> +
> + if ((This == NULL) || (RedfishService == NULL)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + Service = (REDFISH_SERVICE_PRIVATE *)RedfishService;
> + if (Service->Signature != REDFISH_HTTP_SERVICE_SIGNATURE) {
> + DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
> + }
> +
> + return ReleaseRedfishService (Service);
> +}
> +
> +/**
> + This function returns JSON value in given RedfishPayload. Returned JSON
> value
> + is a reference to the JSON value in RedfishPayload. Any modification to
> returned
> + JSON value will change JSON value in RedfishPayload.
> +
> + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL
> instance.
> + @param[in] RedfishPayload Pointer to Redfish payload.
> +
> + @retval EDKII_JSON_VALUE JSON value is returned.
> + @retval NULL Errors occur.
> +
> +**/
> +EDKII_JSON_VALUE
> +EFIAPI
> +RedfishJsonInRedfishPayload (
> + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> + IN REDFISH_PAYLOAD RedfishPayload
> + )
> +{
> + REDFISH_PAYLOAD_PRIVATE *Payload;
> +
> + if ((This == NULL) || (RedfishPayload == NULL)) {
> + return NULL;
> + }
> +
> + Payload = (REDFISH_PAYLOAD_PRIVATE *)RedfishPayload;
> + if (Payload->Signature != REDFISH_HTTP_PAYLOAD_SIGNATURE) {
> + DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
> + }
> +
> + return Payload->JsonValue;
> +}
> +
> +/**
> + Perform HTTP GET to Get redfish resource from given resource URI with
> + cache mechanism supported. It's caller's responsibility to free Response
> + by calling FreeResponse ().
> +
> + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> + @param[in] Service Redfish service instance to perform HTTP GET.
> + @param[in] Uri Target resource URI.
> + @param[in] Request Additional request context. This is optional.
> + @param[out] Response HTTP response from redfish service.
> + @param[in] UseCache If it is TRUE, this function will search for
> + cache first. If it is FALSE, this function
> + will query Redfish URI directly.
> +
> + @retval EFI_SUCCESS Resrouce is returned successfully.
> + @retval Others Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishGetResource (
> + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> + IN REDFISH_SERVICE Service,
> + IN EFI_STRING Uri,
> + IN REDFISH_REQUEST *Request OPTIONAL,
> + OUT REDFISH_RESPONSE *Response,
> + IN BOOLEAN UseCache
> + )
> +{
> + EFI_STATUS Status;
> + REDFISH_HTTP_CACHE_DATA *CacheData;
> + UINTN RetryCount;
> + REDFISH_HTTP_CACHE_PRIVATE *Private;
> +
> + if ((This == NULL) || (Service == NULL) || (Response == NULL) ||
> IS_EMPTY_STRING (Uri)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Get URI: %s cache: %a\n",
> __func__, Uri, (UseCache ? "true" : "false")));
> +
> + Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> + CacheData = NULL;
> + RetryCount = 0;
> + ZeroMem (Response, sizeof (REDFISH_RESPONSE));
> +
> + if (Private->CacheDisabled) {
> + UseCache = FALSE;
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache is disabled by
> PCD!\n", __func__));
> + }
> +
> + //
> + // Search for cache list.
> + //
> + if (UseCache) {
> + CacheData = FindHttpCacheData (&Private->CacheList.Head, Uri);
> + if (CacheData != NULL) {
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: cache hit!
> %s\n", __func__, Uri));
> +
> + //
> + // Copy cached response to caller's buffer.
> + //
> + Status = CopyRedfishResponse (CacheData->Response, Response);
> + CacheData->HitCount += 1;
> + return Status;
> + }
> + }
> +
> + //
> + // Get resource from redfish service.
> + //
> + do {
> + RetryCount += 1;
> + Status = HttpSendReceive (
> + Service,
> + Uri,
> + HttpMethodGet,
> + Request,
> + Response
> + );
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request:
> %s :%r\n", __func__, Uri, Status));
> + if (!EFI_ERROR (Status) || (RetryCount >= Private-
> >RetrySetting.MaximumRetryGet)) {
> + break;
> + }
> +
> + //
> + // Retry when BMC is not ready.
> + //
> + if ((Response->StatusCode != NULL)) {
> + DEBUG_CODE (
> + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> + );
> +
> + if (!RedfishRetryRequired (Response->StatusCode)) {
> + break;
> + }
> +
> + //
> + // Release response for next round of request.
> + //
> + This->FreeResponse (This, Response);
> + }
> +
> + DEBUG ((DEBUG_WARN, "%a: RedfishGetByUriEx failed, retry (%d/%d)\n",
> __func__, RetryCount, Private->RetrySetting.MaximumRetryGet));
> + if (Private->RetrySetting.RetryWait > 0) {
> + gBS->Stall (Private->RetrySetting.RetryWait);
> + }
> + } while (TRUE);
> +
> + if (EFI_ERROR (Status)) {
> + DEBUG_CODE (
> + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> + );
> + //
> + // Report status code for Redfish failure
> + //
> + ReportHttpError (HttpMethodGet, Uri, Response->StatusCode);
> + DEBUG ((DEBUG_ERROR, "%a: get %s failed (%d/%d): %r\n", __func__, Uri,
> RetryCount, Private->RetrySetting.MaximumRetryGet, Status));
> + goto ON_RELEASE;
> + }
> +
> + if (!Private->CacheDisabled) {
> + //
> + // Keep response in cache list
> + //
> + Status = AddHttpCacheData (&Private->CacheList, Uri, Response);
> + if (EFI_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "%a: failed to cache %s: %r\n", __func__, Uri,
> Status));
> + goto ON_RELEASE;
> + }
> +
> + DEBUG_CODE (
> + DebugPrintHttpCacheList (__func__,
> REDFISH_HTTP_CACHE_DEBUG_DUMP, &Private->CacheList);
> + );
> + }
> +
> +ON_RELEASE:
> +
> + return Status;
> +}
> +
> +/**
> + This function free resources in Request. Request is no longer available
> + after this function returns successfully.
> +
> + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> + @param[in] Request HTTP request to be released.
> +
> + @retval EFI_SUCCESS Resrouce is released successfully.
> + @retval Others Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishFreeRequest (
> + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> + IN REDFISH_REQUEST *Request
> + )
> +{
> + if ((This == NULL) || (Request == NULL)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: entry\n", __func__));
> +
> + return ReleaseRedfishRequest (Request);
> +}
> +
> +/**
> + This function free resources in given Response.
> +
> + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> + @param[in] Response HTTP response to be released.
> +
> + @retval EFI_SUCCESS Resrouce is released successfully.
> + @retval Others Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishFreeResponse (
> + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> + IN REDFISH_RESPONSE *Response
> + )
> +{
> + if ((This == NULL) || (Response == NULL)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: entry\n", __func__));
> +
> + return ReleaseRedfishResponse (Response);
> +}
> +
> +/**
> + This function expire the cached response of given URI.
> +
> + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> + @param[in] Uri Target response of URI.
> +
> + @retval EFI_SUCCESS Target response is expired successfully.
> + @retval Others Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishExpireResponse (
> + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> + IN EFI_STRING Uri
> + )
> +{
> + REDFISH_HTTP_CACHE_PRIVATE *Private;
> + REDFISH_HTTP_CACHE_DATA *CacheData;
> +
> + if ((This == NULL) || IS_EMPTY_STRING (Uri)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: expire URI: %s\n", __func__,
> Uri));
> +
> + Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> +
> + CacheData = FindHttpCacheData (&Private->CacheList.Head, Uri);
> + if (CacheData == NULL) {
> + return EFI_NOT_FOUND;
> + }
> +
> + return DeleteHttpCacheData (&Private->CacheList, CacheData);
> +}
> +
> +/**
> + Perform HTTP PATCH to send redfish resource to given resource URI.
> + It's caller's responsibility to free Response by calling FreeResponse ().
> +
> + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> + @param[in] Service Redfish service instance to perform HTTP PATCH.
> + @param[in] Uri Target resource URI.
> + @param[in] Content Data to patch.
> + @param[in] ContentSize Size of the Content to be send to Redfish service.
> + This is optional. When ContentSize is 0, ContentSize
> + is the size of Content.
> + @param[in] ContentType Type of the Content to be send to Redfish
> service.
> + This is optional. When ContentType is NULL, content
> + type HTTP_CONTENT_TYPE_APP_JSON will be used.
> + @param[out] Response HTTP response from redfish service.
> +
> + @retval EFI_SUCCESS Resrouce is returned successfully.
> + @retval Others Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishPatchResource (
> + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> + IN REDFISH_SERVICE Service,
> + IN EFI_STRING Uri,
> + IN CHAR8 *Content,
> + IN UINTN ContentSize OPTIONAL,
> + IN CHAR8 *ContentType OPTIONAL,
> + OUT REDFISH_RESPONSE *Response
> + )
> +{
> + EFI_STATUS Status;
> + UINTN RetryCount;
> + REDFISH_REQUEST Request;
> + REDFISH_HTTP_CACHE_PRIVATE *Private;
> +
> + if ((This == NULL) || (Service == NULL) || (Response == NULL) ||
> IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Patch URI: %s\n", __func__,
> Uri));
> +
> + Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> + RetryCount = 0;
> + ZeroMem (Response, sizeof (REDFISH_RESPONSE));
> + ZeroMem (&Request, sizeof (REDFISH_REQUEST));
> +
> + Request.Content = Content;
> + Request.ContentLength = ContentSize;
> + Request.ContentType = ContentType;
> +
> + //
> + // Patch resource to redfish service.
> + //
> + do {
> + RetryCount += 1;
> + Status = HttpSendReceive (
> + Service,
> + Uri,
> + HttpMethodPatch,
> + &Request,
> + Response
> + );
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request:
> %s :%r\n", __func__, Uri, Status));
> + if (!EFI_ERROR (Status) || (RetryCount >= Private-
> >RetrySetting.MaximumRetryPatch)) {
> + break;
> + }
> +
> + //
> + // Retry when BMC is not ready.
> + //
> + if ((Response->StatusCode != NULL)) {
> + DEBUG_CODE (
> + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> + );
> +
> + if (!RedfishRetryRequired (Response->StatusCode)) {
> + break;
> + }
> +
> + //
> + // Release response for next round of request.
> + //
> + This->FreeResponse (This, Response);
> + }
> +
> + DEBUG ((DEBUG_WARN, "%a: RedfishPatchToUriEx failed, retry
> (%d/%d)\n", __func__, RetryCount, Private-
> >RetrySetting.MaximumRetryPatch));
> + if (Private->RetrySetting.RetryWait > 0) {
> + gBS->Stall (Private->RetrySetting.RetryWait);
> + }
> + } while (TRUE);
> +
> + //
> + // Redfish resource is updated. Automatically expire the cached response
> + // so application can directly get resource from Redfish service again.
> + //
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire
> URI: %s\n", __func__, Uri));
> + RedfishExpireResponse (This, Uri);
> +
> + if (EFI_ERROR (Status)) {
> + DEBUG_CODE (
> + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> + );
> + //
> + // Report status code for Redfish failure
> + //
> + ReportHttpError (HttpMethodPatch, Uri, Response->StatusCode);
> + DEBUG ((DEBUG_ERROR, "%a: patch %s failed (%d/%d): %r\n", __func__,
> Uri, RetryCount, Private->RetrySetting.MaximumRetryPatch, Status));
> + goto ON_RELEASE;
> + }
> +
> +ON_RELEASE:
> +
> + return Status;
> +}
> +
> +/**
> + Perform HTTP PUT to send redfish resource to given resource URI.
> + It's caller's responsibility to free Response by calling FreeResponse ().
> +
> + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> + @param[in] Service Redfish service instance to perform HTTP PUT.
> + @param[in] Uri Target resource URI.
> + @param[in] Content Data to put.
> + @param[in] ContentSize Size of the Content to be send to Redfish service.
> + This is optional. When ContentSize is 0, ContentSize
> + is the size of Content.
> + @param[in] ContentType Type of the Content to be send to Redfish
> service.
> + This is optional. When ContentType is NULL, content
> + type HTTP_CONTENT_TYPE_APP_JSON will be used.
> + @param[out] Response HTTP response from redfish service.
> +
> + @retval EFI_SUCCESS Resrouce is returned successfully.
> + @retval Others Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishPutResource (
> + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> + IN REDFISH_SERVICE Service,
> + IN EFI_STRING Uri,
> + IN CHAR8 *Content,
> + IN UINTN ContentSize OPTIONAL,
> + IN CHAR8 *ContentType OPTIONAL,
> + OUT REDFISH_RESPONSE *Response
> + )
> +{
> + EFI_STATUS Status;
> + UINTN RetryCount;
> + REDFISH_REQUEST Request;
> + REDFISH_HTTP_CACHE_PRIVATE *Private;
> +
> + if ((This == NULL) || (Service == NULL) || (Response == NULL) ||
> IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Put URI: %s\n", __func__,
> Uri));
> +
> + Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> + RetryCount = 0;
> + ZeroMem (Response, sizeof (REDFISH_RESPONSE));
> + ZeroMem (&Request, sizeof (REDFISH_REQUEST));
> +
> + Request.Content = Content;
> + Request.ContentLength = ContentSize;
> + Request.ContentType = ContentType;
> +
> + //
> + // Patch resource to redfish service.
> + //
> + do {
> + RetryCount += 1;
> + Status = HttpSendReceive (
> + Service,
> + Uri,
> + HttpMethodPut,
> + &Request,
> + Response
> + );
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request:
> %s :%r\n", __func__, Uri, Status));
> + if (!EFI_ERROR (Status) || (RetryCount >= Private-
> >RetrySetting.MaximumRetryPut)) {
> + break;
> + }
> +
> + //
> + // Retry when BMC is not ready.
> + //
> + if ((Response->StatusCode != NULL)) {
> + DEBUG_CODE (
> + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> + );
> +
> + if (!RedfishRetryRequired (Response->StatusCode)) {
> + break;
> + }
> +
> + //
> + // Release response for next round of request.
> + //
> + This->FreeResponse (This, Response);
> + }
> +
> + DEBUG ((DEBUG_WARN, "%a: RedfishPutToUri failed, retry (%d/%d)\n",
> __func__, RetryCount, Private->RetrySetting.MaximumRetryPut));
> + if (Private->RetrySetting.RetryWait > 0) {
> + gBS->Stall (Private->RetrySetting.RetryWait);
> + }
> + } while (TRUE);
> +
> + //
> + // Redfish resource is updated. Automatically expire the cached response
> + // so application can directly get resource from Redfish service again.
> + //
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire
> URI: %s\n", __func__, Uri));
> + RedfishExpireResponse (This, Uri);
> +
> + if (EFI_ERROR (Status)) {
> + DEBUG_CODE (
> + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> + );
> + //
> + // Report status code for Redfish failure
> + //
> + ReportHttpError (HttpMethodPut, Uri, Response->StatusCode);
> + DEBUG ((DEBUG_ERROR, "%a: put %s failed (%d/%d): %r\n", __func__,
> Uri, RetryCount, Private->RetrySetting.MaximumRetryPut, Status));
> + goto ON_RELEASE;
> + }
> +
> +ON_RELEASE:
> +
> + return Status;
> +}
> +
> +/**
> + Perform HTTP POST to send redfish resource to given resource URI.
> + It's caller's responsibility to free Response by calling FreeResponse ().
> +
> + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> + @param[in] Service Redfish service instance to perform HTTP POST.
> + @param[in] Uri Target resource URI.
> + @param[in] Content Data to post.
> + @param[in] ContentSize Size of the Content to be send to Redfish service.
> + This is optional. When ContentSize is 0, ContentSize
> + is the size of Content.
> + @param[in] ContentType Type of the Content to be send to Redfish
> service.
> + This is optional. When ContentType is NULL, content
> + type HTTP_CONTENT_TYPE_APP_JSON will be used.
> + @param[out] Response HTTP response from redfish service.
> +
> + @retval EFI_SUCCESS Resrouce is returned successfully.
> + @retval Others Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishPostResource (
> + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> + IN REDFISH_SERVICE Service,
> + IN EFI_STRING Uri,
> + IN CHAR8 *Content,
> + IN UINTN ContentSize OPTIONAL,
> + IN CHAR8 *ContentType OPTIONAL,
> + OUT REDFISH_RESPONSE *Response
> + )
> +{
> + EFI_STATUS Status;
> + UINTN RetryCount;
> + REDFISH_REQUEST Request;
> + REDFISH_HTTP_CACHE_PRIVATE *Private;
> +
> + if ((This == NULL) || (Service == NULL) || (Response == NULL) ||
> IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Post URI: %s\n", __func__,
> Uri));
> +
> + Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> + RetryCount = 0;
> + ZeroMem (Response, sizeof (REDFISH_RESPONSE));
> + ZeroMem (&Request, sizeof (REDFISH_REQUEST));
> +
> + Request.Content = Content;
> + Request.ContentLength = ContentSize;
> + Request.ContentType = ContentType;
> +
> + //
> + // Patch resource to redfish service.
> + //
> + do {
> + RetryCount += 1;
> + Status = HttpSendReceive (
> + Service,
> + Uri,
> + HttpMethodPost,
> + &Request,
> + Response
> + );
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request:
> %s :%r\n", __func__, Uri, Status));
> + if (!EFI_ERROR (Status) || (RetryCount >= Private-
> >RetrySetting.MaximumRetryPost)) {
> + break;
> + }
> +
> + //
> + // Retry when BMC is not ready.
> + //
> + if ((Response->StatusCode != NULL)) {
> + DEBUG_CODE (
> + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> + );
> +
> + if (!RedfishRetryRequired (Response->StatusCode)) {
> + break;
> + }
> +
> + //
> + // Release response for next round of request.
> + //
> + This->FreeResponse (This, Response);
> + }
> +
> + DEBUG ((DEBUG_WARN, "%a: RedfishPostToUri failed, retry (%d/%d)\n",
> __func__, RetryCount, Private->RetrySetting.MaximumRetryPost));
> + if (Private->RetrySetting.RetryWait > 0) {
> + gBS->Stall (Private->RetrySetting.RetryWait);
> + }
> + } while (TRUE);
> +
> + //
> + // Redfish resource is updated. Automatically expire the cached response
> + // so application can directly get resource from Redfish service again.
> + //
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire
> URI: %s\n", __func__, Uri));
> + RedfishExpireResponse (This, Uri);
> +
> + if (EFI_ERROR (Status)) {
> + DEBUG_CODE (
> + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> + );
> + //
> + // Report status code for Redfish failure
> + //
> + ReportHttpError (HttpMethodPost, Uri, Response->StatusCode);
> + DEBUG ((DEBUG_ERROR, "%a: post %s failed (%d/%d): %r\n", __func__,
> Uri, RetryCount, Private->RetrySetting.MaximumRetryPost, Status));
> + goto ON_RELEASE;
> + }
> +
> +ON_RELEASE:
> +
> + return Status;
> +}
> +
> +/**
> + Perform HTTP DELETE to delete redfish resource on given resource URI.
> + It's caller's responsibility to free Response by calling FreeResponse ().
> +
> + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> + @param[in] Service Redfish service instance to perform HTTP DELETE.
> + @param[in] Uri Target resource URI.
> + @param[in] Content JSON represented properties to be deleted. This is
> + optional.
> + @param[in] ContentSize Size of the Content to be send to Redfish service.
> + This is optional. When ContentSize is 0, ContentSize
> + is the size of Content if Content is not NULL.
> + @param[in] ContentType Type of the Content to be send to Redfish
> service.
> + This is optional. When Content is not NULL and
> + ContentType is NULL, content type
> HTTP_CONTENT_TYPE_APP_JSON
> + will be used.
> + @param[out] Response HTTP response from redfish service.
> +
> + @retval EFI_SUCCESS Resrouce is returned successfully.
> + @retval Others Errors occur.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishDeleteResource (
> + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> + IN REDFISH_SERVICE Service,
> + IN EFI_STRING Uri,
> + IN CHAR8 *Content OPTIONAL,
> + IN UINTN ContentSize OPTIONAL,
> + IN CHAR8 *ContentType OPTIONAL,
> + OUT REDFISH_RESPONSE *Response
> + )
> +{
> + EFI_STATUS Status;
> + UINTN RetryCount;
> + REDFISH_REQUEST Request;
> + REDFISH_HTTP_CACHE_PRIVATE *Private;
> +
> + if ((This == NULL) || (Service == NULL) || (Response == NULL) ||
> IS_EMPTY_STRING (Uri)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Delete URI: %s\n", __func__,
> Uri));
> +
> + Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> + RetryCount = 0;
> + ZeroMem (Response, sizeof (REDFISH_RESPONSE));
> + ZeroMem (&Request, sizeof (REDFISH_REQUEST));
> +
> + Request.Content = Content;
> + Request.ContentLength = ContentSize;
> + Request.ContentType = ContentType;
> +
> + //
> + // Patch resource to redfish service.
> + //
> + do {
> + RetryCount += 1;
> + Status = HttpSendReceive (
> + Service,
> + Uri,
> + HttpMethodDelete,
> + &Request,
> + Response
> + );
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request:
> %s :%r\n", __func__, Uri, Status));
> + if (!EFI_ERROR (Status) || (RetryCount >= Private-
> >RetrySetting.MaximumRetryDelete)) {
> + break;
> + }
> +
> + //
> + // Retry when BMC is not ready.
> + //
> + if ((Response->StatusCode != NULL)) {
> + DEBUG_CODE (
> + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> + );
> +
> + if (!RedfishRetryRequired (Response->StatusCode)) {
> + break;
> + }
> +
> + //
> + // Release response for next round of request.
> + //
> + This->FreeResponse (This, Response);
> + }
> +
> + DEBUG ((DEBUG_WARN, "%a: RedfishDeleteByUri failed, retry
> (%d/%d)\n", __func__, RetryCount, Private-
> >RetrySetting.MaximumRetryDelete));
> + if (Private->RetrySetting.RetryWait > 0) {
> + gBS->Stall (Private->RetrySetting.RetryWait);
> + }
> + } while (TRUE);
> +
> + //
> + // Redfish resource is updated. Automatically expire the cached response
> + // so application can directly get resource from Redfish service again.
> + //
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire
> URI: %s\n", __func__, Uri));
> + RedfishExpireResponse (This, Uri);
> +
> + if (EFI_ERROR (Status)) {
> + DEBUG_CODE (
> + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> + );
> + //
> + // Report status code for Redfish failure
> + //
> + ReportHttpError (HttpMethodDelete, Uri, Response->StatusCode);
> + DEBUG ((DEBUG_ERROR, "%a: delete %s failed (%d/%d): %r\n", __func__,
> Uri, RetryCount, Private->RetrySetting.MaximumRetryDelete, Status));
> + goto ON_RELEASE;
> + }
> +
> +ON_RELEASE:
> +
> + return Status;
> +}
> +
> +EDKII_REDFISH_HTTP_PROTOCOL mEdkIIRedfishHttpProtocol = {
> + EDKII_REDFISH_HTTP_PROTOCOL_REVISION,
> + RedfishCreateRedfishService,
> + RedfishFreeRedfishService,
> + RedfishJsonInRedfishPayload,
> + RedfishGetResource,
> + RedfishPatchResource,
> + RedfishPutResource,
> + RedfishPostResource,
> + RedfishDeleteResource,
> + RedfishFreeRequest,
> + RedfishFreeResponse,
> + RedfishExpireResponse
> +};
> +
> +/**
> + Unloads an image.
> +
> + @param[in] ImageHandle Handle that identifies the image to be
> unloaded.
> +
> + @retval EFI_SUCCESS The image has been unloaded.
> + @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image
> handle.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishHttpDriverUnload (
> + IN EFI_HANDLE ImageHandle
> + )
> +{
> + if (mRedfishHttpCachePrivate == NULL) {
> + return EFI_SUCCESS;
> + }
> +
> + if (!IsListEmpty (&mRedfishHttpCachePrivate->CacheList.Head)) {
> + ReleaseCacheList (&mRedfishHttpCachePrivate->CacheList);
> + }
> +
> + gBS->UninstallMultipleProtocolInterfaces (
> + ImageHandle,
> + &gEdkIIRedfishHttpProtocolGuid,
> + &mRedfishHttpCachePrivate->Protocol,
> + NULL
> + );
> +
> + FreePool (mRedfishHttpCachePrivate);
> + mRedfishHttpCachePrivate = NULL;
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + This is a EDKII_REDFISH_CREDENTIAL_PROTOCOL notification event handler.
> +
> + @param[in] Event Event whose notification function is being invoked.
> + @param[in] Context Pointer to the notification function's context.
> +
> +**/
> +VOID
> +EFIAPI
> +CredentialProtocolInstalled (
> + IN EFI_EVENT Event,
> + IN VOID *Context
> + )
> +{
> + EFI_STATUS Status;
> + REDFISH_HTTP_CACHE_PRIVATE *Private;
> +
> + Private = (REDFISH_HTTP_CACHE_PRIVATE *)Context;
> + if (Private->Signature != REDFISH_HTTP_DRIVER_SIGNATURE) {
> + DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
> + return;
> + }
> +
> + //
> + // Locate HII database protocol.
> + //
> + Status = gBS->LocateProtocol (
> + &gEdkIIRedfishCredentialProtocolGuid,
> + NULL,
> + (VOID **)&Private->CredentialProtocol
> + );
> + if (EFI_ERROR (Status)) {
> + return;
> + }
> +
> + gBS->CloseEvent (Event);
> +}
> +
> +/**
> + Main entry for this driver.
> +
> + @param[in] ImageHandle Image handle this driver.
> + @param[in] SystemTable Pointer to SystemTable.
> +
> + @retval EFI_SUCCESS This function always complete successfully.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishHttpEntryPoint (
> + IN EFI_HANDLE ImageHandle,
> + IN EFI_SYSTEM_TABLE *SystemTable
> + )
> +{
> + EFI_STATUS Status;
> + VOID *Registration;
> +
> + if (mRedfishHttpCachePrivate != NULL) {
> + return EFI_ALREADY_STARTED;
> + }
> +
> + mRedfishHttpCachePrivate = AllocateZeroPool (sizeof
> (REDFISH_HTTP_CACHE_PRIVATE));
> + if (mRedfishHttpCachePrivate == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + //
> + // Initial cache list and protocol instance.
> + //
> + mRedfishHttpCachePrivate->Signature =
> REDFISH_HTTP_DRIVER_SIGNATURE;
> + mRedfishHttpCachePrivate->ImageHandle = ImageHandle;
> + CopyMem (&mRedfishHttpCachePrivate->Protocol,
> &mEdkIIRedfishHttpProtocol, sizeof (EDKII_REDFISH_HTTP_PROTOCOL));
> + mRedfishHttpCachePrivate->CacheList.Capacity =
> REDFISH_HTTP_CACHE_LIST_SIZE;
> + mRedfishHttpCachePrivate->CacheList.Count = 0x00;
> + mRedfishHttpCachePrivate->CacheDisabled = PcdGetBool
> (PcdHttpCacheDisabled);
> + InitializeListHead (&mRedfishHttpCachePrivate->CacheList.Head);
> +
> + //
> + // Get retry settings
> + //
> + mRedfishHttpCachePrivate->RetrySetting.MaximumRetryGet = PcdGet16
> (PcdHttpGetRetry);
> + mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPut = PcdGet16
> (PcdHttpPutRetry);
> + mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPatch = PcdGet16
> (PcdHttpPatchRetry);
> + mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPost = PcdGet16
> (PcdHttpPostRetry);
> + mRedfishHttpCachePrivate->RetrySetting.MaximumRetryDelete = PcdGet16
> (PcdHttpDeleteRetry);
> + mRedfishHttpCachePrivate->RetrySetting.RetryWait = PcdGet16
> (PcdHttpRetryWaitInSecond) * 1000000U;
> +
> + //
> + // Install the gEdkIIRedfishHttpProtocolGuid onto Handle.
> + //
> + Status = gBS->InstallMultipleProtocolInterfaces (
> + &mRedfishHttpCachePrivate->ImageHandle,
> + &gEdkIIRedfishHttpProtocolGuid,
> + &mRedfishHttpCachePrivate->Protocol,
> + NULL
> + );
> + if (EFI_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "%a: cannot install Redfish http protocol: %r\n",
> __func__, Status));
> + RedfishHttpDriverUnload (ImageHandle);
> + return Status;
> + }
> +
> + //
> + // Install protocol notification if credential protocol is installed.
> + //
> + mRedfishHttpCachePrivate->NotifyEvent = EfiCreateProtocolNotifyEvent (
> + &gEdkIIRedfishCredentialProtocolGuid,
> + TPL_CALLBACK,
> + CredentialProtocolInstalled,
> + mRedfishHttpCachePrivate,
> + &Registration
> + );
> + if (mRedfishHttpCachePrivate->NotifyEvent == NULL) {
> + DEBUG ((DEBUG_ERROR, "%a: failed to create protocol notification for
> gEdkIIRedfishCredentialProtocolGuid\n", __func__));
> + ASSERT (FALSE);
> + RedfishHttpDriverUnload (ImageHandle);
> + return Status;
> + }
> +
> + return EFI_SUCCESS;
> +}
> diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
> b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
> new file mode 100644
> index 0000000000..5652818d16
> --- /dev/null
> +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
> @@ -0,0 +1,693 @@
> +/** @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;
> + }
> +
> + for (Index = 0; Index < SrcHeaderCount; Index++) {
> + (*DstHeaders)[Index].FieldName = AllocateCopyPool (AsciiStrSize
> (SrcHeaders[Index].FieldName), SrcHeaders[Index].FieldName);
> + if ((*DstHeaders)[Index].FieldName == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + (*DstHeaders)[Index].FieldValue = AllocateCopyPool (AsciiStrSize
> (SrcHeaders[Index].FieldValue), SrcHeaders[Index].FieldValue);
> + if ((*DstHeaders)[Index].FieldValue == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + *DstHeaderCount += 1;
> + }
> +
> + 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 Resrouce 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 Resrouce 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 Resrouce 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;
> +}
> diff --git a/RedfishPkg/Redfish.fdf.inc b/RedfishPkg/Redfish.fdf.inc
> index 3e5a77766e..5cbe3592fd 100644
> --- a/RedfishPkg/Redfish.fdf.inc
> +++ b/RedfishPkg/Redfish.fdf.inc
> @@ -6,7 +6,7 @@
> # to be built in the firmware volume.
> #
> # (C) Copyright 2020-2021 Hewlett Packard Enterprise Development LP<BR>
> -# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> #
> # SPDX-License-Identifier: BSD-2-Clause-Patent
> #
> @@ -20,4 +20,5 @@
> INF RedfishPkg/RedfishConfigHandler/RedfishConfigHandlerDriver.inf
> INF RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf
> INF
> MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf
> + INF RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
> !endif
> --
> 2.34.1
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115692): https://edk2.groups.io/g/devel/message/115692
Mute This Topic: https://groups.io/mt/104463404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [edk2-devel] [PATCH 2/6] RedfishPkg: implement Redfish HTTP protocol
2024-02-21 1:51 ` Chang, Abner via groups.io
@ 2024-02-22 9:16 ` Nickle Wang via groups.io
0 siblings, 0 replies; 3+ messages in thread
From: Nickle Wang via groups.io @ 2024-02-22 9:16 UTC (permalink / raw)
To: Chang, Abner, devel@edk2.groups.io; +Cc: Igor Kulchytskyy, Nick Ramirez
Thanks for catching this typo, Abner. Version 2 patch is sent.
Regards,
Nickle
> -----Original Message-----
> From: Chang, Abner <Abner.Chang@amd.com>
> Sent: Wednesday, February 21, 2024 9:52 AM
> To: Nickle Wang <nicklew@nvidia.com>; devel@edk2.groups.io
> Cc: Igor Kulchytskyy <igork@ami.com>; Nick Ramirez <nramirez@nvidia.com>
> Subject: RE: [PATCH 2/6] RedfishPkg: implement Redfish HTTP protocol
>
> External email: Use caution opening links or attachments
>
>
> [AMD Official Use Only - General]
>
> One comment below.
>
> > -----Original Message-----
> > From: Nickle Wang <nicklew@nvidia.com>
> > Sent: Tuesday, February 20, 2024 2:41 PM
> > To: devel@edk2.groups.io
> > Cc: Igor Kulchytskyy <igork@ami.com>; Chang, Abner
> > <Abner.Chang@amd.com>; Nick Ramirez <nramirez@nvidia.com>
> > Subject: [PATCH 2/6] RedfishPkg: implement Redfish HTTP protocol
> >
> > Caution: This message originated from an External Source. Use proper caution
> > when opening attachments, clicking links, or responding.
> >
> >
> > 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>
> > ---
> > RedfishPkg/RedfishPkg.dec | 7 +-
> > RedfishPkg/RedfishComponents.dsc.inc | 3 +-
> > RedfishPkg/RedfishPkg.dsc | 2 +
> > RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf | 73 +
> > RedfishPkg/RedfishHttpDxe/RedfishHttpData.h | 256 ++++
> > RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h | 44 +
> > .../RedfishHttpDxe/RedfishHttpOperation.h | 76 +
> > RedfishPkg/RedfishHttpDxe/RedfishHttpData.c | 667 ++++++++
> > RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c | 1344
> > +++++++++++++++++
> > .../RedfishHttpDxe/RedfishHttpOperation.c | 693 +++++++++
> > RedfishPkg/Redfish.fdf.inc | 3 +-
> > 11 files changed, 3164 insertions(+), 4 deletions(-)
> > create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
> > create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
> > create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
> > create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
> > create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
> > create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
> > create mode 100644 RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
> >
> > diff --git a/RedfishPkg/RedfishPkg.dec b/RedfishPkg/RedfishPkg.dec
> > index 9b424efdf3..114f8d2ad8 100644
> > --- a/RedfishPkg/RedfishPkg.dec
> > +++ b/RedfishPkg/RedfishPkg.dec
> > @@ -157,8 +157,11 @@
> > # set to EFI_REST_EX_PROTOCOL.
> > #
> >
> > gEfiRedfishPkgTokenSpaceGuid.PcdRedfishSendReceiveTimeout|5000|UINT3
> > 2|0x00001009
> > - ## This is used to enable HTTP content encoding on Redfish communication.
> > -
> > gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|TRUE|BO
> > OLEAN|0x0000100A
> > + #
> > + # This PCD string is introduced for platform developer to set the encoding
> > method supported by BMC Redfish.
> > + # Currently only "None" and "gzip" are supported.
> > + #
> > +
> > gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|"None"|V
> > OID*|0x0000100A
> > #
> > # Use below PCDs to control Redfhs HTTP protocol.
> > #
> > diff --git a/RedfishPkg/RedfishComponents.dsc.inc
> > b/RedfishPkg/RedfishComponents.dsc.inc
> > index 464ffc8606..d6c5b73d7f 100644
> > --- a/RedfishPkg/RedfishComponents.dsc.inc
> > +++ b/RedfishPkg/RedfishComponents.dsc.inc
> > @@ -7,7 +7,7 @@
> > # "RedfishDefines.dsc.inc".
> > #
> > # (C) Copyright 2020-2021 Hewlett Packard Enterprise Development LP<BR>
> > -# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights
> > reserved.
> > +# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> > reserved.
> > #
> > # SPDX-License-Identifier: BSD-2-Clause-Patent
> > #
> > @@ -28,4 +28,5 @@
> > RedfishPkg/RedfishConfigHandler/RedfishConfigHandlerDriver.inf
> > RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf
> > MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf
> > + RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
> > !endif
> > diff --git a/RedfishPkg/RedfishPkg.dsc b/RedfishPkg/RedfishPkg.dsc
> > index 25ed193182..5849e7cf9e 100644
> > --- a/RedfishPkg/RedfishPkg.dsc
> > +++ b/RedfishPkg/RedfishPkg.dsc
> > @@ -45,6 +45,8 @@
> >
> > UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesL
> > ib.inf
> >
> > RedfishPlatformCredentialLib|RedfishPkg/Library/PlatformCredentialLibNull/
> > PlatformCredentialLibNull.inf
> >
> > RedfishContentCodingLib|RedfishPkg/Library/RedfishContentCodingLibNull/
> > RedfishContentCodingLibNull.inf
> > +
> > ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeR
> > eportStatusCodeLib.inf
> > + SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf
> >
> > # NULL instance of IPMI related library.
> > IpmiLib|MdeModulePkg/Library/BaseIpmiLibNull/BaseIpmiLibNull.inf
> > diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
> > b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
> > new file mode 100644
> > index 0000000000..c7dfdffacf
> > --- /dev/null
> > +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
> > @@ -0,0 +1,73 @@
> > +## @file
> > +# RedfishHttpDxe is the DXE driver which provides
> > +# EdkIIRedfishHttpProtocol to EDK2 Redfish Feature
> > +# drivers for HTTP operation.
> > +#
> > +# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> > reserved.
> > +#
> > +# SPDX-License-Identifier: BSD-2-Clause-Patent
> > +#
> > +##
> > +
> > +[Defines]
> > + INF_VERSION = 0x0001000b
> > + BASE_NAME = RedfishHttpDxe
> > + FILE_GUID = 85ADB2F1-DA93-47D4-AF4F-3D920D9BD2C0
> > + MODULE_TYPE = DXE_DRIVER
> > + VERSION_STRING = 1.0
> > + ENTRY_POINT = RedfishHttpEntryPoint
> > + UNLOAD_IMAGE = RedfishHttpDriverUnload
> > +
> > +#
> > +# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 RISCV64
> > +#
> > +
> > +[Sources]
> > + RedfishHttpData.c
> > + RedfishHttpData.h
> > + RedfishHttpDxe.c
> > + RedfishHttpDxe.h
> > + RedfishHttpOperation.c
> > + RedfishHttpOperation.h
> > +
> > +[Packages]
> > + MdePkg/MdePkg.dec
> > + MdeModulePkg/MdeModulePkg.dec
> > + NetworkPkg/NetworkPkg.dec
> > + RedfishPkg/RedfishPkg.dec
> > +
> > +[LibraryClasses.ARM]
> > + ArmSoftFloatLib
> > +
> > +[LibraryClasses]
> > + BaseLib
> > + BaseMemoryLib
> > + RedfishContentCodingLib
> > + DebugLib
> > + HttpLib
> > + JsonLib
> > + MemoryAllocationLib
> > + PrintLib
> > + RedfishDebugLib
> > + ReportStatusCodeLib
> > + UefiBootServicesTableLib
> > + UefiDriverEntryPoint
> > + UefiLib
> > +
> > +[Protocols]
> > + gEdkIIRedfishHttpProtocolGuid ## PRODUCED
> > + gEdkIIRedfishCredentialProtocolGuid ## CONSUMES
> > + gEfiRestExProtocolGuid ## CONSUEMS
> > +
> > +[Pcd]
> > + gEfiRedfishPkgTokenSpaceGuid.PcdHttpGetRetry
> > + gEfiRedfishPkgTokenSpaceGuid.PcdHttpPutRetry
> > + gEfiRedfishPkgTokenSpaceGuid.PcdHttpPatchRetry
> > + gEfiRedfishPkgTokenSpaceGuid.PcdHttpPostRetry
> > + gEfiRedfishPkgTokenSpaceGuid.PcdHttpDeleteRetry
> > + gEfiRedfishPkgTokenSpaceGuid.PcdHttpRetryWaitInSecond
> > + gEfiRedfishPkgTokenSpaceGuid.PcdHttpCacheDisabled
> > + gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding
> > +
> > +[Depex]
> > + TRUE
> > diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
> > b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
> > new file mode 100644
> > index 0000000000..6be610142e
> > --- /dev/null
> > +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.h
> > @@ -0,0 +1,256 @@
> > +/** @file
> > + Definitions of RedfishHttpData
> > +
> > + Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> > reserved.
> > +
> > + SPDX-License-Identifier: BSD-2-Clause-Patent
> > +
> > +**/
> > +
> > +#ifndef EDKII_REDFISH_HTTP_DATA_H_
> > +#define EDKII_REDFISH_HTTP_DATA_H_
> > +
> > +#include "RedfishHttpDxe.h"
> > +
> > +#define REDFISH_HTTP_DRIVER_SIGNATURE SIGNATURE_32 ('r', 'f', 'h', 'p')
> > +#define REDFISH_HTTP_CACHE_SIGNATURE SIGNATURE_32 ('r', 'f', 'c', 'h')
> > +#define REDFISH_HTTP_SERVICE_SIGNATURE SIGNATURE_32 ('r', 'f', 's', 'v')
> > +#define REDFISH_HTTP_PAYLOAD_SIGNATURE SIGNATURE_32 ('r', 'f', 'p', 'l')
> > +#define REDFISH_HTTP_BASIC_AUTH_STR "Basic "
> > +
> > +///
> > +/// REDFISH_SERVICE_PRIVATE definition.
> > +///
> > +typedef struct {
> > + UINT32 Signature;
> > + CHAR8 *Host;
> > + CHAR8 *HostName;
> > + CHAR8 *BasicAuth;
> > + CHAR8 *SessionToken;
> > + EFI_REST_EX_PROTOCOL *RestEx;
> > +} REDFISH_SERVICE_PRIVATE;
> > +
> > +///
> > +/// REDFISH_PAYLOAD_PRIVATE definition.
> > +///
> > +typedef struct {
> > + UINT32 Signature;
> > + REDFISH_SERVICE_PRIVATE *Service;
> > + EDKII_JSON_VALUE JsonValue;
> > +} REDFISH_PAYLOAD_PRIVATE;
> > +
> > +///
> > +/// Definition of REDFISH_HTTP_CACHE_DATA
> > +///
> > +typedef struct {
> > + UINT32 Signature;
> > + LIST_ENTRY List;
> > + EFI_STRING Uri;
> > + UINTN HitCount;
> > + REDFISH_RESPONSE *Response;
> > +} REDFISH_HTTP_CACHE_DATA;
> > +
> > +#define REDFISH_HTTP_CACHE_FROM_LIST(a) CR (a,
> > REDFISH_HTTP_CACHE_DATA, List, REDFISH_HTTP_CACHE_SIGNATURE)
> > +
> > +///
> > +/// Definition of REDFISH_HTTP_CACHE_LIST
> > +///
> > +typedef struct {
> > + LIST_ENTRY Head;
> > + UINTN Count;
> > + UINTN Capacity;
> > +} REDFISH_HTTP_CACHE_LIST;
> > +
> > +///
> > +/// Definition of REDFISH_HTTP_RETRY_SETTING
> > +///
> > +typedef struct {
> > + UINT16 MaximumRetryGet;
> > + UINT16 MaximumRetryPut;
> > + UINT16 MaximumRetryPost;
> > + UINT16 MaximumRetryPatch;
> > + UINT16 MaximumRetryDelete;
> > + UINTN RetryWait;
> > +} REDFISH_HTTP_RETRY_SETTING;
> > +
> > +///
> > +/// Definition of REDFISH_HTTP_CACHE_PRIVATE
> > +///
> > +typedef struct {
> > + UINT32 Signature;
> > + EFI_HANDLE ImageHandle;
> > + BOOLEAN CacheDisabled;
> > + EFI_EVENT NotifyEvent;
> > + REDFISH_HTTP_CACHE_LIST CacheList;
> > + EDKII_REDFISH_HTTP_PROTOCOL Protocol;
> > + EDKII_REDFISH_CREDENTIAL_PROTOCOL *CredentialProtocol;
> > + REDFISH_HTTP_RETRY_SETTING RetrySetting;
> > +} REDFISH_HTTP_CACHE_PRIVATE;
> > +
> > +#define REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS(a) CR (a,
> > REDFISH_HTTP_CACHE_PRIVATE, Protocol,
> > REDFISH_HTTP_DRIVER_SIGNATURE)
> > +
> > +/**
> > + Search on given ListHeader for given URI string.
> > +
> > + @param[in] ListHeader Target list to search.
> > + @param[in] Uri Target URI to search.
> > +
> > + @retval REDFISH_HTTP_CACHE_DATA Target cache data is found.
> > + @retval NULL No cache data with given URI is found.
> > +
> > +**/
> > +REDFISH_HTTP_CACHE_DATA *
> > +FindHttpCacheData (
> > + IN LIST_ENTRY *ListHeader,
> > + IN EFI_STRING Uri
> > + );
> > +
> > +/**
> > + This function copy the data in SrcResponse to DstResponse.
> > +
> > + @param[in] SrcResponse Source Response to copy.
> > + @param[out] DstResponse Destination Response.
> > +
> > + @retval EFI_SUCCESS Response is copied successfully.
> > + @retval Others Error occurs.
> > +
> > +**/
> > +EFI_STATUS
> > +CopyRedfishResponse (
> > + IN REDFISH_RESPONSE *SrcResponse,
> > + OUT REDFISH_RESPONSE *DstResponse
> > + );
> > +
> > +/**
> > + Release all cache from list.
> > +
> > + @param[in] CacheList The list to be released.
> > +
> > + @retval EFI_SUCCESS All cache data are released.
> > + @retval EFI_INVALID_PARAMETER CacheList is NULL.
> > +
> > +**/
> > +EFI_STATUS
> > +ReleaseCacheList (
> > + IN REDFISH_HTTP_CACHE_LIST *CacheList
> > + );
> > +
> > +/**
> > + Add new cache by given URI and HTTP response to specify List.
> > +
> > + @param[in] List Target cache list to add.
> > + @param[in] Uri The URI string matching to this cache data.
> > + @param[in] Response HTTP response.
> > +
> > + @retval EFI_SUCCESS Cache data is added.
> > + @retval Others Fail to add cache data.
> > +
> > +**/
> > +EFI_STATUS
> > +AddHttpCacheData (
> > + IN REDFISH_HTTP_CACHE_LIST *List,
> > + IN EFI_STRING Uri,
> > + IN REDFISH_RESPONSE *Response
> > + );
> > +
> > +/**
> > + Delete a cache data by given cache instance.
> > +
> > + @param[in] List Target cache list to be removed.
> > + @param[in] Data Pointer to the instance to be deleted.
> > +
> > + @retval EFI_SUCCESS Cache data is removed.
> > + @retval Others Fail to remove cache data.
> > +
> > +**/
> > +EFI_STATUS
> > +DeleteHttpCacheData (
> > + IN REDFISH_HTTP_CACHE_LIST *List,
> > + IN REDFISH_HTTP_CACHE_DATA *Data
> > + );
> > +
> > +/**
> > + This function release Redfish Payload.
> > +
> > + @param[in] Payload Pointer to payload instance.
> > +
> > + @retval EFI_SUCCESS Payload is released.
> > + @retval Others Error occurs.
> > +
> > +**/
> > +EFI_STATUS
> > +ReleaseRedfishPayload (
> > + IN REDFISH_PAYLOAD_PRIVATE *Payload
> > + );
> > +
> > +/**
> > + This function creat new payload. Server and JsonObj are
> > + copied to newly created payload.
> > +
> > + @param[in] Service Pointer to Service instance.
> > + @param[in] JsonObj Pointer to JSON object.
> > +
> > + @retval REDFISH_PAYLOAD_PRIVATE Newly created payload.
> > + @retval NULL Error occurs.
> > +
> > +**/
> > +REDFISH_PAYLOAD_PRIVATE *
> > +CreateRedfishPayload (
> > + IN REDFISH_SERVICE_PRIVATE *Service,
> > + IN EDKII_JSON_VALUE JsonValue
> > + );
> > +
> > +/**
> > + This function release Redfish Service.
> > +
> > + @param[in] Service Pointer to service instance.
> > +
> > + @retval EFI_SUCCESS Service is released.
> > + @retval Others Error occurs.
> > +
> > +**/
> > +EFI_STATUS
> > +ReleaseRedfishService (
> > + IN REDFISH_SERVICE_PRIVATE *Service
> > + );
> > +
> > +/**
> > + This function creat new service. Host and HostName are copied to
> > + newly created service instance.
> > +
> > + @param[in] Host Host string.
> > + @param[in] HostName Hostname string.
> > + @param[in] BasicAuth Basic Authorization string.
> > + @param[in] SessionToken Session token string.
> > + @param[in] RestEx Rest EX protocol instance.
> > +
> > + @retval REDFISH_PAYLOAD_PRIVATE Newly created service.
> > + @retval NULL Error occurs.
> > +
> > +**/
> > +REDFISH_SERVICE_PRIVATE *
> > +CreateRedfishService (
> > + IN CHAR8 *Host,
> > + IN CHAR8 *HostName,
> > + IN CHAR8 *BasicAuth OPTIONAL,
> > + IN CHAR8 *SessionToken OPTIONAL,
> > + IN EFI_REST_EX_PROTOCOL *RestEx
> > + );
> > +
> > +/**
> > + This function update session token in Redfish Service.
> > +
> > + @param[in] Service Pointer to service instance.
> > + @param[in] Token Session token.
> > +
> > + @retval EFI_SUCCESS Session token is updated.
> > + @retval Others Error occurs.
> > +
> > +**/
> > +EFI_STATUS
> > +UpdateSessionToken (
> > + IN REDFISH_SERVICE_PRIVATE *Service,
> > + IN CHAR8 *Token
> > + );
> > +
> > +#endif
> > diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
> > b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
> > new file mode 100644
> > index 0000000000..cf6ba9cb47
> > --- /dev/null
> > +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.h
> > @@ -0,0 +1,44 @@
> > +/** @file
> > + Definitions of RedfishHttpDxe
> > +
> > + Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> > reserved.
> > +
> > + SPDX-License-Identifier: BSD-2-Clause-Patent
> > +
> > +**/
> > +
> > +#ifndef EDKII_REDFISH_HTTP_DXE_H_
> > +#define EDKII_REDFISH_HTTP_DXE_H_
> > +
> > +#include <Uefi.h>
> > +#include <IndustryStandard/Http11.h>
> > +
> > +#include <Library/UefiLib.h>
> > +#include <Library/BaseLib.h>
> > +#include <Library/BaseMemoryLib.h>
> > +#include <Library/RedfishContentCodingLib.h>
> > +#include <Library/DebugLib.h>
> > +#include <Library/HttpLib.h>
> > +#include <Library/JsonLib.h>
> > +#include <Library/UefiBootServicesTableLib.h>
> > +#include <Library/MemoryAllocationLib.h>
> > +#include <Library/RedfishDebugLib.h>
> > +#include <Library/ReportStatusCodeLib.h>
> > +#include <Library/PrintLib.h>
> > +
> > +#include <Protocol/Http.h>
> > +#include <Protocol/EdkIIRedfishHttpProtocol.h>
> > +#include <Protocol/EdkIIRedfishCredential.h>
> > +#include <Protocol/RestEx.h>
> > +
> > +#define IS_EMPTY_STRING(a) ((a) == NULL || (a)[0] == '\0')
> > +#define REDFISH_HTTP_CACHE_LIST_SIZE 0x80
> > +#define REDFISH_ERROR_MSG_MAX 128
> > +#define REDFISH_DEBUG_STRING_LENGTH 200
> > +#define REDFISH_HOST_NAME_MAX 64 // IPv6 maximum length (39)
> > + "https://" (8) + port number (maximum 5)
> > +#define REDFISH_HTTP_ERROR_REPORT "Redfish HTTP %a failure(0x%x):
> > %s"
> > +#define REDFISH_HTTP_CACHE_DEBUG DEBUG_MANAGEABILITY
> > +#define REDFISH_HTTP_CACHE_DEBUG_DUMP DEBUG_MANAGEABILITY
> > +#define REDFISH_HTTP_CACHE_DEBUG_REQUEST DEBUG_MANAGEABILITY
> > +
> > +#endif
> > diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
> > b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
> > new file mode 100644
> > index 0000000000..e4fc053e96
> > --- /dev/null
> > +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.h
> > @@ -0,0 +1,76 @@
> > +/** @file
> > + Definitions of RedfishHttpOperation
> > +
> > + Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights
> > reserved.
> > +
> > + SPDX-License-Identifier: BSD-2-Clause-Patent
> > +
> > +**/
> > +
> > +#ifndef EDKII_REDFISH_HTTP_OP_H_
> > +#define EDKII_REDFISH_HTTP_OP_H_
>
> Nickle, I missed this when I reviewed it offline.
> Could you please update the macro to align with header file name,
> EDKII_REDFISH_HTTP_OPERATION_H_
>
> Thanks
> Abner
>
>
> > +
> > +#include "RedfishHttpDxe.h"
> > +
> > +#define REDFISH_CONTENT_LENGTH_SIZE 80
> > +#define REDFISH_COMMON_HEADER_SIZE 5
> > +#define REDFISH_HTTP_HEADER_ODATA_VERSION_STR "OData-Version"
> > +#define REDFISH_HTTP_HEADER_ODATA_VERSION_VALUE "4.0"
> > +#define REDFISH_HTTP_HEADER_USER_AGENT_VALUE "edk2redfish"
> > +#define REDFISH_HTTP_HEADER_CONNECTION_STR "Connection"
> > +#define REDFISH_HTTP_HEADER_CONNECTION_VALUE "Keep-Alive"
> > +#define REDFISH_HTTP_CONTENT_ENCODING_NONE "None"
> > +
> > +/**
> > + 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 Resrouce is released successfully.
> > + @retval Others Errors occur.
> > +
> > +**/
> > +EFI_STATUS
> > +ReleaseRedfishRequest (
> > + IN REDFISH_REQUEST *Request
> > + );
> > +
> > +/**
> > + This function free resources in given Response.
> > +
> > + @param[in] Response HTTP response to be released.
> > +
> > + @retval EFI_SUCCESS Resrouce is released successfully.
> > + @retval Others Errors occur.
> > +
> > +**/
> > +EFI_STATUS
> > +ReleaseRedfishResponse (
> > + IN REDFISH_RESPONSE *Response
> > + );
> > +
> > +/**
> > + 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
> > + );
> > +
> > +#endif
> > diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
> > b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
> > new file mode 100644
> > index 0000000000..bf95e9f8d4
> > --- /dev/null
> > +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpData.c
> > @@ -0,0 +1,667 @@
> > +/** @file
> > + RedfishHttpData handles internal data to support Redfish HTTP protocol.
> > +
> > + Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> > reserved.
> > +
> > + SPDX-License-Identifier: BSD-2-Clause-Patent
> > +
> > +**/
> > +
> > +#include "RedfishHttpData.h"
> > +#include "RedfishHttpOperation.h"
> > +
> > +/**
> > + This function update session token in Redfish Service.
> > +
> > + @param[in] Service Pointer to service instance.
> > + @param[in] Token Session token.
> > +
> > + @retval EFI_SUCCESS Session token is updated.
> > + @retval Others Error occurs.
> > +
> > +**/
> > +EFI_STATUS
> > +UpdateSessionToken (
> > + IN REDFISH_SERVICE_PRIVATE *Service,
> > + IN CHAR8 *Token
> > + )
> > +{
> > + if ((Service == NULL) || IS_EMPTY_STRING (Token)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + if (Service->SessionToken != NULL) {
> > + FreePool (Service->SessionToken);
> > + }
> > +
> > + Service->SessionToken = AllocateCopyPool (AsciiStrSize (Token), Token);
> > + if (Service->SessionToken == NULL) {
> > + return EFI_OUT_OF_RESOURCES;
> > + }
> > +
> > + return EFI_SUCCESS;
> > +}
> > +
> > +/**
> > + This function release Redfish Service.
> > +
> > + @param[in] Service Pointer to service instance.
> > +
> > + @retval EFI_SUCCESS Service is released.
> > + @retval Others Error occurs.
> > +
> > +**/
> > +EFI_STATUS
> > +ReleaseRedfishService (
> > + IN REDFISH_SERVICE_PRIVATE *Service
> > + )
> > +{
> > + if (Service == NULL) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + if (Service->Host != NULL) {
> > + FreePool (Service->Host);
> > + }
> > +
> > + if (Service->HostName != NULL) {
> > + FreePool (Service->HostName);
> > + }
> > +
> > + if (Service->BasicAuth != NULL) {
> > + ZeroMem (Service->BasicAuth, AsciiStrSize (Service->BasicAuth));
> > + FreePool (Service->BasicAuth);
> > + }
> > +
> > + if (Service->SessionToken != NULL) {
> > + ZeroMem (Service->SessionToken, AsciiStrSize (Service->SessionToken));
> > + FreePool (Service->SessionToken);
> > + }
> > +
> > + FreePool (Service);
> > +
> > + return EFI_SUCCESS;
> > +}
> > +
> > +/**
> > + This function creat new service. Host and HostName are copied to
> > + newly created service instance.
> > +
> > + @param[in] Host Host string.
> > + @param[in] HostName Hostname string.
> > + @param[in] BasicAuth Basic Authorization string.
> > + @param[in] SessionToken Session token string.
> > + @param[in] RestEx Rest EX protocol instance.
> > +
> > + @retval REDFISH_PAYLOAD_PRIVATE Newly created service.
> > + @retval NULL Error occurs.
> > +
> > +**/
> > +REDFISH_SERVICE_PRIVATE *
> > +CreateRedfishService (
> > + IN CHAR8 *Host,
> > + IN CHAR8 *HostName,
> > + IN CHAR8 *BasicAuth OPTIONAL,
> > + IN CHAR8 *SessionToken OPTIONAL,
> > + IN EFI_REST_EX_PROTOCOL *RestEx
> > + )
> > +{
> > + REDFISH_SERVICE_PRIVATE *NewService;
> > + UINTN AuthStrSize;
> > +
> > + if (IS_EMPTY_STRING (Host) || IS_EMPTY_STRING (HostName) || (RestEx
> > == NULL)) {
> > + return NULL;
> > + }
> > +
> > + NewService = AllocateZeroPool (sizeof (REDFISH_SERVICE_PRIVATE));
> > + if (NewService == NULL) {
> > + return NULL;
> > + }
> > +
> > + NewService->Signature = REDFISH_HTTP_SERVICE_SIGNATURE;
> > + NewService->Host = AllocateCopyPool (AsciiStrSize (Host), Host);
> > + if (NewService->Host == NULL) {
> > + goto ON_ERROR;
> > + }
> > +
> > + NewService->HostName = AllocateCopyPool (AsciiStrSize (HostName),
> > HostName);
> > + if (NewService->HostName == NULL) {
> > + goto ON_ERROR;
> > + }
> > +
> > + if (!IS_EMPTY_STRING (BasicAuth)) {
> > + AuthStrSize = AsciiStrSize (BasicAuth) + AsciiStrLen
> > (REDFISH_HTTP_BASIC_AUTH_STR);
> > + NewService->BasicAuth = AllocateZeroPool (AuthStrSize);
> > + if (NewService->BasicAuth == NULL) {
> > + goto ON_ERROR;
> > + }
> > +
> > + AsciiSPrint (NewService->BasicAuth, AuthStrSize, "%a%a",
> > REDFISH_HTTP_BASIC_AUTH_STR, BasicAuth);
> > + }
> > +
> > + if (!IS_EMPTY_STRING (SessionToken)) {
> > + NewService->SessionToken = AllocateCopyPool (AsciiStrSize
> > (SessionToken), SessionToken);
> > + if (NewService->SessionToken == NULL) {
> > + goto ON_ERROR;
> > + }
> > + }
> > +
> > + NewService->RestEx = RestEx;
> > +
> > + return NewService;
> > +
> > +ON_ERROR:
> > +
> > + ReleaseRedfishService (NewService);
> > +
> > + return NULL;
> > +}
> > +
> > +/**
> > + This function release Redfish Payload.
> > +
> > + @param[in] Payload Pointer to payload instance.
> > +
> > + @retval EFI_SUCCESS Payload is released.
> > + @retval Others Error occurs.
> > +
> > +**/
> > +EFI_STATUS
> > +ReleaseRedfishPayload (
> > + IN REDFISH_PAYLOAD_PRIVATE *Payload
> > + )
> > +{
> > + if (Payload == NULL) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + if (Payload->Service != NULL) {
> > + ReleaseRedfishService (Payload->Service);
> > + }
> > +
> > + if (Payload->JsonValue != NULL) {
> > + JsonValueFree (Payload->JsonValue);
> > + }
> > +
> > + FreePool (Payload);
> > +
> > + return EFI_SUCCESS;
> > +}
> > +
> > +/**
> > + This function creat new payload. Server and JsonObj are
> > + copied to newly created payload.
> > +
> > + @param[in] Service Pointer to Service instance.
> > + @param[in] JsonValue Pointer to JSON value.
> > +
> > + @retval REDFISH_PAYLOAD_PRIVATE Newly created payload.
> > + @retval NULL Error occurs.
> > +
> > +**/
> > +REDFISH_PAYLOAD_PRIVATE *
> > +CreateRedfishPayload (
> > + IN REDFISH_SERVICE_PRIVATE *Service,
> > + IN EDKII_JSON_VALUE JsonValue
> > + )
> > +{
> > + REDFISH_PAYLOAD_PRIVATE *NewPayload;
> > +
> > + if ((Service == NULL) || (JsonValue == NULL)) {
> > + return NULL;
> > + }
> > +
> > + NewPayload = AllocateZeroPool (sizeof (REDFISH_PAYLOAD_PRIVATE));
> > + if (NewPayload == NULL) {
> > + return NULL;
> > + }
> > +
> > + NewPayload->Signature = REDFISH_HTTP_PAYLOAD_SIGNATURE;
> > + NewPayload->Service = CreateRedfishService (Service->Host, Service-
> > >HostName, Service->BasicAuth, Service->SessionToken, Service->RestEx);
> > + if (NewPayload->Service == NULL) {
> > + goto ON_ERROR;
> > + }
> > +
> > + NewPayload->JsonValue = JsonValueClone (JsonValue);
> > + if (NewPayload->JsonValue == NULL) {
> > + goto ON_ERROR;
> > + }
> > +
> > + return NewPayload;
> > +
> > +ON_ERROR:
> > +
> > + ReleaseRedfishPayload (NewPayload);
> > +
> > + return NULL;
> > +}
> > +
> > +/**
> > + This function copy the data in SrcResponse to DstResponse.
> > +
> > + @param[in] SrcResponse Source Response to copy.
> > + @param[out] DstResponse Destination Response.
> > +
> > + @retval EFI_SUCCESS Response is copied successfully.
> > + @retval Others Error occurs.
> > +
> > +**/
> > +EFI_STATUS
> > +CopyRedfishResponse (
> > + IN REDFISH_RESPONSE *SrcResponse,
> > + OUT REDFISH_RESPONSE *DstResponse
> > + )
> > +{
> > + REDFISH_PAYLOAD_PRIVATE *Payload;
> > + UINTN Index;
> > +
> > + if ((SrcResponse == NULL) || (DstResponse == NULL)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + if (SrcResponse == DstResponse) {
> > + return EFI_SUCCESS;
> > + }
> > +
> > + //
> > + // Status code
> > + //
> > + if (SrcResponse->StatusCode != NULL) {
> > + DstResponse->StatusCode = AllocateCopyPool (sizeof
> > (EFI_HTTP_STATUS_CODE), SrcResponse->StatusCode);
> > + if (DstResponse->StatusCode == NULL) {
> > + goto ON_ERROR;
> > + }
> > + }
> > +
> > + //
> > + // Header
> > + //
> > + if ((SrcResponse->HeaderCount > 0) && (SrcResponse->Headers != NULL)) {
> > + DstResponse->HeaderCount = 0;
> > + DstResponse->Headers = AllocateZeroPool (sizeof (EFI_HTTP_HEADER) *
> > SrcResponse->HeaderCount);
> > + if (DstResponse->Headers == NULL) {
> > + goto ON_ERROR;
> > + }
> > +
> > + for (Index = 0; Index < SrcResponse->HeaderCount; Index++) {
> > + DstResponse->Headers[Index].FieldName = AllocateCopyPool (AsciiStrSize
> > (SrcResponse->Headers[Index].FieldName), SrcResponse-
> > >Headers[Index].FieldName);
> > + if (DstResponse->Headers[Index].FieldName == NULL) {
> > + goto ON_ERROR;
> > + }
> > +
> > + DstResponse->Headers[Index].FieldValue = AllocateCopyPool (AsciiStrSize
> > (SrcResponse->Headers[Index].FieldValue), SrcResponse-
> > >Headers[Index].FieldValue);
> > + if (DstResponse->Headers[Index].FieldValue == NULL) {
> > + goto ON_ERROR;
> > + }
> > +
> > + DstResponse->HeaderCount += 1;
> > + }
> > + }
> > +
> > + //
> > + // Payload
> > + //
> > + if (SrcResponse->Payload != NULL) {
> > + Payload = (REDFISH_PAYLOAD_PRIVATE *)SrcResponse->Payload;
> > + if (Payload->Signature != REDFISH_HTTP_PAYLOAD_SIGNATURE) {
> > + DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
> > + goto ON_ERROR;
> > + }
> > +
> > + DstResponse->Payload = CreateRedfishPayload (Payload->Service, Payload-
> > >JsonValue);
> > + if (DstResponse->Payload == NULL) {
> > + goto ON_ERROR;
> > + }
> > + }
> > +
> > + return EFI_SUCCESS;
> > +
> > +ON_ERROR:
> > +
> > + ReleaseRedfishResponse (DstResponse);
> > +
> > + return EFI_OUT_OF_RESOURCES;
> > +}
> > +
> > +/**
> > + This function clone input response and return to caller
> > +
> > + @param[in] Response Response to clone.
> > +
> > + @retval REDFISH_RESPONSE * Response is cloned.
> > + @retval NULL Errors occur.
> > +
> > +**/
> > +REDFISH_RESPONSE *
> > +CloneRedfishResponse (
> > + IN REDFISH_RESPONSE *Response
> > + )
> > +{
> > + EFI_STATUS Status;
> > + REDFISH_RESPONSE *NewResponse;
> > +
> > + if (Response == NULL) {
> > + return NULL;
> > + }
> > +
> > + NewResponse = AllocateZeroPool (sizeof (REDFISH_RESPONSE));
> > + if (NewResponse == NULL) {
> > + return NULL;
> > + }
> > +
> > + Status = CopyRedfishResponse (Response, NewResponse);
> > + if (EFI_ERROR (Status)) {
> > + FreePool (NewResponse);
> > + return NULL;
> > + }
> > +
> > + return NewResponse;
> > +}
> > +
> > +/**
> > + Release REDFISH_HTTP_CACHE_DATA resource
> > +
> > + @param[in] Data Pointer to REDFISH_HTTP_CACHE_DATA instance
> > +
> > + @retval EFI_SUCCESS REDFISH_HTTP_CACHE_DATA is released
> > successfully.
> > + @retval EFI_INVALID_PARAMETER Data is NULL
> > +
> > +**/
> > +EFI_STATUS
> > +ReleaseHttpCacheData (
> > + IN REDFISH_HTTP_CACHE_DATA *Data
> > + )
> > +{
> > + if (Data == NULL) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + if (Data->Uri != NULL) {
> > + FreePool (Data->Uri);
> > + }
> > +
> > + if (Data->Response != NULL) {
> > + ReleaseRedfishResponse (Data->Response);
> > + FreePool (Data->Response);
> > + }
> > +
> > + FreePool (Data);
> > +
> > + return EFI_SUCCESS;
> > +}
> > +
> > +/**
> > + Create new cache data.
> > +
> > + @param[in] Uri The URI string matching to this cache data.
> > + @param[in] Response HTTP response.
> > +
> > + @retval REDFISH_HTTP_CACHE_DATA * Pointer to newly created cache
> > data.
> > + @retval NULL No memory available.
> > +
> > +**/
> > +REDFISH_HTTP_CACHE_DATA *
> > +NewHttpCacheData (
> > + IN EFI_STRING Uri,
> > + IN REDFISH_RESPONSE *Response
> > + )
> > +{
> > + REDFISH_HTTP_CACHE_DATA *NewData;
> > + UINTN Size;
> > +
> > + if (IS_EMPTY_STRING (Uri) || (Response == NULL)) {
> > + return NULL;
> > + }
> > +
> > + NewData = AllocateZeroPool (sizeof (REDFISH_HTTP_CACHE_DATA));
> > + if (NewData == NULL) {
> > + return NULL;
> > + }
> > +
> > + NewData->Signature = REDFISH_HTTP_CACHE_SIGNATURE;
> > + Size = StrSize (Uri);
> > + NewData->Uri = AllocateCopyPool (Size, Uri);
> > + if (NewData->Uri == NULL) {
> > + goto ON_ERROR;
> > + }
> > +
> > + NewData->Response = Response;
> > + NewData->HitCount = 1;
> > +
> > + return NewData;
> > +
> > +ON_ERROR:
> > +
> > + if (NewData != NULL) {
> > + ReleaseHttpCacheData (NewData);
> > + }
> > +
> > + return NULL;
> > +}
> > +
> > +/**
> > + Search on given ListHeader for given URI string.
> > +
> > + @param[in] ListHeader Target list to search.
> > + @param[in] Uri Target URI to search.
> > +
> > + @retval REDFISH_HTTP_CACHE_DATA Target cache data is found.
> > + @retval NULL No cache data with given URI is found.
> > +
> > +**/
> > +REDFISH_HTTP_CACHE_DATA *
> > +FindHttpCacheData (
> > + IN LIST_ENTRY *ListHeader,
> > + IN EFI_STRING Uri
> > + )
> > +{
> > + LIST_ENTRY *List;
> > + REDFISH_HTTP_CACHE_DATA *Data;
> > +
> > + if (IS_EMPTY_STRING (Uri)) {
> > + return NULL;
> > + }
> > +
> > + if (IsListEmpty (ListHeader)) {
> > + return NULL;
> > + }
> > +
> > + Data = NULL;
> > + List = GetFirstNode (ListHeader);
> > + while (!IsNull (ListHeader, List)) {
> > + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> > +
> > + if (StrCmp (Data->Uri, Uri) == 0) {
> > + return Data;
> > + }
> > +
> > + List = GetNextNode (ListHeader, List);
> > + }
> > +
> > + return NULL;
> > +}
> > +
> > +/**
> > + Search on given ListHeader and return cache data with minimum hit count.
> > +
> > + @param[in] ListHeader Target list to search.
> > +
> > + @retval REDFISH_HTTP_CACHE_DATA Target cache data is returned.
> > + @retval NULL No cache data is found.
> > +
> > +**/
> > +REDFISH_HTTP_CACHE_DATA *
> > +FindUnusedHttpCacheData (
> > + IN LIST_ENTRY *ListHeader
> > + )
> > +{
> > + LIST_ENTRY *List;
> > + REDFISH_HTTP_CACHE_DATA *Data;
> > + REDFISH_HTTP_CACHE_DATA *UnusedData;
> > + UINTN HitCount;
> > +
> > + if (IsListEmpty (ListHeader)) {
> > + return NULL;
> > + }
> > +
> > + Data = NULL;
> > + UnusedData = NULL;
> > + HitCount = 0;
> > +
> > + List = GetFirstNode (ListHeader);
> > + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> > + UnusedData = Data;
> > + HitCount = Data->HitCount;
> > + List = GetNextNode (ListHeader, List);
> > +
> > + while (!IsNull (ListHeader, List)) {
> > + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> > +
> > + if (Data->HitCount < HitCount) {
> > + HitCount = Data->HitCount;
> > + UnusedData = Data;
> > + }
> > +
> > + List = GetNextNode (ListHeader, List);
> > + }
> > +
> > + return UnusedData;
> > +}
> > +
> > +/**
> > + Delete a cache data by given cache instance.
> > +
> > + @param[in] List Target cache list to be removed.
> > + @param[in] Data Pointer to the instance to be deleted.
> > +
> > + @retval EFI_SUCCESS Cache data is removed.
> > + @retval Others Fail to remove cache data.
> > +
> > +**/
> > +EFI_STATUS
> > +DeleteHttpCacheData (
> > + IN REDFISH_HTTP_CACHE_LIST *List,
> > + IN REDFISH_HTTP_CACHE_DATA *Data
> > + )
> > +{
> > + if ((List == NULL) || (Data == NULL)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: delete: %s\n", __func__,
> > Data->Uri));
> > +
> > + RemoveEntryList (&Data->List);
> > + --List->Count;
> > +
> > + return ReleaseHttpCacheData (Data);
> > +}
> > +
> > +/**
> > + Add new cache by given URI and HTTP response to specify List.
> > +
> > + @param[in] List Target cache list to add.
> > + @param[in] Uri The URI string matching to this cache data.
> > + @param[in] Response HTTP response.
> > +
> > + @retval EFI_SUCCESS Cache data is added.
> > + @retval Others Fail to add cache data.
> > +
> > +**/
> > +EFI_STATUS
> > +AddHttpCacheData (
> > + IN REDFISH_HTTP_CACHE_LIST *List,
> > + IN EFI_STRING Uri,
> > + IN REDFISH_RESPONSE *Response
> > + )
> > +{
> > + REDFISH_HTTP_CACHE_DATA *NewData;
> > + REDFISH_HTTP_CACHE_DATA *OldData;
> > + REDFISH_HTTP_CACHE_DATA *UnusedData;
> > + REDFISH_RESPONSE *NewResponse;
> > +
> > + if ((List == NULL) || IS_EMPTY_STRING (Uri) || (Response == NULL)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + //
> > + // If same cache data exist, replace it with latest one.
> > + //
> > + OldData = FindHttpCacheData (&List->Head, Uri);
> > + if (OldData != NULL) {
> > + DeleteHttpCacheData (List, OldData);
> > + }
> > +
> > + //
> > + // Check capacity
> > + //
> > + if (List->Count >= List->Capacity) {
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: list is full and retire unused
> > cache\n", __func__));
> > + UnusedData = FindUnusedHttpCacheData (&List->Head);
> > + if (UnusedData == NULL) {
> > + return EFI_OUT_OF_RESOURCES;
> > + }
> > +
> > + DeleteHttpCacheData (List, UnusedData);
> > + }
> > +
> > + //
> > + // Clone a local copy
> > + //
> > + NewResponse = CloneRedfishResponse (Response);
> > + if (NewResponse == NULL) {
> > + return EFI_OUT_OF_RESOURCES;
> > + }
> > +
> > + NewData = NewHttpCacheData (Uri, NewResponse);
> > + if (NewData == NULL) {
> > + return EFI_OUT_OF_RESOURCES;
> > + }
> > +
> > + InsertTailList (&List->Head, &NewData->List);
> > + ++List->Count;
> > +
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache(%d/%d) %s\n",
> > __func__, List->Count, List->Capacity, NewData->Uri));
> > +
> > + return EFI_SUCCESS;
> > +}
> > +
> > +/**
> > + Release all cache from list.
> > +
> > + @param[in] CacheList The list to be released.
> > +
> > + @retval EFI_SUCCESS All cache data are released.
> > + @retval EFI_INVALID_PARAMETER CacheList is NULL.
> > +
> > +**/
> > +EFI_STATUS
> > +ReleaseCacheList (
> > + IN REDFISH_HTTP_CACHE_LIST *CacheList
> > + )
> > +{
> > + LIST_ENTRY *List;
> > + LIST_ENTRY *Next;
> > + REDFISH_HTTP_CACHE_DATA *Data;
> > +
> > + if (CacheList == NULL) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + if (IsListEmpty (&CacheList->Head)) {
> > + return EFI_SUCCESS;
> > + }
> > +
> > + Data = NULL;
> > + Next = NULL;
> > + List = GetFirstNode (&CacheList->Head);
> > + while (!IsNull (&CacheList->Head, List)) {
> > + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> > + Next = GetNextNode (&CacheList->Head, List);
> > +
> > + DeleteHttpCacheData (CacheList, Data);
> > +
> > + List = Next;
> > + }
> > +
> > + return EFI_SUCCESS;
> > +}
> > diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
> > b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
> > new file mode 100644
> > index 0000000000..39958d4865
> > --- /dev/null
> > +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.c
> > @@ -0,0 +1,1344 @@
> > +/** @file
> > + RedfishHttpDxe produces EdkIIRedfishHttpProtocol
> > + for EDK2 Redfish Feature driver to do HTTP operations.
> > +
> > + Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> > reserved.
> > +
> > + SPDX-License-Identifier: BSD-2-Clause-Patent
> > +
> > +**/
> > +
> > +#include "RedfishHttpDxe.h"
> > +#include "RedfishHttpData.h"
> > +#include "RedfishHttpOperation.h"
> > +
> > +REDFISH_HTTP_CACHE_PRIVATE *mRedfishHttpCachePrivate = NULL;
> > +
> > +/**
> > + Debug output the cache list.
> > +
> > + @param[in] Msg Debug message string.
> > + @param[in] ErrorLevel Output error level.
> > + @param[in] CacheList Target list to dump.
> > +
> > + @retval EFI_SUCCESS Debug dump finished.
> > + @retval EFI_INVALID_PARAMETER HttpCacheList is NULL.
> > +
> > +**/
> > +EFI_STATUS
> > +DebugPrintHttpCacheList (
> > + IN CONST CHAR8 *Msg,
> > + IN UINTN ErrorLevel,
> > + IN REDFISH_HTTP_CACHE_LIST *CacheList
> > + )
> > +{
> > + LIST_ENTRY *List;
> > + REDFISH_HTTP_CACHE_DATA *Data;
> > + UINTN Index;
> > +
> > + if (CacheList == NULL) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + if (!IS_EMPTY_STRING (Msg)) {
> > + DEBUG ((ErrorLevel, "%a\n", Msg));
> > + }
> > +
> > + if (IsListEmpty (&CacheList->Head)) {
> > + DEBUG ((ErrorLevel, "list is empty\n"));
> > + return EFI_NOT_FOUND;
> > + }
> > +
> > + DEBUG ((ErrorLevel, "list count: %d capacity: %d\n", CacheList->Count,
> > CacheList->Capacity));
> > + Data = NULL;
> > + Index = 0;
> > + List = GetFirstNode (&CacheList->Head);
> > + while (!IsNull (&CacheList->Head, List)) {
> > + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> > +
> > + DEBUG ((ErrorLevel, "%d) Uri: %s Hit: %d\n", ++Index, Data->Uri, Data-
> > >HitCount));
> > +
> > + List = GetNextNode (&CacheList->Head, List);
> > + }
> > +
> > + return EFI_SUCCESS;
> > +}
> > +
> > +/**
> > +
> > + Check HTTP status code to see if we like to retry HTTP request or not.
> > +
> > + @param[in] StatusCode HTTP status code.
> > +
> > + @retval BOOLEAN Return true when we like to retry request.
> > + Return false when we don't want to retry request.
> > +
> > +**/
> > +BOOLEAN
> > +RedfishRetryRequired (
> > + IN EFI_HTTP_STATUS_CODE *StatusCode
> > + )
> > +{
> > + if (StatusCode == NULL) {
> > + return TRUE;
> > + }
> > +
> > + if ((*StatusCode == HTTP_STATUS_500_INTERNAL_SERVER_ERROR) ||
> > + (*StatusCode == HTTP_STATUS_UNSUPPORTED_STATUS))
> > + {
> > + return TRUE;
> > + }
> > +
> > + return FALSE;
> > +}
> > +
> > +/**
> > +
> > + Convert Unicode string to ASCII string. It's call responsibility to release
> > returned buffer.
> > +
> > + @param[in] UnicodeStr Unicode string to convert.
> > +
> > + @retval CHAR8 * ASCII string returned.
> > + @retval NULL Errors occur.
> > +
> > +**/
> > +CHAR8 *
> > +StringUnicodeToAscii (
> > + IN EFI_STRING UnicodeStr
> > + )
> > +{
> > + CHAR8 *AsciiStr;
> > + UINTN AsciiStrSize;
> > + EFI_STATUS Status;
> > +
> > + if (IS_EMPTY_STRING (UnicodeStr)) {
> > + return NULL;
> > + }
> > +
> > + AsciiStrSize = StrLen (UnicodeStr) + 1;
> > + AsciiStr = AllocateZeroPool (AsciiStrSize);
> > + if (AsciiStr == NULL) {
> > + return NULL;
> > + }
> > +
> > + Status = UnicodeStrToAsciiStrS (UnicodeStr, AsciiStr, AsciiStrSize);
> > + if (EFI_ERROR (Status)) {
> > + DEBUG ((DEBUG_ERROR, "UnicodeStrToAsciiStrS failed: %r\n", Status));
> > + FreePool (AsciiStr);
> > + return NULL;
> > + }
> > +
> > + return AsciiStr;
> > +}
> > +
> > +/**
> > + Return HTTP method in ASCII string. Caller does not need
> > + to free returned string buffer.
> > +
> > + @param[in] Method HTTP method.
> > +
> > + @retval CHAR8 * Method in string.
> > +**/
> > +CHAR8 *
> > +HttpMethodToString (
> > + IN EFI_HTTP_METHOD Method
> > + )
> > +{
> > + switch (Method) {
> > + case HttpMethodGet:
> > + return HTTP_METHOD_GET;
> > + break;
> > + case HttpMethodPost:
> > + return HTTP_METHOD_POST;
> > + break;
> > + case HttpMethodPatch:
> > + return HTTP_METHOD_PATCH;
> > + break;
> > + case HttpMethodPut:
> > + return HTTP_METHOD_PUT;
> > + break;
> > + case HttpMethodDelete:
> > + return HTTP_METHOD_DELETE;
> > + break;
> > + default:
> > + break;
> > + }
> > +
> > + return "Unknown";
> > +}
> > +
> > +/**
> > + Report HTTP communication error via report status code.
> > +
> > + @param[in] Method HTTP method.
> > + @param[in] Uri The URI which has failure.
> > + @param[in] HttpStatusCode HTTP status code.
> > +
> > +**/
> > +VOID
> > +ReportHttpError (
> > + IN EFI_HTTP_METHOD Method,
> > + IN EFI_STRING Uri,
> > + IN EFI_HTTP_STATUS_CODE *HttpStatusCode OPTIONAL
> > + )
> > +{
> > + CHAR8 ErrorMsg[REDFISH_ERROR_MSG_MAX];
> > +
> > + if (IS_EMPTY_STRING (Uri)) {
> > + DEBUG ((DEBUG_ERROR, "%a: no URI to report error status\n",
> > __func__));
> > + return;
> > + }
> > +
> > + //
> > + // Report failure of URI and HTTP status code.
> > + //
> > + AsciiSPrint (ErrorMsg, sizeof (ErrorMsg), REDFISH_HTTP_ERROR_REPORT,
> > HttpMethodToString (Method), (HttpStatusCode == NULL ?
> > HTTP_STATUS_UNSUPPORTED_STATUS : *HttpStatusCode), Uri);
> > + DEBUG ((DEBUG_ERROR, "%a\n", ErrorMsg));
> > + //
> > + // TODO:
> > + // Below PI status code is approved by PIWG and wait for specification
> > published.
> > + // We will uncomment below report status code after PI status code get
> > published.
> > + // REF:
> https://bugzilla.ti/
> anocore.org%2Fshow_bug.cgi%3Fid%3D4483&data=05%7C02%7Cnicklew%40nvi
> dia.com%7C170113d9065b49ddd38b08dc327faeab%7C43083d15727340c1b7db
> 39efd9ccc17a%7C0%7C0%7C638440771190654082%7CUnknown%7CTWFpbGZs
> b3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%
> 3D%7C0%7C%7C%7C&sdata=na71Yv8MEbiiQFzc437B3fNcMZ5%2B7wrhzFjwTZb
> KPl4%3D&reserved=0
> > + //
> > + // REPORT_STATUS_CODE_WITH_EXTENDED_DATA (
> > + // EFI_ERROR_CODE | EFI_ERROR_MAJOR,
> > + // EFI_COMPUTING_UNIT_MANAGEABILITY |
> > EFI_MANAGEABILITY_EC_REDFISH_COMMUNICATION_ERROR,
> > + // ErrorMsg,
> > + // AsciiStrSize (ErrorMsg)
> > + // );
> > +}
> > +
> > +/**
> > + This function create Redfish service. It's caller's responsibility to free
> > returned
> > + Redfish service by calling FreeService ().
> > +
> > + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL
> > instance.
> > + @param[in] RedfishConfigServiceInfo Redfish config service information.
> > +
> > + @retval REDFISH_SERVICE Redfish service is created.
> > + @retval NULL Errors occur.
> > +
> > +**/
> > +REDFISH_SERVICE
> > +EFIAPI
> > +RedfishCreateRedfishService (
> > + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> > + IN REDFISH_CONFIG_SERVICE_INFORMATION *RedfishConfigServiceInfo
> > + )
> > +{
> > + EFI_STATUS Status;
> > + REDFISH_HTTP_CACHE_PRIVATE *Private;
> > + REDFISH_SERVICE_PRIVATE *NewService;
> > + CHAR8 *AsciiLocation;
> > + CHAR8 *Host;
> > + CHAR8 *BasicAuthString;
> > + UINTN BasicAuthStrSize;
> > + CHAR8 *EncodedAuthString;
> > + UINTN EncodedAuthStrSize;
> > + EDKII_REDFISH_AUTH_METHOD AuthMethod;
> > + CHAR8 *Username;
> > + CHAR8 *Password;
> > + UINTN UsernameSize;
> > + UINTN PasswordSize;
> > + EFI_REST_EX_PROTOCOL *RestEx;
> > +
> > + if ((This == NULL) || (RedfishConfigServiceInfo == NULL)) {
> > + return NULL;
> > + }
> > +
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: service location: %s\n",
> > __func__, RedfishConfigServiceInfo->RedfishServiceLocation));
> > +
> > + Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> > + BasicAuthString = NULL;
> > + EncodedAuthString = NULL;
> > + Username = NULL;
> > + Password = NULL;
> > + NewService = NULL;
> > + AsciiLocation = NULL;
> > + Host = NULL;
> > + BasicAuthStrSize = 0;
> > + EncodedAuthStrSize = 0;
> > + UsernameSize = 0;
> > + PasswordSize = 0;
> > +
> > + //
> > + // Build host and host name from service location
> > + //
> > + if (!IS_EMPTY_STRING (RedfishConfigServiceInfo->RedfishServiceLocation))
> > {
> > + AsciiLocation = StringUnicodeToAscii (RedfishConfigServiceInfo-
> > >RedfishServiceLocation);
> > + if (AsciiLocation == NULL) {
> > + goto ON_RELEASE;
> > + }
> > +
> > + Host = AllocateZeroPool (REDFISH_HOST_NAME_MAX);
> > + if (AsciiLocation == NULL) {
> > + goto ON_RELEASE;
> > + }
> > +
> > + if (RedfishConfigServiceInfo->RedfishServiceUseHttps) {
> > + AsciiSPrint (Host, REDFISH_HOST_NAME_MAX, "https://%a",
> > AsciiLocation);
> > + } else {
> > + AsciiSPrint (Host, REDFISH_HOST_NAME_MAX, "http://%a",
> > AsciiLocation);
> > + }
> > +
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Host: %a\n", __func__,
> > Host));
> > + }
> > +
> > + //
> > + // Find Rest Ex protocol
> > + //
> > + if (RedfishConfigServiceInfo->RedfishServiceRestExHandle != NULL) {
> > + Status = gBS->HandleProtocol (
> > + RedfishConfigServiceInfo->RedfishServiceRestExHandle,
> > + &gEfiRestExProtocolGuid,
> > + (VOID **)&RestEx
> > + );
> > + } else {
> > + DEBUG ((DEBUG_ERROR, "%a: Rest Ex protocol is not available\n",
> > __func__));
> > + goto ON_RELEASE;
> > + }
> > +
> > + //
> > + // Get credential
> > + //
> > + if (Private->CredentialProtocol == NULL) {
> > + //
> > + // No credential available on this system.
> > + //
> > + DEBUG ((DEBUG_WARN, "%a: no credential protocol available\n",
> > __func__));
> > + } else {
> > + Status = Private->CredentialProtocol->GetAuthInfo (
> > + Private->CredentialProtocol,
> > + &AuthMethod,
> > + &Username,
> > + &Password
> > + );
> > + if (EFI_ERROR (Status) || IS_EMPTY_STRING (Username) ||
> > IS_EMPTY_STRING (Password)) {
> > + DEBUG ((DEBUG_ERROR, "%a: cannot get authentication information:
> > %r\n", __func__, Status));
> > + goto ON_RELEASE;
> > + } else {
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Auth method: 0x%x
> > username: %a password: %a\n", __func__, AuthMethod, Username,
> > Password));
> > +
> > + //
> > + // Perform base64 encoding (RFC 7617)
> > + //
> > + UsernameSize = AsciiStrSize (Username);
> > + PasswordSize = AsciiStrSize (Password);
> > + BasicAuthStrSize = UsernameSize + PasswordSize; // one byte taken from
> > null-terminator for ':'
> > + BasicAuthString = AllocateZeroPool (BasicAuthStrSize);
> > + if (BasicAuthString == NULL) {
> > + goto ON_RELEASE;
> > + }
> > +
> > + AsciiSPrint (
> > + BasicAuthString,
> > + BasicAuthStrSize,
> > + "%a:%a",
> > + Username,
> > + Password
> > + );
> > +
> > + Status = Base64Encode (
> > + (CONST UINT8 *)BasicAuthString,
> > + BasicAuthStrSize,
> > + EncodedAuthString,
> > + &EncodedAuthStrSize
> > + );
> > + if ((Status == EFI_BUFFER_TOO_SMALL) && (EncodedAuthStrSize > 0)) {
> > + EncodedAuthString = AllocateZeroPool (EncodedAuthStrSize);
> > + if (EncodedAuthString == NULL) {
> > + goto ON_RELEASE;
> > + }
> > +
> > + Status = Base64Encode (
> > + (CONST UINT8 *)BasicAuthString,
> > + BasicAuthStrSize,
> > + EncodedAuthString,
> > + &EncodedAuthStrSize
> > + );
> > + if (EFI_ERROR (Status)) {
> > + DEBUG ((DEBUG_ERROR, "%a: Base64Encode failure: %r\n", __func__,
> > Status));
> > + }
> > +
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Basic authorization:
> > %a\n", __func__, EncodedAuthString));
> > + } else {
> > + DEBUG ((DEBUG_ERROR, "%a: Base64Encode failure: %r\n", __func__,
> > Status));
> > + goto ON_RELEASE;
> > + }
> > + }
> > + }
> > +
> > + NewService = CreateRedfishService (Host, AsciiLocation,
> > EncodedAuthString, NULL, RestEx);
> > + if (NewService == NULL) {
> > + DEBUG ((DEBUG_ERROR, "%a: CreateRedfishService\n", __func__));
> > + }
> > +
> > +ON_RELEASE:
> > +
> > + if (BasicAuthString != NULL) {
> > + ZeroMem (BasicAuthString, BasicAuthStrSize);
> > + FreePool (BasicAuthString);
> > + }
> > +
> > + if (EncodedAuthString != NULL) {
> > + ZeroMem (BasicAuthString, EncodedAuthStrSize);
> > + FreePool (EncodedAuthString);
> > + }
> > +
> > + if (Username != NULL) {
> > + ZeroMem (Username, UsernameSize);
> > + FreePool (Username);
> > + }
> > +
> > + if (Password != NULL) {
> > + ZeroMem (Password, PasswordSize);
> > + FreePool (Password);
> > + }
> > +
> > + if (AsciiLocation != NULL) {
> > + FreePool (AsciiLocation);
> > + }
> > +
> > + if (Host != NULL) {
> > + FreePool (Host);
> > + }
> > +
> > + return NewService;
> > +}
> > +
> > +/**
> > + This function free resources in Redfish service. RedfishService is no longer
> > available
> > + after this function returns successfully.
> > +
> > + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL
> > instance.
> > + @param[in] RedfishService Pointer to Redfish service to be released.
> > +
> > + @retval EFI_SUCCESS Resrouce is released successfully.
> > + @retval Others Errors occur.
> > +
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +RedfishFreeRedfishService (
> > + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> > + IN REDFISH_SERVICE RedfishService
> > + )
> > +{
> > + REDFISH_SERVICE_PRIVATE *Service;
> > +
> > + if ((This == NULL) || (RedfishService == NULL)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + Service = (REDFISH_SERVICE_PRIVATE *)RedfishService;
> > + if (Service->Signature != REDFISH_HTTP_SERVICE_SIGNATURE) {
> > + DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
> > + }
> > +
> > + return ReleaseRedfishService (Service);
> > +}
> > +
> > +/**
> > + This function returns JSON value in given RedfishPayload. Returned JSON
> > value
> > + is a reference to the JSON value in RedfishPayload. Any modification to
> > returned
> > + JSON value will change JSON value in RedfishPayload.
> > +
> > + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL
> > instance.
> > + @param[in] RedfishPayload Pointer to Redfish payload.
> > +
> > + @retval EDKII_JSON_VALUE JSON value is returned.
> > + @retval NULL Errors occur.
> > +
> > +**/
> > +EDKII_JSON_VALUE
> > +EFIAPI
> > +RedfishJsonInRedfishPayload (
> > + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> > + IN REDFISH_PAYLOAD RedfishPayload
> > + )
> > +{
> > + REDFISH_PAYLOAD_PRIVATE *Payload;
> > +
> > + if ((This == NULL) || (RedfishPayload == NULL)) {
> > + return NULL;
> > + }
> > +
> > + Payload = (REDFISH_PAYLOAD_PRIVATE *)RedfishPayload;
> > + if (Payload->Signature != REDFISH_HTTP_PAYLOAD_SIGNATURE) {
> > + DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
> > + }
> > +
> > + return Payload->JsonValue;
> > +}
> > +
> > +/**
> > + Perform HTTP GET to Get redfish resource from given resource URI with
> > + cache mechanism supported. It's caller's responsibility to free Response
> > + by calling FreeResponse ().
> > +
> > + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> > + @param[in] Service Redfish service instance to perform HTTP GET.
> > + @param[in] Uri Target resource URI.
> > + @param[in] Request Additional request context. This is optional.
> > + @param[out] Response HTTP response from redfish service.
> > + @param[in] UseCache If it is TRUE, this function will search for
> > + cache first. If it is FALSE, this function
> > + will query Redfish URI directly.
> > +
> > + @retval EFI_SUCCESS Resrouce is returned successfully.
> > + @retval Others Errors occur.
> > +
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +RedfishGetResource (
> > + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> > + IN REDFISH_SERVICE Service,
> > + IN EFI_STRING Uri,
> > + IN REDFISH_REQUEST *Request OPTIONAL,
> > + OUT REDFISH_RESPONSE *Response,
> > + IN BOOLEAN UseCache
> > + )
> > +{
> > + EFI_STATUS Status;
> > + REDFISH_HTTP_CACHE_DATA *CacheData;
> > + UINTN RetryCount;
> > + REDFISH_HTTP_CACHE_PRIVATE *Private;
> > +
> > + if ((This == NULL) || (Service == NULL) || (Response == NULL) ||
> > IS_EMPTY_STRING (Uri)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Get URI: %s cache: %a\n",
> > __func__, Uri, (UseCache ? "true" : "false")));
> > +
> > + Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> > + CacheData = NULL;
> > + RetryCount = 0;
> > + ZeroMem (Response, sizeof (REDFISH_RESPONSE));
> > +
> > + if (Private->CacheDisabled) {
> > + UseCache = FALSE;
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache is disabled by
> > PCD!\n", __func__));
> > + }
> > +
> > + //
> > + // Search for cache list.
> > + //
> > + if (UseCache) {
> > + CacheData = FindHttpCacheData (&Private->CacheList.Head, Uri);
> > + if (CacheData != NULL) {
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: cache hit!
> > %s\n", __func__, Uri));
> > +
> > + //
> > + // Copy cached response to caller's buffer.
> > + //
> > + Status = CopyRedfishResponse (CacheData->Response, Response);
> > + CacheData->HitCount += 1;
> > + return Status;
> > + }
> > + }
> > +
> > + //
> > + // Get resource from redfish service.
> > + //
> > + do {
> > + RetryCount += 1;
> > + Status = HttpSendReceive (
> > + Service,
> > + Uri,
> > + HttpMethodGet,
> > + Request,
> > + Response
> > + );
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request:
> > %s :%r\n", __func__, Uri, Status));
> > + if (!EFI_ERROR (Status) || (RetryCount >= Private-
> > >RetrySetting.MaximumRetryGet)) {
> > + break;
> > + }
> > +
> > + //
> > + // Retry when BMC is not ready.
> > + //
> > + if ((Response->StatusCode != NULL)) {
> > + DEBUG_CODE (
> > + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> > + );
> > +
> > + if (!RedfishRetryRequired (Response->StatusCode)) {
> > + break;
> > + }
> > +
> > + //
> > + // Release response for next round of request.
> > + //
> > + This->FreeResponse (This, Response);
> > + }
> > +
> > + DEBUG ((DEBUG_WARN, "%a: RedfishGetByUriEx failed, retry (%d/%d)\n",
> > __func__, RetryCount, Private->RetrySetting.MaximumRetryGet));
> > + if (Private->RetrySetting.RetryWait > 0) {
> > + gBS->Stall (Private->RetrySetting.RetryWait);
> > + }
> > + } while (TRUE);
> > +
> > + if (EFI_ERROR (Status)) {
> > + DEBUG_CODE (
> > + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> > + );
> > + //
> > + // Report status code for Redfish failure
> > + //
> > + ReportHttpError (HttpMethodGet, Uri, Response->StatusCode);
> > + DEBUG ((DEBUG_ERROR, "%a: get %s failed (%d/%d): %r\n", __func__, Uri,
> > RetryCount, Private->RetrySetting.MaximumRetryGet, Status));
> > + goto ON_RELEASE;
> > + }
> > +
> > + if (!Private->CacheDisabled) {
> > + //
> > + // Keep response in cache list
> > + //
> > + Status = AddHttpCacheData (&Private->CacheList, Uri, Response);
> > + if (EFI_ERROR (Status)) {
> > + DEBUG ((DEBUG_ERROR, "%a: failed to cache %s: %r\n", __func__, Uri,
> > Status));
> > + goto ON_RELEASE;
> > + }
> > +
> > + DEBUG_CODE (
> > + DebugPrintHttpCacheList (__func__,
> > REDFISH_HTTP_CACHE_DEBUG_DUMP, &Private->CacheList);
> > + );
> > + }
> > +
> > +ON_RELEASE:
> > +
> > + return Status;
> > +}
> > +
> > +/**
> > + This function free resources in Request. Request is no longer available
> > + after this function returns successfully.
> > +
> > + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> > + @param[in] Request HTTP request to be released.
> > +
> > + @retval EFI_SUCCESS Resrouce is released successfully.
> > + @retval Others Errors occur.
> > +
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +RedfishFreeRequest (
> > + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> > + IN REDFISH_REQUEST *Request
> > + )
> > +{
> > + if ((This == NULL) || (Request == NULL)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: entry\n", __func__));
> > +
> > + return ReleaseRedfishRequest (Request);
> > +}
> > +
> > +/**
> > + This function free resources in given Response.
> > +
> > + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> > + @param[in] Response HTTP response to be released.
> > +
> > + @retval EFI_SUCCESS Resrouce is released successfully.
> > + @retval Others Errors occur.
> > +
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +RedfishFreeResponse (
> > + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> > + IN REDFISH_RESPONSE *Response
> > + )
> > +{
> > + if ((This == NULL) || (Response == NULL)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: entry\n", __func__));
> > +
> > + return ReleaseRedfishResponse (Response);
> > +}
> > +
> > +/**
> > + This function expire the cached response of given URI.
> > +
> > + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> > + @param[in] Uri Target response of URI.
> > +
> > + @retval EFI_SUCCESS Target response is expired successfully.
> > + @retval Others Errors occur.
> > +
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +RedfishExpireResponse (
> > + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> > + IN EFI_STRING Uri
> > + )
> > +{
> > + REDFISH_HTTP_CACHE_PRIVATE *Private;
> > + REDFISH_HTTP_CACHE_DATA *CacheData;
> > +
> > + if ((This == NULL) || IS_EMPTY_STRING (Uri)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: expire URI: %s\n", __func__,
> > Uri));
> > +
> > + Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> > +
> > + CacheData = FindHttpCacheData (&Private->CacheList.Head, Uri);
> > + if (CacheData == NULL) {
> > + return EFI_NOT_FOUND;
> > + }
> > +
> > + return DeleteHttpCacheData (&Private->CacheList, CacheData);
> > +}
> > +
> > +/**
> > + Perform HTTP PATCH to send redfish resource to given resource URI.
> > + It's caller's responsibility to free Response by calling FreeResponse ().
> > +
> > + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> > + @param[in] Service Redfish service instance to perform HTTP PATCH.
> > + @param[in] Uri Target resource URI.
> > + @param[in] Content Data to patch.
> > + @param[in] ContentSize Size of the Content to be send to Redfish service.
> > + This is optional. When ContentSize is 0, ContentSize
> > + is the size of Content.
> > + @param[in] ContentType Type of the Content to be send to Redfish
> > service.
> > + This is optional. When ContentType is NULL, content
> > + type HTTP_CONTENT_TYPE_APP_JSON will be used.
> > + @param[out] Response HTTP response from redfish service.
> > +
> > + @retval EFI_SUCCESS Resrouce is returned successfully.
> > + @retval Others Errors occur.
> > +
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +RedfishPatchResource (
> > + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> > + IN REDFISH_SERVICE Service,
> > + IN EFI_STRING Uri,
> > + IN CHAR8 *Content,
> > + IN UINTN ContentSize OPTIONAL,
> > + IN CHAR8 *ContentType OPTIONAL,
> > + OUT REDFISH_RESPONSE *Response
> > + )
> > +{
> > + EFI_STATUS Status;
> > + UINTN RetryCount;
> > + REDFISH_REQUEST Request;
> > + REDFISH_HTTP_CACHE_PRIVATE *Private;
> > +
> > + if ((This == NULL) || (Service == NULL) || (Response == NULL) ||
> > IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Patch URI: %s\n", __func__,
> > Uri));
> > +
> > + Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> > + RetryCount = 0;
> > + ZeroMem (Response, sizeof (REDFISH_RESPONSE));
> > + ZeroMem (&Request, sizeof (REDFISH_REQUEST));
> > +
> > + Request.Content = Content;
> > + Request.ContentLength = ContentSize;
> > + Request.ContentType = ContentType;
> > +
> > + //
> > + // Patch resource to redfish service.
> > + //
> > + do {
> > + RetryCount += 1;
> > + Status = HttpSendReceive (
> > + Service,
> > + Uri,
> > + HttpMethodPatch,
> > + &Request,
> > + Response
> > + );
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request:
> > %s :%r\n", __func__, Uri, Status));
> > + if (!EFI_ERROR (Status) || (RetryCount >= Private-
> > >RetrySetting.MaximumRetryPatch)) {
> > + break;
> > + }
> > +
> > + //
> > + // Retry when BMC is not ready.
> > + //
> > + if ((Response->StatusCode != NULL)) {
> > + DEBUG_CODE (
> > + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> > + );
> > +
> > + if (!RedfishRetryRequired (Response->StatusCode)) {
> > + break;
> > + }
> > +
> > + //
> > + // Release response for next round of request.
> > + //
> > + This->FreeResponse (This, Response);
> > + }
> > +
> > + DEBUG ((DEBUG_WARN, "%a: RedfishPatchToUriEx failed, retry
> > (%d/%d)\n", __func__, RetryCount, Private-
> > >RetrySetting.MaximumRetryPatch));
> > + if (Private->RetrySetting.RetryWait > 0) {
> > + gBS->Stall (Private->RetrySetting.RetryWait);
> > + }
> > + } while (TRUE);
> > +
> > + //
> > + // Redfish resource is updated. Automatically expire the cached response
> > + // so application can directly get resource from Redfish service again.
> > + //
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire
> > URI: %s\n", __func__, Uri));
> > + RedfishExpireResponse (This, Uri);
> > +
> > + if (EFI_ERROR (Status)) {
> > + DEBUG_CODE (
> > + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> > + );
> > + //
> > + // Report status code for Redfish failure
> > + //
> > + ReportHttpError (HttpMethodPatch, Uri, Response->StatusCode);
> > + DEBUG ((DEBUG_ERROR, "%a: patch %s failed (%d/%d): %r\n", __func__,
> > Uri, RetryCount, Private->RetrySetting.MaximumRetryPatch, Status));
> > + goto ON_RELEASE;
> > + }
> > +
> > +ON_RELEASE:
> > +
> > + return Status;
> > +}
> > +
> > +/**
> > + Perform HTTP PUT to send redfish resource to given resource URI.
> > + It's caller's responsibility to free Response by calling FreeResponse ().
> > +
> > + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> > + @param[in] Service Redfish service instance to perform HTTP PUT.
> > + @param[in] Uri Target resource URI.
> > + @param[in] Content Data to put.
> > + @param[in] ContentSize Size of the Content to be send to Redfish service.
> > + This is optional. When ContentSize is 0, ContentSize
> > + is the size of Content.
> > + @param[in] ContentType Type of the Content to be send to Redfish
> > service.
> > + This is optional. When ContentType is NULL, content
> > + type HTTP_CONTENT_TYPE_APP_JSON will be used.
> > + @param[out] Response HTTP response from redfish service.
> > +
> > + @retval EFI_SUCCESS Resrouce is returned successfully.
> > + @retval Others Errors occur.
> > +
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +RedfishPutResource (
> > + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> > + IN REDFISH_SERVICE Service,
> > + IN EFI_STRING Uri,
> > + IN CHAR8 *Content,
> > + IN UINTN ContentSize OPTIONAL,
> > + IN CHAR8 *ContentType OPTIONAL,
> > + OUT REDFISH_RESPONSE *Response
> > + )
> > +{
> > + EFI_STATUS Status;
> > + UINTN RetryCount;
> > + REDFISH_REQUEST Request;
> > + REDFISH_HTTP_CACHE_PRIVATE *Private;
> > +
> > + if ((This == NULL) || (Service == NULL) || (Response == NULL) ||
> > IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Put URI: %s\n", __func__,
> > Uri));
> > +
> > + Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> > + RetryCount = 0;
> > + ZeroMem (Response, sizeof (REDFISH_RESPONSE));
> > + ZeroMem (&Request, sizeof (REDFISH_REQUEST));
> > +
> > + Request.Content = Content;
> > + Request.ContentLength = ContentSize;
> > + Request.ContentType = ContentType;
> > +
> > + //
> > + // Patch resource to redfish service.
> > + //
> > + do {
> > + RetryCount += 1;
> > + Status = HttpSendReceive (
> > + Service,
> > + Uri,
> > + HttpMethodPut,
> > + &Request,
> > + Response
> > + );
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request:
> > %s :%r\n", __func__, Uri, Status));
> > + if (!EFI_ERROR (Status) || (RetryCount >= Private-
> > >RetrySetting.MaximumRetryPut)) {
> > + break;
> > + }
> > +
> > + //
> > + // Retry when BMC is not ready.
> > + //
> > + if ((Response->StatusCode != NULL)) {
> > + DEBUG_CODE (
> > + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> > + );
> > +
> > + if (!RedfishRetryRequired (Response->StatusCode)) {
> > + break;
> > + }
> > +
> > + //
> > + // Release response for next round of request.
> > + //
> > + This->FreeResponse (This, Response);
> > + }
> > +
> > + DEBUG ((DEBUG_WARN, "%a: RedfishPutToUri failed, retry (%d/%d)\n",
> > __func__, RetryCount, Private->RetrySetting.MaximumRetryPut));
> > + if (Private->RetrySetting.RetryWait > 0) {
> > + gBS->Stall (Private->RetrySetting.RetryWait);
> > + }
> > + } while (TRUE);
> > +
> > + //
> > + // Redfish resource is updated. Automatically expire the cached response
> > + // so application can directly get resource from Redfish service again.
> > + //
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire
> > URI: %s\n", __func__, Uri));
> > + RedfishExpireResponse (This, Uri);
> > +
> > + if (EFI_ERROR (Status)) {
> > + DEBUG_CODE (
> > + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> > + );
> > + //
> > + // Report status code for Redfish failure
> > + //
> > + ReportHttpError (HttpMethodPut, Uri, Response->StatusCode);
> > + DEBUG ((DEBUG_ERROR, "%a: put %s failed (%d/%d): %r\n", __func__,
> > Uri, RetryCount, Private->RetrySetting.MaximumRetryPut, Status));
> > + goto ON_RELEASE;
> > + }
> > +
> > +ON_RELEASE:
> > +
> > + return Status;
> > +}
> > +
> > +/**
> > + Perform HTTP POST to send redfish resource to given resource URI.
> > + It's caller's responsibility to free Response by calling FreeResponse ().
> > +
> > + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> > + @param[in] Service Redfish service instance to perform HTTP POST.
> > + @param[in] Uri Target resource URI.
> > + @param[in] Content Data to post.
> > + @param[in] ContentSize Size of the Content to be send to Redfish service.
> > + This is optional. When ContentSize is 0, ContentSize
> > + is the size of Content.
> > + @param[in] ContentType Type of the Content to be send to Redfish
> > service.
> > + This is optional. When ContentType is NULL, content
> > + type HTTP_CONTENT_TYPE_APP_JSON will be used.
> > + @param[out] Response HTTP response from redfish service.
> > +
> > + @retval EFI_SUCCESS Resrouce is returned successfully.
> > + @retval Others Errors occur.
> > +
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +RedfishPostResource (
> > + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> > + IN REDFISH_SERVICE Service,
> > + IN EFI_STRING Uri,
> > + IN CHAR8 *Content,
> > + IN UINTN ContentSize OPTIONAL,
> > + IN CHAR8 *ContentType OPTIONAL,
> > + OUT REDFISH_RESPONSE *Response
> > + )
> > +{
> > + EFI_STATUS Status;
> > + UINTN RetryCount;
> > + REDFISH_REQUEST Request;
> > + REDFISH_HTTP_CACHE_PRIVATE *Private;
> > +
> > + if ((This == NULL) || (Service == NULL) || (Response == NULL) ||
> > IS_EMPTY_STRING (Uri) || IS_EMPTY_STRING (Content)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Post URI: %s\n", __func__,
> > Uri));
> > +
> > + Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> > + RetryCount = 0;
> > + ZeroMem (Response, sizeof (REDFISH_RESPONSE));
> > + ZeroMem (&Request, sizeof (REDFISH_REQUEST));
> > +
> > + Request.Content = Content;
> > + Request.ContentLength = ContentSize;
> > + Request.ContentType = ContentType;
> > +
> > + //
> > + // Patch resource to redfish service.
> > + //
> > + do {
> > + RetryCount += 1;
> > + Status = HttpSendReceive (
> > + Service,
> > + Uri,
> > + HttpMethodPost,
> > + &Request,
> > + Response
> > + );
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request:
> > %s :%r\n", __func__, Uri, Status));
> > + if (!EFI_ERROR (Status) || (RetryCount >= Private-
> > >RetrySetting.MaximumRetryPost)) {
> > + break;
> > + }
> > +
> > + //
> > + // Retry when BMC is not ready.
> > + //
> > + if ((Response->StatusCode != NULL)) {
> > + DEBUG_CODE (
> > + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> > + );
> > +
> > + if (!RedfishRetryRequired (Response->StatusCode)) {
> > + break;
> > + }
> > +
> > + //
> > + // Release response for next round of request.
> > + //
> > + This->FreeResponse (This, Response);
> > + }
> > +
> > + DEBUG ((DEBUG_WARN, "%a: RedfishPostToUri failed, retry (%d/%d)\n",
> > __func__, RetryCount, Private->RetrySetting.MaximumRetryPost));
> > + if (Private->RetrySetting.RetryWait > 0) {
> > + gBS->Stall (Private->RetrySetting.RetryWait);
> > + }
> > + } while (TRUE);
> > +
> > + //
> > + // Redfish resource is updated. Automatically expire the cached response
> > + // so application can directly get resource from Redfish service again.
> > + //
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire
> > URI: %s\n", __func__, Uri));
> > + RedfishExpireResponse (This, Uri);
> > +
> > + if (EFI_ERROR (Status)) {
> > + DEBUG_CODE (
> > + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> > + );
> > + //
> > + // Report status code for Redfish failure
> > + //
> > + ReportHttpError (HttpMethodPost, Uri, Response->StatusCode);
> > + DEBUG ((DEBUG_ERROR, "%a: post %s failed (%d/%d): %r\n", __func__,
> > Uri, RetryCount, Private->RetrySetting.MaximumRetryPost, Status));
> > + goto ON_RELEASE;
> > + }
> > +
> > +ON_RELEASE:
> > +
> > + return Status;
> > +}
> > +
> > +/**
> > + Perform HTTP DELETE to delete redfish resource on given resource URI.
> > + It's caller's responsibility to free Response by calling FreeResponse ().
> > +
> > + @param[in] This Pointer to EDKII_REDFISH_HTTP_PROTOCOL instance.
> > + @param[in] Service Redfish service instance to perform HTTP DELETE.
> > + @param[in] Uri Target resource URI.
> > + @param[in] Content JSON represented properties to be deleted. This is
> > + optional.
> > + @param[in] ContentSize Size of the Content to be send to Redfish service.
> > + This is optional. When ContentSize is 0, ContentSize
> > + is the size of Content if Content is not NULL.
> > + @param[in] ContentType Type of the Content to be send to Redfish
> > service.
> > + This is optional. When Content is not NULL and
> > + ContentType is NULL, content type
> > HTTP_CONTENT_TYPE_APP_JSON
> > + will be used.
> > + @param[out] Response HTTP response from redfish service.
> > +
> > + @retval EFI_SUCCESS Resrouce is returned successfully.
> > + @retval Others Errors occur.
> > +
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +RedfishDeleteResource (
> > + IN EDKII_REDFISH_HTTP_PROTOCOL *This,
> > + IN REDFISH_SERVICE Service,
> > + IN EFI_STRING Uri,
> > + IN CHAR8 *Content OPTIONAL,
> > + IN UINTN ContentSize OPTIONAL,
> > + IN CHAR8 *ContentType OPTIONAL,
> > + OUT REDFISH_RESPONSE *Response
> > + )
> > +{
> > + EFI_STATUS Status;
> > + UINTN RetryCount;
> > + REDFISH_REQUEST Request;
> > + REDFISH_HTTP_CACHE_PRIVATE *Private;
> > +
> > + if ((This == NULL) || (Service == NULL) || (Response == NULL) ||
> > IS_EMPTY_STRING (Uri)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Delete URI: %s\n", __func__,
> > Uri));
> > +
> > + Private = REDFISH_HTTP_CACHE_PRIVATE_FROM_THIS (This);
> > + RetryCount = 0;
> > + ZeroMem (Response, sizeof (REDFISH_RESPONSE));
> > + ZeroMem (&Request, sizeof (REDFISH_REQUEST));
> > +
> > + Request.Content = Content;
> > + Request.ContentLength = ContentSize;
> > + Request.ContentType = ContentType;
> > +
> > + //
> > + // Patch resource to redfish service.
> > + //
> > + do {
> > + RetryCount += 1;
> > + Status = HttpSendReceive (
> > + Service,
> > + Uri,
> > + HttpMethodDelete,
> > + &Request,
> > + Response
> > + );
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG_REQUEST, "%a: HTTP request:
> > %s :%r\n", __func__, Uri, Status));
> > + if (!EFI_ERROR (Status) || (RetryCount >= Private-
> > >RetrySetting.MaximumRetryDelete)) {
> > + break;
> > + }
> > +
> > + //
> > + // Retry when BMC is not ready.
> > + //
> > + if ((Response->StatusCode != NULL)) {
> > + DEBUG_CODE (
> > + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> > + );
> > +
> > + if (!RedfishRetryRequired (Response->StatusCode)) {
> > + break;
> > + }
> > +
> > + //
> > + // Release response for next round of request.
> > + //
> > + This->FreeResponse (This, Response);
> > + }
> > +
> > + DEBUG ((DEBUG_WARN, "%a: RedfishDeleteByUri failed, retry
> > (%d/%d)\n", __func__, RetryCount, Private-
> > >RetrySetting.MaximumRetryDelete));
> > + if (Private->RetrySetting.RetryWait > 0) {
> > + gBS->Stall (Private->RetrySetting.RetryWait);
> > + }
> > + } while (TRUE);
> > +
> > + //
> > + // Redfish resource is updated. Automatically expire the cached response
> > + // so application can directly get resource from Redfish service again.
> > + //
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: Resource is updated, expire
> > URI: %s\n", __func__, Uri));
> > + RedfishExpireResponse (This, Uri);
> > +
> > + if (EFI_ERROR (Status)) {
> > + DEBUG_CODE (
> > + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> > + );
> > + //
> > + // Report status code for Redfish failure
> > + //
> > + ReportHttpError (HttpMethodDelete, Uri, Response->StatusCode);
> > + DEBUG ((DEBUG_ERROR, "%a: delete %s failed (%d/%d): %r\n", __func__,
> > Uri, RetryCount, Private->RetrySetting.MaximumRetryDelete, Status));
> > + goto ON_RELEASE;
> > + }
> > +
> > +ON_RELEASE:
> > +
> > + return Status;
> > +}
> > +
> > +EDKII_REDFISH_HTTP_PROTOCOL mEdkIIRedfishHttpProtocol = {
> > + EDKII_REDFISH_HTTP_PROTOCOL_REVISION,
> > + RedfishCreateRedfishService,
> > + RedfishFreeRedfishService,
> > + RedfishJsonInRedfishPayload,
> > + RedfishGetResource,
> > + RedfishPatchResource,
> > + RedfishPutResource,
> > + RedfishPostResource,
> > + RedfishDeleteResource,
> > + RedfishFreeRequest,
> > + RedfishFreeResponse,
> > + RedfishExpireResponse
> > +};
> > +
> > +/**
> > + Unloads an image.
> > +
> > + @param[in] ImageHandle Handle that identifies the image to be
> > unloaded.
> > +
> > + @retval EFI_SUCCESS The image has been unloaded.
> > + @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image
> > handle.
> > +
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +RedfishHttpDriverUnload (
> > + IN EFI_HANDLE ImageHandle
> > + )
> > +{
> > + if (mRedfishHttpCachePrivate == NULL) {
> > + return EFI_SUCCESS;
> > + }
> > +
> > + if (!IsListEmpty (&mRedfishHttpCachePrivate->CacheList.Head)) {
> > + ReleaseCacheList (&mRedfishHttpCachePrivate->CacheList);
> > + }
> > +
> > + gBS->UninstallMultipleProtocolInterfaces (
> > + ImageHandle,
> > + &gEdkIIRedfishHttpProtocolGuid,
> > + &mRedfishHttpCachePrivate->Protocol,
> > + NULL
> > + );
> > +
> > + FreePool (mRedfishHttpCachePrivate);
> > + mRedfishHttpCachePrivate = NULL;
> > +
> > + return EFI_SUCCESS;
> > +}
> > +
> > +/**
> > + This is a EDKII_REDFISH_CREDENTIAL_PROTOCOL notification event handler.
> > +
> > + @param[in] Event Event whose notification function is being invoked.
> > + @param[in] Context Pointer to the notification function's context.
> > +
> > +**/
> > +VOID
> > +EFIAPI
> > +CredentialProtocolInstalled (
> > + IN EFI_EVENT Event,
> > + IN VOID *Context
> > + )
> > +{
> > + EFI_STATUS Status;
> > + REDFISH_HTTP_CACHE_PRIVATE *Private;
> > +
> > + Private = (REDFISH_HTTP_CACHE_PRIVATE *)Context;
> > + if (Private->Signature != REDFISH_HTTP_DRIVER_SIGNATURE) {
> > + DEBUG ((DEBUG_ERROR, "%a: signature check failure\n", __func__));
> > + return;
> > + }
> > +
> > + //
> > + // Locate HII database protocol.
> > + //
> > + Status = gBS->LocateProtocol (
> > + &gEdkIIRedfishCredentialProtocolGuid,
> > + NULL,
> > + (VOID **)&Private->CredentialProtocol
> > + );
> > + if (EFI_ERROR (Status)) {
> > + return;
> > + }
> > +
> > + gBS->CloseEvent (Event);
> > +}
> > +
> > +/**
> > + Main entry for this driver.
> > +
> > + @param[in] ImageHandle Image handle this driver.
> > + @param[in] SystemTable Pointer to SystemTable.
> > +
> > + @retval EFI_SUCCESS This function always complete successfully.
> > +
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +RedfishHttpEntryPoint (
> > + IN EFI_HANDLE ImageHandle,
> > + IN EFI_SYSTEM_TABLE *SystemTable
> > + )
> > +{
> > + EFI_STATUS Status;
> > + VOID *Registration;
> > +
> > + if (mRedfishHttpCachePrivate != NULL) {
> > + return EFI_ALREADY_STARTED;
> > + }
> > +
> > + mRedfishHttpCachePrivate = AllocateZeroPool (sizeof
> > (REDFISH_HTTP_CACHE_PRIVATE));
> > + if (mRedfishHttpCachePrivate == NULL) {
> > + return EFI_OUT_OF_RESOURCES;
> > + }
> > +
> > + //
> > + // Initial cache list and protocol instance.
> > + //
> > + mRedfishHttpCachePrivate->Signature =
> > REDFISH_HTTP_DRIVER_SIGNATURE;
> > + mRedfishHttpCachePrivate->ImageHandle = ImageHandle;
> > + CopyMem (&mRedfishHttpCachePrivate->Protocol,
> > &mEdkIIRedfishHttpProtocol, sizeof (EDKII_REDFISH_HTTP_PROTOCOL));
> > + mRedfishHttpCachePrivate->CacheList.Capacity =
> > REDFISH_HTTP_CACHE_LIST_SIZE;
> > + mRedfishHttpCachePrivate->CacheList.Count = 0x00;
> > + mRedfishHttpCachePrivate->CacheDisabled = PcdGetBool
> > (PcdHttpCacheDisabled);
> > + InitializeListHead (&mRedfishHttpCachePrivate->CacheList.Head);
> > +
> > + //
> > + // Get retry settings
> > + //
> > + mRedfishHttpCachePrivate->RetrySetting.MaximumRetryGet = PcdGet16
> > (PcdHttpGetRetry);
> > + mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPut = PcdGet16
> > (PcdHttpPutRetry);
> > + mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPatch = PcdGet16
> > (PcdHttpPatchRetry);
> > + mRedfishHttpCachePrivate->RetrySetting.MaximumRetryPost = PcdGet16
> > (PcdHttpPostRetry);
> > + mRedfishHttpCachePrivate->RetrySetting.MaximumRetryDelete = PcdGet16
> > (PcdHttpDeleteRetry);
> > + mRedfishHttpCachePrivate->RetrySetting.RetryWait = PcdGet16
> > (PcdHttpRetryWaitInSecond) * 1000000U;
> > +
> > + //
> > + // Install the gEdkIIRedfishHttpProtocolGuid onto Handle.
> > + //
> > + Status = gBS->InstallMultipleProtocolInterfaces (
> > + &mRedfishHttpCachePrivate->ImageHandle,
> > + &gEdkIIRedfishHttpProtocolGuid,
> > + &mRedfishHttpCachePrivate->Protocol,
> > + NULL
> > + );
> > + if (EFI_ERROR (Status)) {
> > + DEBUG ((DEBUG_ERROR, "%a: cannot install Redfish http protocol: %r\n",
> > __func__, Status));
> > + RedfishHttpDriverUnload (ImageHandle);
> > + return Status;
> > + }
> > +
> > + //
> > + // Install protocol notification if credential protocol is installed.
> > + //
> > + mRedfishHttpCachePrivate->NotifyEvent = EfiCreateProtocolNotifyEvent (
> > + &gEdkIIRedfishCredentialProtocolGuid,
> > + TPL_CALLBACK,
> > + CredentialProtocolInstalled,
> > + mRedfishHttpCachePrivate,
> > + &Registration
> > + );
> > + if (mRedfishHttpCachePrivate->NotifyEvent == NULL) {
> > + DEBUG ((DEBUG_ERROR, "%a: failed to create protocol notification for
> > gEdkIIRedfishCredentialProtocolGuid\n", __func__));
> > + ASSERT (FALSE);
> > + RedfishHttpDriverUnload (ImageHandle);
> > + return Status;
> > + }
> > +
> > + return EFI_SUCCESS;
> > +}
> > diff --git a/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
> > b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
> > new file mode 100644
> > index 0000000000..5652818d16
> > --- /dev/null
> > +++ b/RedfishPkg/RedfishHttpDxe/RedfishHttpOperation.c
> > @@ -0,0 +1,693 @@
> > +/** @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;
> > + }
> > +
> > + for (Index = 0; Index < SrcHeaderCount; Index++) {
> > + (*DstHeaders)[Index].FieldName = AllocateCopyPool (AsciiStrSize
> > (SrcHeaders[Index].FieldName), SrcHeaders[Index].FieldName);
> > + if ((*DstHeaders)[Index].FieldName == NULL) {
> > + return EFI_OUT_OF_RESOURCES;
> > + }
> > +
> > + (*DstHeaders)[Index].FieldValue = AllocateCopyPool (AsciiStrSize
> > (SrcHeaders[Index].FieldValue), SrcHeaders[Index].FieldValue);
> > + if ((*DstHeaders)[Index].FieldValue == NULL) {
> > + return EFI_OUT_OF_RESOURCES;
> > + }
> > +
> > + *DstHeaderCount += 1;
> > + }
> > +
> > + 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 Resrouce 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 Resrouce 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 Resrouce 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;
> > +}
> > diff --git a/RedfishPkg/Redfish.fdf.inc b/RedfishPkg/Redfish.fdf.inc
> > index 3e5a77766e..5cbe3592fd 100644
> > --- a/RedfishPkg/Redfish.fdf.inc
> > +++ b/RedfishPkg/Redfish.fdf.inc
> > @@ -6,7 +6,7 @@
> > # to be built in the firmware volume.
> > #
> > # (C) Copyright 2020-2021 Hewlett Packard Enterprise Development LP<BR>
> > -# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights
> > reserved.
> > +# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> > reserved.
> > #
> > # SPDX-License-Identifier: BSD-2-Clause-Patent
> > #
> > @@ -20,4 +20,5 @@
> > INF RedfishPkg/RedfishConfigHandler/RedfishConfigHandlerDriver.inf
> > INF RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf
> > INF
> > MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf
> > + INF RedfishPkg/RedfishHttpDxe/RedfishHttpDxe.inf
> > !endif
> > --
> > 2.34.1
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115781): https://edk2.groups.io/g/devel/message/115781
Mute This Topic: https://groups.io/mt/104463404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2024-02-22 9:16 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-02-20 6:41 [edk2-devel] [PATCH 2/6] RedfishPkg: implement Redfish HTTP protocol Nickle Wang via groups.io
2024-02-21 1:51 ` Chang, Abner via groups.io
2024-02-22 9:16 ` Nickle Wang via groups.io
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox