* [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
@ 2024-02-22 9:11 Nickle Wang via groups.io
2024-02-22 13:39 ` Chang, Abner via groups.io
` (2 more replies)
0 siblings, 3 replies; 15+ messages in thread
From: Nickle Wang via groups.io @ 2024-02-22 9:11 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..d2f7cf4c27
--- /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_OPERATION_H_
+#define EDKII_REDFISH_HTTP_OPERATION_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 (#115776): https://edk2.groups.io/g/devel/message/115776
Mute This Topic: https://groups.io/mt/104505404/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] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
2024-02-22 9:11 [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol Nickle Wang via groups.io
@ 2024-02-22 13:39 ` Chang, Abner via groups.io
2024-02-22 14:17 ` Igor Kulchytskyy via groups.io
2024-02-23 11:29 ` Mike Maslenkin
2 siblings, 0 replies; 15+ messages in thread
From: Chang, Abner via groups.io @ 2024-02-22 13:39 UTC (permalink / raw)
To: Nickle Wang, devel@edk2.groups.io; +Cc: Igor Kulchytskyy, Nick Ramirez
[AMD Official Use Only - General]
Thanks!
Reviewed-by: Abner Chang <abner.chang@amd.com>
> -----Original Message-----
> From: Nickle Wang <nicklew@nvidia.com>
> Sent: Thursday, February 22, 2024 5:11 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 v2 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..d2f7cf4c27
> --- /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_OPERATION_H_
> +#define EDKII_REDFISH_HTTP_OPERATION_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 (#115815): https://edk2.groups.io/g/devel/message/115815
Mute This Topic: https://groups.io/mt/104505404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
2024-02-22 9:11 [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol Nickle Wang via groups.io
2024-02-22 13:39 ` Chang, Abner via groups.io
@ 2024-02-22 14:17 ` Igor Kulchytskyy via groups.io
2024-02-23 11:29 ` Mike Maslenkin
2 siblings, 0 replies; 15+ messages in thread
From: Igor Kulchytskyy via groups.io @ 2024-02-22 14:17 UTC (permalink / raw)
To: Nickle Wang, devel@edk2.groups.io; +Cc: Abner Chang, Nick Ramirez
Reviewed-by: Igor Kulchytskyy <igork@ami.com>
-----Original Message-----
From: Nickle Wang <nicklew@nvidia.com>
Sent: Thursday, February 22, 2024 4:11 AM
To: devel@edk2.groups.io
Cc: Igor Kulchytskyy <igork@ami.com>; Abner Chang <abner.chang@amd.com>; Nick Ramirez <nramirez@nvidia.com>
Subject: [EXTERNAL] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
**CAUTION: The e-mail below is from an external source. Please exercise caution before opening attachments, clicking links, or following guidance.**
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..d2f7cf4c27
--- /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_OPERATION_H_
+#define EDKII_REDFISH_HTTP_OPERATION_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
-The information contained in this message may be confidential and proprietary to American Megatrends (AMI). This communication is intended to be read only by the individual or entity to whom it is addressed or by their designee. If the reader of this message is not the intended recipient, you are on notice that any distribution of this message, in any form, is strictly prohibited. Please promptly notify the sender by reply e-mail or by telephone at 770-246-8600, and then delete or destroy all copies of the transmission.
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115820): https://edk2.groups.io/g/devel/message/115820
Mute This Topic: https://groups.io/mt/104505404/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] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
2024-02-22 9:11 [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol Nickle Wang via groups.io
2024-02-22 13:39 ` Chang, Abner via groups.io
2024-02-22 14:17 ` Igor Kulchytskyy via groups.io
@ 2024-02-23 11:29 ` Mike Maslenkin
2024-02-23 14:07 ` Nickle Wang via groups.io
2 siblings, 1 reply; 15+ messages in thread
From: Mike Maslenkin @ 2024-02-23 11:29 UTC (permalink / raw)
To: devel, Nickle Wang; +Cc: Igor Kulchytskyy, Abner Chang, Nick Ramirez
Hi Nickle,
%s/Resrouce/Resource/ this comes from RedfishClient autogenerated files... there are thousands of "Resrouce" typos.
please, find my minor notes below:
On Thu, Feb 22, 2024 at 12:11 PM Nickle Wang via groups.io <nicklew=nvidia.com@groups.io> wrote:
>
> 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..d2f7cf4c27
> --- /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_OPERATION_H_
> +#define EDKII_REDFISH_HTTP_OPERATION_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);
> +
NewPayload->Service is leaked
> + 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;
Looks like orevious allocations leaked.
Didn't you think to implement smth like this https://github.com/tianocore/edk2/blob/master/OvmfPkg/XenBusDxe/Helpers.c#L4 ?
> + }
> +
> + *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
>
>
>
>
>
Regards,
Mike.
>
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115881): https://edk2.groups.io/g/devel/message/115881
Mute This Topic: https://groups.io/mt/104505404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
2024-02-23 11:29 ` Mike Maslenkin
@ 2024-02-23 14:07 ` Nickle Wang via groups.io
2024-02-23 16:04 ` Mike Maslenkin
0 siblings, 1 reply; 15+ messages in thread
From: Nickle Wang via groups.io @ 2024-02-23 14:07 UTC (permalink / raw)
To: Mike Maslenkin, devel@edk2.groups.io
Cc: Igor Kulchytskyy, Abner Chang, Nick Ramirez
Thanks for your review, Mike.
> %s/Resrouce/Resource/ this comes from RedfishClient autogenerated files...
Typos are Addressed. I will send v3 later.
>> +ON_ERROR:
>> +
>> + ReleaseRedfishPayload (NewPayload);
>> +
>
> NewPayload->Service is leaked
NewPayload->Service will be released in ReleaseRedfishPayload function. ReleaseRedfishService() will be called in ReleaseRedfishPayload() when Service is not NULL. Please let me know if I misunderstand your comment.
>> + (*DstHeaders)[Index].FieldValue = AllocateCopyPool (AsciiStrSize (SrcHeaders[Index].FieldValue), SrcHeaders[Index].FieldValue);
>> + if ((*DstHeaders)[Index].FieldValue == NULL) {
>> + return EFI_OUT_OF_RESOURCES;
>
> Looks like orevious allocations leaked.
> Didn't you think to implement smth like this ....?
Yes, I can implement AsciiStrDup() here. But this won't fix the allocation leaking, right? To fix allocation leaking, my idea is to call HttpFreeHeaderFields() before returning EFI_OUT_OF_RESOURCES. HttpFreeHeaderFields() will skip NULL FieldName and FieldValue automatically. Does this sound good to you?
I think I covered all your comments, but I am bad to find review comment in email. If I missed any review comment, please kindly let me know. Thanks!
Regards,
Nickle
> -----Original Message-----
> From: Mike Maslenkin <mike.maslenkin@gmail.com>
> Sent: Friday, February 23, 2024 7:29 PM
> To: devel@edk2.groups.io; Nickle Wang <nicklew@nvidia.com>
> Cc: Igor Kulchytskyy <igork@ami.com>; Abner Chang <abner.chang@amd.com>;
> Nick Ramirez <nramirez@nvidia.com>
> Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP
> protocol
>
> External email: Use caution opening links or attachments
>
>
> Hi Nickle,
>
> %s/Resrouce/Resource/ this comes from RedfishClient autogenerated files...
> there are thousands of "Resrouce" typos.
>
> please, find my minor notes below:
>
>
> On Thu, Feb 22, 2024 at 12:11 PM Nickle Wang via groups.io
> <nicklew=nvidia.com@groups.io> wrote:
> >
> > 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|0
> x00001009
> > - ## This is used to enable HTTP content encoding on Redfish communication.
> > -
> gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|TRUE|BOOLE
> AN|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/Platf
> ormCredentialLibNull.inf
> >
> RedfishContentCodingLib|RedfishPkg/Library/RedfishContentCodingLibNull/Redfi
> shContentCodingLibNull.inf
> > +
> ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeRepo
> rtStatusCodeLib.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..d2f7cf4c27
> > --- /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_OPERATION_H_
> > +#define EDKII_REDFISH_HTTP_OPERATION_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);
> > +
>
>
> NewPayload->Service is leaked
>
>
> > + 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%7Cf659c54c3edb43527c3608dc3462ab2a%7C43083d15727340c1b7db
> 39efd9ccc17a%7C0%7C0%7C638442845601051396%7CUnknown%7CTWFpbGZs
> b3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%
> 3D%7C0%7C%7C%7C&sdata=T1bN7KmTa1v49cOFkd9%2F9hlzkdzbHPLebxSiUkp
> VuU4%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;
>
>
>
> Looks like orevious allocations leaked.
> Didn't you think to implement smth like this
> https://github.co/
> m%2Ftianocore%2Fedk2%2Fblob%2Fmaster%2FOvmfPkg%2FXenBusDxe%2FHelp
> ers.c%23L4&data=05%7C02%7Cnicklew%40nvidia.com%7Cf659c54c3edb43527c
> 3608dc3462ab2a%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7C6384
> 42845601059719%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJ
> QIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=oYv
> XkfjOpakMOXV1w6nYVphDRuPM7R73lqr8QVqh%2BI4%3D&reserved=0 ?
>
>
> > + }
> > +
> > + *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
> >
> >
> >
> >
> >
>
> Regards,
> Mike.
> >
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115885): https://edk2.groups.io/g/devel/message/115885
Mute This Topic: https://groups.io/mt/104505404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
2024-02-23 14:07 ` Nickle Wang via groups.io
@ 2024-02-23 16:04 ` Mike Maslenkin
2024-02-26 13:43 ` Nickle Wang via groups.io
0 siblings, 1 reply; 15+ messages in thread
From: Mike Maslenkin @ 2024-02-23 16:04 UTC (permalink / raw)
To: Nickle Wang
Cc: devel@edk2.groups.io, Igor Kulchytskyy, Abner Chang, Nick Ramirez
Hi Nickle,
On Fri, Feb 23, 2024 at 5:07 PM Nickle Wang <nicklew@nvidia.com> wrote:
>
> Thanks for your review, Mike.
>
> > %s/Resrouce/Resource/ this comes from RedfishClient autogenerated files...
>
> Typos are Addressed. I will send v3 later.
>
> >> +ON_ERROR:
> >> +
> >> + ReleaseRedfishPayload (NewPayload);
> >> +
> >
> > NewPayload->Service is leaked
>
> NewPayload->Service will be released in ReleaseRedfishPayload function. ReleaseRedfishService() will be called in ReleaseRedfishPayload() when Service is not NULL. Please let me know if I misunderstand your comment.
Oh, I see.
I missed ReleaseRedfishService() call.
>
>
> >> + (*DstHeaders)[Index].FieldValue = AllocateCopyPool (AsciiStrSize (SrcHeaders[Index].FieldValue), SrcHeaders[Index].FieldValue);
> >> + if ((*DstHeaders)[Index].FieldValue == NULL) {
> >> + return EFI_OUT_OF_RESOURCES;
> >
> > Looks like orevious allocations leaked.
> > Didn't you think to implement smth like this ....?
>
> Yes, I can implement AsciiStrDup() here. But this won't fix the allocation leaking, right? To fix allocation leaking, my idea is to call HttpFreeHeaderFields() before returning EFI_OUT_OF_RESOURCES. HttpFreeHeaderFields() will skip NULL FieldName and FieldValue automatically. Does this sound good to you?
>
> I think I covered all your comments, but I am bad to find review comment in email. If I missed any review comment, please kindly let me know. Thanks!
AsciiStrDup() is not handy here, because it requires preallocated buffer.
I mentioned about function similar to StrDup just to get shorter and
cleaner code.
I think I understood the idea now.
I'm just looking into code and assume that if function returns an
error, it must deallocate/cleanup resources.
So finally we have
HttpFreeHeaderFields (Response->Headers, Response->HeaderCount);
but Response->HeaderCount does not count partially allocated elements. Right?
To fix this, it is required to set
*DstHeaderCount = SrcHeaderCount unconditionally right after
DstHeaders allocation, and HttpFreeHeaderFields() will do the work
then.
Regards,
Mike.
>
> Regards,
> Nickle
>
> > -----Original Message-----
> > From: Mike Maslenkin <mike.maslenkin@gmail.com>
> > Sent: Friday, February 23, 2024 7:29 PM
> > To: devel@edk2.groups.io; Nickle Wang <nicklew@nvidia.com>
> > Cc: Igor Kulchytskyy <igork@ami.com>; Abner Chang <abner.chang@amd.com>;
> > Nick Ramirez <nramirez@nvidia.com>
> > Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP
> > protocol
> >
> > External email: Use caution opening links or attachments
> >
> >
> > Hi Nickle,
> >
> > %s/Resrouce/Resource/ this comes from RedfishClient autogenerated files...
> > there are thousands of "Resrouce" typos.
> >
> > please, find my minor notes below:
> >
> >
> > On Thu, Feb 22, 2024 at 12:11 PM Nickle Wang via groups.io
> > <nicklew=nvidia.com@groups.io> wrote:
> > >
> > > 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|0
> > x00001009
> > > - ## This is used to enable HTTP content encoding on Redfish communication.
> > > -
> > gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|TRUE|BOOLE
> > AN|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/Platf
> > ormCredentialLibNull.inf
> > >
> > RedfishContentCodingLib|RedfishPkg/Library/RedfishContentCodingLibNull/Redfi
> > shContentCodingLibNull.inf
> > > +
> > ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeRepo
> > rtStatusCodeLib.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..d2f7cf4c27
> > > --- /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_OPERATION_H_
> > > +#define EDKII_REDFISH_HTTP_OPERATION_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);
> > > +
> >
> >
> > NewPayload->Service is leaked
> >
> >
> > > + 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%7Cf659c54c3edb43527c3608dc3462ab2a%7C43083d15727340c1b7db
> > 39efd9ccc17a%7C0%7C0%7C638442845601051396%7CUnknown%7CTWFpbGZs
> > b3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%
> > 3D%7C0%7C%7C%7C&sdata=T1bN7KmTa1v49cOFkd9%2F9hlzkdzbHPLebxSiUkp
> > VuU4%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;
> >
> >
> >
> > Looks like orevious allocations leaked.
> > Didn't you think to implement smth like this
> > https://github.co/
> > m%2Ftianocore%2Fedk2%2Fblob%2Fmaster%2FOvmfPkg%2FXenBusDxe%2FHelp
> > ers.c%23L4&data=05%7C02%7Cnicklew%40nvidia.com%7Cf659c54c3edb43527c
> > 3608dc3462ab2a%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7C6384
> > 42845601059719%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJ
> > QIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=oYv
> > XkfjOpakMOXV1w6nYVphDRuPM7R73lqr8QVqh%2BI4%3D&reserved=0 ?
> >
> >
> > > + }
> > > +
> > > + *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
> > >
> > >
> > >
> > >
> > >
> >
> > Regards,
> > Mike.
> > >
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115889): https://edk2.groups.io/g/devel/message/115889
Mute This Topic: https://groups.io/mt/104505404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
2024-02-23 16:04 ` Mike Maslenkin
@ 2024-02-26 13:43 ` Nickle Wang via groups.io
2024-02-27 0:13 ` Mike Maslenkin
0 siblings, 1 reply; 15+ messages in thread
From: Nickle Wang via groups.io @ 2024-02-26 13:43 UTC (permalink / raw)
To: Mike Maslenkin
Cc: devel@edk2.groups.io, Igor Kulchytskyy, Abner Chang, Nick Ramirez
Hi Mike,
> So finally we have
> HttpFreeHeaderFields (Response->Headers, Response->HeaderCount);
> but Response->HeaderCount does not count partially allocated elements. Right?
>
> To fix this, it is required to set
> *DstHeaderCount = SrcHeaderCount unconditionally right after
> DstHeaders allocation, and HttpFreeHeaderFields() will do the work
> then.
I follow your suggestion to update DstHeaderCount right after DstHeaders is allocated. So, HttpFreeHeaderFields can release headers correctly. I also create a macro to implemented AsciiStrCpy. Please check below link to see my changes:
https://github.com/tianocore/edk2/compare/0f391b1c2f988d90a3ac723b314ac28ba7b0b8df..f0fa1b8fdcd933beb52fd3127c2476443c00ef8d
Thanks,
Nickle
> -----Original Message-----
> From: Mike Maslenkin <mike.maslenkin@gmail.com>
> Sent: Saturday, February 24, 2024 12:05 AM
> To: Nickle Wang <nicklew@nvidia.com>
> Cc: devel@edk2.groups.io; Igor Kulchytskyy <igork@ami.com>; Abner Chang
> <abner.chang@amd.com>; Nick Ramirez <nramirez@nvidia.com>
> Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP
> protocol
>
> External email: Use caution opening links or attachments
>
>
> Hi Nickle,
>
> On Fri, Feb 23, 2024 at 5:07 PM Nickle Wang <nicklew@nvidia.com> wrote:
> >
> > Thanks for your review, Mike.
> >
> > > %s/Resrouce/Resource/ this comes from RedfishClient autogenerated files...
> >
> > Typos are Addressed. I will send v3 later.
> >
> > >> +ON_ERROR:
> > >> +
> > >> + ReleaseRedfishPayload (NewPayload);
> > >> +
> > >
> > > NewPayload->Service is leaked
> >
> > NewPayload->Service will be released in ReleaseRedfishPayload function.
> ReleaseRedfishService() will be called in ReleaseRedfishPayload() when Service is
> not NULL. Please let me know if I misunderstand your comment.
>
> Oh, I see.
> I missed ReleaseRedfishService() call.
>
> >
> >
> > >> + (*DstHeaders)[Index].FieldValue = AllocateCopyPool (AsciiStrSize
> (SrcHeaders[Index].FieldValue), SrcHeaders[Index].FieldValue);
> > >> + if ((*DstHeaders)[Index].FieldValue == NULL) {
> > >> + return EFI_OUT_OF_RESOURCES;
> > >
> > > Looks like orevious allocations leaked.
> > > Didn't you think to implement smth like this ....?
> >
> > Yes, I can implement AsciiStrDup() here. But this won't fix the allocation leaking,
> right? To fix allocation leaking, my idea is to call HttpFreeHeaderFields() before
> returning EFI_OUT_OF_RESOURCES. HttpFreeHeaderFields() will skip NULL
> FieldName and FieldValue automatically. Does this sound good to you?
> >
> > I think I covered all your comments, but I am bad to find review comment in
> email. If I missed any review comment, please kindly let me know. Thanks!
>
> AsciiStrDup() is not handy here, because it requires preallocated buffer.
> I mentioned about function similar to StrDup just to get shorter and
> cleaner code.
>
> I think I understood the idea now.
> I'm just looking into code and assume that if function returns an
> error, it must deallocate/cleanup resources.
>
> So finally we have
> HttpFreeHeaderFields (Response->Headers, Response->HeaderCount);
> but Response->HeaderCount does not count partially allocated elements. Right?
>
> To fix this, it is required to set
> *DstHeaderCount = SrcHeaderCount unconditionally right after
> DstHeaders allocation, and HttpFreeHeaderFields() will do the work
> then.
>
> Regards,
> Mike.
>
> >
> > Regards,
> > Nickle
> >
> > > -----Original Message-----
> > > From: Mike Maslenkin <mike.maslenkin@gmail.com>
> > > Sent: Friday, February 23, 2024 7:29 PM
> > > To: devel@edk2.groups.io; Nickle Wang <nicklew@nvidia.com>
> > > Cc: Igor Kulchytskyy <igork@ami.com>; Abner Chang
> <abner.chang@amd.com>;
> > > Nick Ramirez <nramirez@nvidia.com>
> > > Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP
> > > protocol
> > >
> > > External email: Use caution opening links or attachments
> > >
> > >
> > > Hi Nickle,
> > >
> > > %s/Resrouce/Resource/ this comes from RedfishClient autogenerated files...
> > > there are thousands of "Resrouce" typos.
> > >
> > > please, find my minor notes below:
> > >
> > >
> > > On Thu, Feb 22, 2024 at 12:11 PM Nickle Wang via groups.io
> > > <nicklew=nvidia.com@groups.io> wrote:
> > > >
> > > > 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|0
> > > x00001009
> > > > - ## This is used to enable HTTP content encoding on Redfish
> communication.
> > > > -
> > >
> gEfiRedfishPkgTokenSpaceGuid.PcdRedfishServiceContentEncoding|TRUE|BOOLE
> > > AN|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/Platf
> > > ormCredentialLibNull.inf
> > > >
> > >
> RedfishContentCodingLib|RedfishPkg/Library/RedfishContentCodingLibNull/Redfi
> > > shContentCodingLibNull.inf
> > > > +
> > >
> ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeRepo
> > > rtStatusCodeLib.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..d2f7cf4c27
> > > > --- /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_OPERATION_H_
> > > > +#define EDKII_REDFISH_HTTP_OPERATION_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);
> > > > +
> > >
> > >
> > > NewPayload->Service is leaked
> > >
> > >
> > > > + 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/
> %2F&data=05%7C02%7Cnicklew%40nvidia.com%7Cb82203c2d6124060850308d
> c34894020%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7C638443011
> 350942592%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2
> luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=PbXKoTXf7
> HRXt7q1qhRbtmwEqAisA1qeblTN5GUMPe8%3D&reserved=0
> > >
> anocore.org%2Fshow_bug.cgi%3Fid%3D4483&data=05%7C02%7Cnicklew%40nvi
> > >
> dia.com%7Cf659c54c3edb43527c3608dc3462ab2a%7C43083d15727340c1b7db
> > >
> 39efd9ccc17a%7C0%7C0%7C638442845601051396%7CUnknown%7CTWFpbGZs
> > >
> b3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%
> > >
> 3D%7C0%7C%7C%7C&sdata=T1bN7KmTa1v49cOFkd9%2F9hlzkdzbHPLebxSiUkp
> > > VuU4%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;
> > >
> > >
> > >
> > > Looks like orevious allocations leaked.
> > > Didn't you think to implement smth like this
> > >
> https://github.co/
> %2F&data=05%7C02%7Cnicklew%40nvidia.com%7Cb82203c2d6124060850308d
> c34894020%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7C638443011
> 350956245%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2
> luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=j7ozA8KXgZ
> LpU1HoE0xsWua7Nzzx66BqdA0QGar1xqA%3D&reserved=0
> > >
> m%2Ftianocore%2Fedk2%2Fblob%2Fmaster%2FOvmfPkg%2FXenBusDxe%2FHelp
> > >
> ers.c%23L4&data=05%7C02%7Cnicklew%40nvidia.com%7Cf659c54c3edb43527c
> > >
> 3608dc3462ab2a%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7C6384
> > >
> 42845601059719%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJ
> > >
> QIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=oYv
> > > XkfjOpakMOXV1w6nYVphDRuPM7R73lqr8QVqh%2BI4%3D&reserved=0 ?
> > >
> > >
> > > > + }
> > > > +
> > > > + *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
> > > >
> > > >
> > > >
> > > >
> > > >
> > >
> > > Regards,
> > > Mike.
> > > >
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115958): https://edk2.groups.io/g/devel/message/115958
Mute This Topic: https://groups.io/mt/104505404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
2024-02-26 13:43 ` Nickle Wang via groups.io
@ 2024-02-27 0:13 ` Mike Maslenkin
2024-02-27 0:48 ` Nickle Wang via groups.io
[not found] ` <17B791D5F1D9AF42.14830@groups.io>
0 siblings, 2 replies; 15+ messages in thread
From: Mike Maslenkin @ 2024-02-27 0:13 UTC (permalink / raw)
To: Nickle Wang
Cc: devel@edk2.groups.io, Igor Kulchytskyy, Abner Chang, Nick Ramirez
Hii Nickle,
On Mon, Feb 26, 2024 at 4:44 PM Nickle Wang <nicklew@nvidia.com> wrote:
>
> Hi Mike,
>
> > So finally we have
> > HttpFreeHeaderFields (Response->Headers, Response->HeaderCount);
> > but Response->HeaderCount does not count partially allocated elements. Right?
> >
> > To fix this, it is required to set
> > *DstHeaderCount = SrcHeaderCount unconditionally right after
> > DstHeaders allocation, and HttpFreeHeaderFields() will do the work
> > then.
>
> I follow your suggestion to update DstHeaderCount right after DstHeaders is allocated. So, HttpFreeHeaderFields can release headers correctly. I also create a macro to implemented AsciiStrCpy. Please check below link to see my changes:
> https://github.com/tianocore/edk2/compare/0f391b1c2f988d90a3ac723b314ac28ba7b0b8df..f0fa1b8fdcd933beb52fd3127c2476443c00ef8d
These changes looks good. Internal strings
initialization/deinitialization code much cleaner now and possible
leak seems to have been fixed.
Thank you!
Regards,
Mike.
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115981): https://edk2.groups.io/g/devel/message/115981
Mute This Topic: https://groups.io/mt/104505404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
2024-02-27 0:13 ` Mike Maslenkin
@ 2024-02-27 0:48 ` Nickle Wang via groups.io
[not found] ` <17B791D5F1D9AF42.14830@groups.io>
1 sibling, 0 replies; 15+ messages in thread
From: Nickle Wang via groups.io @ 2024-02-27 0:48 UTC (permalink / raw)
To: Mike Maslenkin
Cc: devel@edk2.groups.io, Igor Kulchytskyy, Abner Chang, Nick Ramirez
Thanks for your confirmation, Mike!
Version 3 patch set is here: https://edk2.groups.io/g/devel/message/115985
Regards,
Nickle
> -----Original Message-----
> From: Mike Maslenkin <mike.maslenkin@gmail.com>
> Sent: Tuesday, February 27, 2024 8:13 AM
> To: Nickle Wang <nicklew@nvidia.com>
> Cc: devel@edk2.groups.io; Igor Kulchytskyy <igork@ami.com>; Abner Chang
> <abner.chang@amd.com>; Nick Ramirez <nramirez@nvidia.com>
> Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP
> protocol
>
> External email: Use caution opening links or attachments
>
>
> Hii Nickle,
>
>
> On Mon, Feb 26, 2024 at 4:44 PM Nickle Wang <nicklew@nvidia.com> wrote:
> >
> > Hi Mike,
> >
> > > So finally we have
> > > HttpFreeHeaderFields (Response->Headers, Response->HeaderCount); but
> > > Response->HeaderCount does not count partially allocated elements. Right?
> > >
> > > To fix this, it is required to set
> > > *DstHeaderCount = SrcHeaderCount unconditionally right after
> > > DstHeaders allocation, and HttpFreeHeaderFields() will do the work
> > > then.
> >
> > I follow your suggestion to update DstHeaderCount right after DstHeaders is
> allocated. So, HttpFreeHeaderFields can release headers correctly. I also create a
> macro to implemented AsciiStrCpy. Please check below link to see my changes:
> > https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgith
> >
> ub.com%2Ftianocore%2Fedk2%2Fcompare%2F0f391b1c2f988d90a3ac723b314a
> c28b
> >
> a7b0b8df..f0fa1b8fdcd933beb52fd3127c2476443c00ef8d&data=05%7C02%7Cnic
> k
> >
> lew%40nvidia.com%7Cf3870f71360e44f3b4e208dc3728ff87%7C43083d1572734
> 0c1
> >
> b7db39efd9ccc17a%7C0%7C0%7C638445896465360452%7CUnknown%7CTWFp
> bGZsb3d8
> >
> eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%
> 7C0
> >
> %7C%7C%7C&sdata=K%2FEA2QWpk%2F8NHQ1QhzqkvQqao4db%2BILn1Jt%2BB
> qQ5n1E%3D
> > &reserved=0
>
> These changes looks good. Internal strings initialization/deinitialization code much
> cleaner now and possible leak seems to have been fixed.
>
> Thank you!
>
> Regards,
> Mike.
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115991): https://edk2.groups.io/g/devel/message/115991
Mute This Topic: https://groups.io/mt/104505404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
[not found] ` <17B791D5F1D9AF42.14830@groups.io>
@ 2024-02-28 11:47 ` Nickle Wang via groups.io
2024-02-28 17:56 ` Mike Maslenkin
0 siblings, 1 reply; 15+ messages in thread
From: Nickle Wang via groups.io @ 2024-02-28 11:47 UTC (permalink / raw)
To: devel@edk2.groups.io, Nickle Wang, Mike Maslenkin
Cc: Igor Kulchytskyy, Abner Chang, Nick Ramirez
[-- Attachment #1: Type: text/plain, Size: 3738 bytes --]
Hi @Mike Maslenkin<mailto:mike.maslenkin@gmail.com>,
May I have your reviewed-by if version 3 patch set look good to you?
Thanks,
Nickle
> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Nickle Wang
> via groups.io
> Sent: Tuesday, February 27, 2024 8:49 AM
> To: Mike Maslenkin <mike.maslenkin@gmail.com>
> Cc: devel@edk2.groups.io; Igor Kulchytskyy <igork@ami.com>; Abner Chang
> <abner.chang@amd.com>; Nick Ramirez <nramirez@nvidia.com>
> Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP
> protocol
>
> External email: Use caution opening links or attachments
>
>
> Thanks for your confirmation, Mike!
>
> Version 3 patch set is here: https://edk2.groups.io/g/devel/message/115985
>
> Regards,
> Nickle
>
> > -----Original Message-----
> > From: Mike Maslenkin <mike.maslenkin@gmail.com<mailto:mike.maslenkin@gmail.com>>
> > Sent: Tuesday, February 27, 2024 8:13 AM
> > To: Nickle Wang <nicklew@nvidia.com<mailto:nicklew@nvidia.com>>
> > Cc: devel@edk2.groups.io<mailto:devel@edk2.groups.io>; Igor Kulchytskyy <igork@ami.com<mailto:igork@ami.com>>; Abner
> > Chang <abner.chang@amd.com<mailto:abner.chang@amd.com>>; Nick Ramirez <nramirez@nvidia.com<mailto:nramirez@nvidia.com>>
> > Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish
> > HTTP protocol
> >
> > External email: Use caution opening links or attachments
> >
> >
> > Hii Nickle,
> >
> >
> > On Mon, Feb 26, 2024 at 4:44 PM Nickle Wang <nicklew@nvidia.com<mailto:nicklew@nvidia.com>> wrote:
> > >
> > > Hi Mike,
> > >
> > > > So finally we have
> > > > HttpFreeHeaderFields (Response->Headers, Response->HeaderCount);
> > > > but
> > > > Response->HeaderCount does not count partially allocated elements. Right?
> > > >
> > > > To fix this, it is required to set *DstHeaderCount =
> > > > SrcHeaderCount unconditionally right after DstHeaders allocation,
> > > > and HttpFreeHeaderFields() will do the work then.
> > >
> > > I follow your suggestion to update DstHeaderCount right after
> > > DstHeaders is
> > allocated. So, HttpFreeHeaderFields can release headers correctly. I
> > also create a macro to implemented AsciiStrCpy. Please check below link to see
> my changes:
> > > https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgi
> > > th
> > >
> >
> ub.com%2Ftianocore%2Fedk2%2Fcompare%2F0f391b1c2f988d90a3ac723b314a
> > c28b
> > >
> >
> a7b0b8df..f0fa1b8fdcd933beb52fd3127c2476443c00ef8d&data=05%7C02%7Cnic
> > k
> > >
> >
> lew%40nvidia.com%7Cf3870f71360e44f3b4e208dc3728ff87%7C43083d1572734
> > 0c1
> > >
> >
> b7db39efd9ccc17a%7C0%7C0%7C638445896465360452%7CUnknown%7CTWFp
> > bGZsb3d8
> > >
> >
> eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%
> > 7C0
> > >
> >
> %7C%7C%7C&sdata=K%2FEA2QWpk%2F8NHQ1QhzqkvQqao4db%2BILn1Jt%2BB
> > qQ5n1E%3D
> > > &reserved=0
> >
> > These changes looks good. Internal strings
> > initialization/deinitialization code much cleaner now and possible leak seems to
> have been fixed.
> >
> > Thank you!
> >
> > Regards,
> > Mike.
>
>
>
>
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#116114): https://edk2.groups.io/g/devel/message/116114
Mute This Topic: https://groups.io/mt/104505404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
[-- Attachment #2: Type: text/html, Size: 9988 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
2024-02-28 11:47 ` Nickle Wang via groups.io
@ 2024-02-28 17:56 ` Mike Maslenkin
2024-02-29 0:11 ` Nickle Wang via groups.io
0 siblings, 1 reply; 15+ messages in thread
From: Mike Maslenkin @ 2024-02-28 17:56 UTC (permalink / raw)
To: Nickle Wang
Cc: devel@edk2.groups.io, Igor Kulchytskyy, Abner Chang, Nick Ramirez
On Wed, Feb 28, 2024 at 2:47 PM Nickle Wang <nicklew@nvidia.com> wrote:
>
> Hi @Mike Maslenkin,
>
>
>
> May I have your reviewed-by if version 3 patch set look good to you?
>
Sure!
Reviewed-by: Mike Maslenkin <mike.maslenkin@gmail.com>
BTW I'm just curious, there is a mention in patch 2 "We currently only
support gzip Content-Encoding."
But I didn't see any implementation of gzip coding/encoding for edk2.
Do you know of any?
I hope you know that patch 5 breaks edk2-redfish-client compilation
(Instance of library class [RedfishHttpLib] is not found)
But I understand these changes are not atomic for edk2 and edk2-redfish-client.
Regards,
Mike.
>
>
> Thanks,
>
> Nickle
>
>
>
> > -----Original Message-----
>
> > From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Nickle Wang
>
> > via groups.io
>
> > Sent: Tuesday, February 27, 2024 8:49 AM
>
> > To: Mike Maslenkin <mike.maslenkin@gmail.com>
>
> > Cc: devel@edk2.groups.io; Igor Kulchytskyy <igork@ami.com>; Abner Chang
>
> > <abner.chang@amd.com>; Nick Ramirez <nramirez@nvidia.com>
>
> > Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP
>
> > protocol
>
> >
>
> > External email: Use caution opening links or attachments
>
> >
>
> >
>
> > Thanks for your confirmation, Mike!
>
> >
>
> > Version 3 patch set is here: https://edk2.groups.io/g/devel/message/115985
>
> >
>
> > Regards,
>
> > Nickle
>
> >
>
> > > -----Original Message-----
>
> > > From: Mike Maslenkin <mike.maslenkin@gmail.com>
>
> > > Sent: Tuesday, February 27, 2024 8:13 AM
>
> > > To: Nickle Wang <nicklew@nvidia.com>
>
> > > Cc: devel@edk2.groups.io; Igor Kulchytskyy <igork@ami.com>; Abner
>
> > > Chang <abner.chang@amd.com>; Nick Ramirez <nramirez@nvidia.com>
>
> > > Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish
>
> > > HTTP protocol
>
> > >
>
> > > External email: Use caution opening links or attachments
>
> > >
>
> > >
>
> > > Hii Nickle,
>
> > >
>
> > >
>
> > > On Mon, Feb 26, 2024 at 4:44 PM Nickle Wang <nicklew@nvidia.com> wrote:
>
> > > >
>
> > > > Hi Mike,
>
> > > >
>
> > > > > So finally we have
>
> > > > > HttpFreeHeaderFields (Response->Headers, Response->HeaderCount);
>
> > > > > but
>
> > > > > Response->HeaderCount does not count partially allocated elements. Right?
>
> > > > >
>
> > > > > To fix this, it is required to set *DstHeaderCount =
>
> > > > > SrcHeaderCount unconditionally right after DstHeaders allocation,
>
> > > > > and HttpFreeHeaderFields() will do the work then.
>
> > > >
>
> > > > I follow your suggestion to update DstHeaderCount right after
>
> > > > DstHeaders is
>
> > > allocated. So, HttpFreeHeaderFields can release headers correctly. I
>
> > > also create a macro to implemented AsciiStrCpy. Please check below link to see
>
> > my changes:
>
> > > > https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgi
>
> > > > th
>
> > > >
>
> > >
>
> > ub.com%2Ftianocore%2Fedk2%2Fcompare%2F0f391b1c2f988d90a3ac723b314a
>
> > > c28b
>
> > > >
>
> > >
>
> > a7b0b8df..f0fa1b8fdcd933beb52fd3127c2476443c00ef8d&data=05%7C02%7Cnic
>
> > > k
>
> > > >
>
> > >
>
> > lew%40nvidia.com%7Cf3870f71360e44f3b4e208dc3728ff87%7C43083d1572734
>
> > > 0c1
>
> > > >
>
> > >
>
> > b7db39efd9ccc17a%7C0%7C0%7C638445896465360452%7CUnknown%7CTWFp
>
> > > bGZsb3d8
>
> > > >
>
> > >
>
> > eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%
>
> > > 7C0
>
> > > >
>
> > >
>
> > %7C%7C%7C&sdata=K%2FEA2QWpk%2F8NHQ1QhzqkvQqao4db%2BILn1Jt%2BB
>
> > > qQ5n1E%3D
>
> > > > &reserved=0
>
> > >
>
> > > These changes looks good. Internal strings
>
> > > initialization/deinitialization code much cleaner now and possible leak seems to
>
> > have been fixed.
>
> > >
>
> > > Thank you!
>
> > >
>
> > > Regards,
>
> > > Mike.
>
> >
>
> >
>
> >
>
> >
>
>
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#116126): https://edk2.groups.io/g/devel/message/116126
Mute This Topic: https://groups.io/mt/104505404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
2024-02-28 17:56 ` Mike Maslenkin
@ 2024-02-29 0:11 ` Nickle Wang via groups.io
2024-02-29 0:30 ` Chang, Abner via groups.io
0 siblings, 1 reply; 15+ messages in thread
From: Nickle Wang via groups.io @ 2024-02-29 0:11 UTC (permalink / raw)
To: Mike Maslenkin
Cc: devel@edk2.groups.io, Igor Kulchytskyy, Abner Chang, Nick Ramirez
> Sure!
>
> Reviewed-by: Mike Maslenkin <mike.maslenkin@gmail.com>
Thanks, Mike!
> But I didn't see any implementation of gzip coding/encoding for edk2.
> Do you know of any?
I just talked to Aber about this. We are working to see if we can provide gzip implementation in edk2 or not. It seems to me that we need 3rd party library to edk2 for supporting gzip. Anber, please feel free to correct me if I am wrong.
> I hope you know that patch 5 breaks edk2-redfish-client compilation (Instance of
> library class [RedfishHttpLib] is not found) But I understand these changes are not
> atomic for edk2 and edk2-redfish-client.
Yes, I also have patch for edk2-redfish-client to use Redfish HTTP protocol. I had tested Redfish HTTP protocol on edk2-redfish-client. I will send out patch for review after Redfish HTTP protocol gets merged in edk2.
Regards,
Nickle
> -----Original Message-----
> From: Mike Maslenkin <mike.maslenkin@gmail.com>
> Sent: Thursday, February 29, 2024 1:56 AM
> To: Nickle Wang <nicklew@nvidia.com>
> Cc: devel@edk2.groups.io; Igor Kulchytskyy <igork@ami.com>; Abner Chang
> <abner.chang@amd.com>; Nick Ramirez <nramirez@nvidia.com>
> Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP
> protocol
>
> External email: Use caution opening links or attachments
>
>
> On Wed, Feb 28, 2024 at 2:47 PM Nickle Wang <nicklew@nvidia.com> wrote:
> >
> > Hi @Mike Maslenkin,
> >
> >
> >
> > May I have your reviewed-by if version 3 patch set look good to you?
> >
>
> Sure!
>
> Reviewed-by: Mike Maslenkin <mike.maslenkin@gmail.com>
>
> BTW I'm just curious, there is a mention in patch 2 "We currently only support
> gzip Content-Encoding."
> But I didn't see any implementation of gzip coding/encoding for edk2.
> Do you know of any?
>
> I hope you know that patch 5 breaks edk2-redfish-client compilation (Instance of
> library class [RedfishHttpLib] is not found) But I understand these changes are not
> atomic for edk2 and edk2-redfish-client.
>
> Regards,
> Mike.
>
>
> >
> >
> > Thanks,
> >
> > Nickle
> >
> >
> >
> > > -----Original Message-----
> >
> > > From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
> > > Nickle Wang
> >
> > > via groups.io
> >
> > > Sent: Tuesday, February 27, 2024 8:49 AM
> >
> > > To: Mike Maslenkin <mike.maslenkin@gmail.com>
> >
> > > Cc: devel@edk2.groups.io; Igor Kulchytskyy <igork@ami.com>; Abner
> > > Chang
> >
> > > <abner.chang@amd.com>; Nick Ramirez <nramirez@nvidia.com>
> >
> > > Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement
> > > Redfish HTTP
> >
> > > protocol
> >
> > >
> >
> > > External email: Use caution opening links or attachments
> >
> > >
> >
> > >
> >
> > > Thanks for your confirmation, Mike!
> >
> > >
> >
> > > Version 3 patch set is here:
> > > https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fed
> > >
> k2.groups.io%2Fg%2Fdevel%2Fmessage%2F115985&data=05%7C02%7Cnicklew
> %4
> > >
> 0nvidia.com%7Ca30038f7379c4f8dad3b08dc3886a03b%7C43083d15727340c1b
> 7d
> > >
> b39efd9ccc17a%7C0%7C0%7C638447398077724632%7CUnknown%7CTWFpbG
> Zsb3d8e
> > >
> yJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7
> C
> > >
> 0%7C%7C%7C&sdata=0tXBIuafvJuG7AM1DpAgSGGLf1QeUbOOmCq2WQCYpeg%
> 3D&rese
> > > rved=0
> >
> > >
> >
> > > Regards,
> >
> > > Nickle
> >
> > >
> >
> > > > -----Original Message-----
> >
> > > > From: Mike Maslenkin <mike.maslenkin@gmail.com>
> >
> > > > Sent: Tuesday, February 27, 2024 8:13 AM
> >
> > > > To: Nickle Wang <nicklew@nvidia.com>
> >
> > > > Cc: devel@edk2.groups.io; Igor Kulchytskyy <igork@ami.com>; Abner
> >
> > > > Chang <abner.chang@amd.com>; Nick Ramirez <nramirez@nvidia.com>
> >
> > > > Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement
> > > > Redfish
> >
> > > > HTTP protocol
> >
> > > >
> >
> > > > External email: Use caution opening links or attachments
> >
> > > >
> >
> > > >
> >
> > > > Hii Nickle,
> >
> > > >
> >
> > > >
> >
> > > > On Mon, Feb 26, 2024 at 4:44 PM Nickle Wang <nicklew@nvidia.com>
> wrote:
> >
> > > > >
> >
> > > > > Hi Mike,
> >
> > > > >
> >
> > > > > > So finally we have
> >
> > > > > > HttpFreeHeaderFields (Response->Headers,
> > > > > > Response->HeaderCount);
> >
> > > > > > but
> >
> > > > > > Response->HeaderCount does not count partially allocated elements.
> Right?
> >
> > > > > >
> >
> > > > > > To fix this, it is required to set *DstHeaderCount =
> >
> > > > > > SrcHeaderCount unconditionally right after DstHeaders
> > > > > > allocation,
> >
> > > > > > and HttpFreeHeaderFields() will do the work then.
> >
> > > > >
> >
> > > > > I follow your suggestion to update DstHeaderCount right after
> >
> > > > > DstHeaders is
> >
> > > > allocated. So, HttpFreeHeaderFields can release headers
> > > > correctly. I
> >
> > > > also create a macro to implemented AsciiStrCpy. Please check below
> > > > link to see
> >
> > > my changes:
> >
> > > > > https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%
> > > > >
> 2Fgi%2F&data=05%7C02%7Cnicklew%40nvidia.com%7Ca30038f7379c4f8dad
> > > > >
> 3b08dc3886a03b%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7C6384
> > > > >
> 47398077735545%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJ
> QI
> > > > >
> joiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=dZf
> > > > >
> %2BXZMEyp4%2BC%2BZgFnVCr12fIyXn1ZDsFfk2ejkYGO8%3D&reserved=0
> >
> > > > > th
> >
> > > > >
> >
> > > >
> >
> > >
> ub.com%2Ftianocore%2Fedk2%2Fcompare%2F0f391b1c2f988d90a3ac723b314a
> >
> > > > c28b
> >
> > > > >
> >
> > > >
> >
> > >
> a7b0b8df..f0fa1b8fdcd933beb52fd3127c2476443c00ef8d&data=05%7C02%7Cni
> > > c
> >
> > > > k
> >
> > > > >
> >
> > > >
> >
> > >
> lew%40nvidia.com%7Cf3870f71360e44f3b4e208dc3728ff87%7C43083d1572734
> >
> > > > 0c1
> >
> > > > >
> >
> > > >
> >
> > >
> b7db39efd9ccc17a%7C0%7C0%7C638445896465360452%7CUnknown%7CTWFp
> >
> > > > bGZsb3d8
> >
> > > > >
> >
> > > >
> >
> > >
> eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%
> >
> > > > 7C0
> >
> > > > >
> >
> > > >
> >
> > >
> %7C%7C%7C&sdata=K%2FEA2QWpk%2F8NHQ1QhzqkvQqao4db%2BILn1Jt%2BB
> >
> > > > qQ5n1E%3D
> >
> > > > > &reserved=0
> >
> > > >
> >
> > > > These changes looks good. Internal strings
> >
> > > > initialization/deinitialization code much cleaner now and possible
> > > > leak seems to
> >
> > > have been fixed.
> >
> > > >
> >
> > > > Thank you!
> >
> > > >
> >
> > > > Regards,
> >
> > > > Mike.
> >
> > >
> >
> > >
> >
> > >
> >
> > >
> >
> >
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#116129): https://edk2.groups.io/g/devel/message/116129
Mute This Topic: https://groups.io/mt/104505404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
2024-02-29 0:11 ` Nickle Wang via groups.io
@ 2024-02-29 0:30 ` Chang, Abner via groups.io
2024-02-29 1:37 ` Igor Kulchytskyy via groups.io
0 siblings, 1 reply; 15+ messages in thread
From: Chang, Abner via groups.io @ 2024-02-29 0:30 UTC (permalink / raw)
To: Nickle Wang, Mike Maslenkin, Igor Kulchytskyy
Cc: devel@edk2.groups.io, Nick Ramirez
[AMD Official Use Only - General]
> -----Original Message-----
> From: Nickle Wang <nicklew@nvidia.com>
> Sent: Thursday, February 29, 2024 8:11 AM
> To: Mike Maslenkin <mike.maslenkin@gmail.com>
> Cc: devel@edk2.groups.io; Igor Kulchytskyy <igork@ami.com>; Chang, Abner
> <Abner.Chang@amd.com>; Nick Ramirez <nramirez@nvidia.com>
> Subject: RE: [edk2-devel] [PATCH v2 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.
>
>
> > Sure!
> >
> > Reviewed-by: Mike Maslenkin <mike.maslenkin@gmail.com>
>
> Thanks, Mike!
>
> > But I didn't see any implementation of gzip coding/encoding for edk2.
> > Do you know of any?
>
> I just talked to Aber about this. We are working to see if we can provide gzip
> implementation in edk2 or not. It seems to me that we need 3rd party library
> to edk2 for supporting gzip. Anber, please feel free to correct me if I am wrong.
Yes and we hope someone can provide the implementation. @Igor Kulchytskyy, does AMI has the implementation of gzip? 😊
BTW, we do have a proposal that introduces EFI_SOURCE_CODING_PROTOCOL long time ago while I was worked for HPE. I think we should pick up this one and promote this protocol in UEFI spec, we can work with AMI on this as well.
Thanks
Abner
>
> > I hope you know that patch 5 breaks edk2-redfish-client compilation
> (Instance of
> > library class [RedfishHttpLib] is not found) But I understand these changes
> are not
> > atomic for edk2 and edk2-redfish-client.
>
> Yes, I also have patch for edk2-redfish-client to use Redfish HTTP protocol. I
> had tested Redfish HTTP protocol on edk2-redfish-client. I will send out patch
> for review after Redfish HTTP protocol gets merged in edk2.
>
> Regards,
> Nickle
>
> > -----Original Message-----
> > From: Mike Maslenkin <mike.maslenkin@gmail.com>
> > Sent: Thursday, February 29, 2024 1:56 AM
> > To: Nickle Wang <nicklew@nvidia.com>
> > Cc: devel@edk2.groups.io; Igor Kulchytskyy <igork@ami.com>; Abner Chang
> > <abner.chang@amd.com>; Nick Ramirez <nramirez@nvidia.com>
> > Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish
> HTTP
> > protocol
> >
> > External email: Use caution opening links or attachments
> >
> >
> > On Wed, Feb 28, 2024 at 2:47 PM Nickle Wang <nicklew@nvidia.com>
> wrote:
> > >
> > > Hi @Mike Maslenkin,
> > >
> > >
> > >
> > > May I have your reviewed-by if version 3 patch set look good to you?
> > >
> >
> > Sure!
> >
> > Reviewed-by: Mike Maslenkin <mike.maslenkin@gmail.com>
> >
> > BTW I'm just curious, there is a mention in patch 2 "We currently only
> support
> > gzip Content-Encoding."
> > But I didn't see any implementation of gzip coding/encoding for edk2.
> > Do you know of any?
> >
> > I hope you know that patch 5 breaks edk2-redfish-client compilation
> (Instance of
> > library class [RedfishHttpLib] is not found) But I understand these changes
> are not
> > atomic for edk2 and edk2-redfish-client.
> >
> > Regards,
> > Mike.
> >
> >
> > >
> > >
> > > Thanks,
> > >
> > > Nickle
> > >
> > >
> > >
> > > > -----Original Message-----
> > >
> > > > From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of
> > > > Nickle Wang
> > >
> > > > via groups.io
> > >
> > > > Sent: Tuesday, February 27, 2024 8:49 AM
> > >
> > > > To: Mike Maslenkin <mike.maslenkin@gmail.com>
> > >
> > > > Cc: devel@edk2.groups.io; Igor Kulchytskyy <igork@ami.com>; Abner
> > > > Chang
> > >
> > > > <abner.chang@amd.com>; Nick Ramirez <nramirez@nvidia.com>
> > >
> > > > Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement
> > > > Redfish HTTP
> > >
> > > > protocol
> > >
> > > >
> > >
> > > > External email: Use caution opening links or attachments
> > >
> > > >
> > >
> > > >
> > >
> > > > Thanks for your confirmation, Mike!
> > >
> > > >
> > >
> > > > Version 3 patch set is here:
> > > >
> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fed
> > > >
> >
> k2.groups.io%2Fg%2Fdevel%2Fmessage%2F115985&data=05%7C02%7Cnic
> klew
> > %4
> > > >
> >
> 0nvidia.com%7Ca30038f7379c4f8dad3b08dc3886a03b%7C43083d157273
> 40c1b
> > 7d
> > > >
> >
> b39efd9ccc17a%7C0%7C0%7C638447398077724632%7CUnknown%7CTW
> FpbG
> > Zsb3d8e
> > > >
> >
> yJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3
> D%7
> > C
> > > >
> >
> 0%7C%7C%7C&sdata=0tXBIuafvJuG7AM1DpAgSGGLf1QeUbOOmCq2WQCY
> peg%
> > 3D&rese
> > > > rved=0
> > >
> > > >
> > >
> > > > Regards,
> > >
> > > > Nickle
> > >
> > > >
> > >
> > > > > -----Original Message-----
> > >
> > > > > From: Mike Maslenkin <mike.maslenkin@gmail.com>
> > >
> > > > > Sent: Tuesday, February 27, 2024 8:13 AM
> > >
> > > > > To: Nickle Wang <nicklew@nvidia.com>
> > >
> > > > > Cc: devel@edk2.groups.io; Igor Kulchytskyy <igork@ami.com>; Abner
> > >
> > > > > Chang <abner.chang@amd.com>; Nick Ramirez
> <nramirez@nvidia.com>
> > >
> > > > > Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement
> > > > > Redfish
> > >
> > > > > HTTP protocol
> > >
> > > > >
> > >
> > > > > External email: Use caution opening links or attachments
> > >
> > > > >
> > >
> > > > >
> > >
> > > > > Hii Nickle,
> > >
> > > > >
> > >
> > > > >
> > >
> > > > > On Mon, Feb 26, 2024 at 4:44 PM Nickle Wang <nicklew@nvidia.com>
> > wrote:
> > >
> > > > > >
> > >
> > > > > > Hi Mike,
> > >
> > > > > >
> > >
> > > > > > > So finally we have
> > >
> > > > > > > HttpFreeHeaderFields (Response->Headers,
> > > > > > > Response->HeaderCount);
> > >
> > > > > > > but
> > >
> > > > > > > Response->HeaderCount does not count partially allocated
> elements.
> > Right?
> > >
> > > > > > >
> > >
> > > > > > > To fix this, it is required to set *DstHeaderCount =
> > >
> > > > > > > SrcHeaderCount unconditionally right after DstHeaders
> > > > > > > allocation,
> > >
> > > > > > > and HttpFreeHeaderFields() will do the work then.
> > >
> > > > > >
> > >
> > > > > > I follow your suggestion to update DstHeaderCount right after
> > >
> > > > > > DstHeaders is
> > >
> > > > > allocated. So, HttpFreeHeaderFields can release headers
> > > > > correctly. I
> > >
> > > > > also create a macro to implemented AsciiStrCpy. Please check below
> > > > > link to see
> > >
> > > > my changes:
> > >
> > > > > >
> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%
> > > > > >
> >
> 2Fgi%2F&data=05%7C02%7Cnicklew%40nvidia.com%7Ca30038f7379c4f8d
> ad
> > > > > >
> >
> 3b08dc3886a03b%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7
> C6384
> > > > > >
> >
> 47398077735545%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMD
> AiLCJ
> > QI
> > > > > >
> >
> joiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=d
> Zf
> > > > > >
> > %2BXZMEyp4%2BC%2BZgFnVCr12fIyXn1ZDsFfk2ejkYGO8%3D&reserved=0
> > >
> > > > > > th
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> ub.com%2Ftianocore%2Fedk2%2Fcompare%2F0f391b1c2f988d90a3ac723b
> 314a
> > >
> > > > > c28b
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> a7b0b8df..f0fa1b8fdcd933beb52fd3127c2476443c00ef8d&data=05%7C02
> %7Cni
> > > > c
> > >
> > > > > k
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> lew%40nvidia.com%7Cf3870f71360e44f3b4e208dc3728ff87%7C43083d15
> 72734
> > >
> > > > > 0c1
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> b7db39efd9ccc17a%7C0%7C0%7C638445896465360452%7CUnknown%7
> CTWFp
> > >
> > > > > bGZsb3d8
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3
> D%
> > >
> > > > > 7C0
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> %7C%7C%7C&sdata=K%2FEA2QWpk%2F8NHQ1QhzqkvQqao4db%2BILn1Jt
> %2BB
> > >
> > > > > qQ5n1E%3D
> > >
> > > > > > &reserved=0
> > >
> > > > >
> > >
> > > > > These changes looks good. Internal strings
> > >
> > > > > initialization/deinitialization code much cleaner now and possible
> > > > > leak seems to
> > >
> > > > have been fixed.
> > >
> > > > >
> > >
> > > > > Thank you!
> > >
> > > > >
> > >
> > > > > Regards,
> > >
> > > > > Mike.
> > >
> > > >
> > >
> > > >
> > >
> > > >
> > >
> > > >
> > >
> > >
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#116130): https://edk2.groups.io/g/devel/message/116130
Mute This Topic: https://groups.io/mt/104505404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
2024-02-29 0:30 ` Chang, Abner via groups.io
@ 2024-02-29 1:37 ` Igor Kulchytskyy via groups.io
2024-02-29 4:01 ` Chang, Abner via groups.io
0 siblings, 1 reply; 15+ messages in thread
From: Igor Kulchytskyy via groups.io @ 2024-02-29 1:37 UTC (permalink / raw)
To: Chang, Abner, Nickle Wang, Mike Maslenkin
Cc: devel@edk2.groups.io, Nick Ramirez
[-- Attachment #1: Type: text/plain, Size: 12447 bytes --]
-----Original Message-----
From: Chang, Abner <Abner.Chang@amd.com>
Sent: Wednesday, February 28, 2024 7:31 PM
To: Nickle Wang <nicklew@nvidia.com>; Mike Maslenkin <mike.maslenkin@gmail.com>; Igor Kulchytskyy <igork@ami.com>
Cc: devel@edk2.groups.io; Nick Ramirez <nramirez@nvidia.com>
Subject: [EXTERNAL] RE: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
**CAUTION: The e-mail below is from an external source. Please exercise caution before opening attachments, clicking links, or following guidance.**
[AMD Official Use Only - General]
> -----Original Message-----
> From: Nickle Wang <nicklew@nvidia.com<mailto:nicklew@nvidia.com>>
> Sent: Thursday, February 29, 2024 8:11 AM
> To: Mike Maslenkin <mike.maslenkin@gmail.com<mailto:mike.maslenkin@gmail.com>>
> Cc: devel@edk2.groups.io<mailto:devel@edk2.groups.io>; Igor Kulchytskyy <igork@ami.com<mailto:igork@ami.com>>; Chang, Abner
> <Abner.Chang@amd.com<mailto:Abner.Chang@amd.com>>; Nick Ramirez <nramirez@nvidia.com<mailto:nramirez@nvidia.com>>
> Subject: RE: [edk2-devel] [PATCH v2 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.
>
>
> > Sure!
> >
> > Reviewed-by: Mike Maslenkin <mike.maslenkin@gmail.com<mailto:mike.maslenkin@gmail.com>>
>
> Thanks, Mike!
>
> > But I didn't see any implementation of gzip coding/encoding for edk2.
> > Do you know of any?
>
> I just talked to Aber about this. We are working to see if we can provide gzip
> implementation in edk2 or not. It seems to me that we need 3rd party library
> to edk2 for supporting gzip. Anber, please feel free to correct me if I am wrong.
Yes and we hope someone can provide the implementation. @Igor Kulchytskyy, does AMI has the implementation of gzip? 😊
BTW, we do have a proposal that introduces EFI_SOURCE_CODING_PROTOCOL long time ago while I was worked for HPE. I think we should pick up this one and promote this protocol in UEFI spec, we can work with AMI on this as well.
Thanks
Abner
Hi Abner,
Unfortunately, AMI does not have the implementation of gzip.
Why do you think AMI has it? 😊
I can investigate the 3rd party libraries to be adopted for using in UEFI environment.
Thank you,
Igor
>
> > I hope you know that patch 5 breaks edk2-redfish-client compilation
> (Instance of
> > library class [RedfishHttpLib] is not found) But I understand these changes
> are not
> > atomic for edk2 and edk2-redfish-client.
>
> Yes, I also have patch for edk2-redfish-client to use Redfish HTTP protocol. I
> had tested Redfish HTTP protocol on edk2-redfish-client. I will send out patch
> for review after Redfish HTTP protocol gets merged in edk2.
>
> Regards,
> Nickle
>
> > -----Original Message-----
> > From: Mike Maslenkin <mike.maslenkin@gmail.com<mailto:mike.maslenkin@gmail.com>>
> > Sent: Thursday, February 29, 2024 1:56 AM
> > To: Nickle Wang <nicklew@nvidia.com<mailto:nicklew@nvidia.com>>
> > Cc: devel@edk2.groups.io<mailto:devel@edk2.groups.io>; Igor Kulchytskyy <igork@ami.com<mailto:igork@ami.com>>; Abner Chang
> > <abner.chang@amd.com<mailto:abner.chang@amd.com>>; Nick Ramirez <nramirez@nvidia.com<mailto:nramirez@nvidia.com>>
> > Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish
> HTTP
> > protocol
> >
> > External email: Use caution opening links or attachments
> >
> >
> > On Wed, Feb 28, 2024 at 2:47 PM Nickle Wang <nicklew@nvidia.com<mailto:nicklew@nvidia.com>>
> wrote:
> > >
> > > Hi @Mike Maslenkin,
> > >
> > >
> > >
> > > May I have your reviewed-by if version 3 patch set look good to you?
> > >
> >
> > Sure!
> >
> > Reviewed-by: Mike Maslenkin <mike.maslenkin@gmail.com<mailto:mike.maslenkin@gmail.com>>
> >
> > BTW I'm just curious, there is a mention in patch 2 "We currently only
> support
> > gzip Content-Encoding."
> > But I didn't see any implementation of gzip coding/encoding for edk2.
> > Do you know of any?
> >
> > I hope you know that patch 5 breaks edk2-redfish-client compilation
> (Instance of
> > library class [RedfishHttpLib] is not found) But I understand these changes
> are not
> > atomic for edk2 and edk2-redfish-client.
> >
> > Regards,
> > Mike.
> >
> >
> > >
> > >
> > > Thanks,
> > >
> > > Nickle
> > >
> > >
> > >
> > > > -----Original Message-----
> > >
> > > > From: devel@edk2.groups.io<mailto:devel@edk2.groups.io> <devel@edk2.groups.io<mailto:devel@edk2.groups.io>> On Behalf Of
> > > > Nickle Wang
> > >
> > > > via groups.io
> > >
> > > > Sent: Tuesday, February 27, 2024 8:49 AM
> > >
> > > > To: Mike Maslenkin <mike.maslenkin@gmail.com<mailto:mike.maslenkin@gmail.com>>
> > >
> > > > Cc: devel@edk2.groups.io<mailto:devel@edk2.groups.io>; Igor Kulchytskyy <igork@ami.com<mailto:igork@ami.com>>; Abner
> > > > Chang
> > >
> > > > <abner.chang@amd.com<mailto:abner.chang@amd.com>>; Nick Ramirez <nramirez@nvidia.com<mailto:nramirez@nvidia.com>>
> > >
> > > > Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement
> > > > Redfish HTTP
> > >
> > > > protocol
> > >
> > > >
> > >
> > > > External email: Use caution opening links or attachments
> > >
> > > >
> > >
> > > >
> > >
> > > > Thanks for your confirmation, Mike!
> > >
> > > >
> > >
> > > > Version 3 patch set is here:
> > > >
> https://nam12.safelinks.protection.outlook.com/?url=https%3A%2F%2Fed%2F&data=05%7C02%7Cigork%40ami.com%7Ca119b05ccbf64656ecd808dc38bda910%7C27e97857e15f486cb58e86c2b3040f93%7C1%7C0%7C638447634515178497%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=%2F7QVT4mZwfDb0zkMILIjWV2u8TldJEwSELjsjIVqkYg%3D&reserved=0<https://ed/>
> > > >
> >
> k2.groups.io%2Fg%2Fdevel%2Fmessage%2F115985&data=05%7C02%7Cnic
> klew
> > %4
> > > >
> >
> 0nvidia.com%7Ca30038f7379c4f8dad3b08dc3886a03b%7C43083d157273
> 40c1b
> > 7d
> > > >
> >
> b39efd9ccc17a%7C0%7C0%7C638447398077724632%7CUnknown%7CTW
> FpbG
> > Zsb3d8e
> > > >
> >
> yJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3
> D%7
> > C
> > > >
> >
> 0%7C%7C%7C&sdata=0tXBIuafvJuG7AM1DpAgSGGLf1QeUbOOmCq2WQCY
> peg%
> > 3D&rese
> > > > rved=0
> > >
> > > >
> > >
> > > > Regards,
> > >
> > > > Nickle
> > >
> > > >
> > >
> > > > > -----Original Message-----
> > >
> > > > > From: Mike Maslenkin <mike.maslenkin@gmail.com<mailto:mike.maslenkin@gmail.com>>
> > >
> > > > > Sent: Tuesday, February 27, 2024 8:13 AM
> > >
> > > > > To: Nickle Wang <nicklew@nvidia.com<mailto:nicklew@nvidia.com>>
> > >
> > > > > Cc: devel@edk2.groups.io<mailto:devel@edk2.groups.io>; Igor Kulchytskyy <igork@ami.com<mailto:igork@ami.com>>; Abner
> > >
> > > > > Chang <abner.chang@amd.com<mailto:abner.chang@amd.com>>; Nick Ramirez
> <nramirez@nvidia.com<mailto:nramirez@nvidia.com>>
> > >
> > > > > Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement
> > > > > Redfish
> > >
> > > > > HTTP protocol
> > >
> > > > >
> > >
> > > > > External email: Use caution opening links or attachments
> > >
> > > > >
> > >
> > > > >
> > >
> > > > > Hii Nickle,
> > >
> > > > >
> > >
> > > > >
> > >
> > > > > On Mon, Feb 26, 2024 at 4:44 PM Nickle Wang <nicklew@nvidia.com<mailto:nicklew@nvidia.com>>
> > wrote:
> > >
> > > > > >
> > >
> > > > > > Hi Mike,
> > >
> > > > > >
> > >
> > > > > > > So finally we have
> > >
> > > > > > > HttpFreeHeaderFields (Response->Headers,
> > > > > > > Response->HeaderCount);
> > >
> > > > > > > but
> > >
> > > > > > > Response->HeaderCount does not count partially allocated
> elements.
> > Right?
> > >
> > > > > > >
> > >
> > > > > > > To fix this, it is required to set *DstHeaderCount =
> > >
> > > > > > > SrcHeaderCount unconditionally right after DstHeaders
> > > > > > > allocation,
> > >
> > > > > > > and HttpFreeHeaderFields() will do the work then.
> > >
> > > > > >
> > >
> > > > > > I follow your suggestion to update DstHeaderCount right after
> > >
> > > > > > DstHeaders is
> > >
> > > > > allocated. So, HttpFreeHeaderFields can release headers
> > > > > correctly. I
> > >
> > > > > also create a macro to implemented AsciiStrCpy. Please check below
> > > > > link to see
> > >
> > > > my changes:
> > >
> > > > > >
> https://nam12.safelinks.protection.outlook.com/?url=https%3A%2F%2Fnam11.safelinks.protection.outlook.com%2F%3Furl%3Dhttps%253A%252F%2525&data=05%7C02%7Cigork%40ami.com%7Ca119b05ccbf64656ecd808dc38bda910%7C27e97857e15f486cb58e86c2b3040f93%7C1%7C0%7C638447634515186935%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=OrYee0doa%2BgFUm6g0M2AVXq2U0UbEvUzUJ20YU7ykSk%3D&reserved=0<https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%25>
> > > > > >
> >
> 2Fgi%2F&data=05%7C02%7Cnicklew%40nvidia.com%7Ca30038f7379c4f8d
> ad
> > > > > >
> >
> 3b08dc3886a03b%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7
> C6384
> > > > > >
> >
> 47398077735545%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMD
> AiLCJ
> > QI
> > > > > >
> >
> joiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=d
> Zf
> > > > > >
> > %2BXZMEyp4%2BC%2BZgFnVCr12fIyXn1ZDsFfk2ejkYGO8%3D&reserved=0
> > >
> > > > > > th
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> ub.com%2Ftianocore%2Fedk2%2Fcompare%2F0f391b1c2f988d90a3ac723b
> 314a
> > >
> > > > > c28b
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> a7b0b8df..f0fa1b8fdcd933beb52fd3127c2476443c00ef8d&data=05%7C02
> %7Cni
> > > > c
> > >
> > > > > k
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> lew%40nvidia.com%7Cf3870f71360e44f3b4e208dc3728ff87%7C43083d15
> 72734
> > >
> > > > > 0c1
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> b7db39efd9ccc17a%7C0%7C0%7C638445896465360452%7CUnknown%7
> CTWFp
> > >
> > > > > bGZsb3d8
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3
> D%
> > >
> > > > > 7C0
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> %7C%7C%7C&sdata=K%2FEA2QWpk%2F8NHQ1QhzqkvQqao4db%2BILn1Jt
> %2BB
> > >
> > > > > qQ5n1E%3D
> > >
> > > > > > &reserved=0
> > >
> > > > >
> > >
> > > > > These changes looks good. Internal strings
> > >
> > > > > initialization/deinitialization code much cleaner now and possible
> > > > > leak seems to
> > >
> > > > have been fixed.
> > >
> > > > >
> > >
> > > > > Thank you!
> > >
> > > > >
> > >
> > > > > Regards,
> > >
> > > > > Mike.
> > >
> > > >
> > >
> > > >
> > >
> > > >
> > >
> > > >
> > >
> > >
-The information contained in this message may be confidential and proprietary to American Megatrends (AMI). This communication is intended to be read only by the individual or entity to whom it is addressed or by their designee. If the reader of this message is not the intended recipient, you are on notice that any distribution of this message, in any form, is strictly prohibited. Please promptly notify the sender by reply e-mail or by telephone at 770-246-8600, and then delete or destroy all copies of the transmission.
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#116131): https://edk2.groups.io/g/devel/message/116131
Mute This Topic: https://groups.io/mt/104505404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
[-- Attachment #2: Type: text/html, Size: 71850 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
2024-02-29 1:37 ` Igor Kulchytskyy via groups.io
@ 2024-02-29 4:01 ` Chang, Abner via groups.io
0 siblings, 0 replies; 15+ messages in thread
From: Chang, Abner via groups.io @ 2024-02-29 4:01 UTC (permalink / raw)
To: Igor Kulchytskyy, Nickle Wang, Mike Maslenkin
Cc: devel@edk2.groups.io, Nick Ramirez
[-- Attachment #1: Type: text/plain, Size: 13248 bytes --]
[AMD Official Use Only - General]
From: Igor Kulchytskyy <igork@ami.com>
Sent: Thursday, February 29, 2024 9:38 AM
To: Chang, Abner <Abner.Chang@amd.com>; Nickle Wang <nicklew@nvidia.com>; Mike Maslenkin <mike.maslenkin@gmail.com>
Cc: devel@edk2.groups.io; Nick Ramirez <nramirez@nvidia.com>
Subject: RE: [EXTERNAL] RE: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
[AMD Official Use Only - General]
Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding.
-----Original Message-----
From: Chang, Abner <Abner.Chang@amd.com>
Sent: Wednesday, February 28, 2024 7:31 PM
To: Nickle Wang <nicklew@nvidia.com>; Mike Maslenkin <mike.maslenkin@gmail.com>; Igor Kulchytskyy <igork@ami.com>
Cc: devel@edk2.groups.io; Nick Ramirez <nramirez@nvidia.com>
Subject: [EXTERNAL] RE: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol
**CAUTION: The e-mail below is from an external source. Please exercise caution before opening attachments, clicking links, or following guidance.**
[AMD Official Use Only - General]
> -----Original Message-----
> From: Nickle Wang <nicklew@nvidia.com<mailto:nicklew@nvidia.com>>
> Sent: Thursday, February 29, 2024 8:11 AM
> To: Mike Maslenkin <mike.maslenkin@gmail.com<mailto:mike.maslenkin@gmail.com>>
> Cc: devel@edk2.groups.io<mailto:devel@edk2.groups.io>; Igor Kulchytskyy <igork@ami.com<mailto:igork@ami.com>>; Chang, Abner
> <Abner.Chang@amd.com<mailto:Abner.Chang@amd.com>>; Nick Ramirez <nramirez@nvidia.com<mailto:nramirez@nvidia.com>>
> Subject: RE: [edk2-devel] [PATCH v2 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.
>
>
> > Sure!
> >
> > Reviewed-by: Mike Maslenkin <mike.maslenkin@gmail.com<mailto:mike.maslenkin@gmail.com>>
>
> Thanks, Mike!
>
> > But I didn't see any implementation of gzip coding/encoding for edk2.
> > Do you know of any?
>
> I just talked to Aber about this. We are working to see if we can provide gzip
> implementation in edk2 or not. It seems to me that we need 3rd party library
> to edk2 for supporting gzip. Anber, please feel free to correct me if I am wrong.
Yes and we hope someone can provide the implementation. @Igor Kulchytskyy, does AMI has the implementation of gzip? 😊
BTW, we do have a proposal that introduces EFI_SOURCE_CODING_PROTOCOL long time ago while I was worked for HPE. I think we should pick up this one and promote this protocol in UEFI spec, we can work with AMI on this as well.
Thanks
Abner
Hi Abner,
Unfortunately, AMI does not have the implementation of gzip.
Why do you think AMI has it? 😊
I can investigate the 3rd party libraries to be adopted for using in UEFI environment.
Thank you,
Igor
I thought AMI has the implementation as you and Nickle considered the content encoded parameter for the Redfish HTTP protocol. I guess you have gzip implemented in AMI BIOS as Nvidia doesn’t. 😊
Abner
>
> > I hope you know that patch 5 breaks edk2-redfish-client compilation
> (Instance of
> > library class [RedfishHttpLib] is not found) But I understand these changes
> are not
> > atomic for edk2 and edk2-redfish-client.
>
> Yes, I also have patch for edk2-redfish-client to use Redfish HTTP protocol. I
> had tested Redfish HTTP protocol on edk2-redfish-client. I will send out patch
> for review after Redfish HTTP protocol gets merged in edk2.
>
> Regards,
> Nickle
>
> > -----Original Message-----
> > From: Mike Maslenkin <mike.maslenkin@gmail.com<mailto:mike.maslenkin@gmail.com>>
> > Sent: Thursday, February 29, 2024 1:56 AM
> > To: Nickle Wang <nicklew@nvidia.com<mailto:nicklew@nvidia.com>>
> > Cc: devel@edk2.groups.io<mailto:devel@edk2.groups.io>; Igor Kulchytskyy <igork@ami.com<mailto:igork@ami.com>>; Abner Chang
> > <abner.chang@amd.com<mailto:abner.chang@amd.com>>; Nick Ramirez <nramirez@nvidia.com<mailto:nramirez@nvidia.com>>
> > Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish
> HTTP
> > protocol
> >
> > External email: Use caution opening links or attachments
> >
> >
> > On Wed, Feb 28, 2024 at 2:47 PM Nickle Wang <nicklew@nvidia.com<mailto:nicklew@nvidia.com>>
> wrote:
> > >
> > > Hi @Mike Maslenkin,
> > >
> > >
> > >
> > > May I have your reviewed-by if version 3 patch set look good to you?
> > >
> >
> > Sure!
> >
> > Reviewed-by: Mike Maslenkin <mike.maslenkin@gmail.com<mailto:mike.maslenkin@gmail.com>>
> >
> > BTW I'm just curious, there is a mention in patch 2 "We currently only
> support
> > gzip Content-Encoding."
> > But I didn't see any implementation of gzip coding/encoding for edk2.
> > Do you know of any?
> >
> > I hope you know that patch 5 breaks edk2-redfish-client compilation
> (Instance of
> > library class [RedfishHttpLib] is not found) But I understand these changes
> are not
> > atomic for edk2 and edk2-redfish-client.
> >
> > Regards,
> > Mike.
> >
> >
> > >
> > >
> > > Thanks,
> > >
> > > Nickle
> > >
> > >
> > >
> > > > -----Original Message-----
> > >
> > > > From: devel@edk2.groups.io<mailto:devel@edk2.groups.io> <devel@edk2.groups.io<mailto:devel@edk2.groups.io>> On Behalf Of
> > > > Nickle Wang
> > >
> > > > via groups.io
> > >
> > > > Sent: Tuesday, February 27, 2024 8:49 AM
> > >
> > > > To: Mike Maslenkin <mike.maslenkin@gmail.com<mailto:mike.maslenkin@gmail.com>>
> > >
> > > > Cc: devel@edk2.groups.io<mailto:devel@edk2.groups.io>; Igor Kulchytskyy <igork@ami.com<mailto:igork@ami.com>>; Abner
> > > > Chang
> > >
> > > > <abner.chang@amd.com<mailto:abner.chang@amd.com>>; Nick Ramirez <nramirez@nvidia.com<mailto:nramirez@nvidia.com>>
> > >
> > > > Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement
> > > > Redfish HTTP
> > >
> > > > protocol
> > >
> > > >
> > >
> > > > External email: Use caution opening links or attachments
> > >
> > > >
> > >
> > > >
> > >
> > > > Thanks for your confirmation, Mike!
> > >
> > > >
> > >
> > > > Version 3 patch set is here:
> > > >
> https://nam12.safelinks.protection.outlook.com/?url=https%3A%2F%2Fed%2F&data=05%7C02%7Cigork%40ami.com%7Ca119b05ccbf64656ecd808dc38bda910%7C27e97857e15f486cb58e86c2b3040f93%7C1%7C0%7C638447634515178497%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=%2F7QVT4mZwfDb0zkMILIjWV2u8TldJEwSELjsjIVqkYg%3D&reserved=0<https://ed/>
> > > >
> >
> k2.groups.io%2Fg%2Fdevel%2Fmessage%2F115985&data=05%7C02%7Cnic
> klew
> > %4
> > > >
> >
> 0nvidia.com%7Ca30038f7379c4f8dad3b08dc3886a03b%7C43083d157273
> 40c1b
> > 7d
> > > >
> >
> b39efd9ccc17a%7C0%7C0%7C638447398077724632%7CUnknown%7CTW
> FpbG
> > Zsb3d8e
> > > >
> >
> yJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3
> D%7
> > C
> > > >
> >
> 0%7C%7C%7C&sdata=0tXBIuafvJuG7AM1DpAgSGGLf1QeUbOOmCq2WQCY
> peg%
> > 3D&rese
> > > > rved=0
> > >
> > > >
> > >
> > > > Regards,
> > >
> > > > Nickle
> > >
> > > >
> > >
> > > > > -----Original Message-----
> > >
> > > > > From: Mike Maslenkin <mike.maslenkin@gmail.com<mailto:mike.maslenkin@gmail.com>>
> > >
> > > > > Sent: Tuesday, February 27, 2024 8:13 AM
> > >
> > > > > To: Nickle Wang <nicklew@nvidia.com<mailto:nicklew@nvidia.com>>
> > >
> > > > > Cc: devel@edk2.groups.io<mailto:devel@edk2.groups.io>; Igor Kulchytskyy <igork@ami.com<mailto:igork@ami.com>>; Abner
> > >
> > > > > Chang <abner.chang@amd.com<mailto:abner.chang@amd.com>>; Nick Ramirez
> <nramirez@nvidia.com<mailto:nramirez@nvidia.com>>
> > >
> > > > > Subject: Re: [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement
> > > > > Redfish
> > >
> > > > > HTTP protocol
> > >
> > > > >
> > >
> > > > > External email: Use caution opening links or attachments
> > >
> > > > >
> > >
> > > > >
> > >
> > > > > Hii Nickle,
> > >
> > > > >
> > >
> > > > >
> > >
> > > > > On Mon, Feb 26, 2024 at 4:44 PM Nickle Wang <nicklew@nvidia.com<mailto:nicklew@nvidia.com>>
> > wrote:
> > >
> > > > > >
> > >
> > > > > > Hi Mike,
> > >
> > > > > >
> > >
> > > > > > > So finally we have
> > >
> > > > > > > HttpFreeHeaderFields (Response->Headers,
> > > > > > > Response->HeaderCount);
> > >
> > > > > > > but
> > >
> > > > > > > Response->HeaderCount does not count partially allocated
> elements.
> > Right?
> > >
> > > > > > >
> > >
> > > > > > > To fix this, it is required to set *DstHeaderCount =
> > >
> > > > > > > SrcHeaderCount unconditionally right after DstHeaders
> > > > > > > allocation,
> > >
> > > > > > > and HttpFreeHeaderFields() will do the work then.
> > >
> > > > > >
> > >
> > > > > > I follow your suggestion to update DstHeaderCount right after
> > >
> > > > > > DstHeaders is
> > >
> > > > > allocated. So, HttpFreeHeaderFields can release headers
> > > > > correctly. I
> > >
> > > > > also create a macro to implemented AsciiStrCpy. Please check below
> > > > > link to see
> > >
> > > > my changes:
> > >
> > > > > >
> https://nam12.safelinks.protection.outlook.com/?url=https%3A%2F%2Fnam11.safelinks.protection.outlook.com%2F%3Furl%3Dhttps%253A%252F%2525&data=05%7C02%7Cigork%40ami.com%7Ca119b05ccbf64656ecd808dc38bda910%7C27e97857e15f486cb58e86c2b3040f93%7C1%7C0%7C638447634515186935%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=OrYee0doa%2BgFUm6g0M2AVXq2U0UbEvUzUJ20YU7ykSk%3D&reserved=0<https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%25>
> > > > > >
> >
> 2Fgi%2F&data=05%7C02%7Cnicklew%40nvidia.com%7Ca30038f7379c4f8d
> ad
> > > > > >
> >
> 3b08dc3886a03b%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7
> C6384
> > > > > >
> >
> 47398077735545%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMD
> AiLCJ
> > QI
> > > > > >
> >
> joiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C0%7C%7C%7C&sdata=d
> Zf
> > > > > >
> > %2BXZMEyp4%2BC%2BZgFnVCr12fIyXn1ZDsFfk2ejkYGO8%3D&reserved=0
> > >
> > > > > > th
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> ub.com%2Ftianocore%2Fedk2%2Fcompare%2F0f391b1c2f988d90a3ac723b
> 314a
> > >
> > > > > c28b
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> a7b0b8df..f0fa1b8fdcd933beb52fd3127c2476443c00ef8d&data=05%7C02
> %7Cni
> > > > c
> > >
> > > > > k
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> lew%40nvidia.com%7Cf3870f71360e44f3b4e208dc3728ff87%7C43083d15
> 72734
> > >
> > > > > 0c1
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> b7db39efd9ccc17a%7C0%7C0%7C638445896465360452%7CUnknown%7
> CTWFp
> > >
> > > > > bGZsb3d8
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3
> D%
> > >
> > > > > 7C0
> > >
> > > > > >
> > >
> > > > >
> > >
> > > >
> >
> %7C%7C%7C&sdata=K%2FEA2QWpk%2F8NHQ1QhzqkvQqao4db%2BILn1Jt
> %2BB
> > >
> > > > > qQ5n1E%3D
> > >
> > > > > > &reserved=0
> > >
> > > > >
> > >
> > > > > These changes looks good. Internal strings
> > >
> > > > > initialization/deinitialization code much cleaner now and possible
> > > > > leak seems to
> > >
> > > > have been fixed.
> > >
> > > > >
> > >
> > > > > Thank you!
> > >
> > > > >
> > >
> > > > > Regards,
> > >
> > > > > Mike.
> > >
> > > >
> > >
> > > >
> > >
> > > >
> > >
> > > >
> > >
> > >
-The information contained in this message may be confidential and proprietary to American Megatrends (AMI). This communication is intended to be read only by the individual or entity to whom it is addressed or by their designee. If the reader of this message is not the intended recipient, you are on notice that any distribution of this message, in any form, is strictly prohibited. Please promptly notify the sender by reply e-mail or by telephone at 770-246-8600, and then delete or destroy all copies of the transmission.
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#116133): https://edk2.groups.io/g/devel/message/116133
Mute This Topic: https://groups.io/mt/104505404/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
[-- Attachment #2: Type: text/html, Size: 38592 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2024-02-29 4:01 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-02-22 9:11 [edk2-devel] [PATCH v2 2/6] RedfishPkg: implement Redfish HTTP protocol Nickle Wang via groups.io
2024-02-22 13:39 ` Chang, Abner via groups.io
2024-02-22 14:17 ` Igor Kulchytskyy via groups.io
2024-02-23 11:29 ` Mike Maslenkin
2024-02-23 14:07 ` Nickle Wang via groups.io
2024-02-23 16:04 ` Mike Maslenkin
2024-02-26 13:43 ` Nickle Wang via groups.io
2024-02-27 0:13 ` Mike Maslenkin
2024-02-27 0:48 ` Nickle Wang via groups.io
[not found] ` <17B791D5F1D9AF42.14830@groups.io>
2024-02-28 11:47 ` Nickle Wang via groups.io
2024-02-28 17:56 ` Mike Maslenkin
2024-02-29 0:11 ` Nickle Wang via groups.io
2024-02-29 0:30 ` Chang, Abner via groups.io
2024-02-29 1:37 ` Igor Kulchytskyy via groups.io
2024-02-29 4:01 ` Chang, Abner 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